...
1
2
3
4
5 package static_test
6
7 import (
8 "fmt"
9 "go/parser"
10 "reflect"
11 "sort"
12 "testing"
13
14 "golang.org/x/tools/go/callgraph"
15 "golang.org/x/tools/go/callgraph/static"
16 "golang.org/x/tools/go/loader"
17 "golang.org/x/tools/go/ssa"
18 "golang.org/x/tools/go/ssa/ssautil"
19 )
20
21 const input = `package P
22
23 type C int
24 func (C) f()
25
26 type I interface{f()}
27
28 func f() {
29 p := func() {}
30 g()
31 p() // SSA constant propagation => static
32
33 if unknown {
34 p = h
35 }
36 p() // dynamic
37
38 C(0).f()
39 }
40
41 func g() {
42 var i I = C(0)
43 i.f()
44 }
45
46 func h()
47
48 var unknown bool
49 `
50
51 const genericsInput = `package P
52
53 type I interface {
54 F()
55 }
56
57 type A struct{}
58
59 func (a A) F() {}
60
61 type B struct{}
62
63 func (b B) F() {}
64
65 func instantiated[X I](x X) {
66 x.F()
67 }
68
69 func Bar() {}
70
71 func f(h func(), a A, b B) {
72 h()
73
74 instantiated[A](a)
75 instantiated[B](b)
76 }
77 `
78
79 func TestStatic(t *testing.T) {
80 for _, e := range []struct {
81 input string
82 want []string
83
84 typeparams bool
85 }{
86 {input, []string{
87 "(*C).f -> (C).f",
88 "f -> (C).f",
89 "f -> f$1",
90 "f -> g",
91 }, false},
92 {genericsInput, []string{
93 "(*A).F -> (A).F",
94 "(*B).F -> (B).F",
95 "f -> instantiated[P.A]",
96 "f -> instantiated[P.B]",
97 "instantiated[P.A] -> (A).F",
98 "instantiated[P.B] -> (B).F",
99 }, true},
100 } {
101 conf := loader.Config{ParserMode: parser.ParseComments}
102 f, err := conf.ParseFile("P.go", e.input)
103 if err != nil {
104 t.Error(err)
105 continue
106 }
107
108 conf.CreateFromFiles("P", f)
109 iprog, err := conf.Load()
110 if err != nil {
111 t.Error(err)
112 continue
113 }
114
115 P := iprog.Created[0].Pkg
116
117 prog := ssautil.CreateProgram(iprog, ssa.InstantiateGenerics)
118 prog.Build()
119
120 cg := static.CallGraph(prog)
121
122 var edges []string
123 callgraph.GraphVisitEdges(cg, func(e *callgraph.Edge) error {
124 edges = append(edges, fmt.Sprintf("%s -> %s",
125 e.Caller.Func.RelString(P),
126 e.Callee.Func.RelString(P)))
127 return nil
128 })
129 sort.Strings(edges)
130
131 if !reflect.DeepEqual(edges, e.want) {
132 t.Errorf("Got edges %v, want %v", edges, e.want)
133 }
134 }
135 }
136
View as plain text