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 | "strings" |
27 | "testing" |
28 | "time" |
29 | |
30 | "golang.org/x/tools/internal/goroot" |
31 | "golang.org/x/tools/internal/testenv" |
32 | ) |
33 | |
34 | func TestMain(m *testing.M) { |
35 | testenv.ExitIfSmallMachine() |
36 | os.Exit(m.Run()) |
37 | } |
38 | |
39 | // ---------------------------------------------------------------------------- |
40 | |
41 | func needsCompiler(t *testing.T, compiler string) { |
42 | if runtime.Compiler == compiler { |
43 | return |
44 | } |
45 | switch compiler { |
46 | case "gc": |
47 | t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) |
48 | } |
49 | } |
50 | |
51 | // compile runs the compiler on filename, with dirname as the working directory, |
52 | // and writes the output file to outdirname. |
53 | // compile gives the resulting package a packagepath of p. |
54 | func compile(t *testing.T, dirname, filename, outdirname string, packagefiles map[string]string) string { |
55 | return compilePkg(t, dirname, filename, outdirname, packagefiles, "p") |
56 | } |
57 | |
58 | func compilePkg(t *testing.T, dirname, filename, outdirname string, packagefiles map[string]string, pkg string) string { |
59 | testenv.NeedsGoBuild(t) |
60 | |
61 | // filename must end with ".go" |
62 | basename := strings.TrimSuffix(filepath.Base(filename), ".go") |
63 | ok := filename != basename |
64 | if !ok { |
65 | t.Fatalf("filename doesn't end in .go: %s", filename) |
66 | } |
67 | objname := basename + ".o" |
68 | outname := filepath.Join(outdirname, objname) |
69 | |
70 | importcfgfile := os.DevNull |
71 | if len(packagefiles) > 0 { |
72 | importcfgfile = filepath.Join(outdirname, basename) + ".importcfg" |
73 | importcfg := new(bytes.Buffer) |
74 | fmt.Fprintf(importcfg, "# import config") |
75 | for k, v := range packagefiles { |
76 | fmt.Fprintf(importcfg, "\npackagefile %s=%s\n", k, v) |
77 | } |
78 | if err := os.WriteFile(importcfgfile, importcfg.Bytes(), 0655); err != nil { |
79 | t.Fatal(err) |
80 | } |
81 | } |
82 | |
83 | importreldir := strings.ReplaceAll(outdirname, string(os.PathSeparator), "/") |
84 | cmd := exec.Command("go", "tool", "compile", "-p", pkg, "-D", importreldir, "-importcfg", importcfgfile, "-o", outname, filename) |
85 | cmd.Dir = dirname |
86 | out, err := cmd.CombinedOutput() |
87 | if 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 := ioutil.TempDir("", "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.NeedsGo1Point(t, 18) // requires generics |
168 | testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache |
169 | |
170 | // This package only handles gc export data. |
171 | if runtime.Compiler != "gc" { |
172 | t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) |
173 | } |
174 | |
175 | tmpdir := mktmpdir(t) |
176 | defer os.RemoveAll(tmpdir) |
177 | |
178 | // Check go files in test/typeparam, except those that fail for a known |
179 | // reason. |
180 | rootDir := filepath.Join(runtime.GOROOT(), "test", "typeparam") |
181 | list, err := os.ReadDir(rootDir) |
182 | if err != nil { |
183 | t.Fatal(err) |
184 | } |
185 | |
186 | var skip map[string]string |
187 | if !unifiedIR { |
188 | // The Go 1.18 frontend still fails several cases. |
189 | skip = map[string]string{ |
190 | "equal.go": "inconsistent embedded sorting", // TODO(rfindley): investigate this. |
191 | "nested.go": "fails to compile", // TODO(rfindley): investigate this. |
192 | "issue47631.go": "can not handle local type declarations", |
193 | "issue55101.go": "fails to compile", |
194 | } |
195 | } |
196 | |
197 | for _, entry := range list { |
198 | if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") { |
199 | // For now, only consider standalone go files. |
200 | continue |
201 | } |
202 | |
203 | t.Run(entry.Name(), func(t *testing.T) { |
204 | if reason, ok := skip[entry.Name()]; ok { |
205 | t.Skip(reason) |
206 | } |
207 | |
208 | filename := filepath.Join(rootDir, entry.Name()) |
209 | src, err := os.ReadFile(filename) |
210 | if err != nil { |
211 | t.Fatal(err) |
212 | } |
213 | if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) { |
214 | // We're bypassing the logic of run.go here, so be conservative about |
215 | // the files we consider in an attempt to make this test more robust to |
216 | // changes in test/typeparams. |
217 | t.Skipf("not detected as a run test") |
218 | } |
219 | |
220 | // Compile and import, and compare the resulting package with the package |
221 | // that was type-checked directly. |
222 | pkgFiles, err := goroot.PkgfileMap() |
223 | if err != nil { |
224 | t.Fatal(err) |
225 | } |
226 | compile(t, rootDir, entry.Name(), filepath.Join(tmpdir, "testdata"), pkgFiles) |
227 | pkgName := strings.TrimSuffix(entry.Name(), ".go") |
228 | imported := importPkg(t, "./testdata/"+pkgName, tmpdir) |
229 | checked := checkFile(t, filename, src) |
230 | |
231 | seen := make(map[string]bool) |
232 | for _, name := range imported.Scope().Names() { |
233 | if !token.IsExported(name) { |
234 | continue // ignore synthetic names like .inittask and .dict.* |
235 | } |
236 | seen[name] = true |
237 | |
238 | importedObj := imported.Scope().Lookup(name) |
239 | got := types.ObjectString(importedObj, types.RelativeTo(imported)) |
240 | got = sanitizeObjectString(got) |
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 | want = sanitizeObjectString(want) |
248 | |
249 | if got != want { |
250 | t.Errorf("imported %q as %q, want %q", name, got, want) |
251 | } |
252 | } |
253 | |
254 | for _, name := range checked.Scope().Names() { |
255 | if !token.IsExported(name) || seen[name] { |
256 | continue |
257 | } |
258 | t.Errorf("did not import object %q", name) |
259 | } |
260 | }) |
261 | } |
262 | } |
263 | |
264 | // sanitizeObjectString removes type parameter debugging markers from an object |
265 | // string, to normalize it for comparison. |
266 | // TODO(rfindley): this should not be necessary. |
267 | func sanitizeObjectString(s string) string { |
268 | var runes []rune |
269 | for _, r := range s { |
270 | if '₀' <= r && r < '₀'+10 { |
271 | continue // trim type parameter subscripts |
272 | } |
273 | runes = append(runes, r) |
274 | } |
275 | return string(runes) |
276 | } |
277 | |
278 | func checkFile(t *testing.T, filename string, src []byte) *types.Package { |
279 | fset := token.NewFileSet() |
280 | f, err := goparser.ParseFile(fset, filename, src, 0) |
281 | if err != nil { |
282 | t.Fatal(err) |
283 | } |
284 | config := types.Config{ |
285 | Importer: goimporter.Default(), |
286 | } |
287 | pkg, err := config.Check("", fset, []*ast.File{f}, nil) |
288 | if err != nil { |
289 | t.Fatal(err) |
290 | } |
291 | return pkg |
292 | } |
293 | |
294 | func TestVersionHandling(t *testing.T) { |
295 | if debug { |
296 | t.Skip("TestVersionHandling panics in debug mode") |
297 | } |
298 | |
299 | // This package only handles gc export data. |
300 | needsCompiler(t, "gc") |
301 | |
302 | const dir = "./testdata/versions" |
303 | list, err := ioutil.ReadDir(dir) |
304 | if err != nil { |
305 | t.Fatal(err) |
306 | } |
307 | |
308 | tmpdir := mktmpdir(t) |
309 | defer os.RemoveAll(tmpdir) |
310 | corruptdir := filepath.Join(tmpdir, "testdata", "versions") |
311 | if err := os.Mkdir(corruptdir, 0700); err != nil { |
312 | t.Fatal(err) |
313 | } |
314 | |
315 | for _, f := range list { |
316 | name := f.Name() |
317 | if !strings.HasSuffix(name, ".a") { |
318 | continue // not a package file |
319 | } |
320 | if strings.Contains(name, "corrupted") { |
321 | continue // don't process a leftover corrupted file |
322 | } |
323 | pkgpath := "./" + name[:len(name)-2] |
324 | |
325 | if testing.Verbose() { |
326 | t.Logf("importing %s", name) |
327 | } |
328 | |
329 | // test that export data can be imported |
330 | _, err := Import(make(map[string]*types.Package), pkgpath, dir, nil) |
331 | if err != nil { |
332 | // ok to fail if it fails with a newer version error for select files |
333 | if strings.Contains(err.Error(), "newer version") { |
334 | switch name { |
335 | case "test_go1.11_999b.a", "test_go1.11_999i.a": |
336 | continue |
337 | } |
338 | // fall through |
339 | } |
340 | t.Errorf("import %q failed: %v", pkgpath, err) |
341 | continue |
342 | } |
343 | |
344 | // create file with corrupted export data |
345 | // 1) read file |
346 | data, err := ioutil.ReadFile(filepath.Join(dir, name)) |
347 | if err != nil { |
348 | t.Fatal(err) |
349 | } |
350 | // 2) find export data |
351 | i := bytes.Index(data, []byte("\n$$B\n")) + 5 |
352 | j := bytes.Index(data[i:], []byte("\n$$\n")) + i |
353 | if i < 0 || j < 0 || i > j { |
354 | t.Fatalf("export data section not found (i = %d, j = %d)", i, j) |
355 | } |
356 | // 3) corrupt the data (increment every 7th byte) |
357 | for k := j - 13; k >= i; k -= 7 { |
358 | data[k]++ |
359 | } |
360 | // 4) write the file |
361 | pkgpath += "_corrupted" |
362 | filename := filepath.Join(corruptdir, pkgpath) + ".a" |
363 | ioutil.WriteFile(filename, data, 0666) |
364 | |
365 | // test that importing the corrupted file results in an error |
366 | _, err = Import(make(map[string]*types.Package), pkgpath, corruptdir, nil) |
367 | if err == nil { |
368 | t.Errorf("import corrupted %q succeeded", pkgpath) |
369 | } else if msg := err.Error(); !strings.Contains(msg, "version skew") { |
370 | t.Errorf("import %q error incorrect (%s)", pkgpath, msg) |
371 | } |
372 | } |
373 | } |
374 | |
375 | func TestImportStdLib(t *testing.T) { |
376 | if testing.Short() { |
377 | t.Skip("the imports can be expensive, and this test is especially slow when the build cache is empty") |
378 | } |
379 | // This package only handles gc export data. |
380 | needsCompiler(t, "gc") |
381 | testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache |
382 | |
383 | // Get list of packages in stdlib. Filter out test-only packages with {{if .GoFiles}} check. |
384 | var stderr bytes.Buffer |
385 | cmd := exec.Command("go", "list", "-f", "{{if .GoFiles}}{{.ImportPath}}{{end}}", "std") |
386 | cmd.Stderr = &stderr |
387 | out, err := cmd.Output() |
388 | if err != nil { |
389 | t.Fatalf("failed to run go list to determine stdlib packages: %v\nstderr:\n%v", err, stderr.String()) |
390 | } |
391 | pkgs := strings.Fields(string(out)) |
392 | |
393 | var nimports int |
394 | for _, pkg := range pkgs { |
395 | t.Run(pkg, func(t *testing.T) { |
396 | if testPath(t, pkg, filepath.Join(testenv.GOROOT(t), "src", path.Dir(pkg))) != nil { |
397 | nimports++ |
398 | } |
399 | }) |
400 | } |
401 | const minPkgs = 225 // 'GOOS=plan9 go1.18 list std | wc -l' reports 228; most other platforms have more. |
402 | if len(pkgs) < minPkgs { |
403 | t.Fatalf("too few packages (%d) were imported", nimports) |
404 | } |
405 | |
406 | t.Logf("tested %d imports", nimports) |
407 | } |
408 | |
409 | var importedObjectTests = []struct { |
410 | name string |
411 | want string |
412 | }{ |
413 | // non-interfaces |
414 | {"crypto.Hash", "type Hash uint"}, |
415 | {"go/ast.ObjKind", "type ObjKind int"}, |
416 | {"go/types.Qualifier", "type Qualifier func(*Package) string"}, |
417 | {"go/types.Comparable", "func Comparable(T Type) bool"}, |
418 | {"math.Pi", "const Pi untyped float"}, |
419 | {"math.Sin", "func Sin(x float64) float64"}, |
420 | {"go/ast.NotNilFilter", "func NotNilFilter(_ string, v reflect.Value) bool"}, |
421 | {"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string)"}, |
422 | |
423 | // interfaces |
424 | {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key any) any}"}, |
425 | {"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"}, |
426 | {"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"}, |
427 | {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"}, |
428 | {"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"}, |
429 | {"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"}, |
430 | {"go/types.Type", "type Type interface{String() string; Underlying() Type}"}, |
431 | } |
432 | |
433 | // TODO(rsc): Delete this init func after x/tools no longer needs to test successfully with Go 1.17. |
434 | func init() { |
435 | if build.Default.ReleaseTags[len(build.Default.ReleaseTags)-1] <= "go1.17" { |
436 | for i := range importedObjectTests { |
437 | if importedObjectTests[i].name == "context.Context" { |
438 | // Expand any to interface{}. |
439 | importedObjectTests[i].want = "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}" |
440 | } |
441 | } |
442 | } |
443 | } |
444 | |
445 | func TestImportedTypes(t *testing.T) { |
446 | // This package only handles gc export data. |
447 | needsCompiler(t, "gc") |
448 | testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache |
449 | |
450 | for _, test := range importedObjectTests { |
451 | obj := importObject(t, test.name) |
452 | if obj == nil { |
453 | continue // error reported elsewhere |
454 | } |
455 | got := types.ObjectString(obj, types.RelativeTo(obj.Pkg())) |
456 | |
457 | // TODO(rsc): Delete this block once go.dev/cl/368254 lands. |
458 | if got != test.want && test.want == strings.ReplaceAll(got, "interface{}", "any") { |
459 | got = test.want |
460 | } |
461 | |
462 | if got != test.want { |
463 | t.Errorf("%s: got %q; want %q", test.name, got, test.want) |
464 | } |
465 | |
466 | if named, _ := obj.Type().(*types.Named); named != nil { |
467 | verifyInterfaceMethodRecvs(t, named, 0) |
468 | } |
469 | } |
470 | } |
471 | |
472 | func TestImportedConsts(t *testing.T) { |
473 | testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache |
474 | |
475 | tests := []struct { |
476 | name string |
477 | want constant.Kind |
478 | }{ |
479 | {"math.Pi", constant.Float}, |
480 | {"math.MaxFloat64", constant.Float}, |
481 | {"math.MaxInt64", constant.Int}, |
482 | } |
483 | |
484 | for _, test := range tests { |
485 | obj := importObject(t, test.name) |
486 | if got := obj.(*types.Const).Val().Kind(); got != test.want { |
487 | t.Errorf("%s: imported as constant.Kind(%v), want constant.Kind(%v)", test.name, got, test.want) |
488 | } |
489 | } |
490 | } |
491 | |
492 | // importObject imports the object specified by a name of the form |
493 | // <import path>.<object name>, e.g. go/types.Type. |
494 | // |
495 | // If any errors occur they are reported via t and the resulting object will |
496 | // be nil. |
497 | func importObject(t *testing.T, name string) types.Object { |
498 | s := strings.Split(name, ".") |
499 | if len(s) != 2 { |
500 | t.Fatal("inconsistent test data") |
501 | } |
502 | importPath := s[0] |
503 | objName := s[1] |
504 | |
505 | pkg, err := Import(make(map[string]*types.Package), importPath, ".", nil) |
506 | if err != nil { |
507 | t.Error(err) |
508 | return nil |
509 | } |
510 | |
511 | obj := pkg.Scope().Lookup(objName) |
512 | if obj == nil { |
513 | t.Errorf("%s: object not found", name) |
514 | return nil |
515 | } |
516 | return obj |
517 | } |
518 | |
519 | // verifyInterfaceMethodRecvs verifies that method receiver types |
520 | // are named if the methods belong to a named interface type. |
521 | func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) { |
522 | // avoid endless recursion in case of an embedding bug that lead to a cycle |
523 | if level > 10 { |
524 | t.Errorf("%s: embeds itself", named) |
525 | return |
526 | } |
527 | |
528 | iface, _ := named.Underlying().(*types.Interface) |
529 | if iface == nil { |
530 | return // not an interface |
531 | } |
532 | |
533 | // check explicitly declared methods |
534 | for i := 0; i < iface.NumExplicitMethods(); i++ { |
535 | m := iface.ExplicitMethod(i) |
536 | recv := m.Type().(*types.Signature).Recv() |
537 | if recv == nil { |
538 | t.Errorf("%s: missing receiver type", m) |
539 | continue |
540 | } |
541 | if recv.Type() != named { |
542 | t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named) |
543 | } |
544 | } |
545 | |
546 | // check embedded interfaces (if they are named, too) |
547 | for i := 0; i < iface.NumEmbeddeds(); i++ { |
548 | // embedding of interfaces cannot have cycles; recursion will terminate |
549 | if etype, _ := iface.EmbeddedType(i).(*types.Named); etype != nil { |
550 | verifyInterfaceMethodRecvs(t, etype, level+1) |
551 | } |
552 | } |
553 | } |
554 | |
555 | func TestIssue5815(t *testing.T) { |
556 | // This package only handles gc export data. |
557 | needsCompiler(t, "gc") |
558 | testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache |
559 | |
560 | pkg := importPkg(t, "strings", ".") |
561 | |
562 | scope := pkg.Scope() |
563 | for _, name := range scope.Names() { |
564 | obj := scope.Lookup(name) |
565 | if obj.Pkg() == nil { |
566 | t.Errorf("no pkg for %s", obj) |
567 | } |
568 | if tname, _ := obj.(*types.TypeName); tname != nil { |
569 | named := tname.Type().(*types.Named) |
570 | for i := 0; i < named.NumMethods(); i++ { |
571 | m := named.Method(i) |
572 | if m.Pkg() == nil { |
573 | t.Errorf("no pkg for %s", m) |
574 | } |
575 | } |
576 | } |
577 | } |
578 | } |
579 | |
580 | // Smoke test to ensure that imported methods get the correct package. |
581 | func TestCorrectMethodPackage(t *testing.T) { |
582 | // This package only handles gc export data. |
583 | needsCompiler(t, "gc") |
584 | testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache |
585 | |
586 | imports := make(map[string]*types.Package) |
587 | _, err := Import(imports, "net/http", ".", nil) |
588 | if err != nil { |
589 | t.Fatal(err) |
590 | } |
591 | |
592 | mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type() |
593 | mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex |
594 | sel := mset.Lookup(nil, "Lock") |
595 | lock := sel.Obj().(*types.Func) |
596 | if got, want := lock.Pkg().Path(), "sync"; got != want { |
597 | t.Errorf("got package path %q; want %q", got, want) |
598 | } |
599 | } |
600 | |
601 | func TestIssue13566(t *testing.T) { |
602 | // This package only handles gc export data. |
603 | needsCompiler(t, "gc") |
604 | testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache |
605 | |
606 | // On windows, we have to set the -D option for the compiler to avoid having a drive |
607 | // letter and an illegal ':' in the import path - just skip it (see also issue #3483). |
608 | if runtime.GOOS == "windows" { |
609 | t.Skip("avoid dealing with relative paths/drive letters on windows") |
610 | } |
611 | |
612 | tmpdir := mktmpdir(t) |
613 | defer os.RemoveAll(tmpdir) |
614 | testoutdir := filepath.Join(tmpdir, "testdata") |
615 | |
616 | // b.go needs to be compiled from the output directory so that the compiler can |
617 | // find the compiled package a. We pass the full path to compile() so that we |
618 | // don't have to copy the file to that directory. |
619 | bpath, err := filepath.Abs(filepath.Join("testdata", "b.go")) |
620 | if err != nil { |
621 | t.Fatal(err) |
622 | } |
623 | |
624 | jsonExport, _ := FindPkg("encoding/json", "testdata") |
625 | if jsonExport == "" { |
626 | t.Fatalf("no export data found for encoding/json") |
627 | } |
628 | |
629 | compilePkg(t, "testdata", "a.go", testoutdir, map[string]string{"encoding/json": jsonExport}, apkg(testoutdir)) |
630 | compile(t, testoutdir, bpath, testoutdir, map[string]string{apkg(testoutdir): filepath.Join(testoutdir, "a.o")}) |
631 | |
632 | // import must succeed (test for issue at hand) |
633 | pkg := importPkg(t, "./testdata/b", tmpdir) |
634 | |
635 | // make sure all indirectly imported packages have names |
636 | for _, imp := range pkg.Imports() { |
637 | if imp.Name() == "" { |
638 | t.Errorf("no name for %s package", imp.Path()) |
639 | } |
640 | } |
641 | } |
642 | |
643 | func TestIssue13898(t *testing.T) { |
644 | // This package only handles gc export data. |
645 | needsCompiler(t, "gc") |
646 | testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache |
647 | |
648 | // import go/internal/gcimporter which imports go/types partially |
649 | imports := make(map[string]*types.Package) |
650 | _, err := Import(imports, "go/internal/gcimporter", ".", nil) |
651 | if err != nil { |
652 | t.Fatal(err) |
653 | } |
654 | |
655 | // look for go/types package |
656 | var goTypesPkg *types.Package |
657 | for path, pkg := range imports { |
658 | if path == "go/types" { |
659 | goTypesPkg = pkg |
660 | break |
661 | } |
662 | } |
663 | if goTypesPkg == nil { |
664 | t.Fatal("go/types not found") |
665 | } |
666 | |
667 | // look for go/types.Object type |
668 | obj := lookupObj(t, goTypesPkg.Scope(), "Object") |
669 | typ, ok := obj.Type().(*types.Named) |
670 | if !ok { |
671 | t.Fatalf("go/types.Object type is %v; wanted named type", typ) |
672 | } |
673 | |
674 | // lookup go/types.Object.Pkg method |
675 | m, index, indirect := types.LookupFieldOrMethod(typ, false, nil, "Pkg") |
676 | if m == nil { |
677 | t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect) |
678 | } |
679 | |
680 | // the method must belong to go/types |
681 | if m.Pkg().Path() != "go/types" { |
682 | t.Fatalf("found %v; want go/types", m.Pkg()) |
683 | } |
684 | } |
685 | |
686 | func TestIssue15517(t *testing.T) { |
687 | // This package only handles gc export data. |
688 | needsCompiler(t, "gc") |
689 | |
690 | // On windows, we have to set the -D option for the compiler to avoid having a drive |
691 | // letter and an illegal ':' in the import path - just skip it (see also issue #3483). |
692 | if runtime.GOOS == "windows" { |
693 | t.Skip("avoid dealing with relative paths/drive letters on windows") |
694 | } |
695 | |
696 | tmpdir := mktmpdir(t) |
697 | defer os.RemoveAll(tmpdir) |
698 | |
699 | compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata"), nil) |
700 | |
701 | // Multiple imports of p must succeed without redeclaration errors. |
702 | // We use an import path that's not cleaned up so that the eventual |
703 | // file path for the package is different from the package path; this |
704 | // will expose the error if it is present. |
705 | // |
706 | // (Issue: Both the textual and the binary importer used the file path |
707 | // of the package to be imported as key into the shared packages map. |
708 | // However, the binary importer then used the package path to identify |
709 | // the imported package to mark it as complete; effectively marking the |
710 | // wrong package as complete. By using an "unclean" package path, the |
711 | // file and package path are different, exposing the problem if present. |
712 | // The same issue occurs with vendoring.) |
713 | imports := make(map[string]*types.Package) |
714 | for i := 0; i < 3; i++ { |
715 | if _, err := Import(imports, "./././testdata/p", tmpdir, nil); err != nil { |
716 | t.Fatal(err) |
717 | } |
718 | } |
719 | } |
720 | |
721 | func TestIssue15920(t *testing.T) { |
722 | // This package only handles gc export data. |
723 | needsCompiler(t, "gc") |
724 | |
725 | // On windows, we have to set the -D option for the compiler to avoid having a drive |
726 | // letter and an illegal ':' in the import path - just skip it (see also issue #3483). |
727 | if runtime.GOOS == "windows" { |
728 | t.Skip("avoid dealing with relative paths/drive letters on windows") |
729 | } |
730 | |
731 | compileAndImportPkg(t, "issue15920") |
732 | } |
733 | |
734 | func TestIssue20046(t *testing.T) { |
735 | // This package only handles gc export data. |
736 | needsCompiler(t, "gc") |
737 | |
738 | // On windows, we have to set the -D option for the compiler to avoid having a drive |
739 | // letter and an illegal ':' in the import path - just skip it (see also issue #3483). |
740 | if runtime.GOOS == "windows" { |
741 | t.Skip("avoid dealing with relative paths/drive letters on windows") |
742 | } |
743 | |
744 | // "./issue20046".V.M must exist |
745 | pkg := compileAndImportPkg(t, "issue20046") |
746 | obj := lookupObj(t, pkg.Scope(), "V") |
747 | if m, index, indirect := types.LookupFieldOrMethod(obj.Type(), false, nil, "M"); m == nil { |
748 | t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect) |
749 | } |
750 | } |
751 | |
752 | func TestIssue25301(t *testing.T) { |
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 | compileAndImportPkg(t, "issue25301") |
763 | } |
764 | |
765 | func TestIssue51836(t *testing.T) { |
766 | testenv.NeedsGo1Point(t, 18) // requires generics |
767 | |
768 | // This package only handles gc export data. |
769 | needsCompiler(t, "gc") |
770 | |
771 | // On windows, we have to set the -D option for the compiler to avoid having a drive |
772 | // letter and an illegal ':' in the import path - just skip it (see also issue #3483). |
773 | if runtime.GOOS == "windows" { |
774 | t.Skip("avoid dealing with relative paths/drive letters on windows") |
775 | } |
776 | |
777 | tmpdir := mktmpdir(t) |
778 | defer os.RemoveAll(tmpdir) |
779 | testoutdir := filepath.Join(tmpdir, "testdata") |
780 | |
781 | dir := filepath.Join("testdata", "issue51836") |
782 | // Following the pattern of TestIssue13898, aa.go needs to be compiled from |
783 | // the output directory. We pass the full path to compile() so that we don't |
784 | // have to copy the file to that directory. |
785 | bpath, err := filepath.Abs(filepath.Join(dir, "aa.go")) |
786 | if err != nil { |
787 | t.Fatal(err) |
788 | } |
789 | compilePkg(t, dir, "a.go", testoutdir, nil, apkg(testoutdir)) |
790 | compile(t, testoutdir, bpath, testoutdir, map[string]string{apkg(testoutdir): filepath.Join(testoutdir, "a.o")}) |
791 | |
792 | // import must succeed (test for issue at hand) |
793 | _ = importPkg(t, "./testdata/aa", tmpdir) |
794 | } |
795 | |
796 | func TestIssue57015(t *testing.T) { |
797 | testenv.NeedsGo1Point(t, 18) // requires generics |
798 | |
799 | // This package only handles gc export data. |
800 | needsCompiler(t, "gc") |
801 | |
802 | // On windows, we have to set the -D option for the compiler to avoid having a drive |
803 | // letter and an illegal ':' in the import path - just skip it (see also issue #3483). |
804 | if runtime.GOOS == "windows" { |
805 | t.Skip("avoid dealing with relative paths/drive letters on windows") |
806 | } |
807 | |
808 | compileAndImportPkg(t, "issue57015") |
809 | } |
810 | |
811 | // apkg returns the package "a" prefixed by (as a package) testoutdir |
812 | func apkg(testoutdir string) string { |
813 | apkg := testoutdir + "/a" |
814 | if os.PathSeparator != '/' { |
815 | apkg = strings.ReplaceAll(apkg, string(os.PathSeparator), "/") |
816 | } |
817 | return apkg |
818 | } |
819 | |
820 | func importPkg(t *testing.T, path, srcDir string) *types.Package { |
821 | pkg, err := Import(make(map[string]*types.Package), path, srcDir, nil) |
822 | if err != nil { |
823 | t.Fatal(err) |
824 | } |
825 | return pkg |
826 | } |
827 | |
828 | func compileAndImportPkg(t *testing.T, name string) *types.Package { |
829 | tmpdir := mktmpdir(t) |
830 | defer os.RemoveAll(tmpdir) |
831 | compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata"), nil) |
832 | return importPkg(t, "./testdata/"+name, tmpdir) |
833 | } |
834 | |
835 | func lookupObj(t *testing.T, scope *types.Scope, name string) types.Object { |
836 | if obj := scope.Lookup(name); obj != nil { |
837 | return obj |
838 | } |
839 | t.Fatalf("%s not found", name) |
840 | return nil |
841 | } |
842 |
Members