From 875c31f1e968e42a193b8c3300e5e449db4c7958 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Mon, 24 Oct 2022 16:02:50 -0400 Subject: [PATCH] go/internal/gcimporter: add test of export/import on std This test ensures that all std and x/tools packages can be re-typechecked using export data. Change-Id: I189a8138d74cb38f69dfb51c613849e43b316322 Reviewed-on: https://go-review.googlesource.com/c/tools/+/445096 Reviewed-by: Robert Findley Run-TryBot: Alan Donovan gopls-CI: kokoro TryBot-Result: Gopher Robot --- go/internal/gcimporter/stdlib_test.go | 82 +++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 go/internal/gcimporter/stdlib_test.go diff --git a/go/internal/gcimporter/stdlib_test.go b/go/internal/gcimporter/stdlib_test.go new file mode 100644 index 0000000000..ec1be3ea03 --- /dev/null +++ b/go/internal/gcimporter/stdlib_test.go @@ -0,0 +1,82 @@ +// 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" + "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 and x/tools. + cfg := &packages.Config{Mode: packages.LoadAllSyntax} + pkgs, err := packages.Load(cfg, "std", "golang.org/x/tools/...") + 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) < 240 { + 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() + }) +}