diff options
Diffstat (limited to 'vendor/golang.org/x/tools/go/packages/packages.go')
-rw-r--r-- | vendor/golang.org/x/tools/go/packages/packages.go | 567 |
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 } |