Skip to content

Commit 0ebff1a

Browse files
committed
go/ast/astutil: update PathEnclosingInterval to handle type parameters
Add support for the new generic syntax to PathEnclosingInterval, notably the new IndexListExpr node and FuncDecl.Type.TypeParams. Change-Id: I013a916a1617e5f08c8d1cb30501bf2bf253c742 Reviewed-on: https://go-review.googlesource.com/c/tools/+/353150 Trust: Robert Findley <[email protected]> Run-TryBot: Robert Findley <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent 942994f commit 0ebff1a

File tree

2 files changed

+90
-25
lines changed

2 files changed

+90
-25
lines changed

go/ast/astutil/enclosing.go

+16-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"go/ast"
1212
"go/token"
1313
"sort"
14+
15+
"golang.org/x/tools/internal/typeparams"
1416
)
1517

1618
// PathEnclosingInterval returns the node that encloses the source
@@ -294,8 +296,8 @@ func childrenOf(n ast.Node) []ast.Node {
294296

295297
case *ast.FieldList:
296298
children = append(children,
297-
tok(n.Opening, len("(")),
298-
tok(n.Closing, len(")")))
299+
tok(n.Opening, len("(")), // or len("[")
300+
tok(n.Closing, len(")"))) // or len("]")
299301

300302
case *ast.File:
301303
// TODO test: Doc
@@ -322,6 +324,9 @@ func childrenOf(n ast.Node) []ast.Node {
322324
children = append(children, n.Recv)
323325
}
324326
children = append(children, n.Name)
327+
if tparams := typeparams.ForFuncType(n.Type); tparams != nil {
328+
children = append(children, tparams)
329+
}
325330
if n.Type.Params != nil {
326331
children = append(children, n.Type.Params)
327332
}
@@ -371,8 +376,13 @@ func childrenOf(n ast.Node) []ast.Node {
371376

372377
case *ast.IndexExpr:
373378
children = append(children,
374-
tok(n.Lbrack, len("{")),
375-
tok(n.Rbrack, len("}")))
379+
tok(n.Lbrack, len("[")),
380+
tok(n.Rbrack, len("]")))
381+
382+
case *typeparams.IndexListExpr:
383+
children = append(children,
384+
tok(n.Lbrack, len("[")),
385+
tok(n.Rbrack, len("]")))
376386

377387
case *ast.InterfaceType:
378388
children = append(children,
@@ -581,6 +591,8 @@ func NodeDescription(n ast.Node) string {
581591
return "decrement statement"
582592
case *ast.IndexExpr:
583593
return "index expression"
594+
case *typeparams.IndexListExpr:
595+
return "index list expression"
584596
case *ast.InterfaceType:
585597
return "interface type"
586598
case *ast.KeyValueExpr:

go/ast/astutil/enclosing_test.go

+74-21
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"testing"
2020

2121
"golang.org/x/tools/go/ast/astutil"
22+
"golang.org/x/tools/internal/typeparams"
2223
)
2324

2425
// pathToString returns a string containing the concrete types of the
@@ -59,7 +60,10 @@ func findInterval(t *testing.T, fset *token.FileSet, input, substr string) (f *a
5960
}
6061

6162
// Common input for following tests.
62-
const input = `
63+
var input = makeInput()
64+
65+
func makeInput() string {
66+
src := `
6367
// Hello.
6468
package main
6569
import "fmt"
@@ -70,52 +74,88 @@ func main() {
7074
}
7175
`
7276

77+
if typeparams.Enabled {
78+
src += `
79+
func g[A any, P interface{ctype1| ~ctype2}](a1 A, p1 P) {}
80+
81+
type PT[T constraint] struct{ t T }
82+
83+
var v GT[targ1]
84+
85+
var h = g[ targ2, targ3]
86+
`
87+
}
88+
return src
89+
}
90+
7391
func TestPathEnclosingInterval_Exact(t *testing.T) {
74-
// For the exact tests, we check that a substring is mapped to
75-
// the canonical string for the node it denotes.
76-
tests := []struct {
92+
type testCase struct {
7793
substr string // first occurrence of this string indicates interval
7894
node string // complete text of expected containing node
79-
}{
95+
}
96+
97+
dup := func(s string) testCase { return testCase{s, s} }
98+
// For the exact tests, we check that a substring is mapped to
99+
// the canonical string for the node it denotes.
100+
tests := []testCase{
80101
{"package",
81102
input[11 : len(input)-1]},
82103
{"\npack",
83104
input[11 : len(input)-1]},
84-
{"main",
85-
"main"},
105+
dup("main"),
86106
{"import",
87107
"import \"fmt\""},
88-
{"\"fmt\"",
89-
"\"fmt\""},
108+
dup("\"fmt\""),
90109
{"\nfunc f() {}\n",
91110
"func f() {}"},
92111
{"x ",
93112
"x"},
94113
{" y",
95114
"y"},
96-
{"z",
97-
"z"},
115+
dup("z"),
98116
{" + ",
99117
"x + y"},
100118
{" :=",
101119
"z := (x + y)"},
102-
{"x + y",
103-
"x + y"},
104-
{"(x + y)",
105-
"(x + y)"},
120+
dup("x + y"),
121+
dup("(x + y)"),
106122
{" (x + y) ",
107123
"(x + y)"},
108124
{" (x + y) // add",
109125
"(x + y)"},
110126
{"func",
111127
"func f() {}"},
112-
{"func f() {}",
113-
"func f() {}"},
128+
dup("func f() {}"),
114129
{"\nfun",
115130
"func f() {}"},
116131
{" f",
117132
"f"},
118133
}
134+
if typeparams.Enabled {
135+
tests = append(tests, []testCase{
136+
dup("[A any, P interface{ctype1| ~ctype2}]"),
137+
{"[", "[A any, P interface{ctype1| ~ctype2}]"},
138+
dup("A"),
139+
{" any", "any"},
140+
dup("ctype1"),
141+
{"|", "ctype1| ~ctype2"},
142+
dup("ctype2"),
143+
{"~", "~ctype2"},
144+
dup("~ctype2"),
145+
{" ~ctype2", "~ctype2"},
146+
{"]", "[A any, P interface{ctype1| ~ctype2}]"},
147+
dup("a1"),
148+
dup("a1 A"),
149+
dup("(a1 A, p1 P)"),
150+
dup("type PT[T constraint] struct{ t T }"),
151+
dup("PT"),
152+
dup("[T constraint]"),
153+
dup("constraint"),
154+
dup("targ1"),
155+
{" targ2", "targ2"},
156+
dup("g[ targ2, targ3]"),
157+
}...)
158+
}
119159
for _, test := range tests {
120160
f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
121161
if f == nil {
@@ -145,13 +185,14 @@ func TestPathEnclosingInterval_Exact(t *testing.T) {
145185
}
146186

147187
func TestPathEnclosingInterval_Paths(t *testing.T) {
188+
type testCase struct {
189+
substr string // first occurrence of this string indicates interval
190+
path string // the pathToString(),exact of the expected path
191+
}
148192
// For these tests, we check only the path of the enclosing
149193
// node, but not its complete text because it's often quite
150194
// large when !exact.
151-
tests := []struct {
152-
substr string // first occurrence of this string indicates interval
153-
path string // the pathToString(),exact of the expected path
154-
}{
195+
tests := []testCase{
155196
{"// add",
156197
"[BlockStmt FuncDecl File],false"},
157198
{"(x + y",
@@ -179,6 +220,18 @@ func TestPathEnclosingInterval_Paths(t *testing.T) {
179220
{"f() // NB",
180221
"[CallExpr ExprStmt BlockStmt FuncDecl File],true"},
181222
}
223+
if typeparams.Enabled {
224+
tests = append(tests, []testCase{
225+
{" any", "[Ident Field FieldList FuncDecl File],true"},
226+
{"|", "[BinaryExpr Field FieldList InterfaceType Field FieldList FuncDecl File],true"},
227+
{"ctype2",
228+
"[Ident UnaryExpr BinaryExpr Field FieldList InterfaceType Field FieldList FuncDecl File],true"},
229+
{"a1", "[Ident Field FieldList FuncDecl File],true"},
230+
{"PT[T constraint]", "[TypeSpec GenDecl File],false"},
231+
{"[T constraint]", "[FieldList TypeSpec GenDecl File],true"},
232+
{"targ2", "[Ident IndexListExpr ValueSpec GenDecl File],true"},
233+
}...)
234+
}
182235
for _, test := range tests {
183236
f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
184237
if f == nil {

0 commit comments

Comments
 (0)