Skip to content

Commit 13c407c

Browse files
committed
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 <[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 58ad7eb commit 13c407c

File tree

4 files changed

+86
-1
lines changed

4 files changed

+86
-1
lines changed

go/ast/inspector/inspector_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ import (
1212
"log"
1313
"path/filepath"
1414
"reflect"
15+
"strconv"
1516
"strings"
1617
"testing"
1718

1819
"golang.org/x/tools/go/ast/inspector"
20+
"golang.org/x/tools/internal/typeparams"
1921
)
2022

2123
var netFiles []*ast.File
@@ -69,6 +71,72 @@ func TestInspectAllNodes(t *testing.T) {
6971
compare(t, nodesA, nodesB)
7072
}
7173

74+
func TestInspectGenericNodes(t *testing.T) {
75+
if !typeparams.Enabled {
76+
t.Skip("type parameters are not supported at this Go version")
77+
}
78+
79+
// src is using the 16 identifiers i0, i1, ... i15 so
80+
// we can easily verify that we've found all of them.
81+
const src = `package a
82+
83+
type I interface { ~i0|i1 }
84+
85+
type T[i2, i3 interface{ ~i4 }] struct {}
86+
87+
func f[i5, i6 any]() {
88+
_ = f[i7, i8]
89+
var x T[i9, i10]
90+
}
91+
92+
func (*T[i11, i12]) m()
93+
94+
var _ i13[i14, i15]
95+
`
96+
fset := token.NewFileSet()
97+
f, _ := parser.ParseFile(fset, "a.go", src, 0)
98+
inspect := inspector.New([]*ast.File{f})
99+
found := make([]bool, 16)
100+
101+
indexListExprs := make(map[*typeparams.IndexListExpr]bool)
102+
103+
// Verify that we reach all i* identifiers, and collect IndexListExpr nodes.
104+
inspect.Preorder(nil, func(n ast.Node) {
105+
switch n := n.(type) {
106+
case *ast.Ident:
107+
if n.Name[0] == 'i' {
108+
index, err := strconv.Atoi(n.Name[1:])
109+
if err != nil {
110+
t.Fatal(err)
111+
}
112+
found[index] = true
113+
}
114+
case *typeparams.IndexListExpr:
115+
indexListExprs[n] = false
116+
}
117+
})
118+
for i, v := range found {
119+
if !v {
120+
t.Errorf("missed identifier i%d", i)
121+
}
122+
}
123+
124+
// Verify that we can filter to IndexListExprs that we found in the first
125+
// step.
126+
if len(indexListExprs) == 0 {
127+
t.Fatal("no index list exprs found")
128+
}
129+
inspect.Preorder([]ast.Node{&typeparams.IndexListExpr{}}, func(n ast.Node) {
130+
ix := n.(*typeparams.IndexListExpr)
131+
indexListExprs[ix] = true
132+
})
133+
for ix, v := range indexListExprs {
134+
if !v {
135+
t.Errorf("inspected node %v not filtered", ix)
136+
}
137+
}
138+
}
139+
72140
// TestPruning compares Inspector against ast.Inspect,
73141
// pruning descent within ast.CallExpr nodes.
74142
func TestInspectPruning(t *testing.T) {

go/ast/inspector/typeof.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ package inspector
99
// The initial map-based implementation was too slow;
1010
// see https://go-review.googlesource.com/c/tools/+/135655/1/go/ast/inspector/inspector.go#196
1111

12-
import "go/ast"
12+
import (
13+
"go/ast"
14+
15+
"golang.org/x/tools/internal/typeparams"
16+
)
1317

1418
const (
1519
nArrayType = iota
@@ -47,6 +51,7 @@ const (
4751
nImportSpec
4852
nIncDecStmt
4953
nIndexExpr
54+
nIndexListExpr
5055
nInterfaceType
5156
nKeyValueExpr
5257
nLabeledStmt
@@ -164,6 +169,8 @@ func typeOf(n ast.Node) uint64 {
164169
return 1 << nIncDecStmt
165170
case *ast.IndexExpr:
166171
return 1 << nIndexExpr
172+
case *typeparams.IndexListExpr:
173+
return 1 << nIndexListExpr
167174
case *ast.InterfaceType:
168175
return 1 << nInterfaceType
169176
case *ast.KeyValueExpr:

internal/typeparams/typeparams_go117.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack toke
4949
}
5050
}
5151

52+
// IndexListExpr is a placeholder type, as type parameters are not supported at
53+
// this Go version. Its methods panic on use.
54+
type IndexListExpr struct {
55+
ast.Expr
56+
}
57+
5258
// ForTypeSpec returns an empty field list, as type parameters on not supported
5359
// at this Go version.
5460
func ForTypeSpec(*ast.TypeSpec) *ast.FieldList {

internal/typeparams/typeparams_go118.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
//
2323
// For nodes that don't represent index expressions, GetIndexExprData returns
2424
// nil.
25+
// TODO(rfindley): remove this function in favor of using the alias below.
2526
func GetIndexExprData(n ast.Node) *IndexExprData {
2627
switch e := n.(type) {
2728
case *ast.IndexExpr:
@@ -61,6 +62,9 @@ func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack toke
6162
}
6263
}
6364

65+
// IndexListExpr is an alias for ast.IndexListExpr.
66+
type IndexListExpr = ast.IndexListExpr
67+
6468
// ForTypeSpec returns n.TypeParams.
6569
func ForTypeSpec(n *ast.TypeSpec) *ast.FieldList {
6670
if n == nil {

0 commit comments

Comments
 (0)