| 1 | // Copyright 2015 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 ssautil_test |
| 6 | |
| 7 | import ( |
| 8 | "bytes" |
| 9 | "go/ast" |
| 10 | "go/importer" |
| 11 | "go/parser" |
| 12 | "go/token" |
| 13 | "go/types" |
| 14 | "os" |
| 15 | "path" |
| 16 | "strings" |
| 17 | "testing" |
| 18 | |
| 19 | "golang.org/x/tools/go/packages" |
| 20 | "golang.org/x/tools/go/packages/packagestest" |
| 21 | "golang.org/x/tools/go/ssa" |
| 22 | "golang.org/x/tools/go/ssa/ssautil" |
| 23 | "golang.org/x/tools/internal/testenv" |
| 24 | ) |
| 25 | |
| 26 | const hello = `package main |
| 27 | |
| 28 | import "fmt" |
| 29 | |
| 30 | func main() { |
| 31 | fmt.Println("Hello, world") |
| 32 | } |
| 33 | ` |
| 34 | |
| 35 | func TestBuildPackage(t *testing.T) { |
| 36 | testenv.NeedsGoBuild(t) // for importer.Default() |
| 37 | |
| 38 | // There is a more substantial test of BuildPackage and the |
| 39 | // SSA program it builds in ../ssa/builder_test.go. |
| 40 | |
| 41 | fset := token.NewFileSet() |
| 42 | f, err := parser.ParseFile(fset, "hello.go", hello, 0) |
| 43 | if err != nil { |
| 44 | t.Fatal(err) |
| 45 | } |
| 46 | |
| 47 | for _, mode := range []ssa.BuilderMode{ |
| 48 | ssa.SanityCheckFunctions, |
| 49 | ssa.InstantiateGenerics | ssa.SanityCheckFunctions, |
| 50 | } { |
| 51 | pkg := types.NewPackage("hello", "") |
| 52 | ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, pkg, []*ast.File{f}, mode) |
| 53 | if err != nil { |
| 54 | t.Fatal(err) |
| 55 | } |
| 56 | if pkg.Name() != "main" { |
| 57 | t.Errorf("pkg.Name() = %s, want main", pkg.Name()) |
| 58 | } |
| 59 | if ssapkg.Func("main") == nil { |
| 60 | ssapkg.WriteTo(os.Stderr) |
| 61 | t.Errorf("ssapkg has no main function") |
| 62 | } |
| 63 | |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | func TestPackages(t *testing.T) { |
| 68 | testenv.NeedsGoPackages(t) |
| 69 | |
| 70 | cfg := &packages.Config{Mode: packages.LoadSyntax} |
| 71 | initial, err := packages.Load(cfg, "bytes") |
| 72 | if err != nil { |
| 73 | t.Fatal(err) |
| 74 | } |
| 75 | if packages.PrintErrors(initial) > 0 { |
| 76 | t.Fatal("there were errors") |
| 77 | } |
| 78 | |
| 79 | for _, mode := range []ssa.BuilderMode{ |
| 80 | ssa.SanityCheckFunctions, |
| 81 | ssa.SanityCheckFunctions | ssa.InstantiateGenerics, |
| 82 | } { |
| 83 | prog, pkgs := ssautil.Packages(initial, mode) |
| 84 | bytesNewBuffer := pkgs[0].Func("NewBuffer") |
| 85 | bytesNewBuffer.Pkg.Build() |
| 86 | |
| 87 | // We'll dump the SSA of bytes.NewBuffer because it is small and stable. |
| 88 | out := new(bytes.Buffer) |
| 89 | bytesNewBuffer.WriteTo(out) |
| 90 | |
| 91 | // For determinism, sanitize the location. |
| 92 | location := prog.Fset.Position(bytesNewBuffer.Pos()).String() |
| 93 | got := strings.Replace(out.String(), location, "$GOROOT/src/bytes/buffer.go:1", -1) |
| 94 | |
| 95 | want := ` |
| 96 | # Name: bytes.NewBuffer |
| 97 | # Package: bytes |
| 98 | # Location: $GOROOT/src/bytes/buffer.go:1 |
| 99 | func NewBuffer(buf []byte) *Buffer: |
| 100 | 0: entry P:0 S:0 |
| 101 | t0 = new Buffer (complit) *Buffer |
| 102 | t1 = &t0.buf [#0] *[]byte |
| 103 | *t1 = buf |
| 104 | return t0 |
| 105 | |
| 106 | `[1:] |
| 107 | if got != want { |
| 108 | t.Errorf("bytes.NewBuffer SSA = <<%s>>, want <<%s>>", got, want) |
| 109 | } |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | func TestBuildPackage_MissingImport(t *testing.T) { |
| 114 | fset := token.NewFileSet() |
| 115 | f, err := parser.ParseFile(fset, "bad.go", `package bad; import "missing"`, 0) |
| 116 | if err != nil { |
| 117 | t.Fatal(err) |
| 118 | } |
| 119 | |
| 120 | pkg := types.NewPackage("bad", "") |
| 121 | ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset, pkg, []*ast.File{f}, ssa.BuilderMode(0)) |
| 122 | if err == nil || ssapkg != nil { |
| 123 | t.Fatal("BuildPackage succeeded unexpectedly") |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | func TestIssue28106(t *testing.T) { |
| 128 | testenv.NeedsGoPackages(t) |
| 129 | |
| 130 | // In go1.10, go/packages loads all packages from source, not |
| 131 | // export data, but does not type check function bodies of |
| 132 | // imported packages. This test ensures that we do not attempt |
| 133 | // to run the SSA builder on functions without type information. |
| 134 | cfg := &packages.Config{Mode: packages.LoadSyntax} |
| 135 | pkgs, err := packages.Load(cfg, "runtime") |
| 136 | if err != nil { |
| 137 | t.Fatal(err) |
| 138 | } |
| 139 | prog, _ := ssautil.Packages(pkgs, ssa.BuilderMode(0)) |
| 140 | prog.Build() // no crash |
| 141 | } |
| 142 | |
| 143 | func TestIssue53604(t *testing.T) { |
| 144 | // Tests that variable initializers are not added to init() when syntax |
| 145 | // is not present but types.Info is available. |
| 146 | // |
| 147 | // Packages x, y, z are loaded with mode `packages.LoadSyntax`. |
| 148 | // Package x imports y, and y imports z. |
| 149 | // Packages are built using ssautil.Packages() with x and z as roots. |
| 150 | // This setup creates y using CreatePackage(pkg, files, info, ...) |
| 151 | // where len(files) == 0 but info != nil. |
| 152 | // |
| 153 | // Tests that globals from y are not initialized. |
| 154 | e := packagestest.Export(t, packagestest.Modules, []packagestest.Module{ |
| 155 | { |
| 156 | Name: "golang.org/fake", |
| 157 | Files: map[string]interface{}{ |
| 158 | "x/x.go": `package x; import "golang.org/fake/y"; var V = y.F()`, |
| 159 | "y/y.go": `package y; import "golang.org/fake/z"; var F = func () *int { return &z.Z } `, |
| 160 | "z/z.go": `package z; var Z int`, |
| 161 | }, |
| 162 | }, |
| 163 | }) |
| 164 | defer e.Cleanup() |
| 165 | |
| 166 | // Load x and z as entry packages using packages.LoadSyntax |
| 167 | e.Config.Mode = packages.LoadSyntax |
| 168 | pkgs, err := packages.Load(e.Config, path.Join(e.Temp(), "fake/x"), path.Join(e.Temp(), "fake/z")) |
| 169 | if err != nil { |
| 170 | t.Fatal(err) |
| 171 | } |
| 172 | for _, p := range pkgs { |
| 173 | if len(p.Errors) > 0 { |
| 174 | t.Fatalf("%v", p.Errors) |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | prog, _ := ssautil.Packages(pkgs, ssa.BuilderMode(0)) |
| 179 | prog.Build() |
| 180 | |
| 181 | // y does not initialize F. |
| 182 | y := prog.ImportedPackage("golang.org/fake/y") |
| 183 | if y == nil { |
| 184 | t.Fatal("Failed to load intermediate package y") |
| 185 | } |
| 186 | yinit := y.Members["init"].(*ssa.Function) |
| 187 | for _, bb := range yinit.Blocks { |
| 188 | for _, i := range bb.Instrs { |
| 189 | if store, ok := i.(*ssa.Store); ok && store.Addr == y.Var("F") { |
| 190 | t.Errorf("y.init() stores to F %v", store) |
| 191 | } |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | } |
| 196 |
Members