go/packages: opt: ignore function bodies in non-initial packages

This saves 35% of wall time in a command such as

 $ gopackages -mode=syntax golang.org/x/tools/go/packages/gopackages fmt

that would otherwise typecheck every function in each package that
depends on fmt. It also applies in -mode=types when source was loaded
as a fall back for missing export data due to an error in a lower package.

Also, remove Config.TypeChecker field now that not a single of its
subfields is passed through go/packages to the type checker.
(The Sizes function is logically a result, not an input, of loading,
though we have yet to implement it properly.)

Change-Id: I472d21b34fc5e2832f7353e82992a67a06e4e4cc
Reviewed-on: https://go-review.googlesource.com/129497
Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
Alan Donovan 2018-08-15 19:53:02 -04:00
parent 8f8fd1f239
commit 9de1900191
2 changed files with 23 additions and 27 deletions

View File

@ -86,7 +86,7 @@ func doMain() error {
case "386", "arm": case "386", "arm":
wordSize = 4 wordSize = 4
} }
cfg.TypeChecker.Sizes = &types.StdSizes{ sizes := &types.StdSizes{
MaxAlign: 8, MaxAlign: 8,
WordSize: wordSize, WordSize: wordSize,
} }
@ -164,7 +164,7 @@ func doMain() error {
// Run first main package. // Run first main package.
for _, main := range ssautil.MainPackages(pkgs) { for _, main := range ssautil.MainPackages(pkgs) {
fmt.Fprintf(os.Stderr, "Running: %s\n", main.Pkg.Path()) fmt.Fprintf(os.Stderr, "Running: %s\n", main.Pkg.Path())
os.Exit(interp.Interpret(main, interpMode, cfg.TypeChecker.Sizes, main.Pkg.Path(), args)) os.Exit(interp.Interpret(main, interpMode, sizes, main.Pkg.Path(), args))
} }
return fmt.Errorf("no main package") return fmt.Errorf("no main package")
} }

View File

@ -124,22 +124,6 @@ type Config struct {
// In build systems with explicit names for tests, // In build systems with explicit names for tests,
// setting Tests may have no effect. // setting Tests may have no effect.
Tests bool Tests bool
// TypeChecker provides additional configuration for type-checking syntax trees.
//
// It is used for all packages in LoadAllSyntax mode,
// and for the packages matching the patterns, but not their dependencies,
// in LoadSyntax mode.
//
// The TypeChecker.Error function is ignored:
// errors are reported using the Error function defined above.
//
// The TypeChecker.Importer function is ignored:
// the loader defines an appropriate importer.
//
// TODO(rsc): TypeChecker.Sizes should use the same sizes as the main build.
// Derive them from the runtime?
TypeChecker types.Config
} }
// driver is the type for functions that query the build system for the // driver is the type for functions that query the build system for the
@ -364,7 +348,8 @@ type loaderPackage struct {
importErrors map[string]error // maps each bad import to its error importErrors map[string]error // maps each bad import to its error
loadOnce sync.Once loadOnce sync.Once
color uint8 // for cycle detection color uint8 // for cycle detection
mark, needsrc bool // used when Mode >= LoadTypes needsrc bool // load from source (Mode >= LoadTypes)
initial bool // package was matched by a pattern
} }
// loader holds the working state of a single call to load. // loader holds the working state of a single call to load.
@ -435,6 +420,7 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
ld.pkgs[lpkg.ID] = lpkg ld.pkgs[lpkg.ID] = lpkg
if isRoot[lpkg.ID] { if isRoot[lpkg.ID] {
initial = append(initial, lpkg) initial = append(initial, lpkg)
lpkg.initial = true
} }
} }
@ -604,9 +590,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
Selections: make(map[*ast.SelectorExpr]*types.Selection), Selections: make(map[*ast.SelectorExpr]*types.Selection),
} }
// Copy the prototype types.Config as it must vary across Packages. importer := importerFunc(func(path string) (*types.Package, error) {
tc := ld.TypeChecker // copy
tc.Importer = importerFunc(func(path string) (*types.Package, error) {
if path == "unsafe" { if path == "unsafe" {
return types.Unsafe, nil return types.Unsafe, nil
} }
@ -630,10 +614,22 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
log.Fatalf("internal error: nil Pkg importing %q from %q", path, lpkg) log.Fatalf("internal error: nil Pkg importing %q from %q", path, lpkg)
panic("unreachable") panic("unreachable")
}) })
tc.Error = appendError
// type-check // type-check
types.NewChecker(&tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax) tc := &types.Config{
Importer: importer,
// Type-check bodies of functions only in non-initial packages.
// Example: for import graph A->B->C and initial packages {A,C},
// we can ignore function bodies in B.
IgnoreFuncBodies: ld.Mode < LoadAllSyntax && !lpkg.initial,
Error: appendError,
// TODO(adonovan): derive Sizes from the underlying
// build system.
}
types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
lpkg.importErrors = nil // no longer needed lpkg.importErrors = nil // no longer needed