golang.org/x/tools@v0.21.0/internal/refactor/inline/calleefx_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 package inline_test 6 7 import ( 8 "fmt" 9 "go/ast" 10 "go/parser" 11 "go/token" 12 "go/types" 13 "testing" 14 15 "golang.org/x/tools/internal/refactor/inline" 16 ) 17 18 // TestCalleeEffects is a unit test of the calleefx analysis. 19 func TestCalleeEffects(t *testing.T) { 20 // Each callee must declare a function or method named f. 21 const funcName = "f" 22 23 var tests = []struct { 24 descr string 25 callee string // Go source file (sans package decl) containing callee decl 26 want string // expected effects string (-1=R∞ -2=W∞) 27 }{ 28 { 29 "Assignments have unknown effects.", 30 `func f(x, y int) { x = y }`, 31 `[0 1 -2]`, 32 }, 33 { 34 "Reads from globals are impure.", 35 `func f() { _ = g }; var g int`, 36 `[-1]`, 37 }, 38 { 39 "Writes to globals have effects.", 40 `func f() { g = 0 }; var g int`, 41 `[-1 -2]`, // the -1 is spurious but benign 42 }, 43 { 44 "Blank assign has no effect.", 45 `func f(x int) { _ = x }`, 46 `[0]`, 47 }, 48 { 49 "Short decl of new var has has no effect.", 50 `func f(x int) { y := x; _ = y }`, 51 `[0]`, 52 }, 53 { 54 "Short decl of existing var (y) is an assignment.", 55 `func f(x int) { y := x; y, z := 1, 2; _, _ = y, z }`, 56 `[0 -2]`, 57 }, 58 { 59 "Unreferenced parameters are excluded.", 60 `func f(x, y, z int) { _ = z + x }`, 61 `[2 0]`, 62 }, 63 { 64 "Built-in len has no effect.", 65 `func f(x, y string) { _ = len(y) + len(x) }`, 66 `[1 0]`, 67 }, 68 { 69 "Built-in println has effects.", 70 `func f(x, y int) { println(y, x) }`, 71 `[1 0 -2]`, 72 }, 73 { 74 "Return has no effect, and no control successor.", 75 `func f(x, y int) int { return x + y; panic(1) }`, 76 `[0 1]`, 77 }, 78 { 79 "Loops (etc) have unknown effects.", 80 `func f(x, y bool) { for x { _ = y } }`, 81 `[0 -2 1]`, 82 }, 83 { 84 "Calls have unknown effects.", 85 `func f(x, y int) { _, _, _ = x, g(), y }; func g() int`, 86 `[0 -2 1]`, 87 }, 88 { 89 "Calls to some built-ins are pure.", 90 `func f(x, y int) { _, _, _ = x, len("hi"), y }`, 91 `[0 1]`, 92 }, 93 { 94 "Calls to some built-ins are pure (variant).", 95 `func f(x, y int) { s := "hi"; _, _, _ = x, len(s), y; s = "bye" }`, 96 `[0 1 -2]`, 97 }, 98 { 99 "Calls to some built-ins are pure (another variants).", 100 `func f(x, y int) { s := "hi"; _, _, _ = x, len(s), y }`, 101 `[0 1]`, 102 }, 103 { 104 "Reading a local var is impure but does not have effects.", 105 `func f(x, y bool) { for x { _ = y } }`, 106 `[0 -2 1]`, 107 }, 108 } 109 for _, test := range tests { 110 test := test 111 t.Run(test.descr, func(t *testing.T) { 112 fset := token.NewFileSet() 113 mustParse := func(filename string, content any) *ast.File { 114 f, err := parser.ParseFile(fset, filename, content, parser.ParseComments|parser.SkipObjectResolution) 115 if err != nil { 116 t.Fatalf("ParseFile: %v", err) 117 } 118 return f 119 } 120 121 // Parse callee file and find first func decl named f. 122 calleeContent := "package p\n" + test.callee 123 calleeFile := mustParse("callee.go", calleeContent) 124 var decl *ast.FuncDecl 125 for _, d := range calleeFile.Decls { 126 if d, ok := d.(*ast.FuncDecl); ok && d.Name.Name == funcName { 127 decl = d 128 break 129 } 130 } 131 if decl == nil { 132 t.Fatalf("declaration of func %s not found: %s", funcName, test.callee) 133 } 134 135 info := &types.Info{ 136 Defs: make(map[*ast.Ident]types.Object), 137 Uses: make(map[*ast.Ident]types.Object), 138 Types: make(map[ast.Expr]types.TypeAndValue), 139 Implicits: make(map[ast.Node]types.Object), 140 Selections: make(map[*ast.SelectorExpr]*types.Selection), 141 Scopes: make(map[ast.Node]*types.Scope), 142 } 143 conf := &types.Config{Error: func(err error) { t.Error(err) }} 144 pkg, err := conf.Check("p", fset, []*ast.File{calleeFile}, info) 145 if err != nil { 146 t.Fatal(err) 147 } 148 149 callee, err := inline.AnalyzeCallee(t.Logf, fset, pkg, info, decl, []byte(calleeContent)) 150 if err != nil { 151 t.Fatal(err) 152 } 153 if got := fmt.Sprint(callee.Effects()); got != test.want { 154 t.Errorf("for effects of %s, got %s want %s", 155 test.callee, got, test.want) 156 } 157 }) 158 } 159 }