aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/golang.org/x/tools/go/packages/packages.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/tools/go/packages/packages.go')
-rw-r--r--vendor/golang.org/x/tools/go/packages/packages.go567
1 files changed, 401 insertions, 166 deletions
diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go
index 7e8e4e2e..4639fcdd 100644
--- a/vendor/golang.org/x/tools/go/packages/packages.go
+++ b/vendor/golang.org/x/tools/go/packages/packages.go
@@ -12,49 +12,100 @@ import (
"fmt"
"go/ast"
"go/parser"
+ "go/scanner"
"go/token"
"go/types"
+ "io/ioutil"
"log"
"os"
+ "path/filepath"
+ "strings"
"sync"
"golang.org/x/tools/go/gcexportdata"
)
-// A LoadMode specifies the amount of detail to return when loading packages.
-// The modes are all strictly additive, as you go through the list it increases
-// the amount of information available to you, but may also increase the cost
-// of collecting the information.
-// Load is always allowed to return more information than requested.
+// A LoadMode specifies the amount of detail to return when loading.
+// Higher-numbered modes cause Load to return more information,
+// but may be slower. Load may return more information than requested.
type LoadMode int
const (
+ // The following constants are used to specify which fields of the Package
+ // should be filled when loading is done. As a special case to provide
+ // backwards compatibility, a LoadMode of 0 is equivalent to LoadFiles.
+ // For all other LoadModes, the bits below specify which fields will be filled
+ // in the result packages.
+ // WARNING: This part of the go/packages API is EXPERIMENTAL. It might
+ // be changed or removed up until April 15 2019. After that date it will
+ // be frozen.
+ // TODO(matloob): Remove this comment on April 15.
+
+ // ID and Errors (if present) will always be filled.
+
+ // NeedName adds Name and PkgPath.
+ NeedName LoadMode = 1 << iota
+
+ // NeedFiles adds GoFiles and OtherFiles.
+ NeedFiles
+
+ // NeedCompiledGoFiles adds CompiledGoFiles.
+ NeedCompiledGoFiles
+
+ // NeedImports adds Imports. If NeedDeps is not set, the Imports field will contain
+ // "placeholder" Packages with only the ID set.
+ NeedImports
+
+ // NeedDeps adds the fields requested by the LoadMode in the packages in Imports. If NeedImports
+ // is not set NeedDeps has no effect.
+ NeedDeps
+
+ // NeedExportsFile adds ExportsFile.
+ NeedExportsFile
+
+ // NeedTypes adds Types, Fset, and IllTyped.
+ NeedTypes
+
+ // NeedSyntax adds Syntax.
+ NeedSyntax
+
+ // NeedTypesInfo adds TypesInfo.
+ NeedTypesInfo
+
+ // NeedTypesSizes adds TypesSizes.
+ NeedTypesSizes
+)
+
+const (
// LoadFiles finds the packages and computes their source file lists.
- // Package fields: ID, Name, Errors, GoFiles, OtherFiles.
- LoadFiles LoadMode = iota
+ // Package fields: ID, Name, Errors, GoFiles, CompiledGoFiles, and OtherFiles.
+ LoadFiles = NeedName | NeedFiles | NeedCompiledGoFiles
// LoadImports adds import information for each package
// and its dependencies.
// Package fields added: Imports.
- LoadImports
+ LoadImports = LoadFiles | NeedImports | NeedDeps
- // LoadTypes adds type information for the package's exported symbols.
- // Package fields added: Types, Fset, IllTyped.
- // This will use the type information provided by the build system if
- // possible, and the ExportFile field may be filled in.
- LoadTypes
+ // LoadTypes adds type information for package-level
+ // declarations in the packages matching the patterns.
+ // Package fields added: Types, TypesSizes, Fset, and IllTyped.
+ // This mode uses type information provided by the build system when
+ // possible, and may fill in the ExportFile field.
+ LoadTypes = LoadImports | NeedTypes | NeedTypesSizes
// LoadSyntax adds typed syntax trees for the packages matching the patterns.
- // Package fields added: Syntax, TypesInfo, for direct pattern matches only.
- LoadSyntax
+ // Package fields added: Syntax, and TypesInfo, for direct pattern matches only.
+ LoadSyntax = LoadTypes | NeedSyntax | NeedTypesInfo
// LoadAllSyntax adds typed syntax trees for the packages matching the patterns
// and all dependencies.
- // Package fields added: Syntax, TypesInfo, for all packages in import graph.
- LoadAllSyntax
+ // Package fields added: Types, Fset, IllTyped, Syntax, and TypesInfo,
+ // for all packages in the import graph.
+ LoadAllSyntax = LoadSyntax
)
-// An Config specifies details about how packages should be loaded.
+// A Config specifies details about how packages should be loaded.
+// The zero value is a valid configuration.
// Calls to Load do not modify this struct.
type Config struct {
// Mode controls the level of information returned for each package.
@@ -66,14 +117,14 @@ type Config struct {
// If Context is nil, the load cannot be cancelled.
Context context.Context
- // Dir is the directory in which to run the build system tool
+ // Dir is the directory in which to run the build system's query tool
// that provides information about the packages.
// If Dir is empty, the tool is run in the current directory.
Dir string
- // Env is the environment to use when invoking the build system tool.
+ // Env is the environment to use when invoking the build system's query tool.
// If Env is nil, the current environment is used.
- // Like in os/exec's Cmd, only the last value in the slice for
+ // As in os/exec's Cmd, only the last value in the slice for
// each environment key is used. To specify the setting of only
// a few variables, append to the current environment, as in:
//
@@ -81,22 +132,12 @@ type Config struct {
//
Env []string
- // Flags is a list of command-line flags to be passed through to
- // the underlying query tool.
- Flags []string
-
- // Error is called for each error encountered during package loading.
- // It must be safe to call Error simultaneously from multiple goroutines.
- // In addition to calling Error, the loader will record each error
- // in the corresponding Package's Errors list.
- // If Error is nil, the loader will print errors to os.Stderr.
- // To disable printing of errors, set opt.Error = func(error){}.
- // TODO(rsc): What happens in the Metadata loader? Currently nothing.
- Error func(error)
-
- // Fset is the token.FileSet to use when parsing source files or
- // type information provided by the build system.
- // If Fset is nil, the loader will create one.
+ // BuildFlags is a list of command-line flags to be passed through to
+ // the build system's query tool.
+ BuildFlags []string
+
+ // Fset provides source position information for syntax trees and types.
+ // If Fset is nil, Load will use a new fileset, but preserve Fset's value.
Fset *token.FileSet
// ParseFile is called to read and parse each file
@@ -104,12 +145,14 @@ type Config struct {
// It must be safe to call ParseFile simultaneously from multiple goroutines.
// If ParseFile is nil, the loader will uses parser.ParseFile.
//
- // Setting ParseFile to a custom implementation can allow
- // providing alternate file content in order to type-check
- // unsaved text editor buffers, or to selectively eliminate
- // unwanted function bodies to reduce the amount of work
- // done by the type checker.
- ParseFile func(fset *token.FileSet, filename string) (*ast.File, error)
+ // ParseFile should parse the source from src and use filename only for
+ // recording position information.
+ //
+ // An application may supply a custom implementation of ParseFile
+ // to change the effective file contents or the behavior of the parser,
+ // or to modify the syntax tree. For example, selectively eliminating
+ // unwanted function bodies can significantly accelerate type checking.
+ ParseFile func(fset *token.FileSet, filename string, src []byte) (*ast.File, error)
// If Tests is set, the loader includes not just the packages
// matching a particular pattern but also any related test packages,
@@ -125,21 +168,13 @@ type Config struct {
// setting Tests may have no effect.
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.
+ // Overlay provides a mapping of absolute file paths to file contents.
+ // If the file with the given path already exists, the parser will use the
+ // alternative file contents provided by the map.
//
- // 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
+ // Overlays provide incomplete support for when a given file doesn't
+ // already exist on disk. See the package doc above for more details.
+ Overlay map[string][]byte
}
// driver is the type for functions that query the build system for the
@@ -148,6 +183,9 @@ type driver func(cfg *Config, patterns ...string) (*driverResponse, error)
// driverResponse contains the results for a driver query.
type driverResponse struct {
+ // Sizes, if not nil, is the types.Sizes to use when type checking.
+ Sizes *types.StdSizes
+
// Roots is the set of package IDs that make up the root packages.
// We have to encode this separately because when we encode a single package
// we cannot know if it is one of the roots as that requires knowledge of the
@@ -171,12 +209,18 @@ type driverResponse struct {
// as defined by the underlying build system.
// It may return an empty list of packages without an error,
// for instance for an empty expansion of a valid wildcard.
+// Errors associated with a particular package are recorded in the
+// corresponding Package's Errors list, and do not cause Load to
+// return an error. Clients may need to handle such errors before
+// proceeding with further analysis. The PrintErrors function is
+// provided for convenient display of all errors.
func Load(cfg *Config, patterns ...string) ([]*Package, error) {
l := newLoader(cfg)
response, err := defaultDriver(&l.Config, patterns...)
if err != nil {
return nil, err
}
+ l.sizes = response.Sizes
return l.refine(response.Roots, response.Packages...)
}
@@ -190,7 +234,7 @@ func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
return driver(cfg, patterns...)
}
-// A Package describes a single loaded Go package.
+// A Package describes a loaded Go package.
type Package struct {
// ID is a unique identifier for a package,
// in a syntax provided by the underlying build system.
@@ -203,15 +247,12 @@ type Package struct {
// Name is the package name as it appears in the package source code.
Name string
- // This is the package path as used by the types package.
- // This is used to map entries in the type information back to the package
- // they come from.
+ // PkgPath is the package path as used by the go/types package.
PkgPath string
- // Errors lists any errors encountered while loading the package.
- // TODO(rsc): Say something about the errors or at least their Strings,
- // as far as file:line being at the beginning and so on.
- Errors []error
+ // Errors contains any errors encountered querying the metadata
+ // of the package, or while parsing or type-checking its files.
+ Errors []Error
// GoFiles lists the absolute file paths of the package's Go source files.
GoFiles []string
@@ -225,60 +266,79 @@ type Package struct {
// including assembly, C, C++, Fortran, Objective-C, SWIG, and so on.
OtherFiles []string
- // ExportFile is the absolute path to a file containing the type information
- // provided by the build system.
+ // ExportFile is the absolute path to a file containing type
+ // information for the package as provided by the build system.
ExportFile string
// Imports maps import paths appearing in the package's Go source files
// to corresponding loaded Packages.
Imports map[string]*Package
- // Types is the type information for the package.
- // Modes LoadTypes and above set this field for all packages.
- //
- // TODO(adonovan): all packages? In Types mode this entails
- // asymptotically more export data processing than is required
- // to load the requested packages. Is that what we want?
+ // Types provides type information for the package.
+ // Modes LoadTypes and above set this field for packages matching the
+ // patterns; type information for dependencies may be missing or incomplete.
+ // Mode LoadAllSyntax sets this field for all packages, including dependencies.
Types *types.Package
// Fset provides position information for Types, TypesInfo, and Syntax.
- // Modes LoadTypes and above set this field for all packages.
+ // It is set only when Types is set.
Fset *token.FileSet
- // IllTyped indicates whether the package has any type errors.
- // Modes LoadTypes and above set this field for all packages.
+ // IllTyped indicates whether the package or any dependency contains errors.
+ // It is set only when Types is set.
IllTyped bool
- // Syntax is the package's syntax trees, for the files listed in GoFiles.
+ // Syntax is the package's syntax trees, for the files listed in CompiledGoFiles.
//
- // Mode LoadSyntax set this field for packages matching the patterns.
- // Mode LoadSyntaxAll sets this field for all packages, including dependencies.
+ // Mode LoadSyntax sets this field for packages matching the patterns.
+ // Mode LoadAllSyntax sets this field for all packages, including dependencies.
Syntax []*ast.File
- // TypesInfo is the type-checking results for the package's syntax trees.
+ // TypesInfo provides type information about the package's syntax trees.
// It is set only when Syntax is set.
TypesInfo *types.Info
+
+ // TypesSizes provides the effective size function for types in TypesInfo.
+ TypesSizes types.Sizes
}
-// packageError is used to serialize structured errors as much as possible.
-// This has members compatible with the golist error type, and possibly some
-// more if we need other error information to survive.
-type packageError struct {
- Pos string // position of error
- Err string // the error itself
+// An Error describes a problem with a package's metadata, syntax, or types.
+type Error struct {
+ Pos string // "file:line:col" or "file:line" or "" or "-"
+ Msg string
+ Kind ErrorKind
}
-func (e *packageError) Error() string {
- return e.Pos + ": " + e.Err
+// ErrorKind describes the source of the error, allowing the user to
+// differentiate between errors generated by the driver, the parser, or the
+// type-checker.
+type ErrorKind int
+
+const (
+ UnknownError ErrorKind = iota
+ ListError
+ ParseError
+ TypeError
+)
+
+func (err Error) Error() string {
+ pos := err.Pos
+ if pos == "" {
+ pos = "-" // like token.Position{}.String()
+ }
+ return pos + ": " + err.Msg
}
// flatPackage is the JSON form of Package
-// It drops all the type and syntax fields, and transforms the Imports and Errors
+// It drops all the type and syntax fields, and transforms the Imports
+//
+// TODO(adonovan): identify this struct with Package, effectively
+// publishing the JSON protocol.
type flatPackage struct {
ID string
Name string `json:",omitempty"`
PkgPath string `json:",omitempty"`
- Errors []*packageError `json:",omitempty"`
+ Errors []Error `json:",omitempty"`
GoFiles []string `json:",omitempty"`
CompiledGoFiles []string `json:",omitempty"`
OtherFiles []string `json:",omitempty"`
@@ -292,6 +352,7 @@ type flatPackage struct {
// The imports are written out as just a map of path to package id.
// The errors are written using a custom type that tries to preserve the
// structure of error types we know about.
+//
// This method exists to enable support for additional build systems. It is
// not intended for use by clients of the API and we may change the format.
func (p *Package) MarshalJSON() ([]byte, error) {
@@ -299,23 +360,12 @@ func (p *Package) MarshalJSON() ([]byte, error) {
ID: p.ID,
Name: p.Name,
PkgPath: p.PkgPath,
+ Errors: p.Errors,
GoFiles: p.GoFiles,
CompiledGoFiles: p.CompiledGoFiles,
OtherFiles: p.OtherFiles,
ExportFile: p.ExportFile,
}
- if len(p.Errors) > 0 {
- flat.Errors = make([]*packageError, len(p.Errors))
- for i, err := range p.Errors {
- //TODO: best effort mapping of errors to the serialized form
- switch err := err.(type) {
- case *packageError:
- flat.Errors[i] = err
- default:
- flat.Errors[i] = &packageError{Err: err.Error()}
- }
- }
- }
if len(p.Imports) > 0 {
flat.Imports = make(map[string]string, len(p.Imports))
for path, ipkg := range p.Imports {
@@ -336,17 +386,12 @@ func (p *Package) UnmarshalJSON(b []byte) error {
ID: flat.ID,
Name: flat.Name,
PkgPath: flat.PkgPath,
+ Errors: flat.Errors,
GoFiles: flat.GoFiles,
CompiledGoFiles: flat.CompiledGoFiles,
OtherFiles: flat.OtherFiles,
ExportFile: flat.ExportFile,
}
- if len(flat.Errors) > 0 {
- p.Errors = make([]error, len(flat.Errors))
- for i, err := range flat.Errors {
- p.Errors[i] = err
- }
- }
if len(flat.Imports) > 0 {
p.Imports = make(map[string]*Package, len(flat.Imports))
for path, id := range flat.Imports {
@@ -361,17 +406,26 @@ func (p *Package) String() string { return p.ID }
// loaderPackage augments Package with state used during the loading phase
type loaderPackage struct {
*Package
- importErrors map[string]error // maps each bad import to its error
- loadOnce sync.Once
- color uint8 // for cycle detection
- mark, needsrc bool // used when Mode >= LoadTypes
+ importErrors map[string]error // maps each bad import to its error
+ loadOnce sync.Once
+ color uint8 // for cycle detection
+ needsrc bool // load from source (Mode >= LoadTypes)
+ needtypes bool // type information is either requested or depended on
+ initial bool // package was matched by a pattern
}
// loader holds the working state of a single call to load.
type loader struct {
pkgs map[string]*loaderPackage
Config
+ sizes types.Sizes
exportMu sync.Mutex // enforces mutual exclusion of exportdata operations
+
+ // TODO(matloob): Add an implied mode here and use that instead of mode.
+ // Implied mode would contain all the fields we need the data for so we can
+ // get the actually requested fields. We'll zero them out before returning
+ // packages to the user. This will make it easier for us to get the conditions
+ // where we need certain modes right.
}
func newLoader(cfg *Config) *loader {
@@ -379,6 +433,12 @@ func newLoader(cfg *Config) *loader {
if cfg != nil {
ld.Config = *cfg
}
+ if ld.Config.Mode == 0 {
+ ld.Config.Mode = LoadFiles // Preserve zero behavior of Mode for backwards compatibility.
+ }
+ if ld.Config.Env == nil {
+ ld.Config.Env = os.Environ()
+ }
if ld.Context == nil {
ld.Context = context.Background()
}
@@ -388,24 +448,21 @@ func newLoader(cfg *Config) *loader {
}
}
- if ld.Mode >= LoadTypes {
+ if ld.Mode&NeedTypes != 0 {
if ld.Fset == nil {
ld.Fset = token.NewFileSet()
}
- // Error and ParseFile are required even in LoadTypes mode
+ // ParseFile is required even in LoadTypes mode
// because we load source if export data is missing.
-
- if ld.Error == nil {
- ld.Error = func(e error) {
- fmt.Fprintln(os.Stderr, e)
- }
- }
-
if ld.ParseFile == nil {
- ld.ParseFile = func(fset *token.FileSet, filename string) (*ast.File, error) {
+ ld.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) {
+ var isrc interface{}
+ if src != nil {
+ isrc = src
+ }
const mode = parser.AllErrors | parser.ParseComments
- return parser.ParseFile(fset, filename, nil, mode)
+ return parser.ParseFile(fset, filename, isrc, mode)
}
}
}
@@ -415,26 +472,34 @@ func newLoader(cfg *Config) *loader {
// refine connects the supplied packages into a graph and then adds type and
// and syntax information as requested by the LoadMode.
func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
- if len(list) == 0 {
- return nil, fmt.Errorf("packages not found")
- }
- isRoot := make(map[string]bool, len(roots))
- for _, root := range roots {
- isRoot[root] = true
+ rootMap := make(map[string]int, len(roots))
+ for i, root := range roots {
+ rootMap[root] = i
}
ld.pkgs = make(map[string]*loaderPackage)
// first pass, fixup and build the map and roots
- var initial []*loaderPackage
+ var initial = make([]*loaderPackage, len(roots))
for _, pkg := range list {
+ rootIndex := -1
+ if i, found := rootMap[pkg.ID]; found {
+ rootIndex = i
+ }
lpkg := &loaderPackage{
- Package: pkg,
- needsrc: ld.Mode >= LoadAllSyntax ||
- (ld.Mode >= LoadSyntax && isRoot[pkg.ID]) ||
- (pkg.ExportFile == "" && pkg.PkgPath != "unsafe"),
+ Package: pkg,
+ needtypes: (ld.Mode&(NeedTypes|NeedTypesInfo) != 0 && rootIndex < 0) || rootIndex >= 0,
+ needsrc: (ld.Mode&(NeedSyntax|NeedTypesInfo) != 0 && rootIndex < 0) || rootIndex >= 0 ||
+ len(ld.Overlay) > 0 || // Overlays can invalidate export data. TODO(matloob): make this check fine-grained based on dependencies on overlaid files
+ pkg.ExportFile == "" && pkg.PkgPath != "unsafe",
}
ld.pkgs[lpkg.ID] = lpkg
- if isRoot[lpkg.ID] {
- initial = append(initial, lpkg)
+ if rootIndex >= 0 {
+ initial[rootIndex] = lpkg
+ lpkg.initial = true
+ }
+ }
+ for i, root := range roots {
+ if initial[i] == nil {
+ return nil, fmt.Errorf("root package %v is missing", root)
}
}
@@ -458,6 +523,7 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
// for which we load source code.
var stack []*loaderPackage
var visit func(lpkg *loaderPackage) bool
+ var srcPkgs []*loaderPackage
visit = func(lpkg *loaderPackage) bool {
switch lpkg.color {
case black:
@@ -491,15 +557,20 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
}
lpkg.Imports[importPath] = imp.Package
}
-
+ if lpkg.needsrc {
+ srcPkgs = append(srcPkgs, lpkg)
+ }
+ if ld.Mode&NeedTypesSizes != 0 {
+ lpkg.TypesSizes = ld.sizes
+ }
stack = stack[:len(stack)-1] // pop
lpkg.color = black
return lpkg.needsrc
}
- if ld.Mode < LoadImports {
- //we do this to drop the stub import packages that we are not even going to try to resolve
+ if ld.Mode&(NeedImports|NeedDeps) == 0 {
+ // We do this to drop the stub import packages that we are not even going to try to resolve.
for _, lpkg := range initial {
lpkg.Imports = nil
}
@@ -509,9 +580,19 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
visit(lpkg)
}
}
+ if ld.Mode&NeedDeps != 0 { // TODO(matloob): This is only the case if NeedTypes is also set, right?
+ for _, lpkg := range srcPkgs {
+ // Complete type information is required for the
+ // immediate dependencies of each source package.
+ for _, ipkg := range lpkg.Imports {
+ imp := ld.pkgs[ipkg.ID]
+ imp.needtypes = true
+ }
+ }
+ }
// Load type data if needed, starting at
// the initial packages (roots of the import DAG).
- if ld.Mode >= LoadTypes {
+ if ld.Mode&NeedTypes != 0 {
var wg sync.WaitGroup
for _, lpkg := range initial {
wg.Add(1)
@@ -524,16 +605,61 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
}
result := make([]*Package, len(initial))
+ importPlaceholders := make(map[string]*Package)
for i, lpkg := range initial {
result[i] = lpkg.Package
}
+ for i := range ld.pkgs {
+ // Clear all unrequested fields, for extra de-Hyrum-ization.
+ if ld.Mode&NeedName == 0 {
+ ld.pkgs[i].Name = ""
+ ld.pkgs[i].PkgPath = ""
+ }
+ if ld.Mode&NeedFiles == 0 {
+ ld.pkgs[i].GoFiles = nil
+ ld.pkgs[i].OtherFiles = nil
+ }
+ if ld.Mode&NeedCompiledGoFiles == 0 {
+ ld.pkgs[i].CompiledGoFiles = nil
+ }
+ if ld.Mode&NeedImports == 0 {
+ ld.pkgs[i].Imports = nil
+ }
+ if ld.Mode&NeedExportsFile == 0 {
+ ld.pkgs[i].ExportFile = ""
+ }
+ if ld.Mode&NeedTypes == 0 {
+ ld.pkgs[i].Types = nil
+ ld.pkgs[i].Fset = nil
+ ld.pkgs[i].IllTyped = false
+ }
+ if ld.Mode&NeedSyntax == 0 {
+ ld.pkgs[i].Syntax = nil
+ }
+ if ld.Mode&NeedTypesInfo == 0 {
+ ld.pkgs[i].TypesInfo = nil
+ }
+ if ld.Mode&NeedTypesSizes == 0 {
+ ld.pkgs[i].TypesSizes = nil
+ }
+ if ld.Mode&NeedDeps == 0 {
+ for j, pkg := range ld.pkgs[i].Imports {
+ ph, ok := importPlaceholders[pkg.ID]
+ if !ok {
+ ph = &Package{ID: pkg.ID}
+ importPlaceholders[pkg.ID] = ph
+ }
+ ld.pkgs[i].Imports[j] = ph
+ }
+ }
+ }
return result, nil
}
// loadRecursive loads the specified package and its dependencies,
// recursively, in parallel, in topological order.
// It is atomic and idempotent.
-// Precondition: ld.Mode >= LoadTypes.
+// Precondition: ld.Mode&NeedTypes.
func (ld *loader) loadRecursive(lpkg *loaderPackage) {
lpkg.loadOnce.Do(func() {
// Load the direct dependencies, in parallel.
@@ -563,6 +689,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
lpkg.Fset = ld.Fset
lpkg.Syntax = []*ast.File{}
lpkg.TypesInfo = new(types.Info)
+ lpkg.TypesSizes = ld.sizes
return
}
@@ -570,21 +697,69 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
// This avoids skew between golist and go/types when the files'
// package declarations are inconsistent.
lpkg.Types = types.NewPackage(lpkg.PkgPath, lpkg.Name)
+ lpkg.Fset = ld.Fset
+ // Subtle: we populate all Types fields with an empty Package
+ // before loading export data so that export data processing
+ // never has to create a types.Package for an indirect dependency,
+ // which would then require that such created packages be explicitly
+ // inserted back into the Import graph as a final step after export data loading.
+ // The Diamond test exercises this case.
+ if !lpkg.needtypes {
+ return
+ }
if !lpkg.needsrc {
ld.loadFromExportData(lpkg)
return // not a source package, don't get syntax trees
}
- hardErrors := false
appendError := func(err error) {
- if terr, ok := err.(types.Error); ok && terr.Soft {
- // Don't mark the package as bad.
- } else {
- hardErrors = true
+ // Convert various error types into the one true Error.
+ var errs []Error
+ switch err := err.(type) {
+ case Error:
+ // from driver
+ errs = append(errs, err)
+
+ case *os.PathError:
+ // from parser
+ errs = append(errs, Error{
+ Pos: err.Path + ":1",
+ Msg: err.Err.Error(),
+ Kind: ParseError,
+ })
+
+ case scanner.ErrorList:
+ // from parser
+ for _, err := range err {
+ errs = append(errs, Error{
+ Pos: err.Pos.String(),
+ Msg: err.Msg,
+ Kind: ParseError,
+ })
+ }
+
+ case types.Error:
+ // from type checker
+ errs = append(errs, Error{
+ Pos: err.Fset.Position(err.Pos).String(),
+ Msg: err.Msg,
+ Kind: TypeError,
+ })
+
+ default:
+ // unexpected impoverished error from parser?
+ errs = append(errs, Error{
+ Pos: "-",
+ Msg: err.Error(),
+ Kind: UnknownError,
+ })
+
+ // If you see this error message, please file a bug.
+ log.Printf("internal error: error %q (%T) without position", err, err)
}
- ld.Error(err)
- lpkg.Errors = append(lpkg.Errors, err)
+
+ lpkg.Errors = append(lpkg.Errors, errs...)
}
files, errs := ld.parseFiles(lpkg.CompiledGoFiles)
@@ -592,7 +767,6 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
appendError(err)
}
- lpkg.Fset = ld.Fset
lpkg.Syntax = files
lpkg.TypesInfo = &types.Info{
@@ -603,10 +777,9 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
}
+ lpkg.TypesSizes = ld.sizes
- // Copy the prototype types.Config as it must vary across Packages.
- tc := ld.TypeChecker // copy
- tc.Importer = importerFunc(func(path string) (*types.Package, error) {
+ importer := importerFunc(func(path string) (*types.Package, error) {
if path == "unsafe" {
return types.Unsafe, nil
}
@@ -630,10 +803,20 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
log.Fatalf("internal error: nil Pkg importing %q from %q", path, lpkg)
panic("unreachable")
})
- tc.Error = appendError
// 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&(NeedDeps|NeedTypesInfo) == 0) && !lpkg.initial,
+
+ Error: appendError,
+ Sizes: ld.sizes,
+ }
+ types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
lpkg.importErrors = nil // no longer needed
@@ -650,8 +833,8 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
for _, f := range lpkg.Syntax {
for _, imp := range f.Imports {
if imp.Path.Value == `"C"` {
- appendError(fmt.Errorf(`%s: import "C" ignored`,
- lpkg.Fset.Position(imp.Pos())))
+ err := types.Error{Fset: ld.Fset, Pos: imp.Pos(), Msg: `import "C" ignored`}
+ appendError(err)
break outer
}
}
@@ -659,14 +842,16 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
}
// Record accumulated errors.
- for _, imp := range lpkg.Imports {
- if imp.IllTyped {
- hardErrors = true
- break
+ illTyped := len(lpkg.Errors) > 0
+ if !illTyped {
+ for _, imp := range lpkg.Imports {
+ if imp.IllTyped {
+ illTyped = true
+ break
+ }
}
}
-
- lpkg.IllTyped = hardErrors
+ lpkg.IllTyped = illTyped
}
// An importFunc is an implementation of the single-method
@@ -692,11 +877,30 @@ func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) {
parsed := make([]*ast.File, n)
errors := make([]error, n)
for i, file := range filenames {
+ if ld.Config.Context.Err() != nil {
+ parsed[i] = nil
+ errors[i] = ld.Config.Context.Err()
+ continue
+ }
wg.Add(1)
go func(i int, filename string) {
ioLimit <- true // wait
// ParseFile may return both an AST and an error.
- parsed[i], errors[i] = ld.ParseFile(ld.Fset, filename)
+ var src []byte
+ for f, contents := range ld.Config.Overlay {
+ if sameFile(f, filename) {
+ src = contents
+ }
+ }
+ var err error
+ if src == nil {
+ src, err = ioutil.ReadFile(filename)
+ }
+ if err != nil {
+ parsed[i], errors[i] = nil, err
+ } else {
+ parsed[i], errors[i] = ld.ParseFile(ld.Fset, filename, src)
+ }
<-ioLimit // signal
wg.Done()
}(i, file)
@@ -725,6 +929,29 @@ func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) {
return parsed, errors
}
+// sameFile returns true if x and y have the same basename and denote
+// the same file.
+//
+func sameFile(x, y string) bool {
+ if x == y {
+ // It could be the case that y doesn't exist.
+ // For instance, it may be an overlay file that
+ // hasn't been written to disk. To handle that case
+ // let x == y through. (We added the exact absolute path
+ // string to the CompiledGoFiles list, so the unwritten
+ // overlay case implies x==y.)
+ return true
+ }
+ if strings.EqualFold(filepath.Base(x), filepath.Base(y)) { // (optimisation)
+ if xi, err := os.Stat(x); err == nil {
+ if yi, err := os.Stat(y); err == nil {
+ return os.SameFile(xi, yi)
+ }
+ }
+ }
+ return false
+}
+
// loadFromExportData returns type information for the specified
// package, loading it from an export data file on the first request.
func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error) {
@@ -787,7 +1014,11 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
// package that might possibly be mentioned by the
// current package---its transitive closure.
//
- // TODO(adonovan): it would be more simpler and more efficient
+ // In loadPackage, we unconditionally create a types.Package for
+ // each dependency so that export data loading does not
+ // create new ones.
+ //
+ // TODO(adonovan): it would be simpler and more efficient
// if the export data machinery invoked a callback to
// get-or-create a package instead of a map.
//
@@ -806,12 +1037,16 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
}
visit(lpkg.Imports)
+ viewLen := len(view) + 1 // adding the self package
// Parse the export data.
- // (May create/modify packages in view.)
+ // (May modify incomplete packages in view but not create new ones.)
tpkg, err := gcexportdata.Read(r, ld.Fset, view, lpkg.PkgPath)
if err != nil {
return nil, fmt.Errorf("reading %s: %v", lpkg.ExportFile, err)
}
+ if viewLen != len(view) {
+ log.Fatalf("Unexpected package creation during export data loading")
+ }
lpkg.Types = tpkg
lpkg.IllTyped = false
@@ -820,5 +1055,5 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
}
func usesExportData(cfg *Config) bool {
- return LoadTypes <= cfg.Mode && cfg.Mode < LoadAllSyntax
+ return cfg.Mode&NeedExportsFile != 0 || cfg.Mode&NeedTypes != 0 && cfg.Mode&NeedTypesInfo == 0
}