Skip to content

Commit 4514f8f

Browse files
author
Andy Hanson
committed
Make goto-definition go to a signature declaration if possible
1 parent b0f7f2f commit 4514f8f

File tree

6 files changed

+98
-29
lines changed

6 files changed

+98
-29
lines changed

src/harness/fourslash.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,7 +1546,7 @@ namespace FourSlash {
15461546
public goToDefinition(definitionIndex: number) {
15471547
const definitions = this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
15481548
if (!definitions || !definitions.length) {
1549-
this.raiseError("goToDefinition failed - expected to at least one definition location but got 0");
1549+
this.raiseError("goToDefinition failed - expected to find at least one definition location but got 0");
15501550
}
15511551

15521552
if (definitionIndex >= definitions.length) {
@@ -1561,7 +1561,7 @@ namespace FourSlash {
15611561
public goToTypeDefinition(definitionIndex: number) {
15621562
const definitions = this.languageService.getTypeDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
15631563
if (!definitions || !definitions.length) {
1564-
this.raiseError("goToTypeDefinition failed - expected to at least one definition location but got 0");
1564+
this.raiseError("goToTypeDefinition failed - expected to find at least one definition location but got 0");
15651565
}
15661566

15671567
if (definitionIndex >= definitions.length) {
@@ -1582,7 +1582,7 @@ namespace FourSlash {
15821582
this.raiseError(`goToDefinition - expected to 0 definition locations but got ${definitions.length}`);
15831583
}
15841584
else if (!foundDefinitions && !negative) {
1585-
this.raiseError("goToDefinition - expected to at least one definition location but got 0");
1585+
this.raiseError("goToDefinition - expected to find at least one definition location but got 0");
15861586
}
15871587
}
15881588

src/services/services.ts

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2788,18 +2788,37 @@ namespace ts {
27882788
return node && node.parent && node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).name === node;
27892789
}
27902790

2791+
function climbPastPropertyAccess(node: Node) {
2792+
return isRightSideOfPropertyAccess(node) ? node.parent : node;
2793+
}
2794+
27912795
function isCallExpressionTarget(node: Node): boolean {
2792-
if (isRightSideOfPropertyAccess(node)) {
2793-
node = node.parent;
2794-
}
2795-
return node && node.parent && node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).expression === node;
2796+
return !!getCallOrNewExpressionWorker(node, SyntaxKind.CallExpression);
27962797
}
27972798

27982799
function isNewExpressionTarget(node: Node): boolean {
2799-
if (isRightSideOfPropertyAccess(node)) {
2800-
node = node.parent;
2800+
return !!getCallOrNewExpressionWorker(node, SyntaxKind.NewExpression);
2801+
}
2802+
2803+
function getCallOrNewExpressionTargetingNode(node: Node): CallExpression | NewExpression | undefined {
2804+
return <CallExpression>getCallOrNewExpressionWorker(node, SyntaxKind.CallExpression) || <NewExpression>getCallOrNewExpressionWorker(node, SyntaxKind.NewExpression);
2805+
}
2806+
2807+
function tryGetCalledDeclaration(typeChecker: TypeChecker, node: Node): SignatureDeclaration | undefined {
2808+
const callOrNewExpression = getCallOrNewExpressionTargetingNode(node);
2809+
if (callOrNewExpression) {
2810+
const signature = typeChecker.getResolvedSignature(callOrNewExpression);
2811+
return signature.declaration;
28012812
}
2802-
return node && node.parent && node.parent.kind === SyntaxKind.NewExpression && (<CallExpression>node.parent).expression === node;
2813+
}
2814+
2815+
function getCallOrNewExpressionWorker(node: Node, kind: SyntaxKind): Node | undefined {
2816+
const target = climbPastPropertyAccess(node);
2817+
return target &&
2818+
target.parent &&
2819+
target.parent.kind === kind &&
2820+
(<CallExpression>target.parent).expression === target &&
2821+
target.parent;
28032822
}
28042823

28052824
function isNameOfModuleDeclaration(node: Node) {
@@ -5068,14 +5087,25 @@ namespace ts {
50685087
};
50695088
}
50705089

5090+
function getSymbolInfo(typeChecker: TypeChecker, symbol: Symbol, node: Node) {
5091+
return {
5092+
symbolName: typeChecker.symbolToString(symbol), // Do not get scoped name, just the name of the symbol
5093+
symbolKind: getSymbolKind(symbol, node),
5094+
containerName: symbol.parent ? typeChecker.symbolToString(symbol.parent, node) : ""
5095+
};
5096+
}
5097+
5098+
function getDefinitionFromSignatureDeclaration(decl: SignatureDeclaration): DefinitionInfo {
5099+
const typeChecker = program.getTypeChecker();
5100+
const { symbolName, symbolKind, containerName } = getSymbolInfo(typeChecker, decl.symbol, decl);
5101+
return createDefinitionInfo(decl, symbolKind, symbolName, containerName);
5102+
}
5103+
50715104
function getDefinitionFromSymbol(symbol: Symbol, node: Node): DefinitionInfo[] {
50725105
const typeChecker = program.getTypeChecker();
50735106
const result: DefinitionInfo[] = [];
50745107
const declarations = symbol.getDeclarations();
5075-
const symbolName = typeChecker.symbolToString(symbol); // Do not get scoped name, just the name of the symbol
5076-
const symbolKind = getSymbolKind(symbol, node);
5077-
const containerSymbol = symbol.parent;
5078-
const containerName = containerSymbol ? typeChecker.symbolToString(containerSymbol, node) : "";
5108+
const { symbolName, symbolKind, containerName } = getSymbolInfo(typeChecker, symbol, node);
50795109

50805110
if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) &&
50815111
!tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) {
@@ -5201,6 +5231,12 @@ namespace ts {
52015231
}
52025232

52035233
const typeChecker = program.getTypeChecker();
5234+
5235+
const calledDeclaration = tryGetCalledDeclaration(typeChecker, node);
5236+
if (calledDeclaration) {
5237+
return [getDefinitionFromSignatureDeclaration(calledDeclaration)];
5238+
}
5239+
52045240
let symbol = typeChecker.getSymbolAtLocation(node);
52055241

52065242
// Could not find a symbol e.g. node is string or number keyword,

tests/cases/fourslash/goToDefinitionConstructorOverloads.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111

1212
goTo.marker('constructorOverloadReference1');
1313
goTo.definition();
14-
verify.caretAtMarker('constructorDefinition');
14+
verify.caretAtMarker('constructorOverload1');
1515

1616
goTo.marker('constructorOverloadReference2');
1717
goTo.definition();
18-
verify.caretAtMarker('constructorDefinition');
18+
verify.caretAtMarker('constructorOverload2');
1919

2020
goTo.marker('constructorOverload1');
2121
goTo.definition();

tests/cases/fourslash/goToDefinitionFunctionOverloads.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99

1010
goTo.marker('functionOverloadReference1');
1111
goTo.definition();
12-
verify.caretAtMarker('functionOverloadDefinition');
12+
verify.caretAtMarker('functionOverload1');
1313

1414
goTo.marker('functionOverloadReference2');
1515
goTo.definition();
16-
verify.caretAtMarker('functionOverloadDefinition');
16+
verify.caretAtMarker('functionOverload2');
1717

1818
goTo.marker('functionOverload');
1919
goTo.definition();
Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/// <reference path='fourslash.ts' />
22

33
////class MethodOverload {
4-
//// static me/*staticMethodOverload1*/thod();
5-
//// static me/*staticMethodOverload2*/thod(foo: string);
6-
/////*staticMethodDefinition*/static method(foo?: any) { }
7-
//// public met/*instanceMethodOverload1*/hod(): any;
8-
//// public met/*instanceMethodOverload2*/hod(foo: string);
4+
//// /*staticMethodOverload1*/static /*staticMethodOverload1Name*/method();
5+
//// /*staticMethodOverload2*/static method(foo: string);
6+
//// /*staticMethodDefinition*/static method(foo?: any) { }
7+
//// /*instanceMethodOverload1*/public /*instanceMethodOverload1Name*/method(): any;
8+
//// /*instanceMethodOverload2*/public method(foo: string);
99
/////*instanceMethodDefinition*/public method(foo?: any) { return "foo" }
1010
////}
1111

@@ -20,25 +20,25 @@
2020

2121
goTo.marker('staticMethodReference1');
2222
goTo.definition();
23-
verify.caretAtMarker('staticMethodDefinition');
23+
verify.caretAtMarker('staticMethodOverload1');
2424

2525
goTo.marker('staticMethodReference2');
2626
goTo.definition();
27-
verify.caretAtMarker('staticMethodDefinition');
27+
verify.caretAtMarker('staticMethodOverload2');
2828

2929
goTo.marker('instanceMethodReference1');
3030
goTo.definition();
31-
verify.caretAtMarker('instanceMethodDefinition');
31+
verify.caretAtMarker('instanceMethodOverload1');
3232

3333
goTo.marker('instanceMethodReference2');
3434
goTo.definition();
35-
verify.caretAtMarker('instanceMethodDefinition');
35+
verify.caretAtMarker('instanceMethodOverload2');
3636

37-
goTo.marker('staticMethodOverload1');
37+
goTo.marker('staticMethodOverload1Name');
3838
goTo.definition();
3939
verify.caretAtMarker('staticMethodDefinition');
4040

41-
goTo.marker('instanceMethodOverload1');
41+
goTo.marker('instanceMethodOverload1Name');
4242
goTo.definition();
4343
verify.caretAtMarker('instanceMethodDefinition');
4444

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
///<reference path="fourslash.ts"/>
2+
3+
////class A {
4+
//// /*ctr*/constructor() {}
5+
//// x() {}
6+
////}
7+
/////*B*/class B extends A {}
8+
////class C extends B {
9+
//// constructor() {
10+
//// /*super*/super();
11+
//// }
12+
//// method() {
13+
//// /*superExpression*/super.x();
14+
//// }
15+
////}
16+
////class D {
17+
//// constructor() {
18+
//// /*superBroken*/super();
19+
//// }
20+
////}
21+
22+
// Super in call position goes to constructor.
23+
goTo.marker("super");
24+
goTo.definition();
25+
verify.caretAtMarker("ctr");
26+
27+
// Super in any other position goes to the superclass.
28+
goTo.marker("superExpression");
29+
goTo.definition();
30+
verify.caretAtMarker("B");
31+
32+
goTo.marker("superBroken");
33+
verify.definitionCountIs(0);

0 commit comments

Comments
 (0)