diff --git a/internal/ast/utilities.go b/internal/ast/utilities.go index 4f690c9490..6cfac259d8 100644 --- a/internal/ast/utilities.go +++ b/internal/ast/utilities.go @@ -2650,3 +2650,8 @@ func getPragmaArgument(pragma *Pragma, name string) string { } return "" } + +func IsRightSideOfPropertyAccess(node *Node) bool { + parent := node.Parent + return IsPropertyAccessExpression(parent) && parent.AsPropertyAccessExpression().Name() == node +} diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 8bbc51ba9e..8fe03bfc27 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -5584,7 +5584,7 @@ func (c *Checker) checkDecorators(node *ast.Node) { func (c *Checker) checkDecorator(node *ast.Node) { c.checkGrammarDecorator(node.AsDecorator()) - signature := c.getResolvedSignature(node, nil, CheckModeNormal) + signature := c.GetResolvedSignature(node, nil, CheckModeNormal) c.checkDeprecatedSignature(signature, node) returnType := c.getReturnTypeOfSignature(signature) if returnType.flags&TypeFlagsAny != 0 { @@ -7820,7 +7820,7 @@ func (c *Checker) checkImportCallExpression(node *ast.Node) *Type { */ func (c *Checker) checkCallExpression(node *ast.Node, checkMode CheckMode) *Type { c.checkGrammarTypeArguments(node, node.TypeArgumentList()) - signature := c.getResolvedSignature(node, nil /*candidatesOutArray*/, checkMode) + signature := c.GetResolvedSignature(node, nil /*candidatesOutArray*/, checkMode) if signature == c.resolvingSignature { // CheckMode.SkipGenericFunctions is enabled and this is a call to a generic function that // returns a function type. We defer checking and return silentNeverType. @@ -7864,7 +7864,7 @@ func (c *Checker) checkDeprecatedSignature(sig *Signature, node *ast.Node) { } if sig.declaration != nil && sig.declaration.Flags&ast.NodeFlagsDeprecated != 0 { suggestionNode := c.getDeprecatedSuggestionNode(node) - name := tryGetPropertyAccessOrIdentifierToString(getInvokedExpression(node)) + name := tryGetPropertyAccessOrIdentifierToString(GetInvokedExpression(node)) c.addDeprecatedSuggestionWithSignature(suggestionNode, sig.declaration, name, c.signatureToString(sig)) } } @@ -7901,7 +7901,7 @@ func (c *Checker) isSymbolOrSymbolForCall(node *ast.Node) bool { * the function will fill it up with appropriate candidate signatures * @return a signature of the call-like expression or undefined if one can't be found */ -func (c *Checker) getResolvedSignature(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature { +func (c *Checker) GetResolvedSignature(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature { links := c.signatureLinks.Get(node) // If getResolvedSignature has already been called, we will have cached the resolvedSignature. // However, it is possible that either candidatesOutArray was not passed in the first time, @@ -9521,7 +9521,7 @@ func (c *Checker) checkTaggedTemplateExpression(node *ast.Node) *Type { if !c.checkGrammarTaggedTemplateChain(node.AsTaggedTemplateExpression()) { c.checkGrammarTypeArguments(node, node.TypeArgumentList()) } - signature := c.getResolvedSignature(node, nil, CheckModeNormal) + signature := c.GetResolvedSignature(node, nil, CheckModeNormal) c.checkDeprecatedSignature(signature, node) return c.getReturnTypeOfSignature(signature) } @@ -10961,7 +10961,7 @@ func (c *Checker) isUncalledFunctionReference(node *ast.Node, symbol *ast.Symbol if parent == nil { parent = node.Parent } - if isCallLikeExpression(parent) { + if IsCallLikeExpression(parent) { return isCallOrNewExpression(parent) && ast.IsIdentifier(node) && c.hasMatchingArgument(parent, node) } return core.Every(symbol.Declarations, func(d *ast.Node) bool { @@ -12261,7 +12261,7 @@ func (c *Checker) checkInstanceOfExpression(left *ast.Expression, right *ast.Exp if !IsTypeAny(leftType) && c.allTypesAssignableToKind(leftType, TypeFlagsPrimitive) { c.error(left, diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter) } - signature := c.getResolvedSignature(left.Parent, nil /*candidatesOutArray*/, checkMode) + signature := c.GetResolvedSignature(left.Parent, nil /*candidatesOutArray*/, checkMode) if signature == c.resolvingSignature { // CheckMode.SkipGenericFunctions is enabled and this is a call to a generic function that // returns a function type. We defer checking and return silentNeverType. @@ -27529,7 +27529,7 @@ func (c *Checker) getContextualTypeForArgumentAtIndex(callTarget *ast.Node, argI if c.signatureLinks.Get(callTarget).resolvedSignature == c.resolvingSignature { signature = c.resolvingSignature } else { - signature = c.getResolvedSignature(callTarget, nil, CheckModeNormal) + signature = c.GetResolvedSignature(callTarget, nil, CheckModeNormal) } if isJsxOpeningLikeElement(callTarget) && argIndex == 0 { return c.getEffectiveFirstArgumentForJsxSignature(signature, callTarget) diff --git a/internal/checker/flow.go b/internal/checker/flow.go index c9f0a0b912..8aa5df236e 100644 --- a/internal/checker/flow.go +++ b/internal/checker/flow.go @@ -2040,7 +2040,7 @@ func (c *Checker) getEffectsSignature(node *ast.Node) *Signature { case len(signatures) == 1 && signatures[0].typeParameters == nil: signature = signatures[0] case core.Some(signatures, c.hasTypePredicateOrNeverReturnType): - signature = c.getResolvedSignature(node, nil, CheckModeNormal) + signature = c.GetResolvedSignature(node, nil, CheckModeNormal) } if !(signature != nil && c.hasTypePredicateOrNeverReturnType(signature)) { signature = c.unknownSignature diff --git a/internal/checker/jsx.go b/internal/checker/jsx.go index eacb7b71fc..2dd8977a51 100644 --- a/internal/checker/jsx.go +++ b/internal/checker/jsx.go @@ -125,7 +125,7 @@ func (c *Checker) checkJsxOpeningLikeElementOrOpeningFragment(node *ast.Node) { c.checkJsxPreconditions(node) c.markJsxAliasReferenced(node) if isNodeOpeningLikeElement { - sig := c.getResolvedSignature(node, nil, CheckModeNormal) + sig := c.GetResolvedSignature(node, nil, CheckModeNormal) c.checkDeprecatedSignature(sig, node) elementTypeConstraint := c.getJsxElementTypeTypeAt(node) if elementTypeConstraint != nil { diff --git a/internal/checker/utilities.go b/internal/checker/utilities.go index e2231126f5..854c6306f6 100644 --- a/internal/checker/utilities.go +++ b/internal/checker/utilities.go @@ -1294,7 +1294,7 @@ func isThisTypeParameter(t *Type) bool { return t.flags&TypeFlagsTypeParameter != 0 && t.AsTypeParameter().isThisType } -func isCallLikeExpression(node *ast.Node) bool { +func IsCallLikeExpression(node *ast.Node) bool { switch node.Kind { case ast.KindJsxOpeningElement, ast.KindJsxSelfClosingElement, ast.KindCallExpression, ast.KindNewExpression, ast.KindTaggedTemplateExpression, ast.KindDecorator: @@ -1829,6 +1829,10 @@ func getNonRestParameterCount(sig *Signature) int { return len(sig.parameters) - core.IfElse(signatureHasRestParameter(sig), 1, 0) } +func GetDeclaration(sig *Signature) *ast.Node { + return sig.declaration +} + func minAndMax[T any](slice []T, getValue func(value T) int) (int, int) { var minValue, maxValue int for i, element := range slice { @@ -2113,7 +2117,7 @@ func tryGetPropertyAccessOrIdentifierToString(expr *ast.Node) string { return "" } -func getInvokedExpression(node *ast.Node) *ast.Node { +func GetInvokedExpression(node *ast.Node) *ast.Node { switch node.Kind { case ast.KindTaggedTemplateExpression: return node.AsTaggedTemplateExpression().Tag diff --git a/internal/ls/definition.go b/internal/ls/definition.go index f4ed52124f..d2c02e0a08 100644 --- a/internal/ls/definition.go +++ b/internal/ls/definition.go @@ -3,6 +3,7 @@ package ls import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/astnav" + "github.com/microsoft/typescript-go/internal/checker" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/scanner" ) @@ -15,6 +16,14 @@ func (l *LanguageService) ProvideDefinitions(fileName string, position int) []Lo } checker := program.GetTypeChecker() + calledDeclaration := tryGetSignatureDeclaration(checker, node) + if calledDeclaration != nil { + name := ast.GetNameOfDeclaration(calledDeclaration) + if name != nil { + return createLocationsFromDeclarations([]*ast.Node{name}) + } + } + if symbol := checker.GetSymbolAtLocation(node); symbol != nil { if symbol.Flags&ast.SymbolFlagsAlias != 0 { if resolved, ok := checker.ResolveAlias(symbol); ok { @@ -22,18 +31,55 @@ func (l *LanguageService) ProvideDefinitions(fileName string, position int) []Lo } } - locations := make([]Location, 0, len(symbol.Declarations)) - for _, decl := range symbol.Declarations { - file := ast.GetSourceFileOfNode(decl) - loc := decl.Loc - pos := scanner.GetTokenPosOfNode(decl, file, false /*includeJSDoc*/) + return createLocationsFromDeclarations(symbol.Declarations) + } + return nil +} + +func createLocationsFromDeclarations(declarations []*ast.Node) []Location { + locations := make([]Location, 0, len(declarations)) + for _, decl := range declarations { + file := ast.GetSourceFileOfNode(decl) + loc := decl.Loc + pos := scanner.GetTokenPosOfNode(decl, file, false /*includeJSDoc*/) + + locations = append(locations, Location{ + FileName: file.FileName(), + Range: core.NewTextRange(pos, loc.End()), + }) + } + return locations +} - locations = append(locations, Location{ - FileName: file.FileName(), - Range: core.NewTextRange(pos, loc.End()), - }) +/** Returns a CallLikeExpression where `node` is the target being invoked. */ +func getAncestorCallLikeExpression(node *ast.Node) *ast.Node { + target := ast.FindAncestor(node, func(n *ast.Node) bool { + return !ast.IsRightSideOfPropertyAccess(n) + }) + + callLike := target.Parent + if callLike != nil && checker.IsCallLikeExpression(callLike) && checker.GetInvokedExpression(callLike) == target { + return callLike + } + + return nil +} + +func tryGetSignatureDeclaration(typeChecker *checker.Checker, node *ast.Node) *ast.Node { + var signature *checker.Signature + callLike := getAncestorCallLikeExpression(node) + if callLike != nil { + signature = typeChecker.GetResolvedSignature(callLike, nil, checker.CheckModeNormal) + } + + // Don't go to a function type, go to the value having that type. + var declaration *ast.Node + if signature != nil && checker.GetDeclaration(signature) != nil { + declaration = checker.GetDeclaration(signature) + if ast.IsFunctionLike(declaration) && !ast.IsFunctionTypeNode(declaration) { + return declaration } - return locations } + return nil }