From 13c407cf48b754e01a69cfb9de784d06a3648270 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Wed, 29 Sep 2021 11:01:47 -0400 Subject: [PATCH] go/ast/inspector: add support for the new IndexListExpr node IndexListExpr nodes were being skipped by go/ast/inspector, due to not having a type value. Add this value, along with a test that we can inspect the new constructs in generic code. Use a type alias in the typeparams package to achieve this, following the pattern for new go/types nodes. Change-Id: I894a9415a93806cc6dbb92cf190b2bdab368d5df Reviewed-on: https://go-review.googlesource.com/c/tools/+/352896 Trust: Robert Findley Run-TryBot: Robert Findley gopls-CI: kokoro TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- go/ast/inspector/inspector_test.go | 68 +++++++++++++++++++++++++ go/ast/inspector/typeof.go | 9 +++- internal/typeparams/typeparams_go117.go | 6 +++ internal/typeparams/typeparams_go118.go | 4 ++ 4 files changed, 86 insertions(+), 1 deletion(-) diff --git a/go/ast/inspector/inspector_test.go b/go/ast/inspector/inspector_test.go index 3e9d3bac4c..9e53918968 100644 --- a/go/ast/inspector/inspector_test.go +++ b/go/ast/inspector/inspector_test.go @@ -12,10 +12,12 @@ import ( "log" "path/filepath" "reflect" + "strconv" "strings" "testing" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/typeparams" ) var netFiles []*ast.File @@ -69,6 +71,72 @@ func TestInspectAllNodes(t *testing.T) { compare(t, nodesA, nodesB) } +func TestInspectGenericNodes(t *testing.T) { + if !typeparams.Enabled { + t.Skip("type parameters are not supported at this Go version") + } + + // src is using the 16 identifiers i0, i1, ... i15 so + // we can easily verify that we've found all of them. + const src = `package a + +type I interface { ~i0|i1 } + +type T[i2, i3 interface{ ~i4 }] struct {} + +func f[i5, i6 any]() { + _ = f[i7, i8] + var x T[i9, i10] +} + +func (*T[i11, i12]) m() + +var _ i13[i14, i15] +` + fset := token.NewFileSet() + f, _ := parser.ParseFile(fset, "a.go", src, 0) + inspect := inspector.New([]*ast.File{f}) + found := make([]bool, 16) + + indexListExprs := make(map[*typeparams.IndexListExpr]bool) + + // Verify that we reach all i* identifiers, and collect IndexListExpr nodes. + inspect.Preorder(nil, func(n ast.Node) { + switch n := n.(type) { + case *ast.Ident: + if n.Name[0] == 'i' { + index, err := strconv.Atoi(n.Name[1:]) + if err != nil { + t.Fatal(err) + } + found[index] = true + } + case *typeparams.IndexListExpr: + indexListExprs[n] = false + } + }) + for i, v := range found { + if !v { + t.Errorf("missed identifier i%d", i) + } + } + + // Verify that we can filter to IndexListExprs that we found in the first + // step. + if len(indexListExprs) == 0 { + t.Fatal("no index list exprs found") + } + inspect.Preorder([]ast.Node{&typeparams.IndexListExpr{}}, func(n ast.Node) { + ix := n.(*typeparams.IndexListExpr) + indexListExprs[ix] = true + }) + for ix, v := range indexListExprs { + if !v { + t.Errorf("inspected node %v not filtered", ix) + } + } +} + // TestPruning compares Inspector against ast.Inspect, // pruning descent within ast.CallExpr nodes. func TestInspectPruning(t *testing.T) { diff --git a/go/ast/inspector/typeof.go b/go/ast/inspector/typeof.go index b6b00cf2e1..11f4fc369a 100644 --- a/go/ast/inspector/typeof.go +++ b/go/ast/inspector/typeof.go @@ -9,7 +9,11 @@ package inspector // The initial map-based implementation was too slow; // see https://go-review.googlesource.com/c/tools/+/135655/1/go/ast/inspector/inspector.go#196 -import "go/ast" +import ( + "go/ast" + + "golang.org/x/tools/internal/typeparams" +) const ( nArrayType = iota @@ -47,6 +51,7 @@ const ( nImportSpec nIncDecStmt nIndexExpr + nIndexListExpr nInterfaceType nKeyValueExpr nLabeledStmt @@ -164,6 +169,8 @@ func typeOf(n ast.Node) uint64 { return 1 << nIncDecStmt case *ast.IndexExpr: return 1 << nIndexExpr + case *typeparams.IndexListExpr: + return 1 << nIndexListExpr case *ast.InterfaceType: return 1 << nInterfaceType case *ast.KeyValueExpr: diff --git a/internal/typeparams/typeparams_go117.go b/internal/typeparams/typeparams_go117.go index 2145b052b7..a86d0ea406 100644 --- a/internal/typeparams/typeparams_go117.go +++ b/internal/typeparams/typeparams_go117.go @@ -49,6 +49,12 @@ func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack toke } } +// IndexListExpr is a placeholder type, as type parameters are not supported at +// this Go version. Its methods panic on use. +type IndexListExpr struct { + ast.Expr +} + // ForTypeSpec returns an empty field list, as type parameters on not supported // at this Go version. func ForTypeSpec(*ast.TypeSpec) *ast.FieldList { diff --git a/internal/typeparams/typeparams_go118.go b/internal/typeparams/typeparams_go118.go index 713efbb5e4..a252183411 100644 --- a/internal/typeparams/typeparams_go118.go +++ b/internal/typeparams/typeparams_go118.go @@ -22,6 +22,7 @@ import ( // // For nodes that don't represent index expressions, GetIndexExprData returns // nil. +// TODO(rfindley): remove this function in favor of using the alias below. func GetIndexExprData(n ast.Node) *IndexExprData { switch e := n.(type) { case *ast.IndexExpr: @@ -61,6 +62,9 @@ func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack toke } } +// IndexListExpr is an alias for ast.IndexListExpr. +type IndexListExpr = ast.IndexListExpr + // ForTypeSpec returns n.TypeParams. func ForTypeSpec(n *ast.TypeSpec) *ast.FieldList { if n == nil {