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 <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/inspector/inspector_test.go b/go/ast/inspector/inspector_test.go
index 3e9d3ba..9e53918 100644
--- a/go/ast/inspector/inspector_test.go
+++ b/go/ast/inspector/inspector_test.go
@@ -12,10 +12,12 @@
 	"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 @@
 	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 b6b00cf..11f4fc3 100644
--- a/go/ast/inspector/typeof.go
+++ b/go/ast/inspector/typeof.go
@@ -9,7 +9,11 @@
 // 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 @@
 	nImportSpec
 	nIncDecStmt
 	nIndexExpr
+	nIndexListExpr
 	nInterfaceType
 	nKeyValueExpr
 	nLabeledStmt
@@ -164,6 +169,8 @@
 		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 2145b05..a86d0ea 100644
--- a/internal/typeparams/typeparams_go117.go
+++ b/internal/typeparams/typeparams_go117.go
@@ -49,6 +49,12 @@
 	}
 }
 
+// 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 713efbb..a252183 100644
--- a/internal/typeparams/typeparams_go118.go
+++ b/internal/typeparams/typeparams_go118.go
@@ -22,6 +22,7 @@
 //
 // 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 @@
 	}
 }
 
+// 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 {