golang.org/x/tools@v0.21.0/internal/gcimporter/gcimporter_test.go (about) 1 // Copyright 2011 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 is a copy of $GOROOT/src/go/internal/gcimporter/gcimporter_test.go, 6 // adjusted to make it build with code from (std lib) internal/testenv copied. 7 8 package gcimporter 9 10 import ( 11 "bytes" 12 "fmt" 13 "go/ast" 14 "go/constant" 15 goimporter "go/importer" 16 goparser "go/parser" 17 "go/token" 18 "go/types" 19 "os" 20 "os/exec" 21 "path" 22 "path/filepath" 23 "runtime" 24 "sort" 25 "strings" 26 "sync" 27 "testing" 28 "time" 29 30 "golang.org/x/tools/internal/aliases" 31 "golang.org/x/tools/internal/goroot" 32 "golang.org/x/tools/internal/testenv" 33 ) 34 35 func TestMain(m *testing.M) { 36 testenv.ExitIfSmallMachine() 37 os.Exit(m.Run()) 38 } 39 40 // ---------------------------------------------------------------------------- 41 42 func needsCompiler(t *testing.T, compiler string) { 43 if runtime.Compiler == compiler { 44 return 45 } 46 switch compiler { 47 case "gc": 48 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 49 } 50 } 51 52 // compile runs the compiler on filename, with dirname as the working directory, 53 // and writes the output file to outdirname. 54 // compile gives the resulting package a packagepath of p. 55 func compile(t *testing.T, dirname, filename, outdirname string, packagefiles map[string]string) string { 56 return compilePkg(t, dirname, filename, outdirname, packagefiles, "p") 57 } 58 59 func compilePkg(t *testing.T, dirname, filename, outdirname string, packagefiles map[string]string, pkg string) string { 60 testenv.NeedsGoBuild(t) 61 62 // filename must end with ".go" 63 basename := strings.TrimSuffix(filepath.Base(filename), ".go") 64 ok := filename != basename 65 if !ok { 66 t.Fatalf("filename doesn't end in .go: %s", filename) 67 } 68 objname := basename + ".o" 69 outname := filepath.Join(outdirname, objname) 70 71 importcfgfile := os.DevNull 72 if len(packagefiles) > 0 { 73 importcfgfile = filepath.Join(outdirname, basename) + ".importcfg" 74 importcfg := new(bytes.Buffer) 75 fmt.Fprintf(importcfg, "# import config") 76 for k, v := range packagefiles { 77 fmt.Fprintf(importcfg, "\npackagefile %s=%s\n", k, v) 78 } 79 if err := os.WriteFile(importcfgfile, importcfg.Bytes(), 0655); err != nil { 80 t.Fatal(err) 81 } 82 } 83 84 importreldir := strings.ReplaceAll(outdirname, string(os.PathSeparator), "/") 85 cmd := exec.Command("go", "tool", "compile", "-p", pkg, "-D", importreldir, "-importcfg", importcfgfile, "-o", outname, filename) 86 cmd.Dir = dirname 87 if out, err := cmd.CombinedOutput(); err != nil { 88 t.Logf("%s", out) 89 t.Fatalf("go tool compile %s failed: %s", filename, err) 90 } 91 return outname 92 } 93 94 func testPath(t *testing.T, path, srcDir string) *types.Package { 95 t0 := time.Now() 96 pkg, err := Import(make(map[string]*types.Package), path, srcDir, nil) 97 if err != nil { 98 t.Errorf("testPath(%s): %s", path, err) 99 return nil 100 } 101 t.Logf("testPath(%s): %v", path, time.Since(t0)) 102 return pkg 103 } 104 105 func mktmpdir(t *testing.T) string { 106 tmpdir, err := os.MkdirTemp("", "gcimporter_test") 107 if err != nil { 108 t.Fatal("mktmpdir:", err) 109 } 110 if err := os.Mkdir(filepath.Join(tmpdir, "testdata"), 0700); err != nil { 111 os.RemoveAll(tmpdir) 112 t.Fatal("mktmpdir:", err) 113 } 114 return tmpdir 115 } 116 117 const testfile = "exports.go" 118 119 func TestImportTestdata(t *testing.T) { 120 needsCompiler(t, "gc") 121 testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache 122 123 tmpdir := mktmpdir(t) 124 defer os.RemoveAll(tmpdir) 125 126 packageFiles := map[string]string{} 127 for _, pkg := range []string{"go/ast", "go/token"} { 128 export, _ := FindPkg(pkg, "testdata") 129 if export == "" { 130 t.Fatalf("no export data found for %s", pkg) 131 } 132 packageFiles[pkg] = export 133 } 134 135 compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"), packageFiles) 136 137 // filename should end with ".go" 138 filename := testfile[:len(testfile)-3] 139 if pkg := testPath(t, "./testdata/"+filename, tmpdir); pkg != nil { 140 // The package's Imports list must include all packages 141 // explicitly imported by testfile, plus all packages 142 // referenced indirectly via exported objects in testfile. 143 // With the textual export format (when run against Go1.6), 144 // the list may also include additional packages that are 145 // not strictly required for import processing alone (they 146 // are exported to err "on the safe side"). 147 // For now, we just test the presence of a few packages 148 // that we know are there for sure. 149 got := fmt.Sprint(pkg.Imports()) 150 wants := []string{"go/ast", "go/token"} 151 if unifiedIR { 152 wants = []string{"go/ast"} 153 } 154 for _, want := range wants { 155 if !strings.Contains(got, want) { 156 t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want) 157 } 158 } 159 } 160 } 161 162 func TestImportTypeparamTests(t *testing.T) { 163 if testing.Short() { 164 t.Skipf("in short mode, skipping test that requires export data for all of std") 165 } 166 167 testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache 168 169 // This package only handles gc export data. 170 if runtime.Compiler != "gc" { 171 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 172 } 173 174 tmpdir := mktmpdir(t) 175 defer os.RemoveAll(tmpdir) 176 177 // Check go files in test/typeparam, except those that fail for a known 178 // reason. 179 rootDir := filepath.Join(runtime.GOROOT(), "test", "typeparam") 180 list, err := os.ReadDir(rootDir) 181 if err != nil { 182 t.Fatal(err) 183 } 184 185 var skip map[string]string 186 if !unifiedIR { 187 // The Go 1.18 frontend still fails several cases. 188 skip = map[string]string{ 189 "equal.go": "inconsistent embedded sorting", // TODO(rfindley): investigate this. 190 "nested.go": "fails to compile", // TODO(rfindley): investigate this. 191 "issue47631.go": "can not handle local type declarations", 192 "issue55101.go": "fails to compile", 193 } 194 } 195 196 for _, entry := range list { 197 if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") { 198 // For now, only consider standalone go files. 199 continue 200 } 201 202 t.Run(entry.Name(), func(t *testing.T) { 203 if reason, ok := skip[entry.Name()]; ok { 204 t.Skip(reason) 205 } 206 207 filename := filepath.Join(rootDir, entry.Name()) 208 src, err := os.ReadFile(filename) 209 if err != nil { 210 t.Fatal(err) 211 } 212 if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) { 213 // We're bypassing the logic of run.go here, so be conservative about 214 // the files we consider in an attempt to make this test more robust to 215 // changes in test/typeparams. 216 t.Skipf("not detected as a run test") 217 } 218 219 // Compile and import, and compare the resulting package with the package 220 // that was type-checked directly. 221 pkgFiles, err := goroot.PkgfileMap() 222 if err != nil { 223 t.Fatal(err) 224 } 225 compile(t, rootDir, entry.Name(), filepath.Join(tmpdir, "testdata"), pkgFiles) 226 pkgName := strings.TrimSuffix(entry.Name(), ".go") 227 imported := importPkg(t, "./testdata/"+pkgName, tmpdir) 228 checked := checkFile(t, filename, src) 229 230 seen := make(map[string]bool) 231 for _, name := range imported.Scope().Names() { 232 if !token.IsExported(name) { 233 continue // ignore synthetic names like .inittask and .dict.* 234 } 235 seen[name] = true 236 237 importedObj := imported.Scope().Lookup(name) 238 got := types.ObjectString(importedObj, types.RelativeTo(imported)) 239 240 checkedObj := checked.Scope().Lookup(name) 241 if checkedObj == nil { 242 t.Fatalf("imported object %q was not type-checked", name) 243 } 244 want := types.ObjectString(checkedObj, types.RelativeTo(checked)) 245 246 if got != want { 247 t.Errorf("imported %q as %q, want %q", name, got, want) 248 } 249 } 250 251 for _, name := range checked.Scope().Names() { 252 if !token.IsExported(name) || seen[name] { 253 continue 254 } 255 t.Errorf("did not import object %q", name) 256 } 257 }) 258 } 259 } 260 261 func checkFile(t *testing.T, filename string, src []byte) *types.Package { 262 fset := token.NewFileSet() 263 f, err := goparser.ParseFile(fset, filename, src, 0) 264 if err != nil { 265 t.Fatal(err) 266 } 267 config := types.Config{ 268 Importer: goimporter.Default(), 269 } 270 pkg, err := config.Check("", fset, []*ast.File{f}, nil) 271 if err != nil { 272 t.Fatal(err) 273 } 274 return pkg 275 } 276 277 func TestVersionHandling(t *testing.T) { 278 if debug { 279 t.Skip("TestVersionHandling panics in debug mode") 280 } 281 282 // This package only handles gc export data. 283 needsCompiler(t, "gc") 284 285 const dir = "./testdata/versions" 286 list, err := os.ReadDir(dir) 287 if err != nil { 288 t.Fatal(err) 289 } 290 291 tmpdir := mktmpdir(t) 292 defer os.RemoveAll(tmpdir) 293 corruptdir := filepath.Join(tmpdir, "testdata", "versions") 294 if err := os.Mkdir(corruptdir, 0700); err != nil { 295 t.Fatal(err) 296 } 297 298 for _, f := range list { 299 name := f.Name() 300 if !strings.HasSuffix(name, ".a") { 301 continue // not a package file 302 } 303 if strings.Contains(name, "corrupted") { 304 continue // don't process a leftover corrupted file 305 } 306 pkgpath := "./" + name[:len(name)-2] 307 308 if testing.Verbose() { 309 t.Logf("importing %s", name) 310 } 311 312 // test that export data can be imported 313 _, err := Import(make(map[string]*types.Package), pkgpath, dir, nil) 314 if err != nil { 315 t.Errorf("import %q failed: %v", pkgpath, err) 316 continue 317 } 318 319 // create file with corrupted export data 320 // 1) read file 321 data, err := os.ReadFile(filepath.Join(dir, name)) 322 if err != nil { 323 t.Fatal(err) 324 } 325 // 2) find export data 326 i := bytes.Index(data, []byte("\n$$B\n")) + 5 327 j := bytes.Index(data[i:], []byte("\n$$\n")) + i 328 if i < 0 || j < 0 || i > j { 329 t.Fatalf("export data section not found (i = %d, j = %d)", i, j) 330 } 331 // 3) corrupt the data (increment every 7th byte) 332 for k := j - 13; k >= i; k -= 7 { 333 data[k]++ 334 } 335 // 4) write the file 336 pkgpath += "_corrupted" 337 filename := filepath.Join(corruptdir, pkgpath) + ".a" 338 os.WriteFile(filename, data, 0666) 339 340 // test that importing the corrupted file results in an error 341 _, err = Import(make(map[string]*types.Package), pkgpath, corruptdir, nil) 342 if err == nil { 343 t.Errorf("import corrupted %q succeeded", pkgpath) 344 } else if msg := err.Error(); !strings.Contains(msg, "internal error") { 345 t.Errorf("import %q error incorrect (%s)", pkgpath, msg) 346 } 347 } 348 } 349 350 func TestImportStdLib(t *testing.T) { 351 if testing.Short() { 352 t.Skip("the imports can be expensive, and this test is especially slow when the build cache is empty") 353 } 354 // This package only handles gc export data. 355 needsCompiler(t, "gc") 356 testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache 357 358 // Get list of packages in stdlib. Filter out test-only packages with {{if .GoFiles}} check. 359 var stderr bytes.Buffer 360 cmd := exec.Command("go", "list", "-f", "{{if .GoFiles}}{{.ImportPath}}{{end}}", "std") 361 cmd.Stderr = &stderr 362 out, err := cmd.Output() 363 if err != nil { 364 t.Fatalf("failed to run go list to determine stdlib packages: %v\nstderr:\n%v", err, stderr.String()) 365 } 366 pkgs := strings.Fields(string(out)) 367 368 var nimports int 369 for _, pkg := range pkgs { 370 t.Run(pkg, func(t *testing.T) { 371 if testPath(t, pkg, filepath.Join(testenv.GOROOT(t), "src", path.Dir(pkg))) != nil { 372 nimports++ 373 } 374 }) 375 } 376 const minPkgs = 225 // 'GOOS=plan9 go1.18 list std | wc -l' reports 228; most other platforms have more. 377 if len(pkgs) < minPkgs { 378 t.Fatalf("too few packages (%d) were imported", nimports) 379 } 380 381 t.Logf("tested %d imports", nimports) 382 } 383 384 var importedObjectTests = []struct { 385 name string 386 want string 387 }{ 388 // non-interfaces 389 {"crypto.Hash", "type Hash uint"}, 390 {"go/ast.ObjKind", "type ObjKind int"}, 391 {"go/types.Qualifier", "type Qualifier func(*Package) string"}, 392 {"go/types.Comparable", "func Comparable(T Type) bool"}, 393 {"math.Pi", "const Pi untyped float"}, 394 {"math.Sin", "func Sin(x float64) float64"}, 395 {"go/ast.NotNilFilter", "func NotNilFilter(_ string, v reflect.Value) bool"}, 396 397 // interfaces 398 {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key any) any}"}, 399 {"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"}, 400 {"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"}, 401 {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"}, 402 {"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"}, 403 {"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"}, 404 {"go/types.Type", "type Type interface{String() string; Underlying() Type}"}, 405 } 406 407 func TestImportedTypes(t *testing.T) { 408 // This package only handles gc export data. 409 needsCompiler(t, "gc") 410 testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache 411 412 for _, test := range importedObjectTests { 413 obj := importObject(t, test.name) 414 if obj == nil { 415 continue // error reported elsewhere 416 } 417 got := types.ObjectString(obj, types.RelativeTo(obj.Pkg())) 418 419 // TODO(rsc): Delete this block once go.dev/cl/368254 lands. 420 if got != test.want && test.want == strings.ReplaceAll(got, "interface{}", "any") { 421 got = test.want 422 } 423 424 if got != test.want { 425 t.Errorf("%s: got %q; want %q", test.name, got, test.want) 426 } 427 428 if named, _ := aliases.Unalias(obj.Type()).(*types.Named); named != nil { 429 verifyInterfaceMethodRecvs(t, named, 0) 430 } 431 } 432 } 433 434 func TestImportedConsts(t *testing.T) { 435 testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache 436 437 tests := []struct { 438 name string 439 want constant.Kind 440 }{ 441 {"math.Pi", constant.Float}, 442 {"math.MaxFloat64", constant.Float}, 443 {"math.MaxInt64", constant.Int}, 444 } 445 446 for _, test := range tests { 447 obj := importObject(t, test.name) 448 if got := obj.(*types.Const).Val().Kind(); got != test.want { 449 t.Errorf("%s: imported as constant.Kind(%v), want constant.Kind(%v)", test.name, got, test.want) 450 } 451 } 452 } 453 454 // importObject imports the object specified by a name of the form 455 // <import path>.<object name>, e.g. go/types.Type. 456 // 457 // If any errors occur they are reported via t and the resulting object will 458 // be nil. 459 func importObject(t *testing.T, name string) types.Object { 460 s := strings.Split(name, ".") 461 if len(s) != 2 { 462 t.Fatal("inconsistent test data") 463 } 464 importPath := s[0] 465 objName := s[1] 466 467 pkg, err := Import(make(map[string]*types.Package), importPath, ".", nil) 468 if err != nil { 469 t.Error(err) 470 return nil 471 } 472 473 obj := pkg.Scope().Lookup(objName) 474 if obj == nil { 475 t.Errorf("%s: object not found", name) 476 return nil 477 } 478 return obj 479 } 480 481 // verifyInterfaceMethodRecvs verifies that method receiver types 482 // are named if the methods belong to a named interface type. 483 func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) { 484 // avoid endless recursion in case of an embedding bug that lead to a cycle 485 if level > 10 { 486 t.Errorf("%s: embeds itself", named) 487 return 488 } 489 490 iface, _ := named.Underlying().(*types.Interface) 491 if iface == nil { 492 return // not an interface 493 } 494 495 // check explicitly declared methods 496 for i := 0; i < iface.NumExplicitMethods(); i++ { 497 m := iface.ExplicitMethod(i) 498 recv := m.Type().(*types.Signature).Recv() 499 if recv == nil { 500 t.Errorf("%s: missing receiver type", m) 501 continue 502 } 503 if recv.Type() != named { 504 t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named) 505 } 506 } 507 508 // check embedded interfaces (if they are named, too) 509 for i := 0; i < iface.NumEmbeddeds(); i++ { 510 // embedding of interfaces cannot have cycles; recursion will terminate 511 if etype, _ := aliases.Unalias(iface.EmbeddedType(i)).(*types.Named); etype != nil { 512 verifyInterfaceMethodRecvs(t, etype, level+1) 513 } 514 } 515 } 516 517 func TestIssue5815(t *testing.T) { 518 // This package only handles gc export data. 519 needsCompiler(t, "gc") 520 testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache 521 522 pkg := importPkg(t, "strings", ".") 523 524 scope := pkg.Scope() 525 for _, name := range scope.Names() { 526 obj := scope.Lookup(name) 527 if obj.Pkg() == nil { 528 t.Errorf("no pkg for %s", obj) 529 } 530 if tname, _ := obj.(*types.TypeName); tname != nil { 531 named := aliases.Unalias(tname.Type()).(*types.Named) 532 for i := 0; i < named.NumMethods(); i++ { 533 m := named.Method(i) 534 if m.Pkg() == nil { 535 t.Errorf("no pkg for %s", m) 536 } 537 } 538 } 539 } 540 } 541 542 // Smoke test to ensure that imported methods get the correct package. 543 func TestCorrectMethodPackage(t *testing.T) { 544 // This package only handles gc export data. 545 needsCompiler(t, "gc") 546 testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache 547 548 imports := make(map[string]*types.Package) 549 _, err := Import(imports, "net/http", ".", nil) 550 if err != nil { 551 t.Fatal(err) 552 } 553 554 mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type() 555 mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex 556 sel := mset.Lookup(nil, "Lock") 557 lock := sel.Obj().(*types.Func) 558 if got, want := lock.Pkg().Path(), "sync"; got != want { 559 t.Errorf("got package path %q; want %q", got, want) 560 } 561 } 562 563 func TestIssue13566(t *testing.T) { 564 // This package only handles gc export data. 565 needsCompiler(t, "gc") 566 testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache 567 568 // On windows, we have to set the -D option for the compiler to avoid having a drive 569 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 570 if runtime.GOOS == "windows" { 571 t.Skip("avoid dealing with relative paths/drive letters on windows") 572 } 573 574 tmpdir := mktmpdir(t) 575 defer os.RemoveAll(tmpdir) 576 testoutdir := filepath.Join(tmpdir, "testdata") 577 578 // b.go needs to be compiled from the output directory so that the compiler can 579 // find the compiled package a. We pass the full path to compile() so that we 580 // don't have to copy the file to that directory. 581 bpath, err := filepath.Abs(filepath.Join("testdata", "b.go")) 582 if err != nil { 583 t.Fatal(err) 584 } 585 586 jsonExport, _ := FindPkg("encoding/json", "testdata") 587 if jsonExport == "" { 588 t.Fatalf("no export data found for encoding/json") 589 } 590 591 compilePkg(t, "testdata", "a.go", testoutdir, map[string]string{"encoding/json": jsonExport}, apkg(testoutdir)) 592 compile(t, testoutdir, bpath, testoutdir, map[string]string{apkg(testoutdir): filepath.Join(testoutdir, "a.o")}) 593 594 // import must succeed (test for issue at hand) 595 pkg := importPkg(t, "./testdata/b", tmpdir) 596 597 // make sure all indirectly imported packages have names 598 for _, imp := range pkg.Imports() { 599 if imp.Name() == "" { 600 t.Errorf("no name for %s package", imp.Path()) 601 } 602 } 603 } 604 605 func TestIssue13898(t *testing.T) { 606 // This package only handles gc export data. 607 needsCompiler(t, "gc") 608 testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache 609 610 // import go/internal/gcimporter which imports go/types partially 611 imports := make(map[string]*types.Package) 612 _, err := Import(imports, "go/internal/gcimporter", ".", nil) 613 if err != nil { 614 t.Fatal(err) 615 } 616 617 // look for go/types package 618 var goTypesPkg *types.Package 619 for path, pkg := range imports { 620 if path == "go/types" { 621 goTypesPkg = pkg 622 break 623 } 624 } 625 if goTypesPkg == nil { 626 t.Fatal("go/types not found") 627 } 628 629 // look for go/types.Object type 630 obj := lookupObj(t, goTypesPkg.Scope(), "Object") 631 typ, ok := aliases.Unalias(obj.Type()).(*types.Named) 632 if !ok { 633 t.Fatalf("go/types.Object type is %v; wanted named type", typ) 634 } 635 636 // lookup go/types.Object.Pkg method 637 m, index, indirect := types.LookupFieldOrMethod(typ, false, nil, "Pkg") 638 if m == nil { 639 t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect) 640 } 641 642 // the method must belong to go/types 643 if m.Pkg().Path() != "go/types" { 644 t.Fatalf("found %v; want go/types", m.Pkg()) 645 } 646 } 647 648 func TestIssue15517(t *testing.T) { 649 // This package only handles gc export data. 650 needsCompiler(t, "gc") 651 652 // On windows, we have to set the -D option for the compiler to avoid having a drive 653 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 654 if runtime.GOOS == "windows" { 655 t.Skip("avoid dealing with relative paths/drive letters on windows") 656 } 657 658 tmpdir := mktmpdir(t) 659 defer os.RemoveAll(tmpdir) 660 661 compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata"), nil) 662 663 // Multiple imports of p must succeed without redeclaration errors. 664 // We use an import path that's not cleaned up so that the eventual 665 // file path for the package is different from the package path; this 666 // will expose the error if it is present. 667 // 668 // (Issue: Both the textual and the binary importer used the file path 669 // of the package to be imported as key into the shared packages map. 670 // However, the binary importer then used the package path to identify 671 // the imported package to mark it as complete; effectively marking the 672 // wrong package as complete. By using an "unclean" package path, the 673 // file and package path are different, exposing the problem if present. 674 // The same issue occurs with vendoring.) 675 imports := make(map[string]*types.Package) 676 for i := 0; i < 3; i++ { 677 if _, err := Import(imports, "./././testdata/p", tmpdir, nil); err != nil { 678 t.Fatal(err) 679 } 680 } 681 } 682 683 func TestIssue15920(t *testing.T) { 684 // This package only handles gc export data. 685 needsCompiler(t, "gc") 686 687 // On windows, we have to set the -D option for the compiler to avoid having a drive 688 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 689 if runtime.GOOS == "windows" { 690 t.Skip("avoid dealing with relative paths/drive letters on windows") 691 } 692 693 compileAndImportPkg(t, "issue15920") 694 } 695 696 func TestIssue20046(t *testing.T) { 697 // This package only handles gc export data. 698 needsCompiler(t, "gc") 699 700 // On windows, we have to set the -D option for the compiler to avoid having a drive 701 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 702 if runtime.GOOS == "windows" { 703 t.Skip("avoid dealing with relative paths/drive letters on windows") 704 } 705 706 // "./issue20046".V.M must exist 707 pkg := compileAndImportPkg(t, "issue20046") 708 obj := lookupObj(t, pkg.Scope(), "V") 709 if m, index, indirect := types.LookupFieldOrMethod(obj.Type(), false, nil, "M"); m == nil { 710 t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect) 711 } 712 } 713 714 func TestIssue25301(t *testing.T) { 715 // This package only handles gc export data. 716 needsCompiler(t, "gc") 717 718 // On windows, we have to set the -D option for the compiler to avoid having a drive 719 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 720 if runtime.GOOS == "windows" { 721 t.Skip("avoid dealing with relative paths/drive letters on windows") 722 } 723 724 compileAndImportPkg(t, "issue25301") 725 } 726 727 func TestIssue51836(t *testing.T) { 728 // This package only handles gc export data. 729 needsCompiler(t, "gc") 730 731 // On windows, we have to set the -D option for the compiler to avoid having a drive 732 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 733 if runtime.GOOS == "windows" { 734 t.Skip("avoid dealing with relative paths/drive letters on windows") 735 } 736 737 tmpdir := mktmpdir(t) 738 defer os.RemoveAll(tmpdir) 739 testoutdir := filepath.Join(tmpdir, "testdata") 740 741 dir := filepath.Join("testdata", "issue51836") 742 // Following the pattern of TestIssue13898, aa.go needs to be compiled from 743 // the output directory. We pass the full path to compile() so that we don't 744 // have to copy the file to that directory. 745 bpath, err := filepath.Abs(filepath.Join(dir, "aa.go")) 746 if err != nil { 747 t.Fatal(err) 748 } 749 compilePkg(t, dir, "a.go", testoutdir, nil, apkg(testoutdir)) 750 compile(t, testoutdir, bpath, testoutdir, map[string]string{apkg(testoutdir): filepath.Join(testoutdir, "a.o")}) 751 752 // import must succeed (test for issue at hand) 753 _ = importPkg(t, "./testdata/aa", tmpdir) 754 } 755 756 func TestIssue61561(t *testing.T) { 757 const src = `package p 758 759 type I[P any] interface { 760 m(P) 761 n() P 762 } 763 764 type J = I[int] 765 766 type StillBad[P any] *interface{b(P)} 767 768 type K = StillBad[string] 769 ` 770 fset := token.NewFileSet() 771 f, err := goparser.ParseFile(fset, "p.go", src, 0) 772 if f == nil { 773 // Some test cases may have parse errors, but we must always have a 774 // file. 775 t.Fatalf("ParseFile returned nil file. Err: %v", err) 776 } 777 778 config := &types.Config{} 779 pkg1, err := config.Check("p", fset, []*ast.File{f}, nil) 780 if err != nil { 781 t.Fatal(err) 782 } 783 784 // Export it. (Shallowness isn't important here.) 785 data, err := IExportShallow(fset, pkg1, nil) 786 if err != nil { 787 t.Fatalf("export: %v", err) // any failure to export is a bug 788 } 789 790 // Re-import it. 791 imports := make(map[string]*types.Package) 792 pkg2, err := IImportShallow(fset, GetPackagesFromMap(imports), data, "p", nil) 793 if err != nil { 794 t.Fatalf("import: %v", err) // any failure of IExport+IImport is a bug. 795 } 796 797 insts := []types.Type{ 798 pkg2.Scope().Lookup("J").Type(), 799 // This test is still racy, because the incomplete interface is contained 800 // within a nested type expression. 801 // 802 // Uncomment this once golang/go#61561 is fixed. 803 // pkg2.Scope().Lookup("K").Type().Underlying().(*types.Pointer).Elem(), 804 } 805 806 // Use the interface instances concurrently. 807 for _, inst := range insts { 808 var wg sync.WaitGroup 809 for i := 0; i < 2; i++ { 810 wg.Add(1) 811 go func() { 812 defer wg.Done() 813 _ = types.NewMethodSet(inst) 814 }() 815 } 816 wg.Wait() 817 } 818 } 819 820 func TestIssue57015(t *testing.T) { 821 // This package only handles gc export data. 822 needsCompiler(t, "gc") 823 824 // On windows, we have to set the -D option for the compiler to avoid having a drive 825 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 826 if runtime.GOOS == "windows" { 827 t.Skip("avoid dealing with relative paths/drive letters on windows") 828 } 829 830 compileAndImportPkg(t, "issue57015") 831 } 832 833 // This is a regression test for a failure to export a package 834 // containing type errors. 835 // 836 // Though the issues and tests are specific, they may be representatives of a 837 // class of exporter bugs on ill-typed code that we have yet to flush out. 838 // 839 // TODO(adonovan): systematize our search for similar problems using 840 // fuzz testing. 841 func TestExportInvalid(t *testing.T) { 842 843 tests := []struct { 844 name string 845 src string 846 objName string 847 }{ 848 // The lack of a receiver causes Recv.Type=Invalid. 849 // (The type checker then treats Foo as a package-level 850 // function, inserting it into the package scope.) 851 // The exporter needs to apply the same treatment. 852 {"issue 57729", `package p; func () Foo() {}`, "Foo"}, 853 854 // It must be possible to export a constant with unknown kind, even if its 855 // type is known. 856 {"issue 60605", `package p; const EPSILON float64 = 1e-`, "EPSILON"}, 857 858 // We must not crash when exporting a struct with unknown package. 859 {"issue 60891", `package p; type I[P any] int; const C I[struct{}] = 42`, "C"}, 860 } 861 862 for _, test := range tests { 863 t.Run(test.name, func(t *testing.T) { 864 // Parse the ill-typed input. 865 fset := token.NewFileSet() 866 867 f, err := goparser.ParseFile(fset, "p.go", test.src, 0) 868 if f == nil { 869 // Some test cases may have parse errors, but we must always have a 870 // file. 871 t.Fatalf("ParseFile returned nil file. Err: %v", err) 872 } 873 874 // Type check it, expecting errors. 875 config := &types.Config{ 876 Error: func(err error) { t.Log(err) }, // don't abort at first error 877 } 878 pkg1, _ := config.Check("p", fset, []*ast.File{f}, nil) 879 880 // Export it. 881 // (Shallowness isn't important here.) 882 data, err := IExportShallow(fset, pkg1, nil) 883 if err != nil { 884 t.Fatalf("export: %v", err) // any failure to export is a bug 885 } 886 887 // Re-import it. 888 imports := make(map[string]*types.Package) 889 pkg2, err := IImportShallow(fset, GetPackagesFromMap(imports), data, "p", nil) 890 if err != nil { 891 t.Fatalf("import: %v", err) // any failure of IExport+IImport is a bug. 892 } 893 894 // Check that the expected object is present in both packages. 895 // We can't assert the type hasn't changed: it may have, in some cases. 896 hasObj1 := pkg1.Scope().Lookup(test.objName) != nil 897 hasObj2 := pkg2.Scope().Lookup(test.objName) != nil 898 if hasObj1 != hasObj2 { 899 t.Errorf("export+import changed Lookup(%q)!=nil: was %t, became %t", test.objName, hasObj1, hasObj2) 900 } 901 }) 902 } 903 } 904 905 func TestIssue58296(t *testing.T) { 906 // Compiles packages c, b, and a where c imports b and b imports a, 907 // then imports c with stub *types.Packages for b and a, and checks that 908 // both a and b are in the Imports() of c. 909 // 910 // This is how go/packages can read the exportdata when NeedDeps is off. 911 912 // This package only handles gc export data. 913 needsCompiler(t, "gc") 914 testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache 915 916 // On windows, we have to set the -D option for the compiler to avoid having a drive 917 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 918 if runtime.GOOS == "windows" { 919 t.Skip("avoid dealing with relative paths/drive letters on windows") 920 } 921 922 tmpdir := mktmpdir(t) 923 defer os.RemoveAll(tmpdir) 924 testoutdir := filepath.Join(tmpdir, "testdata") 925 926 apkg := filepath.Join(testoutdir, "a") 927 bpkg := filepath.Join(testoutdir, "b") 928 cpkg := filepath.Join(testoutdir, "c") 929 930 srcdir := filepath.Join("testdata", "issue58296") 931 compilePkg(t, filepath.Join(srcdir, "a"), "a.go", testoutdir, nil, apkg) 932 compilePkg(t, filepath.Join(srcdir, "b"), "b.go", testoutdir, map[string]string{apkg: filepath.Join(testoutdir, "a.o")}, bpkg) 933 compilePkg(t, filepath.Join(srcdir, "c"), "c.go", testoutdir, map[string]string{bpkg: filepath.Join(testoutdir, "b.o")}, cpkg) 934 935 // The export data reader for c cannot rely on Package.Imports 936 // being populated for a or b. (For the imports {a,b} it is unset.) 937 imports := map[string]*types.Package{ 938 apkg: types.NewPackage(apkg, "a"), 939 bpkg: types.NewPackage(bpkg, "b"), 940 } 941 942 // make sure a and b are both imported by c. 943 pkg, err := Import(imports, "./c", testoutdir, nil) 944 if err != nil { 945 t.Fatal(err) 946 } 947 948 var names []string 949 for _, imp := range pkg.Imports() { 950 names = append(names, imp.Name()) 951 } 952 sort.Strings(names) 953 954 if got, want := strings.Join(names, ","), "a,b"; got != want { 955 t.Errorf("got imports %v for package c. wanted %v", names, want) 956 } 957 } 958 959 // apkg returns the package "a" prefixed by (as a package) testoutdir 960 func apkg(testoutdir string) string { 961 apkg := testoutdir + "/a" 962 if os.PathSeparator != '/' { 963 apkg = strings.ReplaceAll(apkg, string(os.PathSeparator), "/") 964 } 965 return apkg 966 } 967 968 func importPkg(t *testing.T, path, srcDir string) *types.Package { 969 pkg, err := Import(make(map[string]*types.Package), path, srcDir, nil) 970 if err != nil { 971 t.Fatal(err) 972 } 973 return pkg 974 } 975 976 func compileAndImportPkg(t *testing.T, name string) *types.Package { 977 tmpdir := mktmpdir(t) 978 defer os.RemoveAll(tmpdir) 979 compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata"), nil) 980 return importPkg(t, "./testdata/"+name, tmpdir) 981 } 982 983 func lookupObj(t *testing.T, scope *types.Scope, name string) types.Object { 984 if obj := scope.Lookup(name); obj != nil { 985 return obj 986 } 987 t.Fatalf("%s not found", name) 988 return nil 989 }