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>
This commit is contained in:
Robert Findley 2021-09-29 13:58:17 -04:00
Родитель 942994fc75
Коммит 0ebff1a957
2 изменённых файлов: 90 добавлений и 25 удалений

Просмотреть файл

@ -11,6 +11,8 @@ import (
"go/ast" "go/ast"
"go/token" "go/token"
"sort" "sort"
"golang.org/x/tools/internal/typeparams"
) )
// PathEnclosingInterval returns the node that encloses the source // PathEnclosingInterval returns the node that encloses the source
@ -294,8 +296,8 @@ func childrenOf(n ast.Node) []ast.Node {
case *ast.FieldList: case *ast.FieldList:
children = append(children, children = append(children,
tok(n.Opening, len("(")), tok(n.Opening, len("(")), // or len("[")
tok(n.Closing, len(")"))) tok(n.Closing, len(")"))) // or len("]")
case *ast.File: case *ast.File:
// TODO test: Doc // TODO test: Doc
@ -322,6 +324,9 @@ func childrenOf(n ast.Node) []ast.Node {
children = append(children, n.Recv) children = append(children, n.Recv)
} }
children = append(children, n.Name) children = append(children, n.Name)
if tparams := typeparams.ForFuncType(n.Type); tparams != nil {
children = append(children, tparams)
}
if n.Type.Params != nil { if n.Type.Params != nil {
children = append(children, n.Type.Params) children = append(children, n.Type.Params)
} }
@ -371,8 +376,13 @@ func childrenOf(n ast.Node) []ast.Node {
case *ast.IndexExpr: case *ast.IndexExpr:
children = append(children, children = append(children,
tok(n.Lbrack, len("{")), tok(n.Lbrack, len("[")),
tok(n.Rbrack, len("}"))) tok(n.Rbrack, len("]")))
case *typeparams.IndexListExpr:
children = append(children,
tok(n.Lbrack, len("[")),
tok(n.Rbrack, len("]")))
case *ast.InterfaceType: case *ast.InterfaceType:
children = append(children, children = append(children,
@ -581,6 +591,8 @@ func NodeDescription(n ast.Node) string {
return "decrement statement" return "decrement statement"
case *ast.IndexExpr: case *ast.IndexExpr:
return "index expression" return "index expression"
case *typeparams.IndexListExpr:
return "index list expression"
case *ast.InterfaceType: case *ast.InterfaceType:
return "interface type" return "interface type"
case *ast.KeyValueExpr: case *ast.KeyValueExpr:

Просмотреть файл

@ -19,6 +19,7 @@ import (
"testing" "testing"
"golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/internal/typeparams"
) )
// pathToString returns a string containing the concrete types of the // 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
} }
// Common input for following tests. // Common input for following tests.
const input = ` var input = makeInput()
func makeInput() string {
src := `
// Hello. // Hello.
package main package main
import "fmt" import "fmt"
@ -70,52 +74,88 @@ func main() {
} }
` `
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) { func TestPathEnclosingInterval_Exact(t *testing.T) {
// For the exact tests, we check that a substring is mapped to type testCase struct {
// the canonical string for the node it denotes.
tests := []struct {
substr string // first occurrence of this string indicates interval substr string // first occurrence of this string indicates interval
node string // complete text of expected containing node 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", {"package",
input[11 : len(input)-1]}, input[11 : len(input)-1]},
{"\npack", {"\npack",
input[11 : len(input)-1]}, input[11 : len(input)-1]},
{"main", dup("main"),
"main"},
{"import", {"import",
"import \"fmt\""}, "import \"fmt\""},
{"\"fmt\"", dup("\"fmt\""),
"\"fmt\""},
{"\nfunc f() {}\n", {"\nfunc f() {}\n",
"func f() {}"}, "func f() {}"},
{"x ", {"x ",
"x"}, "x"},
{" y", {" y",
"y"}, "y"},
{"z", dup("z"),
"z"},
{" + ", {" + ",
"x + y"}, "x + y"},
{" :=", {" :=",
"z := (x + y)"}, "z := (x + y)"},
{"x + y", dup("x + y"),
"x + y"}, dup("(x + y)"),
{"(x + y)",
"(x + y)"},
{" (x + y) ", {" (x + y) ",
"(x + y)"}, "(x + y)"},
{" (x + y) // add", {" (x + y) // add",
"(x + y)"}, "(x + y)"},
{"func", {"func",
"func f() {}"}, "func f() {}"},
{"func f() {}", dup("func f() {}"),
"func f() {}"},
{"\nfun", {"\nfun",
"func f() {}"}, "func f() {}"},
{" f", {" 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 { for _, test := range tests {
f, start, end := findInterval(t, new(token.FileSet), input, test.substr) f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
if f == nil { if f == nil {
@ -145,13 +185,14 @@ func TestPathEnclosingInterval_Exact(t *testing.T) {
} }
func TestPathEnclosingInterval_Paths(t *testing.T) { 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 // For these tests, we check only the path of the enclosing
// node, but not its complete text because it's often quite // node, but not its complete text because it's often quite
// large when !exact. // large when !exact.
tests := []struct { tests := []testCase{
substr string // first occurrence of this string indicates interval
path string // the pathToString(),exact of the expected path
}{
{"// add", {"// add",
"[BlockStmt FuncDecl File],false"}, "[BlockStmt FuncDecl File],false"},
{"(x + y", {"(x + y",
@ -179,6 +220,18 @@ func TestPathEnclosingInterval_Paths(t *testing.T) {
{"f() // NB", {"f() // NB",
"[CallExpr ExprStmt BlockStmt FuncDecl File],true"}, "[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 { for _, test := range tests {
f, start, end := findInterval(t, new(token.FileSet), input, test.substr) f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
if f == nil { if f == nil {