Skip to content

Commit 8259006

Browse files
authored
Fix crashes related to call hierarchy container names for computed properties (#3786)
1 parent 97c3894 commit 8259006

7 files changed

Lines changed: 413 additions & 33 deletions
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package fourslash_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/microsoft/typescript-go/internal/fourslash"
7+
"github.com/microsoft/typescript-go/internal/testutil"
8+
)
9+
10+
func TestCallHierarchyIncomingCallsObjectLiteralMethodInExpressionComputedProperty(t *testing.T) {
11+
t.Parallel()
12+
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
13+
const content = `const obj = {
14+
[1 + 2]: {
15+
method() {
16+
return ""./*split*/split(",");
17+
}
18+
}
19+
};
20+
`
21+
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
22+
defer done()
23+
f.GoToMarker(t, "split")
24+
f.VerifyBaselineCallHierarchy(t)
25+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package fourslash_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/microsoft/typescript-go/internal/fourslash"
7+
"github.com/microsoft/typescript-go/internal/testutil"
8+
)
9+
10+
func TestCallHierarchyIncomingCallsObjectLiteralMethodInIdentifierComputedProperty(t *testing.T) {
11+
t.Parallel()
12+
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
13+
const content = `const key = "x";
14+
const obj = {
15+
[key]: {
16+
method() {
17+
return ""./*split*/split(",");
18+
}
19+
}
20+
};
21+
`
22+
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
23+
defer done()
24+
f.GoToMarker(t, "split")
25+
f.VerifyBaselineCallHierarchy(t)
26+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package fourslash_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/microsoft/typescript-go/internal/fourslash"
7+
"github.com/microsoft/typescript-go/internal/testutil"
8+
)
9+
10+
func TestCallHierarchyIncomingCallsObjectLiteralMethodInStringLiteralComputedProperty(t *testing.T) {
11+
t.Parallel()
12+
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
13+
const content = `const obj = {
14+
["x"]: {
15+
method() {
16+
return ""./*split*/split(",");
17+
}
18+
}
19+
};
20+
`
21+
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
22+
defer done()
23+
f.GoToMarker(t, "split")
24+
f.VerifyBaselineCallHierarchy(t)
25+
}

internal/ls/callhierarchy.go

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -208,53 +208,54 @@ func getCallHierarchyItemName(program *compiler.Program, node *ast.Node) (text s
208208
debug.Assert(declName != nil, "Expected call hierarchy item to have a name")
209209
}
210210

211-
if ast.IsIdentifier(declName) {
212-
text = declName.Text()
213-
} else if ast.IsStringOrNumericLiteralLike(declName) {
214-
text = declName.Text()
215-
} else if ast.IsComputedPropertyName(declName) {
216-
expr := declName.Expression()
211+
text = getTextOfCallHierarchyName(program, node, declName, node)
212+
213+
sourceFile := ast.GetSourceFileOfNode(node)
214+
namePos := scanner.SkipTrivia(sourceFile.Text(), declName.Pos())
215+
216+
return text, namePos, declName.End()
217+
}
218+
219+
func getTextOfCallHierarchyName(program *compiler.Program, sourceNode *ast.Node, name *ast.Node, printNode *ast.Node) string {
220+
if ast.IsIdentifier(name) || ast.IsStringOrNumericLiteralLike(name) {
221+
return name.Text()
222+
}
223+
if ast.IsComputedPropertyName(name) {
224+
expr := name.Expression()
217225
if ast.IsStringOrNumericLiteralLike(expr) {
218-
text = expr.Text()
226+
return expr.Text()
219227
}
220228
}
221229

222-
if text == "" {
223-
c, done := program.GetTypeCheckerForFile(context.Background(), ast.GetSourceFileOfNode(node))
224-
defer done()
225-
symbol := c.GetSymbolAtLocation(declName)
226-
if symbol != nil {
227-
text = c.SymbolToString(symbol)
230+
c, done := program.GetTypeCheckerForFile(context.Background(), ast.GetSourceFileOfNode(sourceNode))
231+
defer done()
232+
symbol := c.GetSymbolAtLocation(name)
233+
if symbol != nil {
234+
text := c.SymbolToString(symbol)
235+
if text != "" {
236+
return text
228237
}
229238
}
230239

231-
// get the text from printing the node on a single line without comments...
232-
if text == "" {
233-
sourceFile := ast.GetSourceFileOfNode(node)
234-
writer, putWriter := printer.GetSingleLineStringWriter()
235-
defer putWriter()
236-
p := printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, nil)
237-
p.Write(node, sourceFile, writer, nil)
238-
text = writer.String()
239-
}
240-
241-
sourceFile := ast.GetSourceFileOfNode(node)
242-
namePos := scanner.SkipTrivia(sourceFile.Text(), declName.Pos())
243-
244-
return text, namePos, declName.End()
240+
sourceFile := ast.GetSourceFileOfNode(sourceNode)
241+
writer, putWriter := printer.GetSingleLineStringWriter()
242+
defer putWriter()
243+
p := printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, nil)
244+
p.Write(printNode, sourceFile, writer, nil)
245+
return writer.String()
245246
}
246247

247-
func getCallHierarchyItemContainerName(node *ast.Node) string {
248+
func getCallHierarchyItemContainerName(program *compiler.Program, node *ast.Node) string {
248249
if isAssignedExpression(node) {
249250
parent := node.Parent
250251
if ast.IsPropertyDeclaration(parent) && ast.IsClassLike(parent.Parent) {
251252
if ast.IsClassExpression(parent.Parent) {
252253
if assignedName := ast.GetAssignedName(parent.Parent); assignedName != nil {
253-
return assignedName.Text()
254+
return getTextOfCallHierarchyName(program, node, assignedName, assignedName)
254255
}
255256
} else {
256257
if name := parent.Parent.Name(); name != nil {
257-
return name.Text()
258+
return getTextOfCallHierarchyName(program, node, name, name)
258259
}
259260
}
260261
}
@@ -273,11 +274,11 @@ func getCallHierarchyItemContainerName(node *ast.Node) string {
273274
case ast.KindGetAccessor, ast.KindSetAccessor, ast.KindMethodDeclaration:
274275
if node.Parent.Kind == ast.KindObjectLiteralExpression {
275276
if assignedName := ast.GetAssignedName(node.Parent); assignedName != nil {
276-
return assignedName.Text()
277+
return getTextOfCallHierarchyName(program, node, assignedName, assignedName)
277278
}
278279
}
279280
if name := ast.GetNameOfDeclaration(node.Parent); name != nil {
280-
return name.Text()
281+
return getTextOfCallHierarchyName(program, node, name, name)
281282
}
282283
case ast.KindFunctionDeclaration, ast.KindClassDeclaration, ast.KindModuleDeclaration:
283284
if ast.IsModuleBlock(node.Parent) {
@@ -498,7 +499,7 @@ func resolveCallHierarchyDeclaration(program *compiler.Program, location *ast.No
498499
func (l *LanguageService) createCallHierarchyItem(program *compiler.Program, node *ast.Node) *lsproto.CallHierarchyItem {
499500
sourceFile := ast.GetSourceFileOfNode(node)
500501
nameText, namePos, nameEnd := getCallHierarchyItemName(program, node)
501-
containerName := getCallHierarchyItemContainerName(node)
502+
containerName := getCallHierarchyItemContainerName(program, node)
502503

503504
kind := getSymbolKindFromNode(node)
504505

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// === Call Hierarchy ===
2+
╭ name: split
3+
├ kind: method
4+
├ file: bundled:///libs/lib.es2015.symbol.wellknown.d.ts
5+
├ span:
6+
│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:261:5-266:110
7+
│ │ 261: /**
8+
│ │ ^^^
9+
│ │ 262: * Split a string into substrings using the specified separator and return them as an array.
10+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11+
│ │ 263: * @param splitter An object that can split a string.
12+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13+
│ │ 264: * @param limit A value used to limit the number of elements returned in the array.
14+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
│ │ 265: */
16+
│ │ ^^^^^^^
17+
│ │ 266: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[];
18+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
│ ╰
20+
├ selectionSpan:
21+
│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:266:5-266:10
22+
│ │ 266: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[];
23+
│ │ ^^^^^
24+
│ ╰
25+
├ incoming:
26+
│ ╭ from:
27+
│ │ ╭ name: method
28+
│ │ ├ kind: method
29+
│ │ ├ containerName: [1 + 2]
30+
│ │ ├ file: /callHierarchyIncomingCallsObjectLiteralMethodInExpressionComputedProperty.ts
31+
│ │ ├ span:
32+
│ │ │ ╭ /callHierarchyIncomingCallsObjectLiteralMethodInExpressionComputedProperty.ts:3:5-5:6
33+
│ │ │ │ 3: method() {
34+
│ │ │ │ ^^^^^^^^^^
35+
│ │ │ │ 4: return "".split(",");
36+
│ │ │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
37+
│ │ │ │ 5: }
38+
│ │ │ │ ^^^^^
39+
│ │ │ ╰
40+
│ │ ├ selectionSpan:
41+
│ │ │ ╭ /callHierarchyIncomingCallsObjectLiteralMethodInExpressionComputedProperty.ts:3:5-3:11
42+
│ │ │ │ 3: method() {
43+
│ │ │ │ ^^^^^^
44+
│ │ │ ╰
45+
│ │ ╰ incoming: none
46+
│ ├ fromSpans:
47+
│ │ ╭ /callHierarchyIncomingCallsObjectLiteralMethodInExpressionComputedProperty.ts:4:17-4:22
48+
│ │ │ 4: return "".split(",");
49+
│ │ │ ^^^^^
50+
│ ╰ ╰
51+
╰ outgoing: none
52+
╭ name: split
53+
├ kind: method
54+
├ file: bundled:///libs/lib.es5.d.ts
55+
├ span:
56+
│ ╭ bundled:///libs/lib.es5.d.ts:484:5-489:65
57+
│ │ 484: /**
58+
│ │ ^^^
59+
│ │ 485: * Split a string into substrings using the specified separator and return them as an array.
60+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
61+
│ │ 486: * @param separator A string that identifies character or characters to use in separating the string. If omitted, a single-element array containing the entire string is returned.
62+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
63+
│ │ 487: * @param limit A value used to limit the number of elements returned in the array.
64+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
65+
│ │ 488: */
66+
│ │ ^^^^^^^
67+
│ │ 489: split(separator: string | RegExp, limit?: number): string[];
68+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
69+
│ ╰
70+
├ selectionSpan:
71+
│ ╭ bundled:///libs/lib.es5.d.ts:489:5-489:10
72+
│ │ 489: split(separator: string | RegExp, limit?: number): string[];
73+
│ │ ^^^^^
74+
│ ╰
75+
├ incoming:
76+
│ ╭ from:
77+
│ │ ╭ name: method
78+
│ │ ├ kind: method
79+
│ │ ├ containerName: [1 + 2]
80+
│ │ ├ file: /callHierarchyIncomingCallsObjectLiteralMethodInExpressionComputedProperty.ts
81+
│ │ ├ span:
82+
│ │ │ ╭ /callHierarchyIncomingCallsObjectLiteralMethodInExpressionComputedProperty.ts:3:5-5:6
83+
│ │ │ │ 3: method() {
84+
│ │ │ │ ^^^^^^^^^^
85+
│ │ │ │ 4: return "".split(",");
86+
│ │ │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
87+
│ │ │ │ 5: }
88+
│ │ │ │ ^^^^^
89+
│ │ │ ╰
90+
│ │ ├ selectionSpan:
91+
│ │ │ ╭ /callHierarchyIncomingCallsObjectLiteralMethodInExpressionComputedProperty.ts:3:5-3:11
92+
│ │ │ │ 3: method() {
93+
│ │ │ │ ^^^^^^
94+
│ │ │ ╰
95+
│ │ ╰ incoming: none
96+
│ ├ fromSpans:
97+
│ │ ╭ /callHierarchyIncomingCallsObjectLiteralMethodInExpressionComputedProperty.ts:4:17-4:22
98+
│ │ │ 4: return "".split(",");
99+
│ │ │ ^^^^^
100+
│ ╰ ╰
101+
╰ outgoing: none
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// === Call Hierarchy ===
2+
╭ name: split
3+
├ kind: method
4+
├ file: bundled:///libs/lib.es2015.symbol.wellknown.d.ts
5+
├ span:
6+
│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:261:5-266:110
7+
│ │ 261: /**
8+
│ │ ^^^
9+
│ │ 262: * Split a string into substrings using the specified separator and return them as an array.
10+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11+
│ │ 263: * @param splitter An object that can split a string.
12+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13+
│ │ 264: * @param limit A value used to limit the number of elements returned in the array.
14+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
│ │ 265: */
16+
│ │ ^^^^^^^
17+
│ │ 266: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[];
18+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
│ ╰
20+
├ selectionSpan:
21+
│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:266:5-266:10
22+
│ │ 266: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[];
23+
│ │ ^^^^^
24+
│ ╰
25+
├ incoming:
26+
│ ╭ from:
27+
│ │ ╭ name: method
28+
│ │ ├ kind: method
29+
│ │ ├ containerName: [key]
30+
│ │ ├ file: /callHierarchyIncomingCallsObjectLiteralMethodInIdentifierComputedProperty.ts
31+
│ │ ├ span:
32+
│ │ │ ╭ /callHierarchyIncomingCallsObjectLiteralMethodInIdentifierComputedProperty.ts:4:5-6:6
33+
│ │ │ │ 4: method() {
34+
│ │ │ │ ^^^^^^^^^^
35+
│ │ │ │ 5: return "".split(",");
36+
│ │ │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
37+
│ │ │ │ 6: }
38+
│ │ │ │ ^^^^^
39+
│ │ │ ╰
40+
│ │ ├ selectionSpan:
41+
│ │ │ ╭ /callHierarchyIncomingCallsObjectLiteralMethodInIdentifierComputedProperty.ts:4:5-4:11
42+
│ │ │ │ 4: method() {
43+
│ │ │ │ ^^^^^^
44+
│ │ │ ╰
45+
│ │ ╰ incoming: none
46+
│ ├ fromSpans:
47+
│ │ ╭ /callHierarchyIncomingCallsObjectLiteralMethodInIdentifierComputedProperty.ts:5:17-5:22
48+
│ │ │ 5: return "".split(",");
49+
│ │ │ ^^^^^
50+
│ ╰ ╰
51+
╰ outgoing: none
52+
╭ name: split
53+
├ kind: method
54+
├ file: bundled:///libs/lib.es5.d.ts
55+
├ span:
56+
│ ╭ bundled:///libs/lib.es5.d.ts:484:5-489:65
57+
│ │ 484: /**
58+
│ │ ^^^
59+
│ │ 485: * Split a string into substrings using the specified separator and return them as an array.
60+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
61+
│ │ 486: * @param separator A string that identifies character or characters to use in separating the string. If omitted, a single-element array containing the entire string is returned.
62+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
63+
│ │ 487: * @param limit A value used to limit the number of elements returned in the array.
64+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
65+
│ │ 488: */
66+
│ │ ^^^^^^^
67+
│ │ 489: split(separator: string | RegExp, limit?: number): string[];
68+
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
69+
│ ╰
70+
├ selectionSpan:
71+
│ ╭ bundled:///libs/lib.es5.d.ts:489:5-489:10
72+
│ │ 489: split(separator: string | RegExp, limit?: number): string[];
73+
│ │ ^^^^^
74+
│ ╰
75+
├ incoming:
76+
│ ╭ from:
77+
│ │ ╭ name: method
78+
│ │ ├ kind: method
79+
│ │ ├ containerName: [key]
80+
│ │ ├ file: /callHierarchyIncomingCallsObjectLiteralMethodInIdentifierComputedProperty.ts
81+
│ │ ├ span:
82+
│ │ │ ╭ /callHierarchyIncomingCallsObjectLiteralMethodInIdentifierComputedProperty.ts:4:5-6:6
83+
│ │ │ │ 4: method() {
84+
│ │ │ │ ^^^^^^^^^^
85+
│ │ │ │ 5: return "".split(",");
86+
│ │ │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
87+
│ │ │ │ 6: }
88+
│ │ │ │ ^^^^^
89+
│ │ │ ╰
90+
│ │ ├ selectionSpan:
91+
│ │ │ ╭ /callHierarchyIncomingCallsObjectLiteralMethodInIdentifierComputedProperty.ts:4:5-4:11
92+
│ │ │ │ 4: method() {
93+
│ │ │ │ ^^^^^^
94+
│ │ │ ╰
95+
│ │ ╰ incoming: none
96+
│ ├ fromSpans:
97+
│ │ ╭ /callHierarchyIncomingCallsObjectLiteralMethodInIdentifierComputedProperty.ts:5:17-5:22
98+
│ │ │ 5: return "".split(",");
99+
│ │ │ ^^^^^
100+
│ ╰ ╰
101+
╰ outgoing: none

0 commit comments

Comments
 (0)