...

Source file src/golang.org/x/tools/go/ssa/builder_go122_test.go

Documentation: golang.org/x/tools/go/ssa

     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  }
   193  

View as plain text