1213 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			1213 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | // Copyright 2018 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 packages | ||
|  | 
 | ||
|  | // See doc.go for package documentation and implementation notes. | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"context" | ||
|  | 	"encoding/json" | ||
|  | 	"fmt" | ||
|  | 	"go/ast" | ||
|  | 	"go/parser" | ||
|  | 	"go/scanner" | ||
|  | 	"go/token" | ||
|  | 	"go/types" | ||
|  | 	"io/ioutil" | ||
|  | 	"log" | ||
|  | 	"os" | ||
|  | 	"path/filepath" | ||
|  | 	"strings" | ||
|  | 	"sync" | ||
|  | 	"time" | ||
|  | 
 | ||
|  | 	"golang.org/x/tools/go/gcexportdata" | ||
|  | 	"golang.org/x/tools/internal/gocommand" | ||
|  | 	"golang.org/x/tools/internal/packagesinternal" | ||
|  | 	"golang.org/x/tools/internal/typesinternal" | ||
|  | ) | ||
|  | 
 | ||
|  | // A LoadMode controls the amount of detail to return when loading. | ||
|  | // The bits below can be combined to specify which fields should be | ||
|  | // filled in the result packages. | ||
|  | // The zero value is a special case, equivalent to combining | ||
|  | // the NeedName, NeedFiles, and NeedCompiledGoFiles bits. | ||
|  | // ID and Errors (if present) will always be filled. | ||
|  | // Load may return more information than requested. | ||
|  | type LoadMode int | ||
|  | 
 | ||
|  | // TODO(matloob): When a V2 of go/packages is released, rename NeedExportsFile to | ||
|  | // NeedExportFile to make it consistent with the Package field it's adding. | ||
|  | 
 | ||
|  | const ( | ||
|  | 	// 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. | ||
|  | 	NeedDeps | ||
|  | 
 | ||
|  | 	// NeedExportsFile adds ExportFile. | ||
|  | 	NeedExportsFile | ||
|  | 
 | ||
|  | 	// NeedTypes adds Types, Fset, and IllTyped. | ||
|  | 	NeedTypes | ||
|  | 
 | ||
|  | 	// NeedSyntax adds Syntax. | ||
|  | 	NeedSyntax | ||
|  | 
 | ||
|  | 	// NeedTypesInfo adds TypesInfo. | ||
|  | 	NeedTypesInfo | ||
|  | 
 | ||
|  | 	// NeedTypesSizes adds TypesSizes. | ||
|  | 	NeedTypesSizes | ||
|  | 
 | ||
|  | 	// typecheckCgo enables full support for type checking cgo. Requires Go 1.15+. | ||
|  | 	// Modifies CompiledGoFiles and Types, and has no effect on its own. | ||
|  | 	typecheckCgo | ||
|  | 
 | ||
|  | 	// NeedModule adds Module. | ||
|  | 	NeedModule | ||
|  | ) | ||
|  | 
 | ||
|  | const ( | ||
|  | 	// Deprecated: LoadFiles exists for historical compatibility | ||
|  | 	// and should not be used. Please directly specify the needed fields using the Need values. | ||
|  | 	LoadFiles = NeedName | NeedFiles | NeedCompiledGoFiles | ||
|  | 
 | ||
|  | 	// Deprecated: LoadImports exists for historical compatibility | ||
|  | 	// and should not be used. Please directly specify the needed fields using the Need values. | ||
|  | 	LoadImports = LoadFiles | NeedImports | ||
|  | 
 | ||
|  | 	// Deprecated: LoadTypes exists for historical compatibility | ||
|  | 	// and should not be used. Please directly specify the needed fields using the Need values. | ||
|  | 	LoadTypes = LoadImports | NeedTypes | NeedTypesSizes | ||
|  | 
 | ||
|  | 	// Deprecated: LoadSyntax exists for historical compatibility | ||
|  | 	// and should not be used. Please directly specify the needed fields using the Need values. | ||
|  | 	LoadSyntax = LoadTypes | NeedSyntax | NeedTypesInfo | ||
|  | 
 | ||
|  | 	// Deprecated: LoadAllSyntax exists for historical compatibility | ||
|  | 	// and should not be used. Please directly specify the needed fields using the Need values. | ||
|  | 	LoadAllSyntax = LoadSyntax | NeedDeps | ||
|  | ) | ||
|  | 
 | ||
|  | // 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. | ||
|  | 	Mode LoadMode | ||
|  | 
 | ||
|  | 	// Context specifies the context for the load operation. | ||
|  | 	// If the context is cancelled, the loader may stop early | ||
|  | 	// and return an ErrCancelled error. | ||
|  | 	// If Context is nil, the load cannot be cancelled. | ||
|  | 	Context context.Context | ||
|  | 
 | ||
|  | 	// Logf is the logger for the config. | ||
|  | 	// If the user provides a logger, debug logging is enabled. | ||
|  | 	// If the GOPACKAGESDEBUG environment variable is set to true, | ||
|  | 	// but the logger is nil, default to log.Printf. | ||
|  | 	Logf func(format string, args ...interface{}) | ||
|  | 
 | ||
|  | 	// 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's query tool. | ||
|  | 	// If Env is nil, the current environment is used. | ||
|  | 	// 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: | ||
|  | 	// | ||
|  | 	//	opt.Env = append(os.Environ(), "GOOS=plan9", "GOARCH=386") | ||
|  | 	// | ||
|  | 	Env []string | ||
|  | 
 | ||
|  | 	// gocmdRunner guards go command calls from concurrency errors. | ||
|  | 	gocmdRunner *gocommand.Runner | ||
|  | 
 | ||
|  | 	// 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 | ||
|  | 	// when preparing a package's type-checked syntax tree. | ||
|  | 	// It must be safe to call ParseFile simultaneously from multiple goroutines. | ||
|  | 	// If ParseFile is nil, the loader will uses parser.ParseFile. | ||
|  | 	// | ||
|  | 	// 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, | ||
|  | 	// including test-only variants of the package and the test executable. | ||
|  | 	// | ||
|  | 	// For example, when using the go command, loading "fmt" with Tests=true | ||
|  | 	// returns four packages, with IDs "fmt" (the standard package), | ||
|  | 	// "fmt [fmt.test]" (the package as compiled for the test), | ||
|  | 	// "fmt_test" (the test functions from source files in package fmt_test), | ||
|  | 	// and "fmt.test" (the test binary). | ||
|  | 	// | ||
|  | 	// In build systems with explicit names for tests, | ||
|  | 	// setting Tests may have no effect. | ||
|  | 	Tests bool | ||
|  | 
 | ||
|  | 	// 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. | ||
|  | 	// | ||
|  | 	// 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 | ||
|  | // packages named by the patterns. | ||
|  | type driver func(cfg *Config, patterns ...string) (*driverResponse, error) | ||
|  | 
 | ||
|  | // driverResponse contains the results for a driver query. | ||
|  | type driverResponse struct { | ||
|  | 	// NotHandled is returned if the request can't be handled by the current | ||
|  | 	// driver. If an external driver returns a response with NotHandled, the | ||
|  | 	// rest of the driverResponse is ignored, and go/packages will fallback | ||
|  | 	// to the next driver. If go/packages is extended in the future to support | ||
|  | 	// lists of multiple drivers, go/packages will fall back to the next driver. | ||
|  | 	NotHandled bool | ||
|  | 
 | ||
|  | 	// 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 | ||
|  | 	// graph it is part of. | ||
|  | 	Roots []string `json:",omitempty"` | ||
|  | 
 | ||
|  | 	// Packages is the full set of packages in the graph. | ||
|  | 	// The packages are not connected into a graph. | ||
|  | 	// The Imports if populated will be stubs that only have their ID set. | ||
|  | 	// Imports will be connected and then type and syntax information added in a | ||
|  | 	// later pass (see refine). | ||
|  | 	Packages []*Package | ||
|  | } | ||
|  | 
 | ||
|  | // Load loads and returns the Go packages named by the given patterns. | ||
|  | // | ||
|  | // Config specifies loading options; | ||
|  | // nil behaves the same as an empty Config. | ||
|  | // | ||
|  | // Load returns an error if any of the patterns was invalid | ||
|  | // 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...) | ||
|  | } | ||
|  | 
 | ||
|  | // defaultDriver is a driver that implements go/packages' fallback behavior. | ||
|  | // It will try to request to an external driver, if one exists. If there's | ||
|  | // no external driver, or the driver returns a response with NotHandled set, | ||
|  | // defaultDriver will fall back to the go list driver. | ||
|  | func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) { | ||
|  | 	driver := findExternalDriver(cfg) | ||
|  | 	if driver == nil { | ||
|  | 		driver = goListDriver | ||
|  | 	} | ||
|  | 	response, err := driver(cfg, patterns...) | ||
|  | 	if err != nil { | ||
|  | 		return response, err | ||
|  | 	} else if response.NotHandled { | ||
|  | 		return goListDriver(cfg, patterns...) | ||
|  | 	} | ||
|  | 	return response, nil | ||
|  | } | ||
|  | 
 | ||
|  | // 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. | ||
|  | 	// | ||
|  | 	// Because the syntax varies based on the build system, | ||
|  | 	// clients should treat IDs as opaque and not attempt to | ||
|  | 	// interpret them. | ||
|  | 	ID string | ||
|  | 
 | ||
|  | 	// Name is the package name as it appears in the package source code. | ||
|  | 	Name string | ||
|  | 
 | ||
|  | 	// PkgPath is the package path as used by the go/types package. | ||
|  | 	PkgPath string | ||
|  | 
 | ||
|  | 	// 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 | ||
|  | 
 | ||
|  | 	// CompiledGoFiles lists the absolute file paths of the package's source | ||
|  | 	// files that are suitable for type checking. | ||
|  | 	// This may differ from GoFiles if files are processed before compilation. | ||
|  | 	CompiledGoFiles []string | ||
|  | 
 | ||
|  | 	// OtherFiles lists the absolute file paths of the package's non-Go source files, | ||
|  | 	// including assembly, C, C++, Fortran, Objective-C, SWIG, and so on. | ||
|  | 	OtherFiles []string | ||
|  | 
 | ||
|  | 	// 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 provides type information for the package. | ||
|  | 	// The NeedTypes LoadMode bit sets this field for packages matching the | ||
|  | 	// patterns; type information for dependencies may be missing or incomplete, | ||
|  | 	// unless NeedDeps and NeedImports are also set. | ||
|  | 	Types *types.Package | ||
|  | 
 | ||
|  | 	// Fset provides position information for Types, TypesInfo, and Syntax. | ||
|  | 	// It is set only when Types is set. | ||
|  | 	Fset *token.FileSet | ||
|  | 
 | ||
|  | 	// 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 CompiledGoFiles. | ||
|  | 	// | ||
|  | 	// The NeedSyntax LoadMode bit populates this field for packages matching the patterns. | ||
|  | 	// If NeedDeps and NeedImports are also set, this field will also be populated | ||
|  | 	// for dependencies. | ||
|  | 	Syntax []*ast.File | ||
|  | 
 | ||
|  | 	// 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 | ||
|  | 
 | ||
|  | 	// forTest is the package under test, if any. | ||
|  | 	forTest string | ||
|  | 
 | ||
|  | 	// module is the module information for the package if it exists. | ||
|  | 	Module *Module | ||
|  | } | ||
|  | 
 | ||
|  | // Module provides module information for a package. | ||
|  | type Module struct { | ||
|  | 	Path      string       // module path | ||
|  | 	Version   string       // module version | ||
|  | 	Replace   *Module      // replaced by this module | ||
|  | 	Time      *time.Time   // time version was created | ||
|  | 	Main      bool         // is this the main module? | ||
|  | 	Indirect  bool         // is this module only an indirect dependency of main module? | ||
|  | 	Dir       string       // directory holding files for this module, if any | ||
|  | 	GoMod     string       // path to go.mod file used when loading this module, if any | ||
|  | 	GoVersion string       // go version used in module | ||
|  | 	Error     *ModuleError // error loading module | ||
|  | } | ||
|  | 
 | ||
|  | // ModuleError holds errors loading a module. | ||
|  | type ModuleError struct { | ||
|  | 	Err string // the error itself | ||
|  | } | ||
|  | 
 | ||
|  | func init() { | ||
|  | 	packagesinternal.GetForTest = func(p interface{}) string { | ||
|  | 		return p.(*Package).forTest | ||
|  | 	} | ||
|  | 	packagesinternal.GetGoCmdRunner = func(config interface{}) *gocommand.Runner { | ||
|  | 		return config.(*Config).gocmdRunner | ||
|  | 	} | ||
|  | 	packagesinternal.SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) { | ||
|  | 		config.(*Config).gocmdRunner = runner | ||
|  | 	} | ||
|  | 	packagesinternal.TypecheckCgo = int(typecheckCgo) | ||
|  | } | ||
|  | 
 | ||
|  | // 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 | ||
|  | } | ||
|  | 
 | ||
|  | // 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 | ||
|  | // | ||
|  | // 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          []Error           `json:",omitempty"` | ||
|  | 	GoFiles         []string          `json:",omitempty"` | ||
|  | 	CompiledGoFiles []string          `json:",omitempty"` | ||
|  | 	OtherFiles      []string          `json:",omitempty"` | ||
|  | 	ExportFile      string            `json:",omitempty"` | ||
|  | 	Imports         map[string]string `json:",omitempty"` | ||
|  | } | ||
|  | 
 | ||
|  | // MarshalJSON returns the Package in its JSON form. | ||
|  | // For the most part, the structure fields are written out unmodified, and | ||
|  | // the type and syntax fields are skipped. | ||
|  | // 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) { | ||
|  | 	flat := &flatPackage{ | ||
|  | 		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.Imports) > 0 { | ||
|  | 		flat.Imports = make(map[string]string, len(p.Imports)) | ||
|  | 		for path, ipkg := range p.Imports { | ||
|  | 			flat.Imports[path] = ipkg.ID | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return json.Marshal(flat) | ||
|  | } | ||
|  | 
 | ||
|  | // UnmarshalJSON reads in a Package from its JSON format. | ||
|  | // See MarshalJSON for details about the format accepted. | ||
|  | func (p *Package) UnmarshalJSON(b []byte) error { | ||
|  | 	flat := &flatPackage{} | ||
|  | 	if err := json.Unmarshal(b, &flat); err != nil { | ||
|  | 		return err | ||
|  | 	} | ||
|  | 	*p = Package{ | ||
|  | 		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.Imports) > 0 { | ||
|  | 		p.Imports = make(map[string]*Package, len(flat.Imports)) | ||
|  | 		for path, id := range flat.Imports { | ||
|  | 			p.Imports[path] = &Package{ID: id} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | 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 | ||
|  | 	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 | ||
|  | 	parseCache   map[string]*parseValue | ||
|  | 	parseCacheMu sync.Mutex | ||
|  | 	exportMu     sync.Mutex // enforces mutual exclusion of exportdata operations | ||
|  | 
 | ||
|  | 	// Config.Mode contains the implied mode (see impliedLoadMode). | ||
|  | 	// Implied mode contains all the fields we need the data for. | ||
|  | 	// In requestedMode there are the actually requested fields. | ||
|  | 	// We'll zero them out before returning packages to the user. | ||
|  | 	// This makes it easier for us to get the conditions where | ||
|  | 	// we need certain modes right. | ||
|  | 	requestedMode LoadMode | ||
|  | } | ||
|  | 
 | ||
|  | type parseValue struct { | ||
|  | 	f     *ast.File | ||
|  | 	err   error | ||
|  | 	ready chan struct{} | ||
|  | } | ||
|  | 
 | ||
|  | func newLoader(cfg *Config) *loader { | ||
|  | 	ld := &loader{ | ||
|  | 		parseCache: map[string]*parseValue{}, | ||
|  | 	} | ||
|  | 	if cfg != nil { | ||
|  | 		ld.Config = *cfg | ||
|  | 		// If the user has provided a logger, use it. | ||
|  | 		ld.Config.Logf = cfg.Logf | ||
|  | 	} | ||
|  | 	if ld.Config.Logf == nil { | ||
|  | 		// If the GOPACKAGESDEBUG environment variable is set to true, | ||
|  | 		// but the user has not provided a logger, default to log.Printf. | ||
|  | 		if debug { | ||
|  | 			ld.Config.Logf = log.Printf | ||
|  | 		} else { | ||
|  | 			ld.Config.Logf = func(format string, args ...interface{}) {} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	if ld.Config.Mode == 0 { | ||
|  | 		ld.Config.Mode = NeedName | NeedFiles | NeedCompiledGoFiles // Preserve zero behavior of Mode for backwards compatibility. | ||
|  | 	} | ||
|  | 	if ld.Config.Env == nil { | ||
|  | 		ld.Config.Env = os.Environ() | ||
|  | 	} | ||
|  | 	if ld.Config.gocmdRunner == nil { | ||
|  | 		ld.Config.gocmdRunner = &gocommand.Runner{} | ||
|  | 	} | ||
|  | 	if ld.Context == nil { | ||
|  | 		ld.Context = context.Background() | ||
|  | 	} | ||
|  | 	if ld.Dir == "" { | ||
|  | 		if dir, err := os.Getwd(); err == nil { | ||
|  | 			ld.Dir = dir | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Save the actually requested fields. We'll zero them out before returning packages to the user. | ||
|  | 	ld.requestedMode = ld.Mode | ||
|  | 	ld.Mode = impliedLoadMode(ld.Mode) | ||
|  | 
 | ||
|  | 	if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 { | ||
|  | 		if ld.Fset == nil { | ||
|  | 			ld.Fset = token.NewFileSet() | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// ParseFile is required even in LoadTypes mode | ||
|  | 		// because we load source if export data is missing. | ||
|  | 		if ld.ParseFile == nil { | ||
|  | 			ld.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) { | ||
|  | 				const mode = parser.AllErrors | parser.ParseComments | ||
|  | 				return parser.ParseFile(fset, filename, src, mode) | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return ld | ||
|  | } | ||
|  | 
 | ||
|  | // 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) { | ||
|  | 	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 = make([]*loaderPackage, len(roots)) | ||
|  | 	for _, pkg := range list { | ||
|  | 		rootIndex := -1 | ||
|  | 		if i, found := rootMap[pkg.ID]; found { | ||
|  | 			rootIndex = i | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Overlays can invalidate export data. | ||
|  | 		// TODO(matloob): make this check fine-grained based on dependencies on overlaid files | ||
|  | 		exportDataInvalid := len(ld.Overlay) > 0 || pkg.ExportFile == "" && pkg.PkgPath != "unsafe" | ||
|  | 		// This package needs type information if the caller requested types and the package is | ||
|  | 		// either a root, or it's a non-root and the user requested dependencies ... | ||
|  | 		needtypes := (ld.Mode&NeedTypes|NeedTypesInfo != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0)) | ||
|  | 		// This package needs source if the call requested source (or types info, which implies source) | ||
|  | 		// and the package is either a root, or itas a non- root and the user requested dependencies... | ||
|  | 		needsrc := ((ld.Mode&(NeedSyntax|NeedTypesInfo) != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0)) || | ||
|  | 			// ... or if we need types and the exportData is invalid. We fall back to (incompletely) | ||
|  | 			// typechecking packages from source if they fail to compile. | ||
|  | 			(ld.Mode&NeedTypes|NeedTypesInfo != 0 && exportDataInvalid)) && pkg.PkgPath != "unsafe" | ||
|  | 		lpkg := &loaderPackage{ | ||
|  | 			Package:   pkg, | ||
|  | 			needtypes: needtypes, | ||
|  | 			needsrc:   needsrc, | ||
|  | 		} | ||
|  | 		ld.pkgs[lpkg.ID] = 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) | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Materialize the import graph. | ||
|  | 
 | ||
|  | 	const ( | ||
|  | 		white = 0 // new | ||
|  | 		grey  = 1 // in progress | ||
|  | 		black = 2 // complete | ||
|  | 	) | ||
|  | 
 | ||
|  | 	// visit traverses the import graph, depth-first, | ||
|  | 	// and materializes the graph as Packages.Imports. | ||
|  | 	// | ||
|  | 	// Valid imports are saved in the Packages.Import map. | ||
|  | 	// Invalid imports (cycles and missing nodes) are saved in the importErrors map. | ||
|  | 	// Thus, even in the presence of both kinds of errors, the Import graph remains a DAG. | ||
|  | 	// | ||
|  | 	// visit returns whether the package needs src or has a transitive | ||
|  | 	// dependency on a package that does. These are the only packages | ||
|  | 	// 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: | ||
|  | 			return lpkg.needsrc | ||
|  | 		case grey: | ||
|  | 			panic("internal error: grey node") | ||
|  | 		} | ||
|  | 		lpkg.color = grey | ||
|  | 		stack = append(stack, lpkg) // push | ||
|  | 		stubs := lpkg.Imports       // the structure form has only stubs with the ID in the Imports | ||
|  | 		// If NeedImports isn't set, the imports fields will all be zeroed out. | ||
|  | 		if ld.Mode&NeedImports != 0 { | ||
|  | 			lpkg.Imports = make(map[string]*Package, len(stubs)) | ||
|  | 			for importPath, ipkg := range stubs { | ||
|  | 				var importErr error | ||
|  | 				imp := ld.pkgs[ipkg.ID] | ||
|  | 				if imp == nil { | ||
|  | 					// (includes package "C" when DisableCgo) | ||
|  | 					importErr = fmt.Errorf("missing package: %q", ipkg.ID) | ||
|  | 				} else if imp.color == grey { | ||
|  | 					importErr = fmt.Errorf("import cycle: %s", stack) | ||
|  | 				} | ||
|  | 				if importErr != nil { | ||
|  | 					if lpkg.importErrors == nil { | ||
|  | 						lpkg.importErrors = make(map[string]error) | ||
|  | 					} | ||
|  | 					lpkg.importErrors[importPath] = importErr | ||
|  | 					continue | ||
|  | 				} | ||
|  | 
 | ||
|  | 				if visit(imp) { | ||
|  | 					lpkg.needsrc = true | ||
|  | 				} | ||
|  | 				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&NeedImports == 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 | ||
|  | 		} | ||
|  | 	} else { | ||
|  | 		// For each initial package, create its import DAG. | ||
|  | 		for _, lpkg := range initial { | ||
|  | 			visit(lpkg) | ||
|  | 		} | ||
|  | 	} | ||
|  | 	if ld.Mode&NeedImports != 0 && ld.Mode&NeedTypes != 0 { | ||
|  | 		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 and syntax if needed, starting at | ||
|  | 	// the initial packages (roots of the import DAG). | ||
|  | 	if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 { | ||
|  | 		var wg sync.WaitGroup | ||
|  | 		for _, lpkg := range initial { | ||
|  | 			wg.Add(1) | ||
|  | 			go func(lpkg *loaderPackage) { | ||
|  | 				ld.loadRecursive(lpkg) | ||
|  | 				wg.Done() | ||
|  | 			}(lpkg) | ||
|  | 		} | ||
|  | 		wg.Wait() | ||
|  | 	} | ||
|  | 
 | ||
|  | 	result := make([]*Package, len(initial)) | ||
|  | 	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.requestedMode&NeedName == 0 { | ||
|  | 			ld.pkgs[i].Name = "" | ||
|  | 			ld.pkgs[i].PkgPath = "" | ||
|  | 		} | ||
|  | 		if ld.requestedMode&NeedFiles == 0 { | ||
|  | 			ld.pkgs[i].GoFiles = nil | ||
|  | 			ld.pkgs[i].OtherFiles = nil | ||
|  | 		} | ||
|  | 		if ld.requestedMode&NeedCompiledGoFiles == 0 { | ||
|  | 			ld.pkgs[i].CompiledGoFiles = nil | ||
|  | 		} | ||
|  | 		if ld.requestedMode&NeedImports == 0 { | ||
|  | 			ld.pkgs[i].Imports = nil | ||
|  | 		} | ||
|  | 		if ld.requestedMode&NeedExportsFile == 0 { | ||
|  | 			ld.pkgs[i].ExportFile = "" | ||
|  | 		} | ||
|  | 		if ld.requestedMode&NeedTypes == 0 { | ||
|  | 			ld.pkgs[i].Types = nil | ||
|  | 			ld.pkgs[i].Fset = nil | ||
|  | 			ld.pkgs[i].IllTyped = false | ||
|  | 		} | ||
|  | 		if ld.requestedMode&NeedSyntax == 0 { | ||
|  | 			ld.pkgs[i].Syntax = nil | ||
|  | 		} | ||
|  | 		if ld.requestedMode&NeedTypesInfo == 0 { | ||
|  | 			ld.pkgs[i].TypesInfo = nil | ||
|  | 		} | ||
|  | 		if ld.requestedMode&NeedTypesSizes == 0 { | ||
|  | 			ld.pkgs[i].TypesSizes = nil | ||
|  | 		} | ||
|  | 		if ld.requestedMode&NeedModule == 0 { | ||
|  | 			ld.pkgs[i].Module = nil | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	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&NeedTypes. | ||
|  | func (ld *loader) loadRecursive(lpkg *loaderPackage) { | ||
|  | 	lpkg.loadOnce.Do(func() { | ||
|  | 		// Load the direct dependencies, in parallel. | ||
|  | 		var wg sync.WaitGroup | ||
|  | 		for _, ipkg := range lpkg.Imports { | ||
|  | 			imp := ld.pkgs[ipkg.ID] | ||
|  | 			wg.Add(1) | ||
|  | 			go func(imp *loaderPackage) { | ||
|  | 				ld.loadRecursive(imp) | ||
|  | 				wg.Done() | ||
|  | 			}(imp) | ||
|  | 		} | ||
|  | 		wg.Wait() | ||
|  | 		ld.loadPackage(lpkg) | ||
|  | 	}) | ||
|  | } | ||
|  | 
 | ||
|  | // loadPackage loads the specified package. | ||
|  | // It must be called only once per Package, | ||
|  | // after immediate dependencies are loaded. | ||
|  | // Precondition: ld.Mode & NeedTypes. | ||
|  | func (ld *loader) loadPackage(lpkg *loaderPackage) { | ||
|  | 	if lpkg.PkgPath == "unsafe" { | ||
|  | 		// Fill in the blanks to avoid surprises. | ||
|  | 		lpkg.Types = types.Unsafe | ||
|  | 		lpkg.Fset = ld.Fset | ||
|  | 		lpkg.Syntax = []*ast.File{} | ||
|  | 		lpkg.TypesInfo = new(types.Info) | ||
|  | 		lpkg.TypesSizes = ld.sizes | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Call NewPackage directly with explicit name. | ||
|  | 	// 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 && !lpkg.needsrc { | ||
|  | 		return | ||
|  | 	} | ||
|  | 	if !lpkg.needsrc { | ||
|  | 		ld.loadFromExportData(lpkg) | ||
|  | 		return // not a source package, don't get syntax trees | ||
|  | 	} | ||
|  | 
 | ||
|  | 	appendError := func(err error) { | ||
|  | 		// 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) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		lpkg.Errors = append(lpkg.Errors, errs...) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if ld.Config.Mode&NeedTypes != 0 && len(lpkg.CompiledGoFiles) == 0 && lpkg.ExportFile != "" { | ||
|  | 		// The config requested loading sources and types, but sources are missing. | ||
|  | 		// Add an error to the package and fall back to loading from export data. | ||
|  | 		appendError(Error{"-", fmt.Sprintf("sources missing for package %s", lpkg.ID), ParseError}) | ||
|  | 		ld.loadFromExportData(lpkg) | ||
|  | 		return // can't get syntax trees for this package | ||
|  | 	} | ||
|  | 
 | ||
|  | 	files, errs := ld.parseFiles(lpkg.CompiledGoFiles) | ||
|  | 	for _, err := range errs { | ||
|  | 		appendError(err) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	lpkg.Syntax = files | ||
|  | 	if ld.Config.Mode&NeedTypes == 0 { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	lpkg.TypesInfo = &types.Info{ | ||
|  | 		Types:      make(map[ast.Expr]types.TypeAndValue), | ||
|  | 		Defs:       make(map[*ast.Ident]types.Object), | ||
|  | 		Uses:       make(map[*ast.Ident]types.Object), | ||
|  | 		Implicits:  make(map[ast.Node]types.Object), | ||
|  | 		Scopes:     make(map[ast.Node]*types.Scope), | ||
|  | 		Selections: make(map[*ast.SelectorExpr]*types.Selection), | ||
|  | 	} | ||
|  | 	lpkg.TypesSizes = ld.sizes | ||
|  | 
 | ||
|  | 	importer := importerFunc(func(path string) (*types.Package, error) { | ||
|  | 		if path == "unsafe" { | ||
|  | 			return types.Unsafe, nil | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// The imports map is keyed by import path. | ||
|  | 		ipkg := lpkg.Imports[path] | ||
|  | 		if ipkg == nil { | ||
|  | 			if err := lpkg.importErrors[path]; err != nil { | ||
|  | 				return nil, err | ||
|  | 			} | ||
|  | 			// There was skew between the metadata and the | ||
|  | 			// import declarations, likely due to an edit | ||
|  | 			// race, or because the ParseFile feature was | ||
|  | 			// used to supply alternative file contents. | ||
|  | 			return nil, fmt.Errorf("no metadata for %s", path) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if ipkg.Types != nil && ipkg.Types.Complete() { | ||
|  | 			return ipkg.Types, nil | ||
|  | 		} | ||
|  | 		log.Fatalf("internal error: package %q without types was imported from %q", path, lpkg) | ||
|  | 		panic("unreachable") | ||
|  | 	}) | ||
|  | 
 | ||
|  | 	// type-check | ||
|  | 	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 == 0 && !lpkg.initial, | ||
|  | 
 | ||
|  | 		Error: appendError, | ||
|  | 		Sizes: ld.sizes, | ||
|  | 	} | ||
|  | 	if (ld.Mode & typecheckCgo) != 0 { | ||
|  | 		if !typesinternal.SetUsesCgo(tc) { | ||
|  | 			appendError(Error{ | ||
|  | 				Msg:  "typecheckCgo requires Go 1.15+", | ||
|  | 				Kind: ListError, | ||
|  | 			}) | ||
|  | 			return | ||
|  | 		} | ||
|  | 	} | ||
|  | 	types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax) | ||
|  | 
 | ||
|  | 	lpkg.importErrors = nil // no longer needed | ||
|  | 
 | ||
|  | 	// If !Cgo, the type-checker uses FakeImportC mode, so | ||
|  | 	// it doesn't invoke the importer for import "C", | ||
|  | 	// nor report an error for the import, | ||
|  | 	// or for any undefined C.f reference. | ||
|  | 	// We must detect this explicitly and correctly | ||
|  | 	// mark the package as IllTyped (by reporting an error). | ||
|  | 	// TODO(adonovan): if these errors are annoying, | ||
|  | 	// we could just set IllTyped quietly. | ||
|  | 	if tc.FakeImportC { | ||
|  | 	outer: | ||
|  | 		for _, f := range lpkg.Syntax { | ||
|  | 			for _, imp := range f.Imports { | ||
|  | 				if imp.Path.Value == `"C"` { | ||
|  | 					err := types.Error{Fset: ld.Fset, Pos: imp.Pos(), Msg: `import "C" ignored`} | ||
|  | 					appendError(err) | ||
|  | 					break outer | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Record accumulated errors. | ||
|  | 	illTyped := len(lpkg.Errors) > 0 | ||
|  | 	if !illTyped { | ||
|  | 		for _, imp := range lpkg.Imports { | ||
|  | 			if imp.IllTyped { | ||
|  | 				illTyped = true | ||
|  | 				break | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	lpkg.IllTyped = illTyped | ||
|  | } | ||
|  | 
 | ||
|  | // An importFunc is an implementation of the single-method | ||
|  | // types.Importer interface based on a function value. | ||
|  | type importerFunc func(path string) (*types.Package, error) | ||
|  | 
 | ||
|  | func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) } | ||
|  | 
 | ||
|  | // We use a counting semaphore to limit | ||
|  | // the number of parallel I/O calls per process. | ||
|  | var ioLimit = make(chan bool, 20) | ||
|  | 
 | ||
|  | func (ld *loader) parseFile(filename string) (*ast.File, error) { | ||
|  | 	ld.parseCacheMu.Lock() | ||
|  | 	v, ok := ld.parseCache[filename] | ||
|  | 	if ok { | ||
|  | 		// cache hit | ||
|  | 		ld.parseCacheMu.Unlock() | ||
|  | 		<-v.ready | ||
|  | 	} else { | ||
|  | 		// cache miss | ||
|  | 		v = &parseValue{ready: make(chan struct{})} | ||
|  | 		ld.parseCache[filename] = v | ||
|  | 		ld.parseCacheMu.Unlock() | ||
|  | 
 | ||
|  | 		var src []byte | ||
|  | 		for f, contents := range ld.Config.Overlay { | ||
|  | 			if sameFile(f, filename) { | ||
|  | 				src = contents | ||
|  | 			} | ||
|  | 		} | ||
|  | 		var err error | ||
|  | 		if src == nil { | ||
|  | 			ioLimit <- true // wait | ||
|  | 			src, err = ioutil.ReadFile(filename) | ||
|  | 			<-ioLimit // signal | ||
|  | 		} | ||
|  | 		if err != nil { | ||
|  | 			v.err = err | ||
|  | 		} else { | ||
|  | 			v.f, v.err = ld.ParseFile(ld.Fset, filename, src) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		close(v.ready) | ||
|  | 	} | ||
|  | 	return v.f, v.err | ||
|  | } | ||
|  | 
 | ||
|  | // parseFiles reads and parses the Go source files and returns the ASTs | ||
|  | // of the ones that could be at least partially parsed, along with a | ||
|  | // list of I/O and parse errors encountered. | ||
|  | // | ||
|  | // Because files are scanned in parallel, the token.Pos | ||
|  | // positions of the resulting ast.Files are not ordered. | ||
|  | // | ||
|  | func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) { | ||
|  | 	var wg sync.WaitGroup | ||
|  | 	n := len(filenames) | ||
|  | 	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) { | ||
|  | 			parsed[i], errors[i] = ld.parseFile(filename) | ||
|  | 			wg.Done() | ||
|  | 		}(i, file) | ||
|  | 	} | ||
|  | 	wg.Wait() | ||
|  | 
 | ||
|  | 	// Eliminate nils, preserving order. | ||
|  | 	var o int | ||
|  | 	for _, f := range parsed { | ||
|  | 		if f != nil { | ||
|  | 			parsed[o] = f | ||
|  | 			o++ | ||
|  | 		} | ||
|  | 	} | ||
|  | 	parsed = parsed[:o] | ||
|  | 
 | ||
|  | 	o = 0 | ||
|  | 	for _, err := range errors { | ||
|  | 		if err != nil { | ||
|  | 			errors[o] = err | ||
|  | 			o++ | ||
|  | 		} | ||
|  | 	} | ||
|  | 	errors = errors[:o] | ||
|  | 
 | ||
|  | 	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) { | ||
|  | 	if lpkg.PkgPath == "" { | ||
|  | 		log.Fatalf("internal error: Package %s has no PkgPath", lpkg) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Because gcexportdata.Read has the potential to create or | ||
|  | 	// modify the types.Package for each node in the transitive | ||
|  | 	// closure of dependencies of lpkg, all exportdata operations | ||
|  | 	// must be sequential. (Finer-grained locking would require | ||
|  | 	// changes to the gcexportdata API.) | ||
|  | 	// | ||
|  | 	// The exportMu lock guards the Package.Pkg field and the | ||
|  | 	// types.Package it points to, for each Package in the graph. | ||
|  | 	// | ||
|  | 	// Not all accesses to Package.Pkg need to be protected by exportMu: | ||
|  | 	// graph ordering ensures that direct dependencies of source | ||
|  | 	// packages are fully loaded before the importer reads their Pkg field. | ||
|  | 	ld.exportMu.Lock() | ||
|  | 	defer ld.exportMu.Unlock() | ||
|  | 
 | ||
|  | 	if tpkg := lpkg.Types; tpkg != nil && tpkg.Complete() { | ||
|  | 		return tpkg, nil // cache hit | ||
|  | 	} | ||
|  | 
 | ||
|  | 	lpkg.IllTyped = true // fail safe | ||
|  | 
 | ||
|  | 	if lpkg.ExportFile == "" { | ||
|  | 		// Errors while building export data will have been printed to stderr. | ||
|  | 		return nil, fmt.Errorf("no export data file") | ||
|  | 	} | ||
|  | 	f, err := os.Open(lpkg.ExportFile) | ||
|  | 	if err != nil { | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 	defer f.Close() | ||
|  | 
 | ||
|  | 	// Read gc export data. | ||
|  | 	// | ||
|  | 	// We don't currently support gccgo export data because all | ||
|  | 	// underlying workspaces use the gc toolchain. (Even build | ||
|  | 	// systems that support gccgo don't use it for workspace | ||
|  | 	// queries.) | ||
|  | 	r, err := gcexportdata.NewReader(f) | ||
|  | 	if err != nil { | ||
|  | 		return nil, fmt.Errorf("reading %s: %v", lpkg.ExportFile, err) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Build the view. | ||
|  | 	// | ||
|  | 	// The gcexportdata machinery has no concept of package ID. | ||
|  | 	// It identifies packages by their PkgPath, which although not | ||
|  | 	// globally unique is unique within the scope of one invocation | ||
|  | 	// of the linker, type-checker, or gcexportdata. | ||
|  | 	// | ||
|  | 	// So, we must build a PkgPath-keyed view of the global | ||
|  | 	// (conceptually ID-keyed) cache of packages and pass it to | ||
|  | 	// gcexportdata. The view must contain every existing | ||
|  | 	// package that might possibly be mentioned by the | ||
|  | 	// current package---its transitive closure. | ||
|  | 	// | ||
|  | 	// 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. | ||
|  | 	// | ||
|  | 	view := make(map[string]*types.Package) // view seen by gcexportdata | ||
|  | 	seen := make(map[*loaderPackage]bool)   // all visited packages | ||
|  | 	var visit func(pkgs map[string]*Package) | ||
|  | 	visit = func(pkgs map[string]*Package) { | ||
|  | 		for _, p := range pkgs { | ||
|  | 			lpkg := ld.pkgs[p.ID] | ||
|  | 			if !seen[lpkg] { | ||
|  | 				seen[lpkg] = true | ||
|  | 				view[lpkg.PkgPath] = lpkg.Types | ||
|  | 				visit(lpkg.Imports) | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	visit(lpkg.Imports) | ||
|  | 
 | ||
|  | 	viewLen := len(view) + 1 // adding the self package | ||
|  | 	// Parse the export data. | ||
|  | 	// (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 | ||
|  | 
 | ||
|  | 	return tpkg, nil | ||
|  | } | ||
|  | 
 | ||
|  | // impliedLoadMode returns loadMode with its dependencies. | ||
|  | func impliedLoadMode(loadMode LoadMode) LoadMode { | ||
|  | 	if loadMode&NeedTypesInfo != 0 && loadMode&NeedImports == 0 { | ||
|  | 		// If NeedTypesInfo, go/packages needs to do typechecking itself so it can | ||
|  | 		// associate type info with the AST. To do so, we need the export data | ||
|  | 		// for dependencies, which means we need to ask for the direct dependencies. | ||
|  | 		// NeedImports is used to ask for the direct dependencies. | ||
|  | 		loadMode |= NeedImports | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if loadMode&NeedDeps != 0 && loadMode&NeedImports == 0 { | ||
|  | 		// With NeedDeps we need to load at least direct dependencies. | ||
|  | 		// NeedImports is used to ask for the direct dependencies. | ||
|  | 		loadMode |= NeedImports | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return loadMode | ||
|  | } | ||
|  | 
 | ||
|  | func usesExportData(cfg *Config) bool { | ||
|  | 	return cfg.Mode&NeedExportsFile != 0 || cfg.Mode&NeedTypes != 0 && cfg.Mode&NeedDeps == 0 | ||
|  | } |