From dcaea06afc12df4781912aae7644da4f8dd59ddc Mon Sep 17 00:00:00 2001 From: Zvonimir Pavlinovic Date: Mon, 25 Apr 2022 15:38:59 -0700 Subject: [PATCH] go/callgraph/vta: adds tests for (instantiated) generics Updates golang/go#48525 Change-Id: Ia84365d7f48f804f2b397782789706ef6d1d4b86 Reviewed-on: https://go-review.googlesource.com/c/tools/+/402274 Run-TryBot: Zvonimir Pavlinovic gopls-CI: kokoro TryBot-Result: Gopher Robot Reviewed-by: Tim King --- go/callgraph/vta/graph_test.go | 7 +- go/callgraph/vta/helpers_test.go | 4 +- .../vta/testdata/src/callgraph_generics.go | 71 +++++++++++++++++++ go/callgraph/vta/vta_go117_test.go | 3 +- go/callgraph/vta/vta_test.go | 26 ++++++- 5 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 go/callgraph/vta/testdata/src/callgraph_generics.go diff --git a/go/callgraph/vta/graph_test.go b/go/callgraph/vta/graph_test.go index 8608844dd3..8b8c6976f7 100644 --- a/go/callgraph/vta/graph_test.go +++ b/go/callgraph/vta/graph_test.go @@ -13,6 +13,7 @@ import ( "testing" "golang.org/x/tools/go/callgraph/cha" + "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" ) @@ -24,7 +25,7 @@ func TestNodeInterface(t *testing.T) { // - global variable "gl" // - "main" function and its // - first register instruction t0 := *gl - prog, _, err := testProg("testdata/src/simple.go") + prog, _, err := testProg("testdata/src/simple.go", ssa.BuilderMode(0)) if err != nil { t.Fatalf("couldn't load testdata/src/simple.go program: %v", err) } @@ -78,7 +79,7 @@ func TestNodeInterface(t *testing.T) { func TestVtaGraph(t *testing.T) { // Get the basic type int from a real program. - prog, _, err := testProg("testdata/src/simple.go") + prog, _, err := testProg("testdata/src/simple.go", ssa.BuilderMode(0)) if err != nil { t.Fatalf("couldn't load testdata/src/simple.go program: %v", err) } @@ -191,7 +192,7 @@ func TestVTAGraphConstruction(t *testing.T) { "testdata/src/panic.go", } { t.Run(file, func(t *testing.T) { - prog, want, err := testProg(file) + prog, want, err := testProg(file, ssa.BuilderMode(0)) if err != nil { t.Fatalf("couldn't load test file '%s': %s", file, err) } diff --git a/go/callgraph/vta/helpers_test.go b/go/callgraph/vta/helpers_test.go index c46dd7e1f2..768365f5b2 100644 --- a/go/callgraph/vta/helpers_test.go +++ b/go/callgraph/vta/helpers_test.go @@ -35,7 +35,7 @@ func want(f *ast.File) []string { // testProg returns an ssa representation of a program at // `path`, assumed to define package "testdata," and the // test want result as list of strings. -func testProg(path string) (*ssa.Program, []string, error) { +func testProg(path string, mode ssa.BuilderMode) (*ssa.Program, []string, error) { content, err := ioutil.ReadFile(path) if err != nil { return nil, nil, err @@ -56,7 +56,7 @@ func testProg(path string) (*ssa.Program, []string, error) { return nil, nil, err } - prog := ssautil.CreateProgram(iprog, 0) + prog := ssautil.CreateProgram(iprog, mode) // Set debug mode to exercise DebugRef instructions. prog.Package(iprog.Created[0].Pkg).SetDebugMode(true) prog.Build() diff --git a/go/callgraph/vta/testdata/src/callgraph_generics.go b/go/callgraph/vta/testdata/src/callgraph_generics.go new file mode 100644 index 0000000000..b0fcb02559 --- /dev/null +++ b/go/callgraph/vta/testdata/src/callgraph_generics.go @@ -0,0 +1,71 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// go:build ignore + +package testdata + +func instantiated[X any](x *X) int { + print(x) + return 0 +} + +type I interface { + Bar() +} + +func interfaceInstantiated[X I](x X) { + x.Bar() +} + +type A struct{} + +func (a A) Bar() {} + +type B struct{} + +func (b B) Bar() {} + +func Foo(a A, b B) { + x := true + instantiated[bool](&x) + y := 1 + instantiated[int](&y) + + interfaceInstantiated[A](a) + interfaceInstantiated[B](b) +} + +// Relevant SSA: +//func Foo(a A, b B): +// t0 = local A (a) +// *t0 = a +// t1 = local B (b) +// *t1 = b +// t2 = new bool (x) +// *t2 = true:bool +// t3 = instantiated[[bool]](t2) +// t4 = new int (y) +// *t4 = 1:int +// t5 = instantiated[[int]](t4) +// t6 = *t0 +// t7 = interfaceInstantiated[[testdata.A]](t6) +// t8 = *t1 +// t9 = interfaceInstantiated[[testdata.B]](t8) +// return +// +//func interfaceInstantiated[[testdata.B]](x B): +// t0 = local B (x) +// *t0 = x +// t1 = *t0 +// t2 = (B).Bar(t1) +// return +// +//func interfaceInstantiated[X I](x X): +// (external) + +// WANT: +// Foo: instantiated[[bool]](t2) -> instantiated[[bool]]; instantiated[[int]](t4) -> instantiated[[int]]; interfaceInstantiated[[testdata.A]](t6) -> interfaceInstantiated[[testdata.A]]; interfaceInstantiated[[testdata.B]](t8) -> interfaceInstantiated[[testdata.B]] +// interfaceInstantiated[[testdata.B]]: (B).Bar(t1) -> B.Bar +// interfaceInstantiated[[testdata.A]]: (A).Bar(t1) -> A.Bar diff --git a/go/callgraph/vta/vta_go117_test.go b/go/callgraph/vta/vta_go117_test.go index 9ce6a88645..04f6980e57 100644 --- a/go/callgraph/vta/vta_go117_test.go +++ b/go/callgraph/vta/vta_go117_test.go @@ -11,12 +11,13 @@ import ( "testing" "golang.org/x/tools/go/callgraph/cha" + "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" ) func TestVTACallGraphGo117(t *testing.T) { file := "testdata/src/go117.go" - prog, want, err := testProg(file) + prog, want, err := testProg(file, ssa.BuilderMode(0)) if err != nil { t.Fatalf("couldn't load test file '%s': %s", file, err) } diff --git a/go/callgraph/vta/vta_test.go b/go/callgraph/vta/vta_test.go index 830390850a..4cd26a54ab 100644 --- a/go/callgraph/vta/vta_test.go +++ b/go/callgraph/vta/vta_test.go @@ -13,6 +13,7 @@ import ( "golang.org/x/tools/go/callgraph/cha" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" + "golang.org/x/tools/internal/typeparams" ) func TestVTACallGraph(t *testing.T) { @@ -27,7 +28,7 @@ func TestVTACallGraph(t *testing.T) { "testdata/src/callgraph_recursive_types.go", } { t.Run(file, func(t *testing.T) { - prog, want, err := testProg(file) + prog, want, err := testProg(file, ssa.BuilderMode(0)) if err != nil { t.Fatalf("couldn't load test file '%s': %s", file, err) } @@ -47,7 +48,7 @@ func TestVTACallGraph(t *testing.T) { // enabled by having an arbitrary function set as input to CallGraph // instead of the whole program (i.e., ssautil.AllFunctions(prog)). func TestVTAProgVsFuncSet(t *testing.T) { - prog, want, err := testProg("testdata/src/callgraph_nested_ptr.go") + prog, want, err := testProg("testdata/src/callgraph_nested_ptr.go", ssa.BuilderMode(0)) if err != nil { t.Fatalf("couldn't load test `testdata/src/callgraph_nested_ptr.go`: %s", err) } @@ -112,3 +113,24 @@ func TestVTAPanicMissingDefinitions(t *testing.T) { } } } + +func TestVTACallGraphGenerics(t *testing.T) { + if !typeparams.Enabled { + t.Skip("TestVTACallGraphGenerics requires type parameters") + } + + // TODO(zpavlinovic): add more tests + file := "testdata/src/callgraph_generics.go" + prog, want, err := testProg(file, ssa.InstantiateGenerics) + if err != nil { + t.Fatalf("couldn't load test file '%s': %s", file, err) + } + if len(want) == 0 { + t.Fatalf("couldn't find want in `%s`", file) + } + + g := CallGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog)) + if got := callGraphStr(g); !subGraph(want, got) { + t.Errorf("computed callgraph %v should contain %v", got, want) + } +}