package lsutil import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/checker" "github.com/microsoft/typescript-go/internal/collections" "github.com/microsoft/typescript-go/internal/core" ) type ScriptElementKind int const ( ScriptElementKindUnknown ScriptElementKind = iota ScriptElementKindWarning // predefined type (void) or keyword (class) ScriptElementKindKeyword // top level script node ScriptElementKindScriptElement // module foo {} ScriptElementKindModuleElement // class X {} ScriptElementKindClassElement // var x = class X {} ScriptElementKindLocalClassElement // interface Y {} ScriptElementKindInterfaceElement // type T = ... ScriptElementKindTypeElement // enum E {} ScriptElementKindEnumElement ScriptElementKindEnumMemberElement // Inside module and script only. // const v = ... ScriptElementKindVariableElement // Inside function. ScriptElementKindLocalVariableElement // using foo = ... ScriptElementKindVariableUsingElement // await using foo = ... ScriptElementKindVariableAwaitUsingElement // Inside module and script only. // function f() {} ScriptElementKindFunctionElement // Inside function. ScriptElementKindLocalFunctionElement // class X { [public|private]* foo() {} } ScriptElementKindMemberFunctionElement // class X { [public|private]* [get|set] foo:number; } ScriptElementKindMemberGetAccessorElement ScriptElementKindMemberSetAccessorElement // class X { [public|private]* foo:number; } // interface Y { foo:number; } ScriptElementKindMemberVariableElement // class X { [public|private]* accessor foo: number; } ScriptElementKindMemberAccessorVariableElement // class X { constructor() { } } // class X { static { } } ScriptElementKindConstructorImplementationElement // interface Y { ():number; } ScriptElementKindCallSignatureElement // interface Y { []:number; } ScriptElementKindIndexSignatureElement // interface Y { new():Y; } ScriptElementKindConstructSignatureElement // function foo(*Y*: string) ScriptElementKindParameterElement ScriptElementKindTypeParameterElement ScriptElementKindPrimitiveType ScriptElementKindLabel ScriptElementKindAlias ScriptElementKindConstElement ScriptElementKindLetElement ScriptElementKindDirectory ScriptElementKindExternalModuleName // String literal ScriptElementKindString // Jsdoc @link: in `{@link C link text}`, the before and after text "{@link " and "}" ScriptElementKindLink // Jsdoc @link: in `{@link C link text}`, the entity name "C" ScriptElementKindLinkName // Jsdoc @link: in `{@link C link text}`, the link text "link text" ScriptElementKindLinkText ) type ScriptElementKindModifier uint32 const ( ScriptElementKindModifierNone ScriptElementKindModifier = 0 ScriptElementKindModifierPublic ScriptElementKindModifier = 1 << iota ScriptElementKindModifierPrivate ScriptElementKindModifierProtected ScriptElementKindModifierExported ScriptElementKindModifierAmbient ScriptElementKindModifierStatic ScriptElementKindModifierAbstract ScriptElementKindModifierOptional ScriptElementKindModifierDeprecated ScriptElementKindModifierDts ScriptElementKindModifierTs ScriptElementKindModifierTsx ScriptElementKindModifierJs ScriptElementKindModifierJsx ScriptElementKindModifierJson ScriptElementKindModifierDmts ScriptElementKindModifierMts ScriptElementKindModifierMjs ScriptElementKindModifierDcts ScriptElementKindModifierCts ScriptElementKindModifierCjs ) var scriptElementKindModifierNames = []struct { flag ScriptElementKindModifier name string }{ {ScriptElementKindModifierPublic, "public"}, {ScriptElementKindModifierPrivate, "private"}, {ScriptElementKindModifierProtected, "protected"}, {ScriptElementKindModifierExported, "export"}, {ScriptElementKindModifierAmbient, "declare"}, {ScriptElementKindModifierStatic, "static"}, {ScriptElementKindModifierAbstract, "abstract"}, {ScriptElementKindModifierOptional, "optional"}, {ScriptElementKindModifierDeprecated, "deprecated"}, {ScriptElementKindModifierDts, ".d.ts"}, {ScriptElementKindModifierTs, ".ts"}, {ScriptElementKindModifierTsx, ".tsx"}, {ScriptElementKindModifierJs, ".js"}, {ScriptElementKindModifierJsx, ".jsx"}, {ScriptElementKindModifierJson, ".json"}, {ScriptElementKindModifierDmts, ".d.mts"}, {ScriptElementKindModifierMts, ".mts"}, {ScriptElementKindModifierMjs, ".mjs"}, {ScriptElementKindModifierDcts, ".d.cts"}, {ScriptElementKindModifierCts, ".cts"}, {ScriptElementKindModifierCjs, ".cjs"}, } func (m ScriptElementKindModifier) Strings() collections.Set[string] { result := collections.Set[string]{} for _, entry := range scriptElementKindModifierNames { if m&entry.flag != 0 { result.Add(entry.name) } } return result } var FileExtensionKindModifiers = ScriptElementKindModifierDts | ScriptElementKindModifierTs | ScriptElementKindModifierTsx | ScriptElementKindModifierJs | ScriptElementKindModifierJsx | ScriptElementKindModifierJson | ScriptElementKindModifierDmts | ScriptElementKindModifierMts | ScriptElementKindModifierMjs | ScriptElementKindModifierDcts | ScriptElementKindModifierCts | ScriptElementKindModifierCjs func GetSymbolKind(typeChecker *checker.Checker, symbol *ast.Symbol, location *ast.Node) ScriptElementKind { result := getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location) if result != ScriptElementKindUnknown { return result } flags := symbol.CombinedLocalAndExportSymbolFlags() if flags&ast.SymbolFlagsClass != 0 { decl := ast.GetDeclarationOfKind(symbol, ast.KindClassExpression) if decl != nil { return ScriptElementKindLocalClassElement } return ScriptElementKindClassElement } if flags&ast.SymbolFlagsEnum != 0 { return ScriptElementKindEnumElement } if flags&ast.SymbolFlagsTypeAlias != 0 { return ScriptElementKindTypeElement } if flags&ast.SymbolFlagsInterface != 0 { return ScriptElementKindInterfaceElement } if flags&ast.SymbolFlagsTypeParameter != 0 { return ScriptElementKindTypeParameterElement } if flags&ast.SymbolFlagsEnumMember != 0 { return ScriptElementKindEnumMemberElement } if flags&ast.SymbolFlagsAlias != 0 { return ScriptElementKindAlias } if flags&ast.SymbolFlagsModule != 0 { return ScriptElementKindModuleElement } return ScriptElementKindUnknown } func getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker *checker.Checker, symbol *ast.Symbol, location *ast.Node) ScriptElementKind { var roots []*ast.Symbol if typeChecker != nil { roots = typeChecker.GetRootSymbols(symbol) } else { roots = []*ast.Symbol{symbol} } // If this is a method from a mapped type, leave as a method so long as it still has a call signature, as opposed to e.g. // `{ [K in keyof I]: number }`. if len(roots) == 1 && roots[0].Flags&ast.SymbolFlagsMethod != 0 && (typeChecker == nil || len(typeChecker.GetCallSignatures(typeChecker.GetNonNullableType(typeChecker.GetTypeOfSymbolAtLocation(symbol, location)))) > 0) { return ScriptElementKindMemberFunctionElement } if typeChecker != nil { if typeChecker.IsUndefinedSymbol(symbol) { return ScriptElementKindVariableElement } if typeChecker.IsArgumentsSymbol(symbol) { return ScriptElementKindLocalVariableElement } if location.Kind == ast.KindThisKeyword && ast.IsExpression(location) || ast.IsThisInTypeQuery(location) { return ScriptElementKindParameterElement } } flags := symbol.CombinedLocalAndExportSymbolFlags() if flags&ast.SymbolFlagsVariable != 0 { if isFirstDeclarationOfSymbolParameter(symbol) { return ScriptElementKindParameterElement } else if symbol.ValueDeclaration != nil && ast.IsVarConst(symbol.ValueDeclaration) { return ScriptElementKindConstElement } else if symbol.ValueDeclaration != nil && ast.IsVarUsing(symbol.ValueDeclaration) { return ScriptElementKindVariableUsingElement } else if symbol.ValueDeclaration != nil && ast.IsVarAwaitUsing(symbol.ValueDeclaration) { return ScriptElementKindVariableAwaitUsingElement } else if core.Some(symbol.Declarations, ast.IsLet) { return ScriptElementKindLetElement } if isLocalVariableOrFunction(symbol) { return ScriptElementKindLocalVariableElement } return ScriptElementKindVariableElement } if flags&ast.SymbolFlagsFunction != 0 { if isLocalVariableOrFunction(symbol) { return ScriptElementKindLocalFunctionElement } return ScriptElementKindFunctionElement } // FIXME: getter and setter use the same symbol. And it is rare to use only setter without getter, so in most cases the symbol always has getter flag. // So, even when the location is just on the declaration of setter, this function returns getter. if flags&ast.SymbolFlagsGetAccessor != 0 { return ScriptElementKindMemberGetAccessorElement } if flags&ast.SymbolFlagsSetAccessor != 0 { return ScriptElementKindMemberSetAccessorElement } if flags&ast.SymbolFlagsMethod != 0 { return ScriptElementKindMemberFunctionElement } if flags&ast.SymbolFlagsConstructor != 0 { return ScriptElementKindConstructorImplementationElement } if flags&ast.SymbolFlagsSignature != 0 { return ScriptElementKindIndexSignatureElement } if flags&ast.SymbolFlagsProperty != 0 { if typeChecker != nil && flags&ast.SymbolFlagsTransient != 0 && symbol.CheckFlags&ast.CheckFlagsSynthetic != 0 { // If union property is result of union of non method (property/accessors/variables), it is labeled as property var unionPropertyKind ScriptElementKind for _, rootSymbol := range roots { if rootSymbol.Flags&(ast.SymbolFlagsPropertyOrAccessor|ast.SymbolFlagsVariable) != 0 { unionPropertyKind = ScriptElementKindMemberVariableElement break } } if unionPropertyKind == ScriptElementKindUnknown { // If this was union of all methods, // make sure it has call signatures before we can label it as method. typeOfUnionProperty := typeChecker.GetTypeOfSymbolAtLocation(symbol, location) if len(typeChecker.GetCallSignatures(typeOfUnionProperty)) > 0 { return ScriptElementKindMemberFunctionElement } return ScriptElementKindMemberVariableElement } return unionPropertyKind } return ScriptElementKindMemberVariableElement } return ScriptElementKindUnknown } func isFirstDeclarationOfSymbolParameter(symbol *ast.Symbol) bool { var declaration *ast.Node if len(symbol.Declarations) > 0 { declaration = symbol.Declarations[0] } result := ast.FindAncestorOrQuit(declaration, func(n *ast.Node) ast.FindAncestorResult { if ast.IsParameterDeclaration(n) { return ast.FindAncestorTrue } if ast.IsBindingElement(n) || ast.IsObjectBindingPattern(n) || ast.IsArrayBindingPattern(n) { return ast.FindAncestorFalse } return ast.FindAncestorQuit }) return result != nil } func isLocalVariableOrFunction(symbol *ast.Symbol) bool { if symbol.Parent != nil { return false // This is exported symbol } for _, decl := range symbol.Declarations { // Function expressions are local if decl.Kind == ast.KindFunctionExpression { return true } if decl.Kind != ast.KindVariableDeclaration && decl.Kind != ast.KindFunctionDeclaration { continue } // If the parent is not source file or module block, it is a local variable. parent := decl.Parent for ; !ast.IsFunctionBlock(parent); parent = parent.Parent { // Reached source file or module block if parent.Kind == ast.KindSourceFile || parent.Kind == ast.KindModuleBlock { break } } if ast.IsFunctionBlock(parent) { // Parent is in function block. return true } } return false } func GetSymbolModifiers(typeChecker *checker.Checker, symbol *ast.Symbol) ScriptElementKindModifier { if symbol == nil { return ScriptElementKindModifierNone } modifiers := getNormalizedSymbolModifiers(typeChecker, symbol) if symbol.Flags&ast.SymbolFlagsAlias != 0 && typeChecker != nil { resolvedSymbol := typeChecker.GetAliasedSymbol(symbol) if resolvedSymbol != symbol { modifiers |= getNormalizedSymbolModifiers(typeChecker, resolvedSymbol) } } if symbol.Flags&ast.SymbolFlagsOptional != 0 { modifiers |= ScriptElementKindModifierOptional } return modifiers } func getNormalizedSymbolModifiers(typeChecker *checker.Checker, symbol *ast.Symbol) ScriptElementKindModifier { var modifierSet ScriptElementKindModifier if len(symbol.Declarations) > 0 { declaration := symbol.Declarations[0] declarations := symbol.Declarations[1:] // omit deprecated flag if some declarations are not deprecated var excludeFlags ast.ModifierFlags if len(declarations) > 0 && isDeprecatedDeclaration(typeChecker, declaration) && // !!! include jsdoc node flags core.Some(declarations, func(d *ast.Node) bool { return !isDeprecatedDeclaration(typeChecker, d) }) { excludeFlags = ast.ModifierFlagsDeprecated } else { excludeFlags = ast.ModifierFlagsNone } modifierSet = getNodeModifiers(typeChecker, declaration, excludeFlags) } return modifierSet } func isDeprecatedDeclaration(typeChecker *checker.Checker, declaration *ast.Node) bool { if typeChecker != nil { return typeChecker.IsDeprecatedDeclaration(declaration) } return ast.IsDeprecatedDeclaration(declaration) } func getNodeModifiers(typeChecker *checker.Checker, node *ast.Node, excludeFlags ast.ModifierFlags) ScriptElementKindModifier { var result ScriptElementKindModifier var flags ast.ModifierFlags if ast.IsDeclaration(node) { flags = ast.GetCombinedModifierFlags(node) if isDeprecatedDeclaration(typeChecker, node) { flags |= ast.ModifierFlagsDeprecated } flags &^= excludeFlags } if flags&ast.ModifierFlagsPrivate != 0 { result |= ScriptElementKindModifierPrivate } if flags&ast.ModifierFlagsProtected != 0 { result |= ScriptElementKindModifierProtected } if flags&ast.ModifierFlagsPublic != 0 { result |= ScriptElementKindModifierPublic } if flags&ast.ModifierFlagsStatic != 0 { result |= ScriptElementKindModifierStatic } if flags&ast.ModifierFlagsAbstract != 0 { result |= ScriptElementKindModifierAbstract } if flags&ast.ModifierFlagsExport != 0 { result |= ScriptElementKindModifierExported } if flags&ast.ModifierFlagsDeprecated != 0 { result |= ScriptElementKindModifierDeprecated } if flags&ast.ModifierFlagsAmbient != 0 { result |= ScriptElementKindModifierAmbient } if node.Flags&ast.NodeFlagsAmbient != 0 { result |= ScriptElementKindModifierAmbient } if node.Kind == ast.KindExportAssignment { result |= ScriptElementKindModifierExported } return result }