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 <zpavlinovic@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Tim King <taking@google.com>
This commit is contained in:
Zvonimir Pavlinovic 2022-04-25 15:38:59 -07:00
parent b44fad8412
commit dcaea06afc
5 changed files with 103 additions and 8 deletions

View File

@ -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)
}

View File

@ -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()

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}
}