Skip to content

Commit 4dbb267

Browse files
authoredJan 2, 2024
fix(56706): "Show call hierarchy" does not work with arrow methods in a class (#56715)
1 parent b999336 commit 4dbb267

File tree

3 files changed

+85
-15
lines changed

3 files changed

+85
-15
lines changed
 

‎src/services/callHierarchy.ts

+28-15
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ import {
9191
ParameterDeclaration,
9292
Program,
9393
PropertyAccessExpression,
94+
PropertyDeclaration,
9495
SatisfiesExpression,
9596
SetAccessorDeclaration,
9697
skipTrivia,
@@ -117,18 +118,27 @@ function isNamedExpression(node: Node): node is NamedExpression {
117118
}
118119

119120
/** @internal */
120-
export type ConstNamedExpression =
121-
| ClassExpression & { name: undefined; parent: VariableDeclaration & { name: Identifier; }; }
122-
| FunctionExpression & { name: undefined; parent: VariableDeclaration & { name: Identifier; }; }
123-
| ArrowFunction & { name: undefined; parent: VariableDeclaration & { name: Identifier; }; };
121+
export type VariableLike =
122+
| VariableDeclaration
123+
| PropertyDeclaration;
124124

125-
/** Indicates whether a node is a function, arrow, or class expression assigned to a constant variable. */
126-
function isConstNamedExpression(node: Node): node is ConstNamedExpression {
125+
function isVariableLike(node: Node): node is VariableLike {
126+
return isPropertyDeclaration(node) || isVariableDeclaration(node);
127+
}
128+
129+
/** @internal */
130+
export type AssignedExpression =
131+
| ClassExpression & { name: undefined; parent: VariableLike & { name: Identifier; }; }
132+
| FunctionExpression & { name: undefined; parent: VariableLike & { name: Identifier; }; }
133+
| ArrowFunction & { name: undefined; parent: VariableLike & { name: Identifier; }; };
134+
135+
/** Indicates whether a node is a function, arrow, or class expression assigned to a constant variable or class property. */
136+
function isAssignedExpression(node: Node): node is AssignedExpression {
127137
return (isFunctionExpression(node) || isArrowFunction(node) || isClassExpression(node))
128-
&& isVariableDeclaration(node.parent)
138+
&& isVariableLike(node.parent)
129139
&& node === node.parent.initializer
130140
&& isIdentifier(node.parent.name)
131-
&& !!(getCombinedNodeFlags(node.parent) & NodeFlags.Const);
141+
&& (!!(getCombinedNodeFlags(node.parent) & NodeFlags.Const) || isPropertyDeclaration(node.parent));
132142
}
133143

134144
/** @internal */
@@ -142,7 +152,7 @@ export type CallHierarchyDeclaration =
142152
| GetAccessorDeclaration
143153
| SetAccessorDeclaration
144154
| NamedExpression
145-
| ConstNamedExpression;
155+
| AssignedExpression;
146156

147157
/**
148158
* Indicates whether a node could possibly be a call hierarchy declaration.
@@ -179,14 +189,14 @@ function isValidCallHierarchyDeclaration(node: Node): node is CallHierarchyDecla
179189
|| isGetAccessorDeclaration(node)
180190
|| isSetAccessorDeclaration(node)
181191
|| isNamedExpression(node)
182-
|| isConstNamedExpression(node);
192+
|| isAssignedExpression(node);
183193
}
184194

185195
/** Gets the node that can be used as a reference to a call hierarchy declaration. */
186196
function getCallHierarchyDeclarationReferenceNode(node: Exclude<CallHierarchyDeclaration, ClassStaticBlockDeclaration>) {
187197
if (isSourceFile(node)) return node;
188198
if (isNamedDeclaration(node)) return node.name;
189-
if (isConstNamedExpression(node)) return node.parent.name;
199+
if (isAssignedExpression(node)) return node.parent.name;
190200
return Debug.checkDefined(node.modifiers && find(node.modifiers, isDefaultModifier));
191201
}
192202

@@ -223,7 +233,7 @@ function getCallHierarchyItemName(program: Program, node: CallHierarchyDeclarati
223233
return { text: `${prefix}static {}`, pos, end };
224234
}
225235

226-
const declName = isConstNamedExpression(node) ? node.parent.name :
236+
const declName = isAssignedExpression(node) ? node.parent.name :
227237
Debug.checkDefined(getNameOfDeclaration(node), "Expected call hierarchy item to have a name");
228238

229239
let text = isIdentifier(declName) ? idText(declName) :
@@ -248,7 +258,10 @@ function getCallHierarchyItemName(program: Program, node: CallHierarchyDeclarati
248258
}
249259

250260
function getCallHierarchItemContainerName(node: CallHierarchyDeclaration): string | undefined {
251-
if (isConstNamedExpression(node)) {
261+
if (isAssignedExpression(node)) {
262+
if (isPropertyDeclaration(node.parent) && isClassLike(node.parent.parent)) {
263+
return isClassExpression(node.parent.parent) ? getAssignedName(node.parent.parent)?.getText() : node.parent.parent.name?.getText();
264+
}
252265
if (isModuleBlock(node.parent.parent.parent.parent) && isIdentifier(node.parent.parent.parent.parent.parent.name)) {
253266
return node.parent.parent.parent.parent.parent.name.getText();
254267
}
@@ -364,7 +377,7 @@ export function resolveCallHierarchyDeclaration(program: Program, location: Node
364377
const ancestor = findAncestor(location.parent, isValidCallHierarchyDeclaration);
365378
return ancestor && findImplementationOrAllInitialDeclarations(typeChecker, ancestor);
366379
}
367-
if (isVariableDeclaration(location.parent) && location.parent.initializer && isConstNamedExpression(location.parent.initializer)) {
380+
if (isVariableLike(location.parent) && location.parent.initializer && isAssignedExpression(location.parent.initializer)) {
368381
return location.parent.initializer;
369382
}
370383
return undefined;
@@ -380,7 +393,7 @@ export function resolveCallHierarchyDeclaration(program: Program, location: Node
380393
continue;
381394
}
382395
// #39453
383-
if (isVariableDeclaration(location) && location.initializer && isConstNamedExpression(location.initializer)) {
396+
if (isVariableDeclaration(location) && location.initializer && isAssignedExpression(location.initializer)) {
384397
return location.initializer;
385398
}
386399
if (!followingSymbol) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// === Call Hierarchy ===
2+
╭ name: callee
3+
├ kind: function
4+
├ containerName: C
5+
├ file: /tests/cases/fourslash/callHierarchyClassPropertyArrowFunction.ts
6+
├ span:
7+
│ ╭ /tests/cases/fourslash/callHierarchyClassPropertyArrowFunction.ts:6:14-7:6
8+
│ │ 6: callee = () => {
9+
│ │ ^^^^^^^
10+
│ │ 7: }
11+
│ │ ^^^^^
12+
│ ╰
13+
├ selectionSpan:
14+
│ ╭ /tests/cases/fourslash/callHierarchyClassPropertyArrowFunction.ts:6:5-6:11
15+
│ │ 6: callee = () => {
16+
│ │ ^^^^^^
17+
│ ╰
18+
├ incoming:
19+
│ ╭ from:
20+
│ │ ╭ name: caller
21+
│ │ ├ kind: function
22+
│ │ ├ containerName: C
23+
│ │ ├ file: /tests/cases/fourslash/callHierarchyClassPropertyArrowFunction.ts
24+
│ │ ├ span:
25+
│ │ │ ╭ /tests/cases/fourslash/callHierarchyClassPropertyArrowFunction.ts:2:14-4:6
26+
│ │ │ │ 2: caller = () => {
27+
│ │ │ │ ^^^^^^^
28+
│ │ │ │ 3: this.callee();
29+
│ │ │ │ ^^^^^^^^^^^^^^^^^^^^^^
30+
│ │ │ │ 4: }
31+
│ │ │ │ ^^^^^
32+
│ │ │ ╰
33+
│ │ ├ selectionSpan:
34+
│ │ │ ╭ /tests/cases/fourslash/callHierarchyClassPropertyArrowFunction.ts:2:5-2:11
35+
│ │ │ │ 2: caller = () => {
36+
│ │ │ │ ^^^^^^
37+
│ │ │ ╰
38+
│ │ ╰ incoming: none
39+
│ ├ fromSpans:
40+
│ │ ╭ /tests/cases/fourslash/callHierarchyClassPropertyArrowFunction.ts:3:14-3:20
41+
│ │ │ 3: this.callee();
42+
│ │ │ ^^^^^^
43+
│ ╰ ╰
44+
╰ outgoing: none
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////class C {
4+
//// caller = () => {
5+
//// this.callee();
6+
//// }
7+
////
8+
//// /**/callee = () => {
9+
//// }
10+
////}
11+
12+
goTo.marker();
13+
verify.baselineCallHierarchy();

0 commit comments

Comments
 (0)
Please sign in to comment.