1
2
3
4
5
6
7
8 package ssa_test
9
10 import (
11 "fmt"
12 "go/ast"
13 "go/parser"
14 "go/token"
15 "go/types"
16 "testing"
17
18 "golang.org/x/tools/go/expect"
19 "golang.org/x/tools/go/ssa"
20 "golang.org/x/tools/go/ssa/ssautil"
21 "golang.org/x/tools/internal/testenv"
22 )
23
24
25
26 func TestMultipleGoversions(t *testing.T) {
27 var contents = map[string]string{
28 "post.go": `
29 //go:build go1.22
30 package p
31
32 var distinct = func(l []int) {
33 for i := range l {
34 print(&i)
35 }
36 }
37 `,
38 "pre.go": `
39 package p
40
41 var same = func(l []int) {
42 for i := range l {
43 print(&i)
44 }
45 }
46 `,
47 }
48
49 fset := token.NewFileSet()
50 var files []*ast.File
51 for _, fname := range []string{"post.go", "pre.go"} {
52 file, err := parser.ParseFile(fset, fname, contents[fname], 0)
53 if err != nil {
54 t.Fatal(err)
55 }
56 files = append(files, file)
57 }
58
59 pkg := types.NewPackage("p", "")
60 conf := &types.Config{Importer: nil, GoVersion: "go1.21"}
61 p, _, err := ssautil.BuildPackage(conf, fset, pkg, files, ssa.SanityCheckFunctions)
62 if err != nil {
63 t.Fatal(err)
64 }
65
66
67
68 for _, test := range []struct {
69 global *ssa.Global
70 want string
71 }{
72 {p.Var("same"), "map[entry:[new int (i)]]"},
73 {p.Var("distinct"), "map[rangeindex.body:[new int (i)]]"},
74 } {
75
76 var fn *ssa.Function
77 for _, b := range p.Func("init").Blocks {
78 for _, instr := range b.Instrs {
79 if s, ok := instr.(*ssa.Store); ok && s.Addr == test.global {
80 fn, _ = s.Val.(*ssa.Function)
81 }
82 }
83 }
84 if fn == nil {
85 t.Fatalf("Failed to find *ssa.Function for initial value of global %s", test.global)
86 }
87
88 allocs := make(map[string][]string)
89 for _, b := range fn.Blocks {
90 for _, instr := range b.Instrs {
91 if a, ok := instr.(*ssa.Alloc); ok {
92 allocs[b.Comment] = append(allocs[b.Comment], a.String())
93 }
94 }
95 }
96 if got := fmt.Sprint(allocs); got != test.want {
97 t.Errorf("[%s:=%s] expected the allocations to be in the basic blocks %q, got %q", test.global, fn, test.want, got)
98 }
99 }
100 }
101
102 const rangeOverIntSrc = `
103 package p
104
105 type I uint8
106
107 func noKey(x int) {
108 for range x {
109 // does not crash
110 }
111 }
112
113 func untypedConstantOperand() {
114 for i := range 10 {
115 print(i) /*@ types("int")*/
116 }
117 }
118
119 func unsignedOperand(x uint64) {
120 for i := range x {
121 print(i) /*@ types("uint64")*/
122 }
123 }
124
125 func namedOperand(x I) {
126 for i := range x {
127 print(i) /*@ types("p.I")*/
128 }
129 }
130
131 func typeparamOperand[T int](x T) {
132 for i := range x {
133 print(i) /*@ types("T")*/
134 }
135 }
136
137 func assignment(x I) {
138 var k I
139 for k = range x {
140 print(k) /*@ types("p.I")*/
141 }
142 }
143 `
144
145
146
147
148 func TestRangeOverInt(t *testing.T) {
149 testenv.NeedsGoExperiment(t, "range")
150
151 fset := token.NewFileSet()
152 f, err := parser.ParseFile(fset, "p.go", rangeOverIntSrc, parser.ParseComments)
153 if err != nil {
154 t.Fatal(err)
155 }
156
157 pkg := types.NewPackage("p", "")
158 conf := &types.Config{}
159 p, _, err := ssautil.BuildPackage(conf, fset, pkg, []*ast.File{f}, ssa.SanityCheckFunctions)
160 if err != nil {
161 t.Fatal(err)
162 }
163
164
165 notes, err := expect.ExtractGo(fset, f)
166 if err != nil {
167 t.Fatal(err)
168 }
169
170
171 probes := callsTo(p, "print")
172 expectations := matchNotes(fset, notes, probes)
173
174 for call := range probes {
175 if expectations[call] == nil {
176 t.Errorf("Unmatched call: %v @ %s", call, fset.Position(call.Pos()))
177 }
178 }
179
180
181 for call, note := range expectations {
182 var args []string
183 for _, a := range call.Args {
184 args = append(args, a.Type().String())
185 }
186 if got, want := fmt.Sprint(args), fmt.Sprint(note.Args); got != want {
187 at := fset.Position(call.Pos())
188 t.Errorf("%s: arguments to print had types %s, want %s", at, got, want)
189 logFunction(t, probes[call])
190 }
191 }
192 }
193
View as plain text