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 <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
diff --git a/go/ast/astutil/enclosing.go b/go/ast/astutil/enclosing.go
index 6b7052b..a5c6d6d 100644
--- a/go/ast/astutil/enclosing.go
+++ b/go/ast/astutil/enclosing.go
@@ -11,6 +11,8 @@
"go/ast"
"go/token"
"sort"
+
+ "golang.org/x/tools/internal/typeparams"
)
// PathEnclosingInterval returns the node that encloses the source
@@ -294,8 +296,8 @@
case *ast.FieldList:
children = append(children,
- tok(n.Opening, len("(")),
- tok(n.Closing, len(")")))
+ tok(n.Opening, len("(")), // or len("[")
+ tok(n.Closing, len(")"))) // or len("]")
case *ast.File:
// TODO test: Doc
@@ -322,6 +324,9 @@
children = append(children, n.Recv)
}
children = append(children, n.Name)
+ if tparams := typeparams.ForFuncType(n.Type); tparams != nil {
+ children = append(children, tparams)
+ }
if n.Type.Params != nil {
children = append(children, n.Type.Params)
}
@@ -371,8 +376,13 @@
case *ast.IndexExpr:
children = append(children,
- tok(n.Lbrack, len("{")),
- tok(n.Rbrack, len("}")))
+ tok(n.Lbrack, len("[")),
+ tok(n.Rbrack, len("]")))
+
+ case *typeparams.IndexListExpr:
+ children = append(children,
+ tok(n.Lbrack, len("[")),
+ tok(n.Rbrack, len("]")))
case *ast.InterfaceType:
children = append(children,
@@ -581,6 +591,8 @@
return "decrement statement"
case *ast.IndexExpr:
return "index expression"
+ case *typeparams.IndexListExpr:
+ return "index list expression"
case *ast.InterfaceType:
return "interface type"
case *ast.KeyValueExpr:
diff --git a/go/ast/astutil/enclosing_test.go b/go/ast/astutil/enclosing_test.go
index 107f87c..5e86ff9 100644
--- a/go/ast/astutil/enclosing_test.go
+++ b/go/ast/astutil/enclosing_test.go
@@ -19,6 +19,7 @@
"testing"
"golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/internal/typeparams"
)
// pathToString returns a string containing the concrete types of the
@@ -59,7 +60,10 @@
}
// Common input for following tests.
-const input = `
+var input = makeInput()
+
+func makeInput() string {
+ src := `
// Hello.
package main
import "fmt"
@@ -70,52 +74,88 @@
}
`
+ if typeparams.Enabled {
+ src += `
+func g[A any, P interface{ctype1| ~ctype2}](a1 A, p1 P) {}
+
+type PT[T constraint] struct{ t T }
+
+var v GT[targ1]
+
+var h = g[ targ2, targ3]
+`
+ }
+ return src
+}
+
func TestPathEnclosingInterval_Exact(t *testing.T) {
- // For the exact tests, we check that a substring is mapped to
- // the canonical string for the node it denotes.
- tests := []struct {
+ type testCase struct {
substr string // first occurrence of this string indicates interval
node string // complete text of expected containing node
- }{
+ }
+
+ dup := func(s string) testCase { return testCase{s, s} }
+ // For the exact tests, we check that a substring is mapped to
+ // the canonical string for the node it denotes.
+ tests := []testCase{
{"package",
input[11 : len(input)-1]},
{"\npack",
input[11 : len(input)-1]},
- {"main",
- "main"},
+ dup("main"),
{"import",
"import \"fmt\""},
- {"\"fmt\"",
- "\"fmt\""},
+ dup("\"fmt\""),
{"\nfunc f() {}\n",
"func f() {}"},
{"x ",
"x"},
{" y",
"y"},
- {"z",
- "z"},
+ dup("z"),
{" + ",
"x + y"},
{" :=",
"z := (x + y)"},
- {"x + y",
- "x + y"},
- {"(x + y)",
- "(x + y)"},
+ dup("x + y"),
+ dup("(x + y)"),
{" (x + y) ",
"(x + y)"},
{" (x + y) // add",
"(x + y)"},
{"func",
"func f() {}"},
- {"func f() {}",
- "func f() {}"},
+ dup("func f() {}"),
{"\nfun",
"func f() {}"},
{" f",
"f"},
}
+ if typeparams.Enabled {
+ tests = append(tests, []testCase{
+ dup("[A any, P interface{ctype1| ~ctype2}]"),
+ {"[", "[A any, P interface{ctype1| ~ctype2}]"},
+ dup("A"),
+ {" any", "any"},
+ dup("ctype1"),
+ {"|", "ctype1| ~ctype2"},
+ dup("ctype2"),
+ {"~", "~ctype2"},
+ dup("~ctype2"),
+ {" ~ctype2", "~ctype2"},
+ {"]", "[A any, P interface{ctype1| ~ctype2}]"},
+ dup("a1"),
+ dup("a1 A"),
+ dup("(a1 A, p1 P)"),
+ dup("type PT[T constraint] struct{ t T }"),
+ dup("PT"),
+ dup("[T constraint]"),
+ dup("constraint"),
+ dup("targ1"),
+ {" targ2", "targ2"},
+ dup("g[ targ2, targ3]"),
+ }...)
+ }
for _, test := range tests {
f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
if f == nil {
@@ -145,13 +185,14 @@
}
func TestPathEnclosingInterval_Paths(t *testing.T) {
+ type testCase struct {
+ substr string // first occurrence of this string indicates interval
+ path string // the pathToString(),exact of the expected path
+ }
// For these tests, we check only the path of the enclosing
// node, but not its complete text because it's often quite
// large when !exact.
- tests := []struct {
- substr string // first occurrence of this string indicates interval
- path string // the pathToString(),exact of the expected path
- }{
+ tests := []testCase{
{"// add",
"[BlockStmt FuncDecl File],false"},
{"(x + y",
@@ -179,6 +220,18 @@
{"f() // NB",
"[CallExpr ExprStmt BlockStmt FuncDecl File],true"},
}
+ if typeparams.Enabled {
+ tests = append(tests, []testCase{
+ {" any", "[Ident Field FieldList FuncDecl File],true"},
+ {"|", "[BinaryExpr Field FieldList InterfaceType Field FieldList FuncDecl File],true"},
+ {"ctype2",
+ "[Ident UnaryExpr BinaryExpr Field FieldList InterfaceType Field FieldList FuncDecl File],true"},
+ {"a1", "[Ident Field FieldList FuncDecl File],true"},
+ {"PT[T constraint]", "[TypeSpec GenDecl File],false"},
+ {"[T constraint]", "[FieldList TypeSpec GenDecl File],true"},
+ {"targ2", "[Ident IndexListExpr ValueSpec GenDecl File],true"},
+ }...)
+ }
for _, test := range tests {
f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
if f == nil {