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