1 | // Copyright 2022 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 | package gcimporter_test |
6 | |
7 | import ( |
8 | "bytes" |
9 | "fmt" |
10 | "go/token" |
11 | "go/types" |
12 | "runtime" |
13 | "testing" |
14 | "unsafe" |
15 | |
16 | "golang.org/x/tools/go/gcexportdata" |
17 | "golang.org/x/tools/go/packages" |
18 | "golang.org/x/tools/internal/testenv" |
19 | ) |
20 | |
21 | // TestStdlib ensures that all packages in std and x/tools can be |
22 | // type-checked using export data. Takes around 3s. |
23 | func TestStdlib(t *testing.T) { |
24 | testenv.NeedsGoPackages(t) |
25 | |
26 | // gcexportdata.Read rapidly consumes FileSet address space, |
27 | // so disable the test on 32-bit machines. |
28 | // (We could use a fresh FileSet per type-check, but that |
29 | // would require us to re-parse the source using it.) |
30 | if unsafe.Sizeof(token.NoPos) < 8 { |
31 | t.Skip("skipping test on 32-bit machine") |
32 | } |
33 | |
34 | // Load, parse and type-check the standard library. |
35 | // If we have the full source code for x/tools, also load and type-check that. |
36 | cfg := &packages.Config{Mode: packages.LoadAllSyntax} |
37 | patterns := []string{"std"} |
38 | minPkgs := 225 // 'GOOS=plan9 go1.18 list std | wc -l' reports 228; most other platforms have more. |
39 | switch runtime.GOOS { |
40 | case "android", "ios": |
41 | // The go_.*_exec script for mobile builders only copies over the source tree |
42 | // for the package under test. |
43 | default: |
44 | patterns = append(patterns, "golang.org/x/tools/...") |
45 | minPkgs += 160 // At the time of writing, 'GOOS=plan9 go list ./... | wc -l' reports 188. |
46 | } |
47 | pkgs, err := packages.Load(cfg, patterns...) |
48 | if err != nil { |
49 | t.Fatalf("failed to load/parse/type-check: %v", err) |
50 | } |
51 | if packages.PrintErrors(pkgs) > 0 { |
52 | t.Fatal("there were errors during loading") |
53 | } |
54 | if len(pkgs) < minPkgs { |
55 | t.Errorf("too few packages (%d) were loaded", len(pkgs)) |
56 | } |
57 | |
58 | export := make(map[string][]byte) // keys are package IDs |
59 | |
60 | // Re-type check them all in post-order, using export data. |
61 | packages.Visit(pkgs, nil, func(pkg *packages.Package) { |
62 | packages := make(map[string]*types.Package) // keys are package paths |
63 | cfg := &types.Config{ |
64 | Error: func(e error) { |
65 | t.Errorf("type error: %v", e) |
66 | }, |
67 | Importer: importerFunc(func(importPath string) (*types.Package, error) { |
68 | // Resolve import path to (vendored?) package path. |
69 | imported := pkg.Imports[importPath] |
70 | |
71 | if imported.PkgPath == "unsafe" { |
72 | return types.Unsafe, nil // unsafe has no exportdata |
73 | } |
74 | |
75 | data, ok := export[imported.ID] |
76 | if !ok { |
77 | return nil, fmt.Errorf("missing export data for %s", importPath) |
78 | } |
79 | return gcexportdata.Read(bytes.NewReader(data), pkg.Fset, packages, imported.PkgPath) |
80 | }), |
81 | } |
82 | |
83 | // Re-typecheck the syntax and save the export data in the map. |
84 | newPkg := types.NewPackage(pkg.PkgPath, pkg.Name) |
85 | check := types.NewChecker(cfg, pkg.Fset, newPkg, nil) |
86 | check.Files(pkg.Syntax) |
87 | |
88 | var out bytes.Buffer |
89 | if err := gcexportdata.Write(&out, pkg.Fset, newPkg); err != nil { |
90 | t.Fatalf("internal error writing export data: %v", err) |
91 | } |
92 | export[pkg.ID] = out.Bytes() |
93 | }) |
94 | } |
95 |
Members