golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/go/ssa/builder_go122_test.go (about) 1 // Copyright 2023 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 //go:build go1.22 6 // +build go1.22 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 // TestMultipleGoversions tests that globals initialized to equivalent 25 // function literals are compiled based on the different GoVersion in each file. 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 // Test that global is initialized to a function literal that was 67 // compiled to have the expected for loop range variable lifetime for i. 68 for _, test := range []struct { 69 global *ssa.Global 70 want string // basic block to []*ssa.Alloc. 71 }{ 72 {p.Var("same"), "map[entry:[new int (i)]]"}, // i is allocated in the entry block. 73 {p.Var("distinct"), "map[rangeindex.body:[new int (i)]]"}, // i is allocated in the body block. 74 } { 75 // Find the function the test.name global is initialized to. 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) // block comments -> []Alloc 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 // TestRangeOverInt tests that, in a range-over-int (#61405), 146 // the type of each range var v (identified by print(v) calls) 147 // has the expected type. 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 // Collect all notes in f, i.e. comments starting with "//@ types". 165 notes, err := expect.ExtractGo(fset, f) 166 if err != nil { 167 t.Fatal(err) 168 } 169 170 // Collect calls to the built-in print function. 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 // Check each expectation. 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 }