From 066796be540d67ce8cd5c3bea124e5810800c096 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Tue, 29 Jun 2021 00:58:06 +0300 Subject: [PATCH] feat(44736): add go-to-definition on overridden members (#44740) --- src/services/goToDefinition.ts | 26 ++++++++++++++++++- .../goToDefinitionOverriddenMember1.ts | 12 +++++++++ .../goToDefinitionOverriddenMember10.ts | 15 +++++++++++ .../goToDefinitionOverriddenMember11.ts | 23 ++++++++++++++++ .../goToDefinitionOverriddenMember12.ts | 12 +++++++++ .../goToDefinitionOverriddenMember13.ts | 12 +++++++++ .../goToDefinitionOverriddenMember14.ts | 13 ++++++++++ .../goToDefinitionOverriddenMember15.ts | 13 ++++++++++ .../goToDefinitionOverriddenMember2.ts | 13 ++++++++++ .../goToDefinitionOverriddenMember3.ts | 13 ++++++++++ .../goToDefinitionOverriddenMember4.ts | 15 +++++++++++ .../goToDefinitionOverriddenMember5.ts | 11 ++++++++ .../goToDefinitionOverriddenMember6.ts | 12 +++++++++ .../goToDefinitionOverriddenMember7.ts | 9 +++++++ .../goToDefinitionOverriddenMember8.ts | 16 ++++++++++++ .../goToDefinitionOverriddenMember9.ts | 15 +++++++++++ 16 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember1.ts create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember10.ts create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember11.ts create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember12.ts create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember13.ts create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember14.ts create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember15.ts create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember2.ts create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember3.ts create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember4.ts create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember5.ts create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember6.ts create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember7.ts create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember8.ts create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember9.ts diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 7c740fed7f..746e5a19a6 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); @@ -126,6 +130,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 0000000000..d645f40733 --- /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 0000000000..96a39c9469 --- /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 0000000000..48eae54dd9 --- /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 0000000000..b03ee57a22 --- /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 0000000000..c884ddab94 --- /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 0000000000..7f406d2bdd --- /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 0000000000..0f1341a2fa --- /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 0000000000..47d7ea695d --- /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 0000000000..a952f9e2ce --- /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 0000000000..1309614186 --- /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 0000000000..56fd2346b6 --- /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 0000000000..7b0456962c --- /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 0000000000..b45271e496 --- /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 0000000000..0154aca9b5 --- /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 0000000000..705b1d771a --- /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");