github.com/AndrienkoAleksandr/go@v0.0.19/src/go/types/eval_test.go (about) 1 // Copyright 2013 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 // This file contains tests for Eval. 6 7 package types_test 8 9 import ( 10 "fmt" 11 "go/ast" 12 "go/importer" 13 "go/parser" 14 "go/token" 15 "internal/testenv" 16 "strings" 17 "testing" 18 19 . "go/types" 20 ) 21 22 func testEval(t *testing.T, fset *token.FileSet, pkg *Package, pos token.Pos, expr string, typ Type, typStr, valStr string) { 23 gotTv, err := Eval(fset, pkg, pos, expr) 24 if err != nil { 25 t.Errorf("Eval(%q) failed: %s", expr, err) 26 return 27 } 28 if gotTv.Type == nil { 29 t.Errorf("Eval(%q) got nil type but no error", expr) 30 return 31 } 32 33 // compare types 34 if typ != nil { 35 // we have a type, check identity 36 if !Identical(gotTv.Type, typ) { 37 t.Errorf("Eval(%q) got type %s, want %s", expr, gotTv.Type, typ) 38 return 39 } 40 } else { 41 // we have a string, compare type string 42 gotStr := gotTv.Type.String() 43 if gotStr != typStr { 44 t.Errorf("Eval(%q) got type %s, want %s", expr, gotStr, typStr) 45 return 46 } 47 } 48 49 // compare values 50 gotStr := "" 51 if gotTv.Value != nil { 52 gotStr = gotTv.Value.ExactString() 53 } 54 if gotStr != valStr { 55 t.Errorf("Eval(%q) got value %s, want %s", expr, gotStr, valStr) 56 } 57 } 58 59 func TestEvalBasic(t *testing.T) { 60 fset := token.NewFileSet() 61 for _, typ := range Typ[Bool : String+1] { 62 testEval(t, fset, nil, nopos, typ.Name(), typ, "", "") 63 } 64 } 65 66 func TestEvalComposite(t *testing.T) { 67 fset := token.NewFileSet() 68 for _, test := range independentTestTypes { 69 testEval(t, fset, nil, nopos, test.src, nil, test.str, "") 70 } 71 } 72 73 func TestEvalArith(t *testing.T) { 74 var tests = []string{ 75 `true`, 76 `false == false`, 77 `12345678 + 87654321 == 99999999`, 78 `10 * 20 == 200`, 79 `(1<<500)*2 >> 100 == 2<<400`, 80 `"foo" + "bar" == "foobar"`, 81 `"abc" <= "bcd"`, 82 `len([10]struct{}{}) == 2*5`, 83 } 84 fset := token.NewFileSet() 85 for _, test := range tests { 86 testEval(t, fset, nil, nopos, test, Typ[UntypedBool], "", "true") 87 } 88 } 89 90 func TestEvalPos(t *testing.T) { 91 testenv.MustHaveGoBuild(t) 92 93 // The contents of /*-style comments are of the form 94 // expr => value, type 95 // where value may be the empty string. 96 // Each expr is evaluated at the position of the comment 97 // and the result is compared with the expected value 98 // and type. 99 var sources = []string{ 100 ` 101 package p 102 import "fmt" 103 import m "math" 104 const c = 3.0 105 type T []int 106 func f(a int, s string) float64 { 107 fmt.Println("calling f") 108 _ = m.Pi // use package math 109 const d int = c + 1 110 var x int 111 x = a + len(s) 112 return float64(x) 113 /* true => true, untyped bool */ 114 /* fmt.Println => , func(a ...any) (n int, err error) */ 115 /* c => 3, untyped float */ 116 /* T => , p.T */ 117 /* a => , int */ 118 /* s => , string */ 119 /* d => 4, int */ 120 /* x => , int */ 121 /* d/c => 1, int */ 122 /* c/2 => 3/2, untyped float */ 123 /* m.Pi < m.E => false, untyped bool */ 124 } 125 `, 126 ` 127 package p 128 /* c => 3, untyped float */ 129 type T1 /* T1 => , p.T1 */ struct {} 130 var v1 /* v1 => , int */ = 42 131 func /* f1 => , func(v1 float64) */ f1(v1 float64) { 132 /* f1 => , func(v1 float64) */ 133 /* v1 => , float64 */ 134 var c /* c => 3, untyped float */ = "foo" /* c => , string */ 135 { 136 var c struct { 137 c /* c => , string */ int 138 } 139 /* c => , struct{c int} */ 140 _ = c 141 } 142 _ = func(a, b, c int) /* c => , string */ { 143 /* c => , int */ 144 } 145 _ = c 146 type FT /* FT => , p.FT */ interface{} 147 } 148 `, 149 ` 150 package p 151 /* T => , p.T */ 152 `, 153 ` 154 package p 155 import "io" 156 type R = io.Reader 157 func _() { 158 /* interface{R}.Read => , func(_ interface{io.Reader}, p []byte) (n int, err error) */ 159 _ = func() { 160 /* interface{io.Writer}.Write => , func(_ interface{io.Writer}, p []byte) (n int, err error) */ 161 type io interface {} // must not shadow io in line above 162 } 163 type R interface {} // must not shadow R in first line of this function body 164 } 165 `, 166 } 167 168 fset := token.NewFileSet() 169 var files []*ast.File 170 for i, src := range sources { 171 file, err := parser.ParseFile(fset, "p", src, parser.ParseComments) 172 if err != nil { 173 t.Fatalf("could not parse file %d: %s", i, err) 174 } 175 files = append(files, file) 176 } 177 178 conf := Config{Importer: importer.Default()} 179 pkg, err := conf.Check("p", fset, files, nil) 180 if err != nil { 181 t.Fatal(err) 182 } 183 184 for _, file := range files { 185 for _, group := range file.Comments { 186 for _, comment := range group.List { 187 s := comment.Text 188 if len(s) >= 4 && s[:2] == "/*" && s[len(s)-2:] == "*/" { 189 str, typ := split(s[2:len(s)-2], ", ") 190 str, val := split(str, "=>") 191 testEval(t, fset, pkg, comment.Pos(), str, nil, typ, val) 192 } 193 } 194 } 195 } 196 } 197 198 // split splits string s at the first occurrence of s, trimming spaces. 199 func split(s, sep string) (string, string) { 200 before, after, _ := strings.Cut(s, sep) 201 return strings.TrimSpace(before), strings.TrimSpace(after) 202 } 203 204 func TestCheckExpr(t *testing.T) { 205 testenv.MustHaveGoBuild(t) 206 207 // Each comment has the form /* expr => object */: 208 // expr is an identifier or selector expression that is passed 209 // to CheckExpr at the position of the comment, and object is 210 // the string form of the object it denotes. 211 const src = ` 212 package p 213 214 import "fmt" 215 216 const c = 3.0 217 type T []int 218 type S struct{ X int } 219 220 func f(a int, s string) S { 221 /* fmt.Println => func fmt.Println(a ...any) (n int, err error) */ 222 /* fmt.Stringer.String => func (fmt.Stringer).String() string */ 223 fmt.Println("calling f") 224 225 var fmt struct{ Println int } 226 /* fmt => var fmt struct{Println int} */ 227 /* fmt.Println => field Println int */ 228 /* f(1, "").X => field X int */ 229 fmt.Println = 1 230 231 /* append => builtin append */ 232 233 /* new(S).X => field X int */ 234 235 return S{} 236 }` 237 238 fset := token.NewFileSet() 239 f, err := parser.ParseFile(fset, "p", src, parser.ParseComments) 240 if err != nil { 241 t.Fatal(err) 242 } 243 244 conf := Config{Importer: importer.Default()} 245 pkg, err := conf.Check("p", fset, []*ast.File{f}, nil) 246 if err != nil { 247 t.Fatal(err) 248 } 249 250 checkExpr := func(pos token.Pos, str string) (Object, error) { 251 expr, err := parser.ParseExprFrom(fset, "eval", str, 0) 252 if err != nil { 253 return nil, err 254 } 255 256 info := &Info{ 257 Uses: make(map[*ast.Ident]Object), 258 Selections: make(map[*ast.SelectorExpr]*Selection), 259 } 260 if err := CheckExpr(fset, pkg, pos, expr, info); err != nil { 261 return nil, fmt.Errorf("CheckExpr(%q) failed: %s", str, err) 262 } 263 switch expr := expr.(type) { 264 case *ast.Ident: 265 if obj, ok := info.Uses[expr]; ok { 266 return obj, nil 267 } 268 case *ast.SelectorExpr: 269 if sel, ok := info.Selections[expr]; ok { 270 return sel.Obj(), nil 271 } 272 if obj, ok := info.Uses[expr.Sel]; ok { 273 return obj, nil // qualified identifier 274 } 275 } 276 return nil, fmt.Errorf("no object for %s", str) 277 } 278 279 for _, group := range f.Comments { 280 for _, comment := range group.List { 281 s := comment.Text 282 if len(s) >= 4 && strings.HasPrefix(s, "/*") && strings.HasSuffix(s, "*/") { 283 pos := comment.Pos() 284 expr, wantObj := split(s[2:len(s)-2], "=>") 285 obj, err := checkExpr(pos, expr) 286 if err != nil { 287 t.Errorf("%s: %s", fset.Position(pos), err) 288 continue 289 } 290 if obj.String() != wantObj { 291 t.Errorf("%s: checkExpr(%s) = %s, want %v", 292 fset.Position(pos), expr, obj, wantObj) 293 } 294 } 295 } 296 } 297 }