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 implements tests for various issues. 6 7package types_test 8 9import ( 10 "bytes" 11 "fmt" 12 "go/ast" 13 "go/importer" 14 "go/parser" 15 "go/token" 16 "internal/testenv" 17 "sort" 18 "strings" 19 "testing" 20 21 . "go/types" 22) 23 24func mustParse(t *testing.T, src string) *ast.File { 25 f, err := parser.ParseFile(fset, "", src, 0) 26 if err != nil { 27 t.Fatal(err) 28 } 29 return f 30} 31func TestIssue5770(t *testing.T) { 32 f := mustParse(t, `package p; type S struct{T}`) 33 conf := Config{Importer: importer.Default()} 34 _, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // do not crash 35 want := "undeclared name: T" 36 if err == nil || !strings.Contains(err.Error(), want) { 37 t.Errorf("got: %v; want: %s", err, want) 38 } 39} 40 41func TestIssue5849(t *testing.T) { 42 src := ` 43package p 44var ( 45 s uint 46 _ = uint8(8) 47 _ = uint16(16) << s 48 _ = uint32(32 << s) 49 _ = uint64(64 << s + s) 50 _ = (interface{})("foo") 51 _ = (interface{})(nil) 52)` 53 f := mustParse(t, src) 54 55 var conf Config 56 types := make(map[ast.Expr]TypeAndValue) 57 _, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types}) 58 if err != nil { 59 t.Fatal(err) 60 } 61 62 for x, tv := range types { 63 var want Type 64 switch x := x.(type) { 65 case *ast.BasicLit: 66 switch x.Value { 67 case `8`: 68 want = Typ[Uint8] 69 case `16`: 70 want = Typ[Uint16] 71 case `32`: 72 want = Typ[Uint32] 73 case `64`: 74 want = Typ[Uint] // because of "+ s", s is of type uint 75 case `"foo"`: 76 want = Typ[String] 77 } 78 case *ast.Ident: 79 if x.Name == "nil" { 80 want = Typ[UntypedNil] 81 } 82 } 83 if want != nil && !Identical(tv.Type, want) { 84 t.Errorf("got %s; want %s", tv.Type, want) 85 } 86 } 87} 88 89func TestIssue6413(t *testing.T) { 90 src := ` 91package p 92func f() int { 93 defer f() 94 go f() 95 return 0 96} 97` 98 f := mustParse(t, src) 99 100 var conf Config 101 types := make(map[ast.Expr]TypeAndValue) 102 _, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types}) 103 if err != nil { 104 t.Fatal(err) 105 } 106 107 want := Typ[Int] 108 n := 0 109 for x, tv := range types { 110 if _, ok := x.(*ast.CallExpr); ok { 111 if tv.Type != want { 112 t.Errorf("%s: got %s; want %s", fset.Position(x.Pos()), tv.Type, want) 113 } 114 n++ 115 } 116 } 117 118 if n != 2 { 119 t.Errorf("got %d CallExprs; want 2", n) 120 } 121} 122 123func TestIssue7245(t *testing.T) { 124 src := ` 125package p 126func (T) m() (res bool) { return } 127type T struct{} // receiver type after method declaration 128` 129 f := mustParse(t, src) 130 131 var conf Config 132 defs := make(map[*ast.Ident]Object) 133 _, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs}) 134 if err != nil { 135 t.Fatal(err) 136 } 137 138 m := f.Decls[0].(*ast.FuncDecl) 139 res1 := defs[m.Name].(*Func).Type().(*Signature).Results().At(0) 140 res2 := defs[m.Type.Results.List[0].Names[0]].(*Var) 141 142 if res1 != res2 { 143 t.Errorf("got %s (%p) != %s (%p)", res1, res2, res1, res2) 144 } 145} 146 147// This tests that uses of existing vars on the LHS of an assignment 148// are Uses, not Defs; and also that the (illegal) use of a non-var on 149// the LHS of an assignment is a Use nonetheless. 150func TestIssue7827(t *testing.T) { 151 const src = ` 152package p 153func _() { 154 const w = 1 // defs w 155 x, y := 2, 3 // defs x, y 156 w, x, z := 4, 5, 6 // uses w, x, defs z; error: cannot assign to w 157 _, _, _ = x, y, z // uses x, y, z 158} 159` 160 f := mustParse(t, src) 161 162 const want = `L3 defs func p._() 163L4 defs const w untyped int 164L5 defs var x int 165L5 defs var y int 166L6 defs var z int 167L6 uses const w untyped int 168L6 uses var x int 169L7 uses var x int 170L7 uses var y int 171L7 uses var z int` 172 173 // don't abort at the first error 174 conf := Config{Error: func(err error) { t.Log(err) }} 175 defs := make(map[*ast.Ident]Object) 176 uses := make(map[*ast.Ident]Object) 177 _, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs, Uses: uses}) 178 if s := fmt.Sprint(err); !strings.HasSuffix(s, "cannot assign to w") { 179 t.Errorf("Check: unexpected error: %s", s) 180 } 181 182 var facts []string 183 for id, obj := range defs { 184 if obj != nil { 185 fact := fmt.Sprintf("L%d defs %s", fset.Position(id.Pos()).Line, obj) 186 facts = append(facts, fact) 187 } 188 } 189 for id, obj := range uses { 190 fact := fmt.Sprintf("L%d uses %s", fset.Position(id.Pos()).Line, obj) 191 facts = append(facts, fact) 192 } 193 sort.Strings(facts) 194 195 got := strings.Join(facts, "\n") 196 if got != want { 197 t.Errorf("Unexpected defs/uses\ngot:\n%s\nwant:\n%s", got, want) 198 } 199} 200 201// This tests that the package associated with the types.Object.Pkg method 202// is the type's package independent of the order in which the imports are 203// listed in the sources src1, src2 below. 204// The actual issue is in go/internal/gcimporter which has a corresponding 205// test; we leave this test here to verify correct behavior at the go/types 206// level. 207func TestIssue13898(t *testing.T) { 208 testenv.MustHaveGoBuild(t) 209 210 const src0 = ` 211package main 212 213import "go/types" 214 215func main() { 216 var info types.Info 217 for _, obj := range info.Uses { 218 _ = obj.Pkg() 219 } 220} 221` 222 // like src0, but also imports go/importer 223 const src1 = ` 224package main 225 226import ( 227 "go/types" 228 _ "go/importer" 229) 230 231func main() { 232 var info types.Info 233 for _, obj := range info.Uses { 234 _ = obj.Pkg() 235 } 236} 237` 238 // like src1 but with different import order 239 // (used to fail with this issue) 240 const src2 = ` 241package main 242 243import ( 244 _ "go/importer" 245 "go/types" 246) 247 248func main() { 249 var info types.Info 250 for _, obj := range info.Uses { 251 _ = obj.Pkg() 252 } 253} 254` 255 f := func(test, src string) { 256 f := mustParse(t, src) 257 cfg := Config{Importer: importer.Default()} 258 info := Info{Uses: make(map[*ast.Ident]Object)} 259 _, err := cfg.Check("main", fset, []*ast.File{f}, &info) 260 if err != nil { 261 t.Fatal(err) 262 } 263 264 var pkg *Package 265 count := 0 266 for id, obj := range info.Uses { 267 if id.Name == "Pkg" { 268 pkg = obj.Pkg() 269 count++ 270 } 271 } 272 if count != 1 { 273 t.Fatalf("%s: got %d entries named Pkg; want 1", test, count) 274 } 275 if pkg.Name() != "types" { 276 t.Fatalf("%s: got %v; want package types", test, pkg) 277 } 278 } 279 280 f("src0", src0) 281 f("src1", src1) 282 f("src2", src2) 283} 284 285func TestIssue22525(t *testing.T) { 286 f := mustParse(t, `package p; func f() { var a, b, c, d, e int }`) 287 288 got := "\n" 289 conf := Config{Error: func(err error) { got += err.Error() + "\n" }} 290 conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // do not crash 291 want := ` 2921:27: a declared but not used 2931:30: b declared but not used 2941:33: c declared but not used 2951:36: d declared but not used 2961:39: e declared but not used 297` 298 if got != want { 299 t.Errorf("got: %swant: %s", got, want) 300 } 301} 302 303func TestIssue25627(t *testing.T) { 304 const prefix = `package p; import "unsafe"; type P *struct{}; type I interface{}; type T ` 305 // The src strings (without prefix) are constructed such that the number of semicolons 306 // plus one corresponds to the number of fields expected in the respective struct. 307 for _, src := range []string{ 308 `struct { x Missing }`, 309 `struct { Missing }`, 310 `struct { *Missing }`, 311 `struct { unsafe.Pointer }`, 312 `struct { P }`, 313 `struct { *I }`, 314 `struct { a int; b Missing; *Missing }`, 315 } { 316 f := mustParse(t, prefix+src) 317 318 cfg := Config{Importer: importer.Default(), Error: func(err error) {}} 319 info := &Info{Types: make(map[ast.Expr]TypeAndValue)} 320 _, err := cfg.Check(f.Name.Name, fset, []*ast.File{f}, info) 321 if err != nil { 322 if _, ok := err.(Error); !ok { 323 t.Fatal(err) 324 } 325 } 326 327 ast.Inspect(f, func(n ast.Node) bool { 328 if spec, _ := n.(*ast.TypeSpec); spec != nil { 329 if tv, ok := info.Types[spec.Type]; ok && spec.Name.Name == "T" { 330 want := strings.Count(src, ";") + 1 331 if got := tv.Type.(*Struct).NumFields(); got != want { 332 t.Errorf("%s: got %d fields; want %d", src, got, want) 333 } 334 } 335 } 336 return true 337 }) 338 } 339} 340 341func TestIssue28005(t *testing.T) { 342 // method names must match defining interface name for this test 343 // (see last comment in this function) 344 sources := [...]string{ 345 "package p; type A interface{ A() }", 346 "package p; type B interface{ B() }", 347 "package p; type X interface{ A; B }", 348 } 349 350 // compute original file ASTs 351 var orig [len(sources)]*ast.File 352 for i, src := range sources { 353 orig[i] = mustParse(t, src) 354 } 355 356 // run the test for all order permutations of the incoming files 357 for _, perm := range [][len(sources)]int{ 358 {0, 1, 2}, 359 {0, 2, 1}, 360 {1, 0, 2}, 361 {1, 2, 0}, 362 {2, 0, 1}, 363 {2, 1, 0}, 364 } { 365 // create file order permutation 366 files := make([]*ast.File, len(sources)) 367 for i := range perm { 368 files[i] = orig[perm[i]] 369 } 370 371 // type-check package with given file order permutation 372 var conf Config 373 info := &Info{Defs: make(map[*ast.Ident]Object)} 374 _, err := conf.Check("", fset, files, info) 375 if err != nil { 376 t.Fatal(err) 377 } 378 379 // look for interface object X 380 var obj Object 381 for name, def := range info.Defs { 382 if name.Name == "X" { 383 obj = def 384 break 385 } 386 } 387 if obj == nil { 388 t.Fatal("interface not found") 389 } 390 iface := obj.Type().Underlying().(*Interface) // I must be an interface 391 392 // Each iface method m is embedded; and m's receiver base type name 393 // must match the method's name per the choice in the source file. 394 for i := 0; i < iface.NumMethods(); i++ { 395 m := iface.Method(i) 396 recvName := m.Type().(*Signature).Recv().Type().(*Named).Obj().Name() 397 if recvName != m.Name() { 398 t.Errorf("perm %v: got recv %s; want %s", perm, recvName, m.Name()) 399 } 400 } 401 } 402} 403 404func TestIssue28282(t *testing.T) { 405 // create type interface { error } 406 et := Universe.Lookup("error").Type() 407 it := NewInterfaceType(nil, []Type{et}) 408 it.Complete() 409 // verify that after completing the interface, the embedded method remains unchanged 410 want := et.Underlying().(*Interface).Method(0) 411 got := it.Method(0) 412 if got != want { 413 t.Fatalf("%s.Method(0): got %q (%p); want %q (%p)", it, got, got, want, want) 414 } 415 // verify that lookup finds the same method in both interfaces (redundant check) 416 obj, _, _ := LookupFieldOrMethod(et, false, nil, "Error") 417 if obj != want { 418 t.Fatalf("%s.Lookup: got %q (%p); want %q (%p)", et, obj, obj, want, want) 419 } 420 obj, _, _ = LookupFieldOrMethod(it, false, nil, "Error") 421 if obj != want { 422 t.Fatalf("%s.Lookup: got %q (%p); want %q (%p)", it, obj, obj, want, want) 423 } 424} 425 426func TestIssue29029(t *testing.T) { 427 f1 := mustParse(t, `package p; type A interface { M() }`) 428 f2 := mustParse(t, `package p; var B interface { A }`) 429 430 // printInfo prints the *Func definitions recorded in info, one *Func per line. 431 printInfo := func(info *Info) string { 432 var buf bytes.Buffer 433 for _, obj := range info.Defs { 434 if fn, ok := obj.(*Func); ok { 435 fmt.Fprintln(&buf, fn) 436 } 437 } 438 return buf.String() 439 } 440 441 // The *Func (method) definitions for package p must be the same 442 // independent on whether f1 and f2 are type-checked together, or 443 // incrementally. 444 445 // type-check together 446 var conf Config 447 info := &Info{Defs: make(map[*ast.Ident]Object)} 448 check := NewChecker(&conf, fset, NewPackage("", "p"), info) 449 if err := check.Files([]*ast.File{f1, f2}); err != nil { 450 t.Fatal(err) 451 } 452 want := printInfo(info) 453 454 // type-check incrementally 455 info = &Info{Defs: make(map[*ast.Ident]Object)} 456 check = NewChecker(&conf, fset, NewPackage("", "p"), info) 457 if err := check.Files([]*ast.File{f1}); err != nil { 458 t.Fatal(err) 459 } 460 if err := check.Files([]*ast.File{f2}); err != nil { 461 t.Fatal(err) 462 } 463 got := printInfo(info) 464 465 if got != want { 466 t.Errorf("\ngot : %swant: %s", got, want) 467 } 468} 469 470func TestIssue34151(t *testing.T) { 471 const asrc = `package a; type I interface{ M() }; type T struct { F interface { I } }` 472 const bsrc = `package b; import "a"; type T struct { F interface { a.I } }; var _ = a.T(T{})` 473 474 a, err := pkgFor("a", asrc, nil) 475 if err != nil { 476 t.Fatalf("package %s failed to typecheck: %v", a.Name(), err) 477 } 478 479 bast := mustParse(t, bsrc) 480 conf := Config{Importer: importHelper{a}} 481 b, err := conf.Check(bast.Name.Name, fset, []*ast.File{bast}, nil) 482 if err != nil { 483 t.Errorf("package %s failed to typecheck: %v", b.Name(), err) 484 } 485} 486 487type importHelper struct { 488 pkg *Package 489} 490 491func (h importHelper) Import(path string) (*Package, error) { 492 if path != h.pkg.Path() { 493 return nil, fmt.Errorf("got package path %q; want %q", path, h.pkg.Path()) 494 } 495 return h.pkg, nil 496} 497 498// TestIssue34921 verifies that we don't update an imported type's underlying 499// type when resolving an underlying type. Specifically, when determining the 500// underlying type of b.T (which is the underlying type of a.T, which is int) 501// we must not set the underlying type of a.T again since that would lead to 502// a race condition if package b is imported elsewhere, in a package that is 503// concurrently type-checked. 504func TestIssue34921(t *testing.T) { 505 defer func() { 506 if r := recover(); r != nil { 507 t.Error(r) 508 } 509 }() 510 511 var sources = []string{ 512 `package a; type T int`, 513 `package b; import "a"; type T a.T`, 514 } 515 516 var pkg *Package 517 for _, src := range sources { 518 f := mustParse(t, src) 519 conf := Config{Importer: importHelper{pkg}} 520 res, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) 521 if err != nil { 522 t.Errorf("%q failed to typecheck: %v", src, err) 523 } 524 pkg = res // res is imported by the next package in this test 525 } 526} 527 528func TestIssue43088(t *testing.T) { 529 // type T1 struct { 530 // _ T2 531 // } 532 // 533 // type T2 struct { 534 // _ struct { 535 // _ T2 536 // } 537 // } 538 n1 := NewTypeName(token.NoPos, nil, "T1", nil) 539 T1 := NewNamed(n1, nil, nil) 540 n2 := NewTypeName(token.NoPos, nil, "T2", nil) 541 T2 := NewNamed(n2, nil, nil) 542 s1 := NewStruct([]*Var{NewField(token.NoPos, nil, "_", T2, false)}, nil) 543 T1.SetUnderlying(s1) 544 s2 := NewStruct([]*Var{NewField(token.NoPos, nil, "_", T2, false)}, nil) 545 s3 := NewStruct([]*Var{NewField(token.NoPos, nil, "_", s2, false)}, nil) 546 T2.SetUnderlying(s3) 547 548 // These calls must terminate (no endless recursion). 549 Comparable(T1) 550 Comparable(T2) 551} 552