Skip to content

Commit 066796b

Browse files
authored
feat(44736): add go-to-definition on overridden members (microsoft#44740)
1 parent 54b913c commit 066796b

16 files changed

+229
-1
lines changed

src/services/goToDefinition.ts

+25-1
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@ namespace ts.GoToDefinition {
1212
if (node === sourceFile) {
1313
return undefined;
1414
}
15-
const { parent } = node;
1615

16+
const { parent } = node;
1717
const typeChecker = program.getTypeChecker();
1818

19+
if (node.kind === SyntaxKind.OverrideKeyword || (isJSDocOverrideTag(node) && rangeContainsPosition(node.tagName, position))) {
20+
return getDefinitionFromOverriddenMember(typeChecker, node) || emptyArray;
21+
}
22+
1923
// Labels
2024
if (isJumpStatementTarget(node)) {
2125
const label = getTargetLabel(node.parent, node.text);
@@ -126,6 +130,26 @@ namespace ts.GoToDefinition {
126130
}
127131
}
128132

133+
function getDefinitionFromOverriddenMember(typeChecker: TypeChecker, node: Node) {
134+
const classElement = findAncestor(node, isClassElement);
135+
if (!(classElement && classElement.name)) return;
136+
137+
const baseDeclaration = findAncestor(classElement, isClassLike);
138+
if (!baseDeclaration) return;
139+
140+
const baseTypeNode = getEffectiveBaseTypeNode(baseDeclaration);
141+
const baseType = baseTypeNode ? typeChecker.getTypeAtLocation(baseTypeNode) : undefined;
142+
if (!baseType) return;
143+
144+
const name = unescapeLeadingUnderscores(getTextOfPropertyName(classElement.name));
145+
const symbol = hasStaticModifier(classElement)
146+
? typeChecker.getPropertyOfType(typeChecker.getTypeOfSymbolAtLocation(baseType.symbol, baseDeclaration), name)
147+
: typeChecker.getPropertyOfType(baseType, name);
148+
if (!symbol) return;
149+
150+
return getDefinitionFromSymbol(typeChecker, symbol, node);
151+
}
152+
129153
export function getReferenceAtPosition(sourceFile: SourceFile, position: number, program: Program): { reference: FileReference, fileName: string, unverified: boolean, file?: SourceFile } | undefined {
130154
const referencePath = findReferenceInPosition(sourceFile.referencedFiles, position);
131155
if (referencePath) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/// <reference path="./fourslash.ts"/>
2+
3+
// @noImplicitOverride: true
4+
5+
////class Foo {
6+
//// /*2*/p = '';
7+
////}
8+
////class Bar extends Foo {
9+
//// [|/*1*/override|] p = '';
10+
////}
11+
12+
verify.goToDefinition("1", "2");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// <reference path="./fourslash.ts"/>
2+
3+
// @allowJs: true
4+
// @checkJs: true
5+
// @noEmit: true
6+
// @noImplicitOverride: true
7+
// @filename: a.js
8+
9+
////class Foo {}
10+
////class Bar extends Foo {
11+
//// /** [|@override{|"name": "1"|} |]*/
12+
//// m() {}
13+
////}
14+
15+
verify.goToDefinition("1", []);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/// <reference path="./fourslash.ts"/>
2+
3+
// @allowJs: true
4+
// @checkJs: true
5+
// @noEmit: true
6+
// @noImplicitOverride: true
7+
// @filename: a.js
8+
9+
////class Foo {
10+
//// /*Foo_m*/m() {}
11+
////}
12+
////class Bar extends Foo {
13+
//// /** [|@over{|"name": "1"|}ride[| se{|"name": "2"|}e {@li{|"name": "3"|}nk https://test.c{|"name": "4"|}om} {|"name": "5"|}description |]|]*/
14+
//// m() {}
15+
////}
16+
17+
verify.goToDefinition({
18+
1: "Foo_m",
19+
2: [],
20+
3: [],
21+
4: [],
22+
5: []
23+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/// <reference path="./fourslash.ts"/>
2+
3+
// @noImplicitOverride: true
4+
5+
////class Foo {
6+
//// static /*2*/p = '';
7+
////}
8+
////class Bar extends Foo {
9+
//// static [|/*1*/override|] p = '';
10+
////}
11+
12+
verify.goToDefinition("1", "2");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/// <reference path="./fourslash.ts"/>
2+
3+
// @noImplicitOverride: true
4+
5+
////class Foo {
6+
//// static /*2*/m() {}
7+
////}
8+
////class Bar extends Foo {
9+
//// static [|/*1*/override|] m() {}
10+
////}
11+
12+
verify.goToDefinition("1", "2");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="./fourslash.ts"/>
2+
3+
// @noImplicitOverride: true
4+
5+
////class A {
6+
//// /*2*/m() {}
7+
////}
8+
////class B extends A {}
9+
////class C extends B {
10+
//// [|/*1*/override|] m() {}
11+
////}
12+
13+
verify.goToDefinition("1", "2");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="./fourslash.ts"/>
2+
3+
// @noImplicitOverride: true
4+
5+
////class A {
6+
//// static /*2*/m() {}
7+
////}
8+
////class B extends A {}
9+
////class C extends B {
10+
//// static [|/*1*/override|] m() {}
11+
////}
12+
13+
verify.goToDefinition("1", "2");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="./fourslash.ts"/>
2+
3+
// @noImplicitOverride: true
4+
5+
////class Foo {
6+
//// /*2*/m() {}
7+
////}
8+
////
9+
////class Bar extends Foo {
10+
//// [|/*1*/override|] m() {}
11+
////}
12+
13+
verify.goToDefinition("1", "2");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="./fourslash.ts"/>
2+
3+
// @noImplicitOverride: true
4+
5+
////abstract class Foo {
6+
//// abstract /*2*/m() {}
7+
////}
8+
////
9+
////export class Bar extends Foo {
10+
//// [|/*1*/override|] m() {}
11+
////}
12+
13+
verify.goToDefinition("1", "2");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// <reference path="./fourslash.ts"/>
2+
3+
// @noImplicitOverride: true
4+
5+
////class Foo {
6+
//// /*2*/m() {}
7+
////}
8+
////function f () {
9+
//// return class extends Foo {
10+
//// [|/*1*/override|] m() {}
11+
//// }
12+
////}
13+
14+
verify.goToDefinition("1", "2");
15+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// <reference path="./fourslash.ts"/>
2+
3+
// @noImplicitOverride: true
4+
5+
////class Foo extends (class {
6+
//// /*2*/m() {}
7+
////}) {
8+
//// [|/*1*/override|] m() {}
9+
////}
10+
11+
verify.goToDefinition("1", "2");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/// <reference path="./fourslash.ts"/>
2+
3+
// @noImplicitOverride: true
4+
5+
////class Foo {
6+
//// m() {}
7+
////}
8+
////class Bar extends Foo {
9+
//// [|/*1*/override|] m1() {}
10+
////}
11+
12+
verify.goToDefinition("1", []);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/// <reference path="./fourslash.ts"/>
2+
3+
// @noImplicitOverride: true
4+
5+
////class Foo {
6+
//// [|/*1*/override|] m() {}
7+
////}
8+
9+
verify.goToDefinition("1", []);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/// <reference path="./fourslash.ts"/>
2+
3+
// @noImplicitOverride: true
4+
5+
// @Filename: ./a.ts
6+
////export class A {
7+
//// /*2*/m() {}
8+
////}
9+
10+
// @Filename: ./b.ts
11+
////import { A } from "./a";
12+
////class B extends A {
13+
//// [|/*1*/override|] m() {}
14+
////}
15+
16+
verify.goToDefinition("1", "2");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// <reference path="./fourslash.ts"/>
2+
3+
// @noImplicitOverride: true
4+
5+
////interface I {
6+
//// m(): void;
7+
////}
8+
////class A {
9+
//// /*2*/m() {};
10+
////}
11+
////class B extends A implements I {
12+
//// [|/*1*/override|] m() {}
13+
////}
14+
15+
verify.goToDefinition("1", "2");

0 commit comments

Comments
 (0)