diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0c026fc6b6d..af156c8f5f9 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1035,6 +1035,18 @@ namespace ts { return undefined; } + export function isCallLikeExpression(node: Node): node is CallLikeExpression { + switch (node.kind) { + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + case SyntaxKind.TaggedTemplateExpression: + case SyntaxKind.Decorator: + return true; + default: + return false; + } + } + export function getInvokedExpression(node: CallLikeExpression): Expression { if (node.kind === SyntaxKind.TaggedTemplateExpression) { return (node).tag; diff --git a/src/services/services.ts b/src/services/services.ts index 29cbcfd59d5..437289e376c 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2793,32 +2793,25 @@ namespace ts { } function isCallExpressionTarget(node: Node): boolean { - return !!getCallOrNewExpressionWorker(node, SyntaxKind.CallExpression); + node = climbPastPropertyAccess(node); + return node && node.parent && node.parent.kind === SyntaxKind.CallExpression && (node.parent).expression === node; } function isNewExpressionTarget(node: Node): boolean { - return !!getCallOrNewExpressionWorker(node, SyntaxKind.NewExpression); + node = climbPastPropertyAccess(node); + return node && node.parent && node.parent.kind === SyntaxKind.NewExpression && (node.parent).expression === node; } - function getCallOrNewExpressionTargetingNode(node: Node): CallExpression | NewExpression | undefined { - return getCallOrNewExpressionWorker(node, SyntaxKind.CallExpression) || getCallOrNewExpressionWorker(node, SyntaxKind.NewExpression); - } - - function tryGetCalledDeclaration(typeChecker: TypeChecker, node: Node): SignatureDeclaration | undefined { - const callOrNewExpression = getCallOrNewExpressionTargetingNode(node); - if (callOrNewExpression) { - const signature = typeChecker.getResolvedSignature(callOrNewExpression); - return signature.declaration; - } - } - - function getCallOrNewExpressionWorker(node: Node, kind: SyntaxKind): Node | undefined { + /** Returns a CallLikeExpression where `node` is the target being invoked. */ + function getAncestorCallLikeExpression(node: Node): CallLikeExpression | undefined { const target = climbPastPropertyAccess(node); - return target && - target.parent && - target.parent.kind === kind && - (target.parent).expression === target && - target.parent; + const callLike = target.parent; + return isCallLikeExpression(callLike) && getInvokedExpression(callLike) === target && callLike; + } + + function tryGetSignatureDeclaration(typeChecker: TypeChecker, node: Node): SignatureDeclaration | undefined { + const callLike = getAncestorCallLikeExpression(node); + return callLike && typeChecker.getResolvedSignature(callLike).declaration; } function isNameOfModuleDeclaration(node: Node) { @@ -5232,7 +5225,7 @@ namespace ts { const typeChecker = program.getTypeChecker(); - const calledDeclaration = tryGetCalledDeclaration(typeChecker, node); + const calledDeclaration = tryGetSignatureDeclaration(typeChecker, node); if (calledDeclaration) { return [getDefinitionFromSignatureDeclaration(calledDeclaration)]; } diff --git a/tests/cases/fourslash/goToDeclarationDecoratorOverloads.ts b/tests/cases/fourslash/goToDeclarationDecoratorOverloads.ts new file mode 100644 index 00000000000..ce012d53aba --- /dev/null +++ b/tests/cases/fourslash/goToDeclarationDecoratorOverloads.ts @@ -0,0 +1,22 @@ +// @Target: ES6 +// @experimentaldecorators: true + +////async function f() {} +//// +/////*defDecString*/function dec(target: any, propertyKey: string): void; +/////*defDecSymbol*/function dec(target: any, propertyKey: symbol): void; +////function dec(target: any, propertyKey: string | symbol) {} +//// +////declare const s: symbol; +////class C { +//// @/*useDecString*/dec f() {} +//// @/*useDecSymbol*/dec [s]() {} +////} + +goTo.marker("useDecString"); +goTo.definition(); +verify.caretAtMarker("defDecString"); + +goTo.marker("useDecSymbol"); +goTo.definition(); +verify.caretAtMarker("defDecSymbol"); diff --git a/tests/cases/fourslash/goToDefinitionFunctionOverloads.ts b/tests/cases/fourslash/goToDefinitionFunctionOverloads.ts index fb690607f1a..4d1c09efd7a 100644 --- a/tests/cases/fourslash/goToDefinitionFunctionOverloads.ts +++ b/tests/cases/fourslash/goToDefinitionFunctionOverloads.ts @@ -1,11 +1,12 @@ /// -/////*functionOverload1*/function /*functionOverload*/functionOverload(); +/////*functionOverload1*/function /*functionOverload*/functionOverload(value: number); /////*functionOverload2*/function functionOverload(value: string); /////*functionOverloadDefinition*/function functionOverload() {} //// -/////*functionOverloadReference1*/functionOverload(); +/////*functionOverloadReference1*/functionOverload(123); /////*functionOverloadReference2*/functionOverload("123"); +/////*brokenOverload*/functionOverload({}); goTo.marker('functionOverloadReference1'); goTo.definition(); @@ -15,6 +16,10 @@ goTo.marker('functionOverloadReference2'); goTo.definition(); verify.caretAtMarker('functionOverload2'); +goTo.marker('brokenOverload'); +goTo.definition(); +verify.caretAtMarker('functionOverload1'); + goTo.marker('functionOverload'); goTo.definition(); verify.caretAtMarker('functionOverloadDefinition'); diff --git a/tests/cases/fourslash/goToDefinitionTaggedTemplateOverloads.ts b/tests/cases/fourslash/goToDefinitionTaggedTemplateOverloads.ts new file mode 100644 index 00000000000..54a2039b0a1 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionTaggedTemplateOverloads.ts @@ -0,0 +1,16 @@ +/// + +/////*defFNumber*/function f(strs: TemplateStringsArray, x: number): void; +/////*defFBool*/function f(strs: TemplateStringsArray, x: boolean): void; +////function f(strs: TemplateStringsArray, x: number | boolean) {} +//// +/////*useFNumber*/f`${0}`; +/////*useFBool*/f`${false}`; + +goTo.marker("useFNumber"); +goTo.definition(); +verify.caretAtMarker("defFNumber"); + +goTo.marker("useFBool"); +goTo.definition(); +verify.caretAtMarker("defFBool");