Skip to content

Commit 4fb36d1

Browse files
xieyuschentimothy-king
authored andcommitted
go/callgraph/rta: add rta analysis test case for multiple go packages
* use go/packages to load packages Change-Id: I6e9f81b282cddc186b4905a23ff635cd98245ac8 GitHub-Last-Rev: a859e27 GitHub-Pull-Request: #513 Reviewed-on: https://go-review.googlesource.com/c/tools/+/609576 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Tim King <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent dc4d64c commit 4fb36d1

File tree

7 files changed

+216
-47
lines changed

7 files changed

+216
-47
lines changed

go/callgraph/rta/rta_test.go

Lines changed: 77 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,47 +12,66 @@ package rta_test
1212
import (
1313
"fmt"
1414
"go/ast"
15-
"go/parser"
1615
"go/types"
1716
"sort"
1817
"strings"
1918
"testing"
2019

2120
"golang.org/x/tools/go/callgraph"
2221
"golang.org/x/tools/go/callgraph/rta"
23-
"golang.org/x/tools/go/loader"
22+
"golang.org/x/tools/go/packages"
2423
"golang.org/x/tools/go/ssa"
2524
"golang.org/x/tools/go/ssa/ssautil"
2625
"golang.org/x/tools/internal/aliases"
26+
"golang.org/x/tools/internal/testfiles"
27+
"golang.org/x/tools/txtar"
2728
)
2829

29-
// TestRTA runs RTA on each testdata/*.go file and compares the
30-
// results with the expectations expressed in the WANT comment.
30+
// TestRTA runs RTA on each testdata/*.txtar file containing a single
31+
// go file in a single package or multiple files in different packages,
32+
// and compares the results with the expectations expressed in the WANT
33+
// comment.
3134
func TestRTA(t *testing.T) {
32-
filenames := []string{
33-
"testdata/func.go",
34-
"testdata/generics.go",
35-
"testdata/iface.go",
36-
"testdata/reflectcall.go",
37-
"testdata/rtype.go",
35+
archivePaths := []string{
36+
"testdata/func.txtar",
37+
"testdata/generics.txtar",
38+
"testdata/iface.txtar",
39+
"testdata/reflectcall.txtar",
40+
"testdata/rtype.txtar",
41+
"testdata/multipkgs.txtar",
3842
}
39-
for _, filename := range filenames {
40-
t.Run(filename, func(t *testing.T) {
41-
// Load main program and build SSA.
42-
// TODO(adonovan): use go/packages instead.
43-
conf := loader.Config{ParserMode: parser.ParseComments}
44-
f, err := conf.ParseFile(filename, nil)
45-
if err != nil {
46-
t.Fatal(err)
43+
for _, archive := range archivePaths {
44+
t.Run(archive, func(t *testing.T) {
45+
pkgs := loadPackages(t, archive)
46+
47+
// find the file which contains the expected result
48+
var f *ast.File
49+
for _, p := range pkgs {
50+
// We assume the packages have a single file or
51+
// the wanted result is in the first file of the main package.
52+
if p.Name == "main" {
53+
f = p.Syntax[0]
54+
}
4755
}
48-
conf.CreateFromFiles("main", f)
49-
lprog, err := conf.Load()
50-
if err != nil {
51-
t.Fatal(err)
56+
if f == nil {
57+
t.Fatalf("failed to find the file with expected result within main package %s", archive)
5258
}
53-
prog := ssautil.CreateProgram(lprog, ssa.InstantiateGenerics)
59+
60+
prog, spkgs := ssautil.Packages(pkgs, ssa.SanityCheckFunctions|ssa.InstantiateGenerics)
61+
62+
// find the main package to get functions for rta analysis
63+
var mainPkg *ssa.Package
64+
for _, sp := range spkgs {
65+
if sp.Pkg.Name() == "main" {
66+
mainPkg = sp
67+
break
68+
}
69+
}
70+
if mainPkg == nil {
71+
t.Fatalf("failed to find main ssa package %s", archive)
72+
}
73+
5474
prog.Build()
55-
mainPkg := prog.Package(lprog.Created[0].Pkg)
5675

5776
res := rta.Analyze([]*ssa.Function{
5877
mainPkg.Func("main"),
@@ -64,6 +83,40 @@ func TestRTA(t *testing.T) {
6483
}
6584
}
6685

86+
// loadPackages unpacks the archive to a temporary directory and loads all packages within it.
87+
func loadPackages(t *testing.T, archive string) []*packages.Package {
88+
ar, err := txtar.ParseFile(archive)
89+
if err != nil {
90+
t.Fatal(err)
91+
}
92+
93+
fs, err := txtar.FS(ar)
94+
if err != nil {
95+
t.Fatal(err)
96+
}
97+
dir := testfiles.CopyToTmp(t, fs)
98+
99+
var baseConfig = &packages.Config{
100+
Mode: packages.NeedSyntax |
101+
packages.NeedTypesInfo |
102+
packages.NeedDeps |
103+
packages.NeedName |
104+
packages.NeedFiles |
105+
packages.NeedImports |
106+
packages.NeedCompiledGoFiles |
107+
packages.NeedTypes,
108+
Dir: dir,
109+
}
110+
pkgs, err := packages.Load(baseConfig, "./...")
111+
if err != nil {
112+
t.Fatal(err)
113+
}
114+
if num := packages.PrintErrors(pkgs); num > 0 {
115+
t.Fatalf("packages contained %d errors", num)
116+
}
117+
return pkgs
118+
}
119+
67120
// check tests the RTA analysis results against the test expectations
68121
// defined by a comment starting with a line "WANT:".
69122
//

go/callgraph/rta/testdata/func.go renamed to go/callgraph/rta/testdata/func.txtar

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
//go:build ignore
2-
// +build ignore
1+
-- go.mod --
2+
module example.com
3+
go 1.18
34

5+
-- func.go --
46
package main
57

68
// Test of dynamic function calls.
@@ -36,4 +38,4 @@ func main() {
3638
// reachable init$1
3739
// reachable init$2
3840
// !reachable B
39-
// reachable main
41+
// reachable main

go/callgraph/rta/testdata/generics.go renamed to go/callgraph/rta/testdata/generics.txtar

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
//go:build ignore
2-
// +build ignore
1+
-- go.mod --
2+
module example.com
3+
go 1.18
34

5+
-- generics.go --
46
package main
57

68
// Test of generic function calls.
@@ -53,27 +55,27 @@ func lambda[X I]() func() func() {
5355
//
5456
// edge (*C).Foo --static method call--> (C).Foo
5557
// edge (A).Foo$bound --static method call--> (A).Foo
56-
// edge instantiated[main.A] --static method call--> (A).Foo
57-
// edge instantiated[main.B] --static method call--> (B).Foo
58+
// edge instantiated[example.com.A] --static method call--> (A).Foo
59+
// edge instantiated[example.com.B] --static method call--> (B).Foo
5860
// edge main --dynamic method call--> (*C).Foo
5961
// edge main --dynamic function call--> (A).Foo$bound
6062
// edge main --dynamic method call--> (C).Foo
61-
// edge main --static function call--> instantiated[main.A]
62-
// edge main --static function call--> instantiated[main.B]
63-
// edge main --static function call--> lambda[main.A]
64-
// edge main --dynamic function call--> lambda[main.A]$1
65-
// edge main --static function call--> local[main.C]
63+
// edge main --static function call--> instantiated[example.com.A]
64+
// edge main --static function call--> instantiated[example.com.B]
65+
// edge main --static function call--> lambda[example.com.A]
66+
// edge main --dynamic function call--> lambda[example.com.A]$1
67+
// edge main --static function call--> local[example.com.C]
6668
//
6769
// reachable (*C).Foo
6870
// reachable (A).Foo
6971
// reachable (A).Foo$bound
7072
// reachable (B).Foo
7173
// reachable (C).Foo
72-
// reachable instantiated[main.A]
73-
// reachable instantiated[main.B]
74-
// reachable lambda[main.A]
75-
// reachable lambda[main.A]$1
76-
// reachable local[main.C]
74+
// reachable instantiated[example.com.A]
75+
// reachable instantiated[example.com.B]
76+
// reachable lambda[example.com.A]
77+
// reachable lambda[example.com.A]$1
78+
// reachable local[example.com.C]
7779
//
7880
// rtype *C
7981
// rtype C

go/callgraph/rta/testdata/iface.go renamed to go/callgraph/rta/testdata/iface.txtar

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
//go:build ignore
2-
// +build ignore
1+
-- go.mod --
2+
module example.com
3+
go 1.18
34

5+
-- iface.go --
46
package main
57

68
// Test of interface calls.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
-- go.mod --
2+
module example.com
3+
go 1.18
4+
5+
-- iface.go --
6+
package main
7+
8+
import (
9+
"example.com/subpkg"
10+
)
11+
12+
func use(interface{})
13+
14+
// Test of interface calls.
15+
16+
func main() {
17+
use(subpkg.A(0))
18+
use(new(subpkg.B))
19+
use(subpkg.B2(0))
20+
21+
var i interface {
22+
F()
23+
}
24+
25+
// assign an interface type with a function return interface value
26+
i = subpkg.NewInterfaceF()
27+
28+
i.F()
29+
}
30+
31+
func dead() {
32+
use(subpkg.D(0))
33+
}
34+
35+
// WANT:
36+
//
37+
// edge (*example.com/subpkg.A).F --static method call--> (example.com/subpkg.A).F
38+
// edge (*example.com/subpkg.B2).F --static method call--> (example.com/subpkg.B2).F
39+
// edge (*example.com/subpkg.C).F --static method call--> (example.com/subpkg.C).F
40+
// edge init --static function call--> example.com/subpkg.init
41+
// edge main --dynamic method call--> (*example.com/subpkg.A).F
42+
// edge main --dynamic method call--> (*example.com/subpkg.B).F
43+
// edge main --dynamic method call--> (*example.com/subpkg.B2).F
44+
// edge main --dynamic method call--> (*example.com/subpkg.C).F
45+
// edge main --dynamic method call--> (example.com/subpkg.A).F
46+
// edge main --dynamic method call--> (example.com/subpkg.B2).F
47+
// edge main --dynamic method call--> (example.com/subpkg.C).F
48+
// edge main --static function call--> example.com/subpkg.NewInterfaceF
49+
// edge main --static function call--> use
50+
//
51+
// reachable (*example.com/subpkg.A).F
52+
// reachable (*example.com/subpkg.B).F
53+
// reachable (*example.com/subpkg.B2).F
54+
// reachable (*example.com/subpkg.C).F
55+
// reachable (example.com/subpkg.A).F
56+
// !reachable (example.com/subpkg.B).F
57+
// reachable (example.com/subpkg.B2).F
58+
// reachable (example.com/subpkg.C).F
59+
// reachable example.com/subpkg.NewInterfaceF
60+
// reachable example.com/subpkg.init
61+
// !reachable (*example.com/subpkg.D).F
62+
// !reachable (example.com/subpkg.D).F
63+
// reachable init
64+
// reachable main
65+
// reachable use
66+
//
67+
// rtype *example.com/subpkg.A
68+
// rtype *example.com/subpkg.B
69+
// rtype *example.com/subpkg.B2
70+
// rtype *example.com/subpkg.C
71+
// rtype example.com/subpkg.B
72+
// rtype example.com/subpkg.A
73+
// rtype example.com/subpkg.B2
74+
// rtype example.com/subpkg.C
75+
// !rtype example.com/subpkg.D
76+
77+
-- subpkg/impl.go --
78+
package subpkg
79+
80+
type InterfaceF interface {
81+
F()
82+
}
83+
84+
type A byte // instantiated but not a reflect type
85+
86+
func (A) F() {} // reachable: exported method of reflect type
87+
88+
type B int // a reflect type
89+
90+
func (*B) F() {} // reachable: exported method of reflect type
91+
92+
type B2 int // a reflect type, and *B2 also
93+
94+
func (B2) F() {} // reachable: exported method of reflect type
95+
96+
type C string
97+
98+
func (C) F() {} // reachable: exported by NewInterfaceF
99+
100+
func NewInterfaceF() InterfaceF {
101+
return C("")
102+
}
103+
104+
type D uint // instantiated only in dead code
105+
106+
func (*D) F() {} // unreachable

go/callgraph/rta/testdata/reflectcall.go renamed to go/callgraph/rta/testdata/reflectcall.txtar

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
//go:build ignore
2-
// +build ignore
1+
-- go.mod --
2+
module example.com
3+
go 1.18
34

5+
-- reflectcall.go --
46
// Test of a reflective call to an address-taken function.
57
//
68
// Dynamically, this program executes both print statements.

go/callgraph/rta/testdata/rtype.go renamed to go/callgraph/rta/testdata/rtype.txtar

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
//go:build ignore
2-
// +build ignore
1+
-- go.mod --
2+
module example.com
3+
go 1.18
34

5+
-- rtype.go --
46
package main
57

68
// Test of runtime types (types for which descriptors are needed).

0 commit comments

Comments
 (0)