diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts
index 9fa856675197a..ccc045b4486dc 100644
--- a/src/services/goToDefinition.ts
+++ b/src/services/goToDefinition.ts
@@ -12,10 +12,14 @@ namespace ts.GoToDefinition {
if (node === sourceFile) {
return undefined;
}
- const { parent } = node;
+ const { parent } = node;
const typeChecker = program.getTypeChecker();
+ if (node.kind === SyntaxKind.OverrideKeyword || (isJSDocOverrideTag(node) && rangeContainsPosition(node.tagName, position))) {
+ return getDefinitionFromOverriddenMember(typeChecker, node) || emptyArray;
+ }
+
// Labels
if (isJumpStatementTarget(node)) {
const label = getTargetLabel(node.parent, node.text);
@@ -113,6 +117,26 @@ namespace ts.GoToDefinition {
}
}
+ function getDefinitionFromOverriddenMember(typeChecker: TypeChecker, node: Node) {
+ const classElement = findAncestor(node, isClassElement);
+ if (!(classElement && classElement.name)) return;
+
+ const baseDeclaration = findAncestor(classElement, isClassLike);
+ if (!baseDeclaration) return;
+
+ const baseTypeNode = getEffectiveBaseTypeNode(baseDeclaration);
+ const baseType = baseTypeNode ? typeChecker.getTypeAtLocation(baseTypeNode) : undefined;
+ if (!baseType) return;
+
+ const name = unescapeLeadingUnderscores(getTextOfPropertyName(classElement.name));
+ const symbol = hasStaticModifier(classElement)
+ ? typeChecker.getPropertyOfType(typeChecker.getTypeOfSymbolAtLocation(baseType.symbol, baseDeclaration), name)
+ : typeChecker.getPropertyOfType(baseType, name);
+ if (!symbol) return;
+
+ return getDefinitionFromSymbol(typeChecker, symbol, node);
+ }
+
export function getReferenceAtPosition(sourceFile: SourceFile, position: number, program: Program): { reference: FileReference, fileName: string, unverified: boolean, file?: SourceFile } | undefined {
const referencePath = findReferenceInPosition(sourceFile.referencedFiles, position);
if (referencePath) {
diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember1.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember1.ts
new file mode 100644
index 0000000000000..d645f40733e4a
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionOverriddenMember1.ts
@@ -0,0 +1,12 @@
+///
+
+// @noImplicitOverride: true
+
+////class Foo {
+//// /*2*/p = '';
+////}
+////class Bar extends Foo {
+//// [|/*1*/override|] p = '';
+////}
+
+verify.goToDefinition("1", "2");
diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember10.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember10.ts
new file mode 100644
index 0000000000000..96a39c9469e81
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionOverriddenMember10.ts
@@ -0,0 +1,15 @@
+///
+
+// @allowJs: true
+// @checkJs: true
+// @noEmit: true
+// @noImplicitOverride: true
+// @filename: a.js
+
+////class Foo {}
+////class Bar extends Foo {
+//// /** [|@override{|"name": "1"|} |]*/
+//// m() {}
+////}
+
+verify.goToDefinition("1", []);
diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember11.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember11.ts
new file mode 100644
index 0000000000000..48eae54dd9f78
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionOverriddenMember11.ts
@@ -0,0 +1,23 @@
+///
+
+// @allowJs: true
+// @checkJs: true
+// @noEmit: true
+// @noImplicitOverride: true
+// @filename: a.js
+
+////class Foo {
+//// /*Foo_m*/m() {}
+////}
+////class Bar extends Foo {
+//// /** [|@over{|"name": "1"|}ride[| se{|"name": "2"|}e {@li{|"name": "3"|}nk https://test.c{|"name": "4"|}om} {|"name": "5"|}description |]|]*/
+//// m() {}
+////}
+
+verify.goToDefinition({
+ 1: "Foo_m",
+ 2: [],
+ 3: [],
+ 4: [],
+ 5: []
+});
diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember12.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember12.ts
new file mode 100644
index 0000000000000..b03ee57a22c3b
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionOverriddenMember12.ts
@@ -0,0 +1,12 @@
+///
+
+// @noImplicitOverride: true
+
+////class Foo {
+//// static /*2*/p = '';
+////}
+////class Bar extends Foo {
+//// static [|/*1*/override|] p = '';
+////}
+
+verify.goToDefinition("1", "2");
diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember13.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember13.ts
new file mode 100644
index 0000000000000..c884ddab941b7
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionOverriddenMember13.ts
@@ -0,0 +1,12 @@
+///
+
+// @noImplicitOverride: true
+
+////class Foo {
+//// static /*2*/m() {}
+////}
+////class Bar extends Foo {
+//// static [|/*1*/override|] m() {}
+////}
+
+verify.goToDefinition("1", "2");
diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember14.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember14.ts
new file mode 100644
index 0000000000000..7f406d2bddec6
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionOverriddenMember14.ts
@@ -0,0 +1,13 @@
+///
+
+// @noImplicitOverride: true
+
+////class A {
+//// /*2*/m() {}
+////}
+////class B extends A {}
+////class C extends B {
+//// [|/*1*/override|] m() {}
+////}
+
+verify.goToDefinition("1", "2");
diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember15.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember15.ts
new file mode 100644
index 0000000000000..0f1341a2fa1eb
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionOverriddenMember15.ts
@@ -0,0 +1,13 @@
+///
+
+// @noImplicitOverride: true
+
+////class A {
+//// static /*2*/m() {}
+////}
+////class B extends A {}
+////class C extends B {
+//// static [|/*1*/override|] m() {}
+////}
+
+verify.goToDefinition("1", "2");
diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember2.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember2.ts
new file mode 100644
index 0000000000000..47d7ea695dacf
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionOverriddenMember2.ts
@@ -0,0 +1,13 @@
+///
+
+// @noImplicitOverride: true
+
+////class Foo {
+//// /*2*/m() {}
+////}
+////
+////class Bar extends Foo {
+//// [|/*1*/override|] m() {}
+////}
+
+verify.goToDefinition("1", "2");
diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember3.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember3.ts
new file mode 100644
index 0000000000000..a952f9e2ce80f
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionOverriddenMember3.ts
@@ -0,0 +1,13 @@
+///
+
+// @noImplicitOverride: true
+
+////abstract class Foo {
+//// abstract /*2*/m() {}
+////}
+////
+////export class Bar extends Foo {
+//// [|/*1*/override|] m() {}
+////}
+
+verify.goToDefinition("1", "2");
diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember4.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember4.ts
new file mode 100644
index 0000000000000..1309614186c66
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionOverriddenMember4.ts
@@ -0,0 +1,15 @@
+///
+
+// @noImplicitOverride: true
+
+////class Foo {
+//// /*2*/m() {}
+////}
+////function f () {
+//// return class extends Foo {
+//// [|/*1*/override|] m() {}
+//// }
+////}
+
+verify.goToDefinition("1", "2");
+
diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember5.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember5.ts
new file mode 100644
index 0000000000000..56fd2346b61e7
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionOverriddenMember5.ts
@@ -0,0 +1,11 @@
+///
+
+// @noImplicitOverride: true
+
+////class Foo extends (class {
+//// /*2*/m() {}
+////}) {
+//// [|/*1*/override|] m() {}
+////}
+
+verify.goToDefinition("1", "2");
diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember6.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember6.ts
new file mode 100644
index 0000000000000..7b0456962cb37
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionOverriddenMember6.ts
@@ -0,0 +1,12 @@
+///
+
+// @noImplicitOverride: true
+
+////class Foo {
+//// m() {}
+////}
+////class Bar extends Foo {
+//// [|/*1*/override|] m1() {}
+////}
+
+verify.goToDefinition("1", []);
diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember7.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember7.ts
new file mode 100644
index 0000000000000..b45271e4960bb
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionOverriddenMember7.ts
@@ -0,0 +1,9 @@
+///
+
+// @noImplicitOverride: true
+
+////class Foo {
+//// [|/*1*/override|] m() {}
+////}
+
+verify.goToDefinition("1", []);
diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember8.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember8.ts
new file mode 100644
index 0000000000000..0154aca9b569b
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionOverriddenMember8.ts
@@ -0,0 +1,16 @@
+///
+
+// @noImplicitOverride: true
+
+// @Filename: ./a.ts
+////export class A {
+//// /*2*/m() {}
+////}
+
+// @Filename: ./b.ts
+////import { A } from "./a";
+////class B extends A {
+//// [|/*1*/override|] m() {}
+////}
+
+verify.goToDefinition("1", "2");
diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember9.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember9.ts
new file mode 100644
index 0000000000000..705b1d771a577
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionOverriddenMember9.ts
@@ -0,0 +1,15 @@
+///
+
+// @noImplicitOverride: true
+
+////interface I {
+//// m(): void;
+////}
+////class A {
+//// /*2*/m() {};
+////}
+////class B extends A implements I {
+//// [|/*1*/override|] m() {}
+////}
+
+verify.goToDefinition("1", "2");