1
2
3
4
5 package gcimporter_test
6
7 import (
8 "bytes"
9 "fmt"
10 "internal/godebug"
11 "internal/testenv"
12 "os"
13 "os/exec"
14 "path"
15 "path/filepath"
16 "runtime"
17 "strings"
18 "testing"
19 "time"
20
21 "go/ast"
22 "go/build"
23 "go/importer"
24 "go/parser"
25 "go/token"
26 "go/types"
27
28 . "go/internal/gcimporter"
29 )
30
31 func TestMain(m *testing.M) {
32 build.Default.GOROOT = testenv.GOROOT(nil)
33 os.Exit(m.Run())
34 }
35
36
37
38
39 func compile(t *testing.T, dirname, filename, outdirname string, packageFiles map[string]string, pkgImports ...string) string {
40
41 basename, ok := strings.CutSuffix(filepath.Base(filename), ".go")
42 if !ok {
43 t.Fatalf("filename doesn't end in .go: %s", filename)
44 }
45 objname := basename + ".o"
46 outname := filepath.Join(outdirname, objname)
47
48 importcfgfile := os.DevNull
49 if len(packageFiles) > 0 || len(pkgImports) > 0 {
50 importcfgfile = filepath.Join(outdirname, basename) + ".importcfg"
51 testenv.WriteImportcfg(t, importcfgfile, packageFiles, pkgImports...)
52 }
53
54 pkgpath := path.Join("testdata", basename)
55 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-p", pkgpath, "-D", "testdata", "-importcfg", importcfgfile, "-o", outname, filename)
56 cmd.Dir = dirname
57 out, err := cmd.CombinedOutput()
58 if err != nil {
59 t.Logf("%s", out)
60 t.Fatalf("go tool compile %s failed: %s", filename, err)
61 }
62 return outname
63 }
64
65 func testPath(t *testing.T, path, srcDir string) *types.Package {
66 t0 := time.Now()
67 fset := token.NewFileSet()
68 pkg, err := Import(fset, make(map[string]*types.Package), path, srcDir, nil)
69 if err != nil {
70 t.Errorf("testPath(%s): %s", path, err)
71 return nil
72 }
73 t.Logf("testPath(%s): %v", path, time.Since(t0))
74 return pkg
75 }
76
77 var pkgExts = [...]string{".a", ".o"}
78
79 func mktmpdir(t *testing.T) string {
80 tmpdir, err := os.MkdirTemp("", "gcimporter_test")
81 if err != nil {
82 t.Fatal("mktmpdir:", err)
83 }
84 if err := os.Mkdir(filepath.Join(tmpdir, "testdata"), 0700); err != nil {
85 os.RemoveAll(tmpdir)
86 t.Fatal("mktmpdir:", err)
87 }
88 return tmpdir
89 }
90
91 func TestImportTestdata(t *testing.T) {
92
93 if runtime.Compiler != "gc" {
94 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
95 }
96
97 testenv.MustHaveGoBuild(t)
98
99 testfiles := map[string][]string{
100 "exports.go": {"go/ast", "go/token"},
101 "generics.go": nil,
102 }
103 if true {
104
105
106
107 testfiles["exports.go"] = []string{"go/ast"}
108 }
109
110 for testfile, wantImports := range testfiles {
111 tmpdir := mktmpdir(t)
112 defer os.RemoveAll(tmpdir)
113
114 compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"), nil, wantImports...)
115 path := "./testdata/" + strings.TrimSuffix(testfile, ".go")
116
117 if pkg := testPath(t, path, tmpdir); pkg != nil {
118
119
120
121 got := fmt.Sprint(pkg.Imports())
122 for _, want := range wantImports {
123 if !strings.Contains(got, want) {
124 t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
125 }
126 }
127 }
128 }
129 }
130
131 func TestImportTypeparamTests(t *testing.T) {
132 if testing.Short() {
133 t.Skipf("in short mode, skipping test that requires export data for all of std")
134 }
135
136
137 if runtime.Compiler != "gc" {
138 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
139 }
140
141
142
143
144 gorootTest := filepath.Join(testenv.GOROOT(t), "test")
145 if _, err := os.Stat(gorootTest); os.IsNotExist(err) {
146 if _, err := os.Stat(filepath.Join(testenv.GOROOT(t), "VERSION")); err == nil {
147 t.Skipf("skipping: GOROOT/test not present")
148 }
149 }
150
151 testenv.MustHaveGoBuild(t)
152
153 tmpdir := mktmpdir(t)
154 defer os.RemoveAll(tmpdir)
155
156
157
158 rootDir := filepath.Join(gorootTest, "typeparam")
159 list, err := os.ReadDir(rootDir)
160 if err != nil {
161 t.Fatal(err)
162 }
163
164 for _, entry := range list {
165 if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") {
166
167 continue
168 }
169
170 t.Run(entry.Name(), func(t *testing.T) {
171 filename := filepath.Join(rootDir, entry.Name())
172 src, err := os.ReadFile(filename)
173 if err != nil {
174 t.Fatal(err)
175 }
176 if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) {
177
178
179
180 t.Skipf("not detected as a run test")
181 }
182
183
184
185 compile(t, rootDir, entry.Name(), filepath.Join(tmpdir, "testdata"), nil, filename)
186 pkgName := strings.TrimSuffix(entry.Name(), ".go")
187 imported := importPkg(t, "./testdata/"+pkgName, tmpdir)
188 checked := checkFile(t, filename, src)
189
190 seen := make(map[string]bool)
191 for _, name := range imported.Scope().Names() {
192 if !token.IsExported(name) {
193 continue
194 }
195 seen[name] = true
196
197 importedObj := imported.Scope().Lookup(name)
198 got := types.ObjectString(importedObj, types.RelativeTo(imported))
199 got = sanitizeObjectString(got)
200
201 checkedObj := checked.Scope().Lookup(name)
202 if checkedObj == nil {
203 t.Fatalf("imported object %q was not type-checked", name)
204 }
205 want := types.ObjectString(checkedObj, types.RelativeTo(checked))
206 want = sanitizeObjectString(want)
207
208
209
210 if godebug.New("gotypesalias").Value() != "0" {
211 symbol := name + " in " + filepath.Base(filename)
212 switch symbol {
213 case "Eint2 in struct.go", "A in issue50259.go":
214 t.Skipf("%s requires gotypesalias=1", symbol)
215 }
216 }
217
218 if got != want {
219 t.Errorf("imported %q as %q, want %q", name, got, want)
220 }
221 }
222
223 for _, name := range checked.Scope().Names() {
224 if !token.IsExported(name) || seen[name] {
225 continue
226 }
227 t.Errorf("did not import object %q", name)
228 }
229 })
230 }
231 }
232
233
234
235
236 func sanitizeObjectString(s string) string {
237 var runes []rune
238 for _, r := range s {
239 if '₀' <= r && r < '₀'+10 {
240 continue
241 }
242 runes = append(runes, r)
243 }
244 return string(runes)
245 }
246
247 func checkFile(t *testing.T, filename string, src []byte) *types.Package {
248 fset := token.NewFileSet()
249 f, err := parser.ParseFile(fset, filename, src, 0)
250 if err != nil {
251 t.Fatal(err)
252 }
253 config := types.Config{
254 Importer: importer.Default(),
255 }
256 pkg, err := config.Check("", fset, []*ast.File{f}, nil)
257 if err != nil {
258 t.Fatal(err)
259 }
260 return pkg
261 }
262
263 func TestVersionHandling(t *testing.T) {
264 testenv.MustHaveGoBuild(t)
265
266
267 if runtime.Compiler != "gc" {
268 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
269 }
270
271 const dir = "./testdata/versions"
272 list, err := os.ReadDir(dir)
273 if err != nil {
274 t.Fatal(err)
275 }
276
277 tmpdir := mktmpdir(t)
278 defer os.RemoveAll(tmpdir)
279 corruptdir := filepath.Join(tmpdir, "testdata", "versions")
280 if err := os.Mkdir(corruptdir, 0700); err != nil {
281 t.Fatal(err)
282 }
283
284 fset := token.NewFileSet()
285
286 for _, f := range list {
287 name := f.Name()
288 if !strings.HasSuffix(name, ".a") {
289 continue
290 }
291 if strings.Contains(name, "corrupted") {
292 continue
293 }
294 pkgpath := "./" + name[:len(name)-2]
295
296 if testing.Verbose() {
297 t.Logf("importing %s", name)
298 }
299
300
301 _, err := Import(fset, make(map[string]*types.Package), pkgpath, dir, nil)
302 if err != nil {
303
304 if strings.Contains(err.Error(), "no longer supported") {
305 switch name {
306 case "test_go1.7_0.a", "test_go1.7_1.a",
307 "test_go1.8_4.a", "test_go1.8_5.a",
308 "test_go1.11_6b.a", "test_go1.11_999b.a":
309 continue
310 }
311
312 }
313
314 if strings.Contains(err.Error(), "newer version") {
315 switch name {
316 case "test_go1.11_999i.a":
317 continue
318 }
319
320 }
321 t.Errorf("import %q failed: %v", pkgpath, err)
322 continue
323 }
324
325
326
327 data, err := os.ReadFile(filepath.Join(dir, name))
328 if err != nil {
329 t.Fatal(err)
330 }
331
332 i := bytes.Index(data, []byte("\n$$B\n")) + 5
333 j := bytes.Index(data[i:], []byte("\n$$\n")) + i
334 if i < 0 || j < 0 || i > j {
335 t.Fatalf("export data section not found (i = %d, j = %d)", i, j)
336 }
337
338 for k := j - 13; k >= i; k -= 7 {
339 data[k]++
340 }
341
342 pkgpath += "_corrupted"
343 filename := filepath.Join(corruptdir, pkgpath) + ".a"
344 os.WriteFile(filename, data, 0666)
345
346
347 _, err = Import(fset, make(map[string]*types.Package), pkgpath, corruptdir, nil)
348 if err == nil {
349 t.Errorf("import corrupted %q succeeded", pkgpath)
350 } else if msg := err.Error(); !strings.Contains(msg, "version skew") {
351 t.Errorf("import %q error incorrect (%s)", pkgpath, msg)
352 }
353 }
354 }
355
356 func TestImportStdLib(t *testing.T) {
357 if testing.Short() {
358 t.Skip("the imports can be expensive, and this test is especially slow when the build cache is empty")
359 }
360 testenv.MustHaveGoBuild(t)
361
362
363 if runtime.Compiler != "gc" {
364 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
365 }
366
367
368 var stderr bytes.Buffer
369 cmd := exec.Command("go", "list", "-f", "{{if .GoFiles}}{{.ImportPath}}{{end}}", "std")
370 cmd.Stderr = &stderr
371 out, err := cmd.Output()
372 if err != nil {
373 t.Fatalf("failed to run go list to determine stdlib packages: %v\nstderr:\n%v", err, stderr.String())
374 }
375 pkgs := strings.Fields(string(out))
376
377 var nimports int
378 for _, pkg := range pkgs {
379 t.Run(pkg, func(t *testing.T) {
380 if testPath(t, pkg, filepath.Join(testenv.GOROOT(t), "src", path.Dir(pkg))) != nil {
381 nimports++
382 }
383 })
384 }
385 const minPkgs = 225
386 if len(pkgs) < minPkgs {
387 t.Fatalf("too few packages (%d) were imported", nimports)
388 }
389
390 t.Logf("tested %d imports", nimports)
391 }
392
393 var importedObjectTests = []struct {
394 name string
395 want string
396 }{
397
398 {"crypto.Hash", "type Hash uint"},
399 {"go/ast.ObjKind", "type ObjKind int"},
400 {"go/types.Qualifier", "type Qualifier func(*Package) string"},
401 {"go/types.Comparable", "func Comparable(T Type) bool"},
402 {"math.Pi", "const Pi untyped float"},
403 {"math.Sin", "func Sin(x float64) float64"},
404 {"go/ast.NotNilFilter", "func NotNilFilter(_ string, v reflect.Value) bool"},
405 {"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string, err error)"},
406
407
408 {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key any) any}"},
409 {"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"},
410 {"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"},
411 {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
412 {"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"},
413 {"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"},
414 {"go/types.Type", "type Type interface{String() string; Underlying() Type}"},
415 }
416
417 func TestImportedTypes(t *testing.T) {
418 testenv.MustHaveGoBuild(t)
419
420
421 if runtime.Compiler != "gc" {
422 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
423 }
424
425 fset := token.NewFileSet()
426 for _, test := range importedObjectTests {
427 s := strings.Split(test.name, ".")
428 if len(s) != 2 {
429 t.Fatal("inconsistent test data")
430 }
431 importPath := s[0]
432 objName := s[1]
433
434 pkg, err := Import(fset, make(map[string]*types.Package), importPath, ".", nil)
435 if err != nil {
436 t.Error(err)
437 continue
438 }
439
440 obj := pkg.Scope().Lookup(objName)
441 if obj == nil {
442 t.Errorf("%s: object not found", test.name)
443 continue
444 }
445
446 got := types.ObjectString(obj, types.RelativeTo(pkg))
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
458
459 func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
460
461 if level > 10 {
462 t.Errorf("%s: embeds itself", named)
463 return
464 }
465
466 iface, _ := named.Underlying().(*types.Interface)
467 if iface == nil {
468 return
469 }
470
471
472 for i := 0; i < iface.NumExplicitMethods(); i++ {
473 m := iface.ExplicitMethod(i)
474 recv := m.Type().(*types.Signature).Recv()
475 if recv == nil {
476 t.Errorf("%s: missing receiver type", m)
477 continue
478 }
479 if recv.Type() != named {
480 t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named)
481 }
482 }
483
484
485 for i := 0; i < iface.NumEmbeddeds(); i++ {
486
487 if etype, _ := iface.EmbeddedType(i).(*types.Named); etype != nil {
488 verifyInterfaceMethodRecvs(t, etype, level+1)
489 }
490 }
491 }
492
493 func TestIssue5815(t *testing.T) {
494 testenv.MustHaveGoBuild(t)
495
496
497 if runtime.Compiler != "gc" {
498 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
499 }
500
501 pkg := importPkg(t, "strings", ".")
502
503 scope := pkg.Scope()
504 for _, name := range scope.Names() {
505 obj := scope.Lookup(name)
506 if obj.Pkg() == nil {
507 t.Errorf("no pkg for %s", obj)
508 }
509 if tname, _ := obj.(*types.TypeName); tname != nil {
510 named := tname.Type().(*types.Named)
511 for i := 0; i < named.NumMethods(); i++ {
512 m := named.Method(i)
513 if m.Pkg() == nil {
514 t.Errorf("no pkg for %s", m)
515 }
516 }
517 }
518 }
519 }
520
521
522 func TestCorrectMethodPackage(t *testing.T) {
523 testenv.MustHaveGoBuild(t)
524
525
526 if runtime.Compiler != "gc" {
527 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
528 }
529
530 imports := make(map[string]*types.Package)
531 fset := token.NewFileSet()
532 _, err := Import(fset, imports, "net/http", ".", nil)
533 if err != nil {
534 t.Fatal(err)
535 }
536
537 mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type()
538 mset := types.NewMethodSet(types.NewPointer(mutex))
539 sel := mset.Lookup(nil, "Lock")
540 lock := sel.Obj().(*types.Func)
541 if got, want := lock.Pkg().Path(), "sync"; got != want {
542 t.Errorf("got package path %q; want %q", got, want)
543 }
544 }
545
546 func TestIssue13566(t *testing.T) {
547 testenv.MustHaveGoBuild(t)
548
549
550 if runtime.Compiler != "gc" {
551 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
552 }
553
554 tmpdir := mktmpdir(t)
555 defer os.RemoveAll(tmpdir)
556 testoutdir := filepath.Join(tmpdir, "testdata")
557
558
559
560
561 bpath, err := filepath.Abs(filepath.Join("testdata", "b.go"))
562 if err != nil {
563 t.Fatal(err)
564 }
565
566 compile(t, "testdata", "a.go", testoutdir, nil, "encoding/json")
567 compile(t, testoutdir, bpath, testoutdir, map[string]string{"testdata/a": filepath.Join(testoutdir, "a.o")}, "encoding/json")
568
569
570 pkg := importPkg(t, "./testdata/b", tmpdir)
571
572
573 for _, imp := range pkg.Imports() {
574 if imp.Name() == "" {
575 t.Errorf("no name for %s package", imp.Path())
576 }
577 }
578 }
579
580 func TestTypeNamingOrder(t *testing.T) {
581 testenv.MustHaveGoBuild(t)
582
583
584 if runtime.Compiler != "gc" {
585 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
586 }
587
588 tmpdir := mktmpdir(t)
589 defer os.RemoveAll(tmpdir)
590 testoutdir := filepath.Join(tmpdir, "testdata")
591
592 compile(t, "testdata", "g.go", testoutdir, nil)
593
594
595 _ = importPkg(t, "./testdata/g", tmpdir)
596 }
597
598 func TestIssue13898(t *testing.T) {
599 testenv.MustHaveGoBuild(t)
600
601
602 if runtime.Compiler != "gc" {
603 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
604 }
605
606
607 fset := token.NewFileSet()
608 imports := make(map[string]*types.Package)
609 _, err := Import(fset, imports, "go/internal/gcimporter", ".", nil)
610 if err != nil {
611 t.Fatal(err)
612 }
613
614
615 var goTypesPkg *types.Package
616 for path, pkg := range imports {
617 if path == "go/types" {
618 goTypesPkg = pkg
619 break
620 }
621 }
622 if goTypesPkg == nil {
623 t.Fatal("go/types not found")
624 }
625
626
627 obj := lookupObj(t, goTypesPkg.Scope(), "Object")
628 typ, ok := obj.Type().(*types.Named)
629 if !ok {
630 t.Fatalf("go/types.Object type is %v; wanted named type", typ)
631 }
632
633
634 m, index, indirect := types.LookupFieldOrMethod(typ, false, nil, "Pkg")
635 if m == nil {
636 t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect)
637 }
638
639
640 if m.Pkg().Path() != "go/types" {
641 t.Fatalf("found %v; want go/types", m.Pkg())
642 }
643 }
644
645 func TestIssue15517(t *testing.T) {
646 testenv.MustHaveGoBuild(t)
647
648
649 if runtime.Compiler != "gc" {
650 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
651 }
652
653 tmpdir := mktmpdir(t)
654 defer os.RemoveAll(tmpdir)
655
656 compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata"), nil)
657
658
659
660
661
662
663
664
665
666
667
668
669
670 imports := make(map[string]*types.Package)
671 fset := token.NewFileSet()
672 for i := 0; i < 3; i++ {
673 if _, err := Import(fset, imports, "./././testdata/p", tmpdir, nil); err != nil {
674 t.Fatal(err)
675 }
676 }
677 }
678
679 func TestIssue15920(t *testing.T) {
680 testenv.MustHaveGoBuild(t)
681
682
683 if runtime.Compiler != "gc" {
684 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
685 }
686
687 compileAndImportPkg(t, "issue15920")
688 }
689
690 func TestIssue20046(t *testing.T) {
691 testenv.MustHaveGoBuild(t)
692
693
694 if runtime.Compiler != "gc" {
695 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
696 }
697
698
699 pkg := compileAndImportPkg(t, "issue20046")
700 obj := lookupObj(t, pkg.Scope(), "V")
701 if m, index, indirect := types.LookupFieldOrMethod(obj.Type(), false, nil, "M"); m == nil {
702 t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect)
703 }
704 }
705 func TestIssue25301(t *testing.T) {
706 testenv.MustHaveGoBuild(t)
707
708
709 if runtime.Compiler != "gc" {
710 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
711 }
712
713 compileAndImportPkg(t, "issue25301")
714 }
715
716 func TestIssue25596(t *testing.T) {
717 testenv.MustHaveGoBuild(t)
718
719
720 if runtime.Compiler != "gc" {
721 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
722 }
723
724 compileAndImportPkg(t, "issue25596")
725 }
726
727 func TestIssue57015(t *testing.T) {
728 testenv.MustHaveGoBuild(t)
729
730
731 if runtime.Compiler != "gc" {
732 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
733 }
734
735 compileAndImportPkg(t, "issue57015")
736 }
737
738 func importPkg(t *testing.T, path, srcDir string) *types.Package {
739 fset := token.NewFileSet()
740 pkg, err := Import(fset, make(map[string]*types.Package), path, srcDir, nil)
741 if err != nil {
742 t.Helper()
743 t.Fatal(err)
744 }
745 return pkg
746 }
747
748 func compileAndImportPkg(t *testing.T, name string) *types.Package {
749 t.Helper()
750 tmpdir := mktmpdir(t)
751 defer os.RemoveAll(tmpdir)
752 compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata"), nil)
753 return importPkg(t, "./testdata/"+name, tmpdir)
754 }
755
756 func lookupObj(t *testing.T, scope *types.Scope, name string) types.Object {
757 if obj := scope.Lookup(name); obj != nil {
758 return obj
759 }
760 t.Helper()
761 t.Fatalf("%s not found", name)
762 return nil
763 }
764
View as plain text