diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index daafc490573..4b39a90c880 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -100,10 +100,13 @@ namespace ts.GoToDefinition { /** * True if we should not add definitions for both the signature symbol and the definition symbol. * True for `const |f = |() => 0`, false for `function |f() {} const |g = f;`. + * Also true for any assignment RHS. */ function symbolMatchesSignature(s: Symbol, calledDeclaration: SignatureDeclaration) { - return s === calledDeclaration.symbol || s === calledDeclaration.symbol.parent || - !isCallLikeExpression(calledDeclaration.parent) && s === calledDeclaration.parent.symbol; + return s === calledDeclaration.symbol + || s === calledDeclaration.symbol.parent + || isAssignmentExpression(calledDeclaration.parent) + || (!isCallLikeExpression(calledDeclaration.parent) && s === calledDeclaration.parent.symbol); } export function getReferenceAtPosition(sourceFile: SourceFile, position: number, program: Program): { fileName: string, file: SourceFile } | undefined { @@ -246,7 +249,9 @@ namespace ts.GoToDefinition { // There are cases when you extend a function by adding properties to it afterwards, // we want to strip those extra properties. // For deduping purposes, we also want to exclude any declarationNodes if provided. - const filteredDeclarations = filter(symbol.declarations, d => d !== declarationNode && (!isAssignmentDeclaration(d) || d === symbol.valueDeclaration)) || undefined; + const filteredDeclarations = + filter(symbol.declarations, d => d !== declarationNode && (!isAssignmentDeclaration(d) || d === symbol.valueDeclaration)) + || undefined; return getConstructSignatureDefinition() || getCallSignatureDefinition() || map(filteredDeclarations, declaration => createDefinitionInfo(declaration, typeChecker, symbol, node)); function getConstructSignatureDefinition(): DefinitionInfo[] | undefined { @@ -330,15 +335,11 @@ namespace ts.GoToDefinition { /** Returns a CallLikeExpression where `node` is the target being invoked. */ function getAncestorCallLikeExpression(node: Node): CallLikeExpression | undefined { - const target = climbPastManyPropertyAccesses(node); - const callLike = target.parent; + const target = findAncestor(node, n => !isRightSideOfPropertyAccess(n)); + const callLike = target?.parent; return callLike && isCallLikeExpression(callLike) && getInvokedExpression(callLike) === target ? callLike : undefined; } - function climbPastManyPropertyAccesses(node: Node): Node { - return isRightSideOfPropertyAccess(node) ? climbPastManyPropertyAccesses(node.parent) : node; - } - function tryGetSignatureDeclaration(typeChecker: TypeChecker, node: Node): SignatureDeclaration | undefined { const callLike = getAncestorCallLikeExpression(node); const signature = callLike && typeChecker.getResolvedSignature(callLike); diff --git a/tests/cases/fourslash/goToDefinitionJsModuleExports.ts b/tests/cases/fourslash/goToDefinitionJsModuleExports.ts new file mode 100644 index 00000000000..43d81085a47 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionJsModuleExports.ts @@ -0,0 +1,10 @@ +/// + +// #33520 + +// @allowJs: true +// @Filename: foo.js +////x.test = /*def*/() => { } +////x.[|/*ref*/test|](); + +verify.goToDefinition("ref", "def"); diff --git a/tests/cases/fourslash/goToDefinitionVariableAssignment.ts b/tests/cases/fourslash/goToDefinitionVariableAssignment.ts index 09d5f1126f1..3b0c14655e7 100644 --- a/tests/cases/fourslash/goToDefinitionVariableAssignment.ts +++ b/tests/cases/fourslash/goToDefinitionVariableAssignment.ts @@ -4,9 +4,9 @@ // @checkJs: true // @filename: foo.js ////const Bar; -////const /*def1*/Foo = /*def2*/Bar = function () {} +////const Foo = /*def*/Bar = function () {} ////Foo.prototype.bar = function() {} ////new [|Foo/*ref*/|](); goTo.file("foo.js"); -verify.goToDefinition("ref", ["def1", "def2"]); +verify.goToDefinition("ref", "def"); diff --git a/tests/cases/fourslash/goToDefinitionVariableAssignment1.ts b/tests/cases/fourslash/goToDefinitionVariableAssignment1.ts index 45e666cafa6..cce4afa072a 100644 --- a/tests/cases/fourslash/goToDefinitionVariableAssignment1.ts +++ b/tests/cases/fourslash/goToDefinitionVariableAssignment1.ts @@ -3,9 +3,9 @@ // @allowJs: true // @checkJs: true // @filename: foo.js -////const /*def1*/Foo = module./*def2*/exports = function () {} +////const Foo = module./*def*/exports = function () {} ////Foo.prototype.bar = function() {} ////new [|Foo/*ref*/|](); goTo.file("foo.js"); -verify.goToDefinition("ref", ["def1", "def2"]); +verify.goToDefinition("ref", "def"); diff --git a/tests/cases/fourslash/goToDefinitionVariableAssignment2.ts b/tests/cases/fourslash/goToDefinitionVariableAssignment2.ts index d5474cda93c..a23c121a340 100644 --- a/tests/cases/fourslash/goToDefinitionVariableAssignment2.ts +++ b/tests/cases/fourslash/goToDefinitionVariableAssignment2.ts @@ -2,9 +2,9 @@ // @filename: foo.ts ////const Bar; -////const /*def1*/Foo = /*def2*/Bar = function () {} +////const Foo = /*def*/Bar = function () {} ////Foo.prototype.bar = function() {} ////new [|Foo/*ref*/|](); goTo.file("foo.ts"); -verify.goToDefinition("ref", ["def1", "def2"]); +verify.goToDefinition("ref", "def"); diff --git a/tests/cases/fourslash/goToDefinitionVariableAssignment3.ts b/tests/cases/fourslash/goToDefinitionVariableAssignment3.ts index 85d6c43e729..e1ac6b9c8ee 100644 --- a/tests/cases/fourslash/goToDefinitionVariableAssignment3.ts +++ b/tests/cases/fourslash/goToDefinitionVariableAssignment3.ts @@ -1,9 +1,9 @@ /// // @filename: foo.ts -////const /*def1*/Foo = module./*def2*/exports = function () {} +////const Foo = module./*def*/exports = function () {} ////Foo.prototype.bar = function() {} ////new [|Foo/*ref*/|](); goTo.file("foo.ts"); -verify.goToDefinition("ref", ["def1", "def2"]); +verify.goToDefinition("ref", "def");