Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.0.1-2020.1.5 / ir / builder_test.go
1 // Copyright 2013 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 //lint:file-ignore SA1019 go/ssa's test suite is built around the deprecated go/loader. We'll leave fixing that to upstream.
6
7 package ir_test
8
9 import (
10         "bytes"
11         "go/ast"
12         "go/importer"
13         "go/parser"
14         "go/token"
15         "go/types"
16         "os"
17         "reflect"
18         "sort"
19         "strings"
20         "testing"
21
22         "golang.org/x/tools/go/loader"
23         "honnef.co/go/tools/ir"
24         "honnef.co/go/tools/ir/irutil"
25 )
26
27 func isEmpty(f *ir.Function) bool { return f.Blocks == nil }
28
29 // Tests that programs partially loaded from gc object files contain
30 // functions with no code for the external portions, but are otherwise ok.
31 func TestBuildPackage(t *testing.T) {
32         input := `
33 package main
34
35 import (
36         "bytes"
37         "io"
38         "testing"
39 )
40
41 func main() {
42         var t testing.T
43         t.Parallel()    // static call to external declared method
44         t.Fail()        // static call to promoted external declared method
45         testing.Short() // static call to external package-level function
46
47         var w io.Writer = new(bytes.Buffer)
48         w.Write(nil)    // interface invoke of external declared method
49 }
50 `
51
52         // Parse the file.
53         fset := token.NewFileSet()
54         f, err := parser.ParseFile(fset, "input.go", input, 0)
55         if err != nil {
56                 t.Error(err)
57                 return
58         }
59
60         // Build an IR program from the parsed file.
61         // Load its dependencies from gc binary export data.
62         mainPkg, _, err := irutil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
63                 types.NewPackage("main", ""), []*ast.File{f}, ir.SanityCheckFunctions)
64         if err != nil {
65                 t.Error(err)
66                 return
67         }
68
69         // The main package, its direct and indirect dependencies are loaded.
70         deps := []string{
71                 // directly imported dependencies:
72                 "bytes", "io", "testing",
73                 // indirect dependencies mentioned by
74                 // the direct imports' export data
75                 "sync", "unicode", "time",
76         }
77
78         prog := mainPkg.Prog
79         all := prog.AllPackages()
80         if len(all) <= len(deps) {
81                 t.Errorf("unexpected set of loaded packages: %q", all)
82         }
83         for _, path := range deps {
84                 pkg := prog.ImportedPackage(path)
85                 if pkg == nil {
86                         t.Errorf("package not loaded: %q", path)
87                         continue
88                 }
89
90                 // External packages should have no function bodies (except for wrappers).
91                 isExt := pkg != mainPkg
92
93                 // init()
94                 if isExt && !isEmpty(pkg.Func("init")) {
95                         t.Errorf("external package %s has non-empty init", pkg)
96                 } else if !isExt && isEmpty(pkg.Func("init")) {
97                         t.Errorf("main package %s has empty init", pkg)
98                 }
99
100                 for _, mem := range pkg.Members {
101                         switch mem := mem.(type) {
102                         case *ir.Function:
103                                 // Functions at package level.
104                                 if isExt && !isEmpty(mem) {
105                                         t.Errorf("external function %s is non-empty", mem)
106                                 } else if !isExt && isEmpty(mem) {
107                                         t.Errorf("function %s is empty", mem)
108                                 }
109
110                         case *ir.Type:
111                                 // Methods of named types T.
112                                 // (In this test, all exported methods belong to *T not T.)
113                                 if !isExt {
114                                         t.Fatalf("unexpected name type in main package: %s", mem)
115                                 }
116                                 mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
117                                 for i, n := 0, mset.Len(); i < n; i++ {
118                                         m := prog.MethodValue(mset.At(i))
119                                         // For external types, only synthetic wrappers have code.
120                                         expExt := !strings.Contains(m.Synthetic, "wrapper")
121                                         if expExt && !isEmpty(m) {
122                                                 t.Errorf("external method %s is non-empty: %s",
123                                                         m, m.Synthetic)
124                                         } else if !expExt && isEmpty(m) {
125                                                 t.Errorf("method function %s is empty: %s",
126                                                         m, m.Synthetic)
127                                         }
128                                 }
129                         }
130                 }
131         }
132
133         expectedCallee := []string{
134                 "(*testing.T).Parallel",
135                 "(*testing.common).Fail",
136                 "testing.Short",
137                 "N/A",
138         }
139         callNum := 0
140         for _, b := range mainPkg.Func("main").Blocks {
141                 for _, instr := range b.Instrs {
142                         switch instr := instr.(type) {
143                         case ir.CallInstruction:
144                                 call := instr.Common()
145                                 if want := expectedCallee[callNum]; want != "N/A" {
146                                         got := call.StaticCallee().String()
147                                         if want != got {
148                                                 t.Errorf("call #%d from main.main: got callee %s, want %s",
149                                                         callNum, got, want)
150                                         }
151                                 }
152                                 callNum++
153                         }
154                 }
155         }
156         if callNum != 4 {
157                 t.Errorf("in main.main: got %d calls, want %d", callNum, 4)
158         }
159 }
160
161 // TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types.
162 func TestRuntimeTypes(t *testing.T) {
163         tests := []struct {
164                 input string
165                 want  []string
166         }{
167                 // An exported package-level type is needed.
168                 {`package A; type T struct{}; func (T) f() {}`,
169                         []string{"*p.T", "p.T"},
170                 },
171                 // An unexported package-level type is not needed.
172                 {`package B; type t struct{}; func (t) f() {}`,
173                         nil,
174                 },
175                 // Subcomponents of type of exported package-level var are needed.
176                 {`package C; import "bytes"; var V struct {*bytes.Buffer}`,
177                         []string{"*bytes.Buffer", "*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"},
178                 },
179                 // Subcomponents of type of unexported package-level var are not needed.
180                 {`package D; import "bytes"; var v struct {*bytes.Buffer}`,
181                         nil,
182                 },
183                 // Subcomponents of type of exported package-level function are needed.
184                 {`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}`,
185                         []string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
186                 },
187                 // Subcomponents of type of unexported package-level function are not needed.
188                 {`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`,
189                         nil,
190                 },
191                 // Subcomponents of type of exported method of uninstantiated unexported type are not needed.
192                 {`package G; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v x`,
193                         nil,
194                 },
195                 // ...unless used by MakeInterface.
196                 {`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`,
197                         []string{"*bytes.Buffer", "*p.x", "p.x", "struct{*bytes.Buffer}"},
198                 },
199                 // Subcomponents of type of unexported method are not needed.
200                 {`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`,
201                         []string{"*bytes.Buffer", "*p.X", "p.X", "struct{*bytes.Buffer}"},
202                 },
203                 // Local types aren't needed.
204                 {`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`,
205                         nil,
206                 },
207                 // ...unless used by MakeInterface.
208                 {`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`,
209                         []string{"*bytes.Buffer", "*p.T", "p.T"},
210                 },
211                 // Types used as operand of MakeInterface are needed.
212                 {`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`,
213                         []string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
214                 },
215                 // MakeInterface is optimized away when storing to a blank.
216                 {`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`,
217                         nil,
218                 },
219         }
220         for _, test := range tests {
221                 // Parse the file.
222                 fset := token.NewFileSet()
223                 f, err := parser.ParseFile(fset, "input.go", test.input, 0)
224                 if err != nil {
225                         t.Errorf("test %q: %s", test.input[:15], err)
226                         continue
227                 }
228
229                 // Create a single-file main package.
230                 // Load dependencies from gc binary export data.
231                 irpkg, _, err := irutil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
232                         types.NewPackage("p", ""), []*ast.File{f}, ir.SanityCheckFunctions)
233                 if err != nil {
234                         t.Errorf("test %q: %s", test.input[:15], err)
235                         continue
236                 }
237
238                 var typstrs []string
239                 for _, T := range irpkg.Prog.RuntimeTypes() {
240                         typstrs = append(typstrs, T.String())
241                 }
242                 sort.Strings(typstrs)
243
244                 if !reflect.DeepEqual(typstrs, test.want) {
245                         t.Errorf("test 'package %s': got %q, want %q",
246                                 f.Name.Name, typstrs, test.want)
247                 }
248         }
249 }
250
251 // TestInit tests that synthesized init functions are correctly formed.
252 func TestInit(t *testing.T) {
253         tests := []struct {
254                 mode        ir.BuilderMode
255                 input, want string
256         }{
257                 {0, `package A; import _ "errors"; var i int = 42`,
258                         `# Name: A.init
259 # Package: A
260 # Synthetic: package initializer
261 func init():
262 b0: # entry
263         t1 = Const <bool> {true}
264         t2 = Const <int> {42}
265         t3 = Load <bool> init$guard
266         If t3 → b1 b2
267
268 b1: ← b0 b2 # exit
269         Return
270
271 b2: ← b0 # init.start
272         Store {bool} init$guard t1
273         t7 = Call <()> errors.init
274         Store {int} i t2
275         Jump → b1
276
277 `},
278         }
279         for _, test := range tests {
280                 // Create a single-file main package.
281                 var conf loader.Config
282                 f, err := conf.ParseFile("<input>", test.input)
283                 if err != nil {
284                         t.Errorf("test %q: %s", test.input[:15], err)
285                         continue
286                 }
287                 conf.CreateFromFiles(f.Name.Name, f)
288
289                 lprog, err := conf.Load()
290                 if err != nil {
291                         t.Errorf("test 'package %s': Load: %s", f.Name.Name, err)
292                         continue
293                 }
294                 prog := irutil.CreateProgram(lprog, test.mode)
295                 mainPkg := prog.Package(lprog.Created[0].Pkg)
296                 prog.Build()
297                 initFunc := mainPkg.Func("init")
298                 if initFunc == nil {
299                         t.Errorf("test 'package %s': no init function", f.Name.Name)
300                         continue
301                 }
302
303                 var initbuf bytes.Buffer
304                 _, err = initFunc.WriteTo(&initbuf)
305                 if err != nil {
306                         t.Errorf("test 'package %s': WriteTo: %s", f.Name.Name, err)
307                         continue
308                 }
309
310                 if initbuf.String() != test.want {
311                         t.Errorf("test 'package %s': got %s, want %s", f.Name.Name, initbuf.String(), test.want)
312                 }
313         }
314 }
315
316 // TestSyntheticFuncs checks that the expected synthetic functions are
317 // created, reachable, and not duplicated.
318 func TestSyntheticFuncs(t *testing.T) {
319         const input = `package P
320 type T int
321 func (T) f() int
322 func (*T) g() int
323 var (
324         // thunks
325         a = T.f
326         b = T.f
327         c = (struct{T}).f
328         d = (struct{T}).f
329         e = (*T).g
330         f = (*T).g
331         g = (struct{*T}).g
332         h = (struct{*T}).g
333
334         // bounds
335         i = T(0).f
336         j = T(0).f
337         k = new(T).g
338         l = new(T).g
339
340         // wrappers
341         m interface{} = struct{T}{}
342         n interface{} = struct{T}{}
343         o interface{} = struct{*T}{}
344         p interface{} = struct{*T}{}
345         q interface{} = new(struct{T})
346         r interface{} = new(struct{T})
347         s interface{} = new(struct{*T})
348         t interface{} = new(struct{*T})
349 )
350 `
351         // Parse
352         var conf loader.Config
353         f, err := conf.ParseFile("<input>", input)
354         if err != nil {
355                 t.Fatalf("parse: %v", err)
356         }
357         conf.CreateFromFiles(f.Name.Name, f)
358
359         // Load
360         lprog, err := conf.Load()
361         if err != nil {
362                 t.Fatalf("Load: %v", err)
363         }
364
365         // Create and build IR
366         prog := irutil.CreateProgram(lprog, 0)
367         prog.Build()
368
369         // Enumerate reachable synthetic functions
370         want := map[string]string{
371                 "(*P.T).g$bound": "bound method wrapper for func (*P.T).g() int",
372                 "(P.T).f$bound":  "bound method wrapper for func (P.T).f() int",
373
374                 "(*P.T).g$thunk":         "thunk for func (*P.T).g() int",
375                 "(P.T).f$thunk":          "thunk for func (P.T).f() int",
376                 "(struct{*P.T}).g$thunk": "thunk for func (*P.T).g() int",
377                 "(struct{P.T}).f$thunk":  "thunk for func (P.T).f() int",
378
379                 "(*P.T).f":          "wrapper for func (P.T).f() int",
380                 "(*struct{*P.T}).f": "wrapper for func (P.T).f() int",
381                 "(*struct{*P.T}).g": "wrapper for func (*P.T).g() int",
382                 "(*struct{P.T}).f":  "wrapper for func (P.T).f() int",
383                 "(*struct{P.T}).g":  "wrapper for func (*P.T).g() int",
384                 "(struct{*P.T}).f":  "wrapper for func (P.T).f() int",
385                 "(struct{*P.T}).g":  "wrapper for func (*P.T).g() int",
386                 "(struct{P.T}).f":   "wrapper for func (P.T).f() int",
387
388                 "P.init": "package initializer",
389         }
390         for fn := range irutil.AllFunctions(prog) {
391                 if fn.Synthetic == "" {
392                         continue
393                 }
394                 name := fn.String()
395                 wantDescr, ok := want[name]
396                 if !ok {
397                         t.Errorf("got unexpected/duplicate func: %q: %q", name, fn.Synthetic)
398                         continue
399                 }
400                 delete(want, name)
401
402                 if wantDescr != fn.Synthetic {
403                         t.Errorf("(%s).Synthetic = %q, want %q", name, fn.Synthetic, wantDescr)
404                 }
405         }
406         for fn, descr := range want {
407                 t.Errorf("want func: %q: %q", fn, descr)
408         }
409 }
410
411 // TestPhiElimination ensures that dead phis, including those that
412 // participate in a cycle, are properly eliminated.
413 func TestPhiElimination(t *testing.T) {
414         const input = `
415 package p
416
417 func f() error
418
419 func g(slice []int) {
420         for {
421                 for range slice {
422                         // e should not be lifted to a dead φ-node.
423                         e := f()
424                         h(e)
425                 }
426         }
427 }
428
429 func h(error)
430 `
431         // The IR code for this function should look something like this:
432         // 0:
433         //         jump 1
434         // 1:
435         //         t0 = len(slice)
436         //         jump 2
437         // 2:
438         //         t1 = phi [1: -1:int, 3: t2]
439         //         t2 = t1 + 1:int
440         //         t3 = t2 < t0
441         //         if t3 goto 3 else 1
442         // 3:
443         //         t4 = f()
444         //         t5 = h(t4)
445         //         jump 2
446         //
447         // But earlier versions of the IR construction algorithm would
448         // additionally generate this cycle of dead phis:
449         //
450         // 1:
451         //         t7 = phi [0: nil:error, 2: t8] #e
452         //         ...
453         // 2:
454         //         t8 = phi [1: t7, 3: t4] #e
455         //         ...
456
457         // Parse
458         var conf loader.Config
459         f, err := conf.ParseFile("<input>", input)
460         if err != nil {
461                 t.Fatalf("parse: %v", err)
462         }
463         conf.CreateFromFiles("p", f)
464
465         // Load
466         lprog, err := conf.Load()
467         if err != nil {
468                 t.Fatalf("Load: %v", err)
469         }
470
471         // Create and build IR
472         prog := irutil.CreateProgram(lprog, 0)
473         p := prog.Package(lprog.Package("p").Pkg)
474         p.Build()
475         g := p.Func("g")
476
477         phis := 0
478         for _, b := range g.Blocks {
479                 for _, instr := range b.Instrs {
480                         if _, ok := instr.(*ir.Phi); ok {
481                                 phis++
482                         }
483                 }
484         }
485         if expected := 1; phis != expected {
486                 g.WriteTo(os.Stderr)
487                 t.Errorf("expected %d Phi nodes (for the range index), got %d", expected, phis)
488         }
489 }