439 lines
14 KiB
Go
439 lines
14 KiB
Go
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
|
|
}
|