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