1 | // Copyright 2014 The Go Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE file. |
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 | "golang.org/x/tools/internal/typeparams" |
20 | ) |
21 | |
22 | const input = `package P |
23 | |
24 | type C int |
25 | func (C) f() |
26 | |
27 | type I interface{f()} |
28 | |
29 | func f() { |
30 | p := func() {} |
31 | g() |
32 | p() // SSA constant propagation => static |
33 | |
34 | if unknown { |
35 | p = h |
36 | } |
37 | p() // dynamic |
38 | |
39 | C(0).f() |
40 | } |
41 | |
42 | func g() { |
43 | var i I = C(0) |
44 | i.f() |
45 | } |
46 | |
47 | func h() |
48 | |
49 | var unknown bool |
50 | ` |
51 | |
52 | const genericsInput = `package P |
53 | |
54 | type I interface { |
55 | F() |
56 | } |
57 | |
58 | type A struct{} |
59 | |
60 | func (a A) F() {} |
61 | |
62 | type B struct{} |
63 | |
64 | func (b B) F() {} |
65 | |
66 | func instantiated[X I](x X) { |
67 | x.F() |
68 | } |
69 | |
70 | func Bar() {} |
71 | |
72 | func f(h func(), a A, b B) { |
73 | h() |
74 | |
75 | instantiated[A](a) |
76 | instantiated[B](b) |
77 | } |
78 | ` |
79 | |
80 | func TestStatic(t *testing.T) { |
81 | for _, e := range []struct { |
82 | input string |
83 | want []string |
84 | // typeparams must be true if input uses type parameters |
85 | typeparams bool |
86 | }{ |
87 | {input, []string{ |
88 | "(*C).f -> (C).f", |
89 | "f -> (C).f", |
90 | "f -> f$1", |
91 | "f -> g", |
92 | }, false}, |
93 | {genericsInput, []string{ |
94 | "(*A).F -> (A).F", |
95 | "(*B).F -> (B).F", |
96 | "f -> instantiated[P.A]", |
97 | "f -> instantiated[P.B]", |
98 | "instantiated[P.A] -> (A).F", |
99 | "instantiated[P.B] -> (B).F", |
100 | }, true}, |
101 | } { |
102 | if e.typeparams && !typeparams.Enabled { |
103 | // Skip tests with type parameters when the build |
104 | // environment is not supporting any. |
105 | continue |
106 | } |
107 | |
108 | conf := loader.Config{ParserMode: parser.ParseComments} |
109 | f, err := conf.ParseFile("P.go", e.input) |
110 | if err != nil { |
111 | t.Error(err) |
112 | continue |
113 | } |
114 | |
115 | conf.CreateFromFiles("P", f) |
116 | iprog, err := conf.Load() |
117 | if err != nil { |
118 | t.Error(err) |
119 | continue |
120 | } |
121 | |
122 | P := iprog.Created[0].Pkg |
123 | |
124 | prog := ssautil.CreateProgram(iprog, ssa.InstantiateGenerics) |
125 | prog.Build() |
126 | |
127 | cg := static.CallGraph(prog) |
128 | |
129 | var edges []string |
130 | callgraph.GraphVisitEdges(cg, func(e *callgraph.Edge) error { |
131 | edges = append(edges, fmt.Sprintf("%s -> %s", |
132 | e.Caller.Func.RelString(P), |
133 | e.Callee.Func.RelString(P))) |
134 | return nil |
135 | }) |
136 | sort.Strings(edges) |
137 | |
138 | if !reflect.DeepEqual(edges, e.want) { |
139 | t.Errorf("Got edges %v, want %v", edges, e.want) |
140 | } |
141 | } |
142 | } |
143 |
Members