go/callgraph/static: adds tests for (instantiated) generics

Updates golang/go#48525

Change-Id: If460999912fecad626ec8cbace251dd6a0358196
Reviewed-on: https://go-review.googlesource.com/c/tools/+/402695
Reviewed-by: Tim King <taking@google.com>
Reviewed-by: Alan Donovan <alan@alandonovan.net>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Zvonimir Pavlinovic <zpavlinovic@google.com>
diff --git a/go/callgraph/static/static_test.go b/go/callgraph/static/static_test.go
index e1bfcd7..47f32d7 100644
--- a/go/callgraph/static/static_test.go
+++ b/go/callgraph/static/static_test.go
@@ -14,7 +14,9 @@
 	"golang.org/x/tools/go/callgraph"
 	"golang.org/x/tools/go/callgraph/static"
 	"golang.org/x/tools/go/loader"
+	"golang.org/x/tools/go/ssa"
 	"golang.org/x/tools/go/ssa/ssautil"
+	"golang.org/x/tools/internal/typeparams"
 )
 
 const input = `package P
@@ -47,42 +49,94 @@
 var unknown bool
 `
 
+const genericsInput = `package P
+
+type I interface {
+	F()
+}
+
+type A struct{}
+
+func (a A) F() {}
+
+type B struct{}
+
+func (b B) F() {}
+
+func instantiated[X I](x X) {
+	x.F()
+}
+
+func Bar() {}
+
+func f(h func(), a A, b B) {
+	h()
+
+	instantiated[A](a)
+	instantiated[B](b)
+}
+`
+
 func TestStatic(t *testing.T) {
-	conf := loader.Config{ParserMode: parser.ParseComments}
-	f, err := conf.ParseFile("P.go", input)
-	if err != nil {
-		t.Fatal(err)
-	}
+	for _, e := range []struct {
+		input string
+		want  []string
+		// typeparams must be true if input uses type parameters
+		typeparams bool
+	}{
+		{input, []string{
+			"(*C).f -> (C).f",
+			"f -> (C).f",
+			"f -> f$1",
+			"f -> g",
+		}, false},
+		{genericsInput, []string{
+			"(*A).F -> (A).F",
+			"(*B).F -> (B).F",
+			"f -> instantiated[[P.A]]",
+			"f -> instantiated[[P.B]]",
+			"instantiated[[P.A]] -> (A).F",
+			"instantiated[[P.B]] -> (B).F",
+		}, true},
+	} {
+		if e.typeparams && !typeparams.Enabled {
+			// Skip tests with type parameters when the build
+			// environment is not supporting any.
+			continue
+		}
 
-	conf.CreateFromFiles("P", f)
-	iprog, err := conf.Load()
-	if err != nil {
-		t.Fatal(err)
-	}
+		conf := loader.Config{ParserMode: parser.ParseComments}
+		f, err := conf.ParseFile("P.go", e.input)
+		if err != nil {
+			t.Error(err)
+			continue
+		}
 
-	P := iprog.Created[0].Pkg
+		conf.CreateFromFiles("P", f)
+		iprog, err := conf.Load()
+		if err != nil {
+			t.Error(err)
+			continue
+		}
 
-	prog := ssautil.CreateProgram(iprog, 0)
-	prog.Build()
+		P := iprog.Created[0].Pkg
 
-	cg := static.CallGraph(prog)
+		prog := ssautil.CreateProgram(iprog, ssa.InstantiateGenerics)
+		prog.Build()
 
-	var edges []string
-	callgraph.GraphVisitEdges(cg, func(e *callgraph.Edge) error {
-		edges = append(edges, fmt.Sprintf("%s -> %s",
-			e.Caller.Func.RelString(P),
-			e.Callee.Func.RelString(P)))
-		return nil
-	})
-	sort.Strings(edges)
+		cg := static.CallGraph(prog)
 
-	want := []string{
-		"(*C).f -> (C).f",
-		"f -> (C).f",
-		"f -> f$1",
-		"f -> g",
-	}
-	if !reflect.DeepEqual(edges, want) {
-		t.Errorf("Got edges %v, want %v", edges, want)
+		var edges []string
+		callgraph.GraphVisitEdges(cg, func(e *callgraph.Edge) error {
+			edges = append(edges, fmt.Sprintf("%s -> %s",
+				e.Caller.Func.RelString(P),
+				e.Callee.Func.RelString(P)))
+			return nil
+		})
+		sort.Strings(edges)
+
+		if !reflect.DeepEqual(edges, e.want) {
+			t.Errorf("Got edges %v, want %v", edges, e.want)
+		}
 	}
 }