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