diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 0da9f34ecec82..add1ad2afab04 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -589,7 +589,8 @@ namespace ts { (isFunctionExpression(declaration) || isClassExpression(declaration) ? getAssignedName(declaration) : undefined); } - function getAssignedName(node: Node): DeclarationName | undefined { + /*@internal*/ + export function getAssignedName(node: Node): DeclarationName | undefined { if (!node.parent) { return undefined; } diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index d3cdcb1655856..3d3347c3532ba 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3440,6 +3440,9 @@ namespace FourSlash { let text = ""; text += `${prefix}╭ name: ${callHierarchyItem.name}\n`; text += `${prefix}├ kind: ${callHierarchyItem.kind}\n`; + if (callHierarchyItem.containerName) { + text += `${prefix}├ containerName: ${callHierarchyItem.containerName}\n`; + } text += `${prefix}├ file: ${callHierarchyItem.file}\n`; text += `${prefix}├ span:\n`; text += this.formatCallHierarchyItemSpan(file, callHierarchyItem.span, `${prefix}│ `); diff --git a/src/server/protocol.ts b/src/server/protocol.ts index db34b0838d96b..d15040ff9bd5c 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -3069,6 +3069,7 @@ namespace ts.server.protocol { file: string; span: TextSpan; selectionSpan: TextSpan; + containerName?: string; } export interface CallHierarchyIncomingCall { diff --git a/src/services/callHierarchy.ts b/src/services/callHierarchy.ts index bbf28ac8f1101..f9cf122cd613d 100644 --- a/src/services/callHierarchy.ts +++ b/src/services/callHierarchy.ts @@ -129,6 +129,31 @@ namespace ts.CallHierarchy { return { text, pos: declName.getStart(), end: declName.getEnd() }; } + function getCallHierarchItemContainerName(node: CallHierarchyDeclaration): string | undefined { + if (isConstNamedExpression(node)) { + if (isModuleBlock(node.parent.parent.parent.parent) && isIdentifier(node.parent.parent.parent.parent.parent.name)) { + return node.parent.parent.parent.parent.parent.name.getText(); + } + return; + } + + switch (node.kind) { + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.MethodDeclaration: + if (node.parent.kind === SyntaxKind.ObjectLiteralExpression) { + return getAssignedName(node.parent)?.getText(); + } + return getNameOfDeclaration(node.parent)?.getText(); + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ModuleDeclaration: + if (isModuleBlock(node.parent) && isIdentifier(node.parent.parent.name)) { + return node.parent.parent.name.getText(); + } + } + } + /** Finds the implementation of a function-like declaration, if one exists. */ function findImplementation(typeChecker: TypeChecker, node: Extract<CallHierarchyDeclaration, FunctionLikeDeclaration>): Extract<CallHierarchyDeclaration, FunctionLikeDeclaration> | undefined; function findImplementation(typeChecker: TypeChecker, node: FunctionLikeDeclaration): FunctionLikeDeclaration | undefined; @@ -245,10 +270,11 @@ namespace ts.CallHierarchy { export function createCallHierarchyItem(program: Program, node: CallHierarchyDeclaration): CallHierarchyItem { const sourceFile = node.getSourceFile(); const name = getCallHierarchyItemName(program, node); + const containerName = getCallHierarchItemContainerName(node); const kind = getNodeKind(node); const span = createTextSpanFromBounds(skipTrivia(sourceFile.text, node.getFullStart(), /*stopAfterLineBreak*/ false, /*stopAtComments*/ true), node.getEnd()); const selectionSpan = createTextSpanFromBounds(name.pos, name.end); - return { file: sourceFile.fileName, kind, name: name.text, span, selectionSpan }; + return { file: sourceFile.fileName, kind, name: name.text, containerName, span, selectionSpan }; } function isDefined<T>(x: T): x is NonNullable<T> { @@ -484,4 +510,4 @@ namespace ts.CallHierarchy { } return group(collectCallSites(program, declaration), getCallSiteGroupKey, entries => convertCallSiteGroupToOutgoingCall(program, entries)); } -} \ No newline at end of file +} diff --git a/src/services/types.ts b/src/services/types.ts index 47a6d96ac8724..997b5290aa912 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -615,6 +615,7 @@ namespace ts { file: string; span: TextSpan; selectionSpan: TextSpan; + containerName?: string; } export interface CallHierarchyIncomingCall { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 41c97b22872c1..b777c66e3a281 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5431,6 +5431,7 @@ declare namespace ts { file: string; span: TextSpan; selectionSpan: TextSpan; + containerName?: string; } interface CallHierarchyIncomingCall { from: CallHierarchyItem; @@ -8590,6 +8591,7 @@ declare namespace ts.server.protocol { file: string; span: TextSpan; selectionSpan: TextSpan; + containerName?: string; } interface CallHierarchyIncomingCall { from: CallHierarchyItem; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 42ae2264d2071..24b2ff120026d 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5431,6 +5431,7 @@ declare namespace ts { file: string; span: TextSpan; selectionSpan: TextSpan; + containerName?: string; } interface CallHierarchyIncomingCall { from: CallHierarchyItem; diff --git a/tests/baselines/reference/callHierarchyAccessor.callHierarchy.txt b/tests/baselines/reference/callHierarchyAccessor.callHierarchy.txt index 0576190daf1f5..cab6d39285ae6 100644 --- a/tests/baselines/reference/callHierarchyAccessor.callHierarchy.txt +++ b/tests/baselines/reference/callHierarchyAccessor.callHierarchy.txt @@ -1,5 +1,6 @@ ╭ name: bar ├ kind: getter +├ containerName: C ├ file: /tests/cases/fourslash/callHierarchyAccessor.ts ├ span: │ ╭ /tests/cases/fourslash/callHierarchyAccessor.ts:6:5-8:6 diff --git a/tests/baselines/reference/callHierarchyContainerName.callHierarchy.txt b/tests/baselines/reference/callHierarchyContainerName.callHierarchy.txt new file mode 100644 index 0000000000000..7b9c24c8006f3 --- /dev/null +++ b/tests/baselines/reference/callHierarchyContainerName.callHierarchy.txt @@ -0,0 +1,165 @@ +╭ name: f +├ kind: function +├ file: /tests/cases/fourslash/callHierarchyContainerName.ts +├ span: +│ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:1:1-1:16 +│ │ 1: function f() {} +│ │ ^^^^^^^^^^^^^^^ +│ ╰ +├ selectionSpan: +│ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:1:10-1:11 +│ │ 1: function f() {} +│ │ ^ +│ ╰ +├ incoming: +│ ╭ from: +│ │ ╭ name: sameName +│ │ ├ kind: method +│ │ ├ containerName: A +│ │ ├ file: /tests/cases/fourslash/callHierarchyContainerName.ts +│ │ ├ span: +│ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:4:3-6:4 +│ │ │ │ 4: static sameName() { +│ │ │ │ ^^^^^^^^^^^^^^^^^^^ +│ │ │ │ 5: f(); +│ │ │ │ ^^^^^^^^ +│ │ │ │ 6: } +│ │ │ │ ^^^ +│ │ │ ╰ +│ │ ├ selectionSpan: +│ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:4:10-4:18 +│ │ │ │ 4: static sameName() { +│ │ │ │ ^^^^^^^^ +│ │ │ ╰ +│ │ ├ incoming: +│ │ │ ╭ from: +│ │ │ │ ╭ name: sameName +│ │ │ │ ├ kind: method +│ │ │ │ ├ containerName: B +│ │ │ │ ├ file: /tests/cases/fourslash/callHierarchyContainerName.ts +│ │ │ │ ├ span: +│ │ │ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:10:3-12:4 +│ │ │ │ │ │ 10: sameName() { +│ │ │ │ │ │ ^^^^^^^^^^^^ +│ │ │ │ │ │ 11: A.sameName(); +│ │ │ │ │ │ ^^^^^^^^^^^^^^^^^ +│ │ │ │ │ │ 12: } +│ │ │ │ │ │ ^^^ +│ │ │ │ │ ╰ +│ │ │ │ ├ selectionSpan: +│ │ │ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:10:3-10:11 +│ │ │ │ │ │ 10: sameName() { +│ │ │ │ │ │ ^^^^^^^^ +│ │ │ │ │ ╰ +│ │ │ │ ├ incoming: +│ │ │ │ │ ╭ from: +│ │ │ │ │ │ ╭ name: sameName +│ │ │ │ │ │ ├ kind: getter +│ │ │ │ │ │ ├ containerName: Obj +│ │ │ │ │ │ ├ file: /tests/cases/fourslash/callHierarchyContainerName.ts +│ │ │ │ │ │ ├ span: +│ │ │ │ │ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:16:3-18:4 +│ │ │ │ │ │ │ │ 16: get sameName() { +│ │ │ │ │ │ │ │ ^^^^^^^^^^^^^^^^ +│ │ │ │ │ │ │ │ 17: return new B().sameName; +│ │ │ │ │ │ │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +│ │ │ │ │ │ │ │ 18: } +│ │ │ │ │ │ │ │ ^^^ +│ │ │ │ │ │ │ ╰ +│ │ │ │ │ │ ├ selectionSpan: +│ │ │ │ │ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:16:7-16:15 +│ │ │ │ │ │ │ │ 16: get sameName() { +│ │ │ │ │ │ │ │ ^^^^^^^^ +│ │ │ │ │ │ │ ╰ +│ │ │ │ │ │ ├ incoming: +│ │ │ │ │ │ │ ╭ from: +│ │ │ │ │ │ │ │ ╭ name: sameName +│ │ │ │ │ │ │ │ ├ kind: function +│ │ │ │ │ │ │ │ ├ containerName: Foo +│ │ │ │ │ │ │ │ ├ file: /tests/cases/fourslash/callHierarchyContainerName.ts +│ │ │ │ │ │ │ │ ├ span: +│ │ │ │ │ │ │ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:22:3-24:4 +│ │ │ │ │ │ │ │ │ │ 22: function sameName() { +│ │ │ │ │ │ │ │ │ │ ^^^^^^^^^^^^^^^^^^^^^ +│ │ │ │ │ │ │ │ │ │ 23: return Obj.sameName; +│ │ │ │ │ │ │ │ │ │ ^^^^^^^^^^^^^^^^^^^^^^^^ +│ │ │ │ │ │ │ │ │ │ 24: } +│ │ │ │ │ │ │ │ │ │ ^^^ +│ │ │ │ │ │ │ │ │ ╰ +│ │ │ │ │ │ │ │ ├ selectionSpan: +│ │ │ │ │ │ │ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:22:12-22:20 +│ │ │ │ │ │ │ │ │ │ 22: function sameName() { +│ │ │ │ │ │ │ │ │ │ ^^^^^^^^ +│ │ │ │ │ │ │ │ │ ╰ +│ │ │ │ │ │ │ │ ├ incoming: +│ │ │ │ │ │ │ │ │ ╭ from: +│ │ │ │ │ │ │ │ │ │ ╭ name: C +│ │ │ │ │ │ │ │ │ │ ├ kind: class +│ │ │ │ │ │ │ │ │ │ ├ containerName: Foo +│ │ │ │ │ │ │ │ │ │ ├ file: /tests/cases/fourslash/callHierarchyContainerName.ts +│ │ │ │ │ │ │ │ │ │ ├ span: +│ │ │ │ │ │ │ │ │ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:26:3-30:4 +│ │ │ │ │ │ │ │ │ │ │ │ 26: export class C { +│ │ │ │ │ │ │ │ │ │ │ │ ^^^^^^^^^^^^^^^^ +│ │ │ │ │ │ │ │ │ │ │ │ 27: constructor() { +│ │ │ │ │ │ │ │ │ │ │ │ ^^^^^^^^^^^^^^^^^^^ +│ │ │ │ │ │ │ │ │ │ │ │ 28: sameName(); +│ │ │ │ │ │ │ │ │ │ │ │ ^^^^^^^^^^^^^^^^^ +│ │ │ │ │ │ │ │ │ │ │ │ 29: } +│ │ │ │ │ │ │ │ │ │ │ │ ^^^^^ +│ │ │ │ │ │ │ │ │ │ │ │ 30: } +│ │ │ │ │ │ │ │ │ │ │ │ ^^^ +│ │ │ │ │ │ │ │ │ │ │ ╰ +│ │ │ │ │ │ │ │ │ │ ├ selectionSpan: +│ │ │ │ │ │ │ │ │ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:26:16-26:17 +│ │ │ │ │ │ │ │ │ │ │ │ 26: export class C { +│ │ │ │ │ │ │ │ │ │ │ │ ^ +│ │ │ │ │ │ │ │ │ │ │ ╰ +│ │ │ │ │ │ │ │ │ │ ├ incoming: +│ │ │ │ │ │ │ │ │ │ │ ╭ from: +│ │ │ │ │ │ │ │ │ │ │ │ ╭ name: sameName +│ │ │ │ │ │ │ │ │ │ │ │ ├ kind: function +│ │ │ │ │ │ │ │ │ │ │ │ ├ containerName: Bar +│ │ │ │ │ │ │ │ │ │ │ │ ├ file: /tests/cases/fourslash/callHierarchyContainerName.ts +│ │ │ │ │ │ │ │ │ │ │ │ ├ span: +│ │ │ │ │ │ │ │ │ │ │ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:34:20-34:37 +│ │ │ │ │ │ │ │ │ │ │ │ │ │ 34: const sameName = () => new Foo.C(); +│ │ │ │ │ │ │ │ │ │ │ │ │ │ ^^^^^^^^^^^^^^^^^ +│ │ │ │ │ │ │ │ │ │ │ │ │ ╰ +│ │ │ │ │ │ │ │ │ │ │ │ ├ selectionSpan: +│ │ │ │ │ │ │ │ │ │ │ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:34:9-34:17 +│ │ │ │ │ │ │ │ │ │ │ │ │ │ 34: const sameName = () => new Foo.C(); +│ │ │ │ │ │ │ │ │ │ │ │ │ │ ^^^^^^^^ +│ │ │ │ │ │ │ │ │ │ │ │ │ ╰ +│ │ │ │ │ │ │ │ │ │ │ │ ╰ incoming: none +│ │ │ │ │ │ │ │ │ │ │ ├ fromSpans: +│ │ │ │ │ │ │ │ │ │ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:34:34-34:35 +│ │ │ │ │ │ │ │ │ │ │ │ │ 34: const sameName = () => new Foo.C(); +│ │ │ │ │ │ │ │ │ │ │ │ │ ^ +│ │ │ │ │ │ │ │ │ │ ╰ ╰ ╰ +│ │ │ │ │ │ │ │ │ ├ fromSpans: +│ │ │ │ │ │ │ │ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:28:7-28:15 +│ │ │ │ │ │ │ │ │ │ │ 28: sameName(); +│ │ │ │ │ │ │ │ │ │ │ ^^^^^^^^ +│ │ │ │ │ │ │ │ ╰ ╰ ╰ +│ │ │ │ │ │ │ ├ fromSpans: +│ │ │ │ │ │ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:23:16-23:24 +│ │ │ │ │ │ │ │ │ 23: return Obj.sameName; +│ │ │ │ │ │ │ │ │ ^^^^^^^^ +│ │ │ │ │ │ ╰ ╰ ╰ +│ │ │ │ │ ├ fromSpans: +│ │ │ │ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:17:20-17:28 +│ │ │ │ │ │ │ 17: return new B().sameName; +│ │ │ │ │ │ │ ^^^^^^^^ +│ │ │ │ ╰ ╰ ╰ +│ │ │ ├ fromSpans: +│ │ │ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:11:7-11:15 +│ │ │ │ │ 11: A.sameName(); +│ │ │ │ │ ^^^^^^^^ +│ │ ╰ ╰ ╰ +│ ├ fromSpans: +│ │ ╭ /tests/cases/fourslash/callHierarchyContainerName.ts:5:5-5:6 +│ │ │ 5: f(); +│ │ │ ^ +│ ╰ ╰ +╰ outgoing: none diff --git a/tests/cases/fourslash/callHierarchyContainerName.ts b/tests/cases/fourslash/callHierarchyContainerName.ts new file mode 100644 index 0000000000000..a01b44afe6bbf --- /dev/null +++ b/tests/cases/fourslash/callHierarchyContainerName.ts @@ -0,0 +1,40 @@ +/// <reference path="fourslash.ts" /> + +////function /**/f() {} +//// +////class A { +//// static sameName() { +//// f(); +//// } +////} +//// +////class B { +//// sameName() { +//// A.sameName(); +//// } +////} +//// +////const Obj = { +//// get sameName() { +//// return new B().sameName; +//// } +////}; +//// +////namespace Foo { +//// function sameName() { +//// return Obj.sameName; +//// } +//// +//// export class C { +//// constructor() { +//// sameName(); +//// } +//// } +////} +//// +////module Foo.Bar { +//// const sameName = () => new Foo.C(); +////} + +goTo.marker(); +verify.baselineCallHierarchy();