// Copyright 2022 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gcimporter_test import ( "bytes" "fmt" "go/token" "go/types" "runtime" "testing" "unsafe" "golang.org/x/tools/go/gcexportdata" "golang.org/x/tools/go/packages" "golang.org/x/tools/internal/testenv" ) // TestStdlib ensures that all packages in std and x/tools can be // type-checked using export data. Takes around 3s. func TestStdlib(t *testing.T) { testenv.NeedsGoPackages(t) // gcexportdata.Read rapidly consumes FileSet address space, // so disable the test on 32-bit machines. // (We could use a fresh FileSet per type-check, but that // would require us to re-parse the source using it.) if unsafe.Sizeof(token.NoPos) < 8 { t.Skip("skipping test on 32-bit machine") } // Load, parse and type-check the standard library. // If we have the full source code for x/tools, also load and type-check that. cfg := &packages.Config{Mode: packages.LoadAllSyntax} patterns := []string{"std"} minPkgs := 225 // 'GOOS=plan9 go1.18 list std | wc -l' reports 228; most other platforms have more. switch runtime.GOOS { case "android", "ios": // The go_.*_exec script for mobile builders only copies over the source tree // for the package under test. default: patterns = append(patterns, "golang.org/x/tools/...") minPkgs += 160 // At the time of writing, 'GOOS=plan9 go list ./... | wc -l' reports 188. } pkgs, err := packages.Load(cfg, patterns...) if err != nil { t.Fatalf("failed to load/parse/type-check: %v", err) } if packages.PrintErrors(pkgs) > 0 { t.Fatal("there were errors during loading") } if len(pkgs) < minPkgs { t.Errorf("too few packages (%d) were loaded", len(pkgs)) } export := make(map[string][]byte) // keys are package IDs // Re-type check them all in post-order, using export data. packages.Visit(pkgs, nil, func(pkg *packages.Package) { packages := make(map[string]*types.Package) // keys are package paths cfg := &types.Config{ Error: func(e error) { t.Errorf("type error: %v", e) }, Importer: importerFunc(func(importPath string) (*types.Package, error) { // Resolve import path to (vendored?) package path. imported := pkg.Imports[importPath] if imported.PkgPath == "unsafe" { return types.Unsafe, nil // unsafe has no exportdata } data, ok := export[imported.ID] if !ok { return nil, fmt.Errorf("missing export data for %s", importPath) } return gcexportdata.Read(bytes.NewReader(data), pkg.Fset, packages, imported.PkgPath) }), } // Re-typecheck the syntax and save the export data in the map. newPkg := types.NewPackage(pkg.PkgPath, pkg.Name) check := types.NewChecker(cfg, pkg.Fset, newPkg, nil) check.Files(pkg.Syntax) var out bytes.Buffer if err := gcexportdata.Write(&out, pkg.Fset, newPkg); err != nil { t.Fatalf("internal error writing export data: %v", err) } export[pkg.ID] = out.Bytes() }) }