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