go/ssa: fix *SelectorExpr within *IndexExpr handling

When a *IndexExpr or *IndexListExpr expr is over a *SelectorExpr and
expr denotes an instantiation, build expr as the *SelectorExpr.

Fixes golang/go#52834

Change-Id: I9a69ac28a6e8fb0ee9eb45db8675872b75d69a0f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/405555
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Tim King <taking@google.com>
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/go/ssa/builder.go b/go/ssa/builder.go
index 5c2a2ef..b36775a 100644
--- a/go/ssa/builder.go
+++ b/go/ssa/builder.go
@@ -840,16 +840,15 @@
 		panic("unexpected expression-relative selector")
 
 	case *typeparams.IndexListExpr:
-		if ident, ok := e.X.(*ast.Ident); ok {
-			// IndexListExpr is an instantiation. It will be handled by the *Ident case.
-			return b.expr(fn, ident)
+		// f[X, Y] must be a generic function
+		if !instance(fn.info, e.X) {
+			panic("unexpected expression-could not match index list to instantiation")
 		}
+		return b.expr(fn, e.X) // Handle instantiation within the *Ident or *SelectorExpr cases.
+
 	case *ast.IndexExpr:
-		if ident, ok := e.X.(*ast.Ident); ok {
-			if _, ok := typeparams.GetInstances(fn.info)[ident]; ok {
-				// If the IndexExpr is an instantiation, it will be handled by the *Ident case.
-				return b.expr(fn, ident)
-			}
+		if instance(fn.info, e.X) {
+			return b.expr(fn, e.X) // Handle instantiation within the *Ident or *SelectorExpr cases.
 		}
 		// not a generic instantiation.
 		switch t := fn.typeOf(e.X).Underlying().(type) {
diff --git a/go/ssa/builder_test.go b/go/ssa/builder_test.go
index 6b9c798..ba9aaf7 100644
--- a/go/ssa/builder_test.go
+++ b/go/ssa/builder_test.go
@@ -20,6 +20,7 @@
 	"strings"
 	"testing"
 
+	"golang.org/x/tools/go/buildutil"
 	"golang.org/x/tools/go/loader"
 	"golang.org/x/tools/go/ssa"
 	"golang.org/x/tools/go/ssa/ssautil"
@@ -823,3 +824,56 @@
 		})
 	}
 }
+
+// TestGenericFunctionSelector ensures generic functions from other packages can be selected.
+func TestGenericFunctionSelector(t *testing.T) {
+	if !typeparams.Enabled {
+		t.Skip("TestGenericFunctionSelector uses type parameters.")
+	}
+
+	pkgs := map[string]map[string]string{
+		"main": {"m.go": `package main; import "a"; func main() { a.F[int](); a.G[int,string](); a.H(0) }`},
+		"a":    {"a.go": `package a; func F[T any](){}; func G[S, T any](){}; func H[T any](a T){} `},
+	}
+
+	for _, mode := range []ssa.BuilderMode{
+		ssa.SanityCheckFunctions,
+		ssa.SanityCheckFunctions | ssa.InstantiateGenerics,
+	} {
+		conf := loader.Config{
+			Build: buildutil.FakeContext(pkgs),
+		}
+		conf.Import("main")
+
+		lprog, err := conf.Load()
+		if err != nil {
+			t.Errorf("Load failed: %s", err)
+		}
+		if lprog == nil {
+			t.Fatalf("Load returned nil *Program")
+		}
+		// Create and build SSA
+		prog := ssautil.CreateProgram(lprog, mode)
+		p := prog.Package(lprog.Package("main").Pkg)
+		p.Build()
+
+		var callees []string // callees of the CallInstruction.String() in main().
+		for _, b := range p.Func("main").Blocks {
+			for _, i := range b.Instrs {
+				if call, ok := i.(ssa.CallInstruction); ok {
+					if callee := call.Common().StaticCallee(); call != nil {
+						callees = append(callees, callee.String())
+					} else {
+						t.Errorf("CallInstruction without StaticCallee() %q", call)
+					}
+				}
+			}
+		}
+		sort.Strings(callees) // ignore the order in the code.
+
+		want := "[a.F[[int]] a.G[[int string]] a.H[[int]]]"
+		if got := fmt.Sprint(callees); got != want {
+			t.Errorf("Expected main() to contain calls %v. got %v", want, got)
+		}
+	}
+}
diff --git a/go/ssa/util.go b/go/ssa/util.go
index dfeaeeb..80c7d5c 100644
--- a/go/ssa/util.go
+++ b/go/ssa/util.go
@@ -175,6 +175,24 @@
 	return typeparams.NewSignatureType(nil, nil, nil, types.NewTuple(params...), sig.Results(), sig.Variadic())
 }
 
+// instance returns whether an expression is a simple or qualified identifier
+// that is a generic instantiation.
+func instance(info *types.Info, expr ast.Expr) bool {
+	// Compare the logic here against go/types.instantiatedIdent,
+	// which also handles  *IndexExpr and *IndexListExpr.
+	var id *ast.Ident
+	switch x := expr.(type) {
+	case *ast.Ident:
+		id = x
+	case *ast.SelectorExpr:
+		id = x.Sel
+	default:
+		return false
+	}
+	_, ok := typeparams.GetInstances(info)[id]
+	return ok
+}
+
 // instanceArgs returns the Instance[id].TypeArgs as a slice.
 func instanceArgs(info *types.Info, id *ast.Ident) []types.Type {
 	targList := typeparams.GetInstances(info)[id].TypeArgs