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.
5 //lint:file-ignore SA1019 go/ssa's test suite is built around the deprecated go/loader. We'll leave fixing that to upstream.
22 "golang.org/x/tools/go/loader"
23 "honnef.co/go/tools/ir"
24 "honnef.co/go/tools/ir/irutil"
27 func isEmpty(f *ir.Function) bool { return f.Blocks == nil }
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) {
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
47 var w io.Writer = new(bytes.Buffer)
48 w.Write(nil) // interface invoke of external declared method
53 fset := token.NewFileSet()
54 f, err := parser.ParseFile(fset, "input.go", input, 0)
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)
69 // The main package, its direct and indirect dependencies are loaded.
71 // directly imported dependencies:
72 "bytes", "io", "testing",
73 // indirect dependencies mentioned by
74 // the direct imports' export data
75 "sync", "unicode", "time",
79 all := prog.AllPackages()
80 if len(all) <= len(deps) {
81 t.Errorf("unexpected set of loaded packages: %q", all)
83 for _, path := range deps {
84 pkg := prog.ImportedPackage(path)
86 t.Errorf("package not loaded: %q", path)
90 // External packages should have no function bodies (except for wrappers).
91 isExt := pkg != mainPkg
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)
100 for _, mem := range pkg.Members {
101 switch mem := mem.(type) {
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)
111 // Methods of named types T.
112 // (In this test, all exported methods belong to *T not T.)
114 t.Fatalf("unexpected name type in main package: %s", mem)
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",
124 } else if !expExt && isEmpty(m) {
125 t.Errorf("method function %s is empty: %s",
133 expectedCallee := []string{
134 "(*testing.T).Parallel",
135 "(*testing.common).Fail",
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()
148 t.Errorf("call #%d from main.main: got callee %s, want %s",
157 t.Errorf("in main.main: got %d calls, want %d", callNum, 4)
161 // TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types.
162 func TestRuntimeTypes(t *testing.T) {
167 // An exported package-level type is needed.
168 {`package A; type T struct{}; func (T) f() {}`,
169 []string{"*p.T", "p.T"},
171 // An unexported package-level type is not needed.
172 {`package B; type t struct{}; func (t) f() {}`,
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}"},
179 // Subcomponents of type of unexported package-level var are not needed.
180 {`package D; import "bytes"; var v struct {*bytes.Buffer}`,
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}"},
187 // Subcomponents of type of unexported package-level function are not needed.
188 {`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`,
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`,
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}"},
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}"},
203 // Local types aren't needed.
204 {`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`,
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"},
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}"},
215 // MakeInterface is optimized away when storing to a blank.
216 {`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`,
220 for _, test := range tests {
222 fset := token.NewFileSet()
223 f, err := parser.ParseFile(fset, "input.go", test.input, 0)
225 t.Errorf("test %q: %s", test.input[:15], err)
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)
234 t.Errorf("test %q: %s", test.input[:15], err)
239 for _, T := range irpkg.Prog.RuntimeTypes() {
240 typstrs = append(typstrs, T.String())
242 sort.Strings(typstrs)
244 if !reflect.DeepEqual(typstrs, test.want) {
245 t.Errorf("test 'package %s': got %q, want %q",
246 f.Name.Name, typstrs, test.want)
251 // TestInit tests that synthesized init functions are correctly formed.
252 func TestInit(t *testing.T) {
257 {0, `package A; import _ "errors"; var i int = 42`,
260 # Synthetic: package initializer
263 t1 = Const <bool> {true}
264 t2 = Const <int> {42}
265 t3 = Load <bool> init$guard
271 b2: ← b0 # init.start
272 Store {bool} init$guard t1
273 t7 = Call <()> errors.init
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)
284 t.Errorf("test %q: %s", test.input[:15], err)
287 conf.CreateFromFiles(f.Name.Name, f)
289 lprog, err := conf.Load()
291 t.Errorf("test 'package %s': Load: %s", f.Name.Name, err)
294 prog := irutil.CreateProgram(lprog, test.mode)
295 mainPkg := prog.Package(lprog.Created[0].Pkg)
297 initFunc := mainPkg.Func("init")
299 t.Errorf("test 'package %s': no init function", f.Name.Name)
303 var initbuf bytes.Buffer
304 _, err = initFunc.WriteTo(&initbuf)
306 t.Errorf("test 'package %s': WriteTo: %s", f.Name.Name, err)
310 if initbuf.String() != test.want {
311 t.Errorf("test 'package %s': got %s, want %s", f.Name.Name, initbuf.String(), test.want)
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
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})
352 var conf loader.Config
353 f, err := conf.ParseFile("<input>", input)
355 t.Fatalf("parse: %v", err)
357 conf.CreateFromFiles(f.Name.Name, f)
360 lprog, err := conf.Load()
362 t.Fatalf("Load: %v", err)
365 // Create and build IR
366 prog := irutil.CreateProgram(lprog, 0)
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",
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",
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",
388 "P.init": "package initializer",
390 for fn := range irutil.AllFunctions(prog) {
391 if fn.Synthetic == "" {
395 wantDescr, ok := want[name]
397 t.Errorf("got unexpected/duplicate func: %q: %q", name, fn.Synthetic)
402 if wantDescr != fn.Synthetic {
403 t.Errorf("(%s).Synthetic = %q, want %q", name, fn.Synthetic, wantDescr)
406 for fn, descr := range want {
407 t.Errorf("want func: %q: %q", fn, descr)
411 // TestPhiElimination ensures that dead phis, including those that
412 // participate in a cycle, are properly eliminated.
413 func TestPhiElimination(t *testing.T) {
419 func g(slice []int) {
422 // e should not be lifted to a dead φ-node.
431 // The IR code for this function should look something like this:
438 // t1 = phi [1: -1:int, 3: t2]
441 // if t3 goto 3 else 1
447 // But earlier versions of the IR construction algorithm would
448 // additionally generate this cycle of dead phis:
451 // t7 = phi [0: nil:error, 2: t8] #e
454 // t8 = phi [1: t7, 3: t4] #e
458 var conf loader.Config
459 f, err := conf.ParseFile("<input>", input)
461 t.Fatalf("parse: %v", err)
463 conf.CreateFromFiles("p", f)
466 lprog, err := conf.Load()
468 t.Fatalf("Load: %v", err)
471 // Create and build IR
472 prog := irutil.CreateProgram(lprog, 0)
473 p := prog.Package(lprog.Package("p").Pkg)
478 for _, b := range g.Blocks {
479 for _, instr := range b.Instrs {
480 if _, ok := instr.(*ir.Phi); ok {
485 if expected := 1; phis != expected {
487 t.Errorf("expected %d Phi nodes (for the range index), got %d", expected, phis)