| // Copyright 2013 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 loader | 
 |  | 
 | // See doc.go for package documentation and implementation notes. | 
 |  | 
 | import ( | 
 | 	"errors" | 
 | 	"fmt" | 
 | 	"go/ast" | 
 | 	"go/build" | 
 | 	"go/parser" | 
 | 	"go/token" | 
 | 	"go/types" | 
 | 	"os" | 
 | 	"path/filepath" | 
 | 	"sort" | 
 | 	"strings" | 
 | 	"sync" | 
 | 	"time" | 
 |  | 
 | 	"golang.org/x/tools/go/ast/astutil" | 
 | 	"golang.org/x/tools/go/internal/cgo" | 
 | 	"golang.org/x/tools/internal/typeparams" | 
 | ) | 
 |  | 
 | var ignoreVendor build.ImportMode | 
 |  | 
 | const trace = false // show timing info for type-checking | 
 |  | 
 | // Config specifies the configuration for loading a whole program from | 
 | // Go source code. | 
 | // The zero value for Config is a ready-to-use default configuration. | 
 | type Config struct { | 
 | 	// Fset is the file set for the parser to use when loading the | 
 | 	// program.  If nil, it may be lazily initialized by any | 
 | 	// method of Config. | 
 | 	Fset *token.FileSet | 
 |  | 
 | 	// ParserMode specifies the mode to be used by the parser when | 
 | 	// loading source packages. | 
 | 	ParserMode parser.Mode | 
 |  | 
 | 	// TypeChecker contains options relating to the type checker. | 
 | 	// | 
 | 	// The supplied IgnoreFuncBodies is not used; the effective | 
 | 	// value comes from the TypeCheckFuncBodies func below. | 
 | 	// The supplied Import function is not used either. | 
 | 	TypeChecker types.Config | 
 |  | 
 | 	// TypeCheckFuncBodies is a predicate over package paths. | 
 | 	// A package for which the predicate is false will | 
 | 	// have its package-level declarations type checked, but not | 
 | 	// its function bodies; this can be used to quickly load | 
 | 	// dependencies from source.  If nil, all func bodies are type | 
 | 	// checked. | 
 | 	TypeCheckFuncBodies func(path string) bool | 
 |  | 
 | 	// If Build is non-nil, it is used to locate source packages. | 
 | 	// Otherwise &build.Default is used. | 
 | 	// | 
 | 	// By default, cgo is invoked to preprocess Go files that | 
 | 	// import the fake package "C".  This behaviour can be | 
 | 	// disabled by setting CGO_ENABLED=0 in the environment prior | 
 | 	// to startup, or by setting Build.CgoEnabled=false. | 
 | 	Build *build.Context | 
 |  | 
 | 	// The current directory, used for resolving relative package | 
 | 	// references such as "./go/loader".  If empty, os.Getwd will be | 
 | 	// used instead. | 
 | 	Cwd string | 
 |  | 
 | 	// If DisplayPath is non-nil, it is used to transform each | 
 | 	// file name obtained from Build.Import().  This can be used | 
 | 	// to prevent a virtualized build.Config's file names from | 
 | 	// leaking into the user interface. | 
 | 	DisplayPath func(path string) string | 
 |  | 
 | 	// If AllowErrors is true, Load will return a Program even | 
 | 	// if some of the its packages contained I/O, parser or type | 
 | 	// errors; such errors are accessible via PackageInfo.Errors.  If | 
 | 	// false, Load will fail if any package had an error. | 
 | 	AllowErrors bool | 
 |  | 
 | 	// CreatePkgs specifies a list of non-importable initial | 
 | 	// packages to create.  The resulting packages will appear in | 
 | 	// the corresponding elements of the Program.Created slice. | 
 | 	CreatePkgs []PkgSpec | 
 |  | 
 | 	// ImportPkgs specifies a set of initial packages to load. | 
 | 	// The map keys are package paths. | 
 | 	// | 
 | 	// The map value indicates whether to load tests.  If true, Load | 
 | 	// will add and type-check two lists of files to the package: | 
 | 	// non-test files followed by in-package *_test.go files.  In | 
 | 	// addition, it will append the external test package (if any) | 
 | 	// to Program.Created. | 
 | 	ImportPkgs map[string]bool | 
 |  | 
 | 	// FindPackage is called during Load to create the build.Package | 
 | 	// for a given import path from a given directory. | 
 | 	// If FindPackage is nil, (*build.Context).Import is used. | 
 | 	// A client may use this hook to adapt to a proprietary build | 
 | 	// system that does not follow the "go build" layout | 
 | 	// conventions, for example. | 
 | 	// | 
 | 	// It must be safe to call concurrently from multiple goroutines. | 
 | 	FindPackage func(ctxt *build.Context, importPath, fromDir string, mode build.ImportMode) (*build.Package, error) | 
 |  | 
 | 	// AfterTypeCheck is called immediately after a list of files | 
 | 	// has been type-checked and appended to info.Files. | 
 | 	// | 
 | 	// This optional hook function is the earliest opportunity for | 
 | 	// the client to observe the output of the type checker, | 
 | 	// which may be useful to reduce analysis latency when loading | 
 | 	// a large program. | 
 | 	// | 
 | 	// The function is permitted to modify info.Info, for instance | 
 | 	// to clear data structures that are no longer needed, which can | 
 | 	// dramatically reduce peak memory consumption. | 
 | 	// | 
 | 	// The function may be called twice for the same PackageInfo: | 
 | 	// once for the files of the package and again for the | 
 | 	// in-package test files. | 
 | 	// | 
 | 	// It must be safe to call concurrently from multiple goroutines. | 
 | 	AfterTypeCheck func(info *PackageInfo, files []*ast.File) | 
 | } | 
 |  | 
 | // A PkgSpec specifies a non-importable package to be created by Load. | 
 | // Files are processed first, but typically only one of Files and | 
 | // Filenames is provided.  The path needn't be globally unique. | 
 | // | 
 | // For vendoring purposes, the package's directory is the one that | 
 | // contains the first file. | 
 | type PkgSpec struct { | 
 | 	Path      string      // package path ("" => use package declaration) | 
 | 	Files     []*ast.File // ASTs of already-parsed files | 
 | 	Filenames []string    // names of files to be parsed | 
 | } | 
 |  | 
 | // A Program is a Go program loaded from source as specified by a Config. | 
 | type Program struct { | 
 | 	Fset *token.FileSet // the file set for this program | 
 |  | 
 | 	// Created[i] contains the initial package whose ASTs or | 
 | 	// filenames were supplied by Config.CreatePkgs[i], followed by | 
 | 	// the external test package, if any, of each package in | 
 | 	// Config.ImportPkgs ordered by ImportPath. | 
 | 	// | 
 | 	// NOTE: these files must not import "C".  Cgo preprocessing is | 
 | 	// only performed on imported packages, not ad hoc packages. | 
 | 	// | 
 | 	// TODO(adonovan): we need to copy and adapt the logic of | 
 | 	// goFilesPackage (from $GOROOT/src/cmd/go/build.go) and make | 
 | 	// Config.Import and Config.Create methods return the same kind | 
 | 	// of entity, essentially a build.Package. | 
 | 	// Perhaps we can even reuse that type directly. | 
 | 	Created []*PackageInfo | 
 |  | 
 | 	// Imported contains the initially imported packages, | 
 | 	// as specified by Config.ImportPkgs. | 
 | 	Imported map[string]*PackageInfo | 
 |  | 
 | 	// AllPackages contains the PackageInfo of every package | 
 | 	// encountered by Load: all initial packages and all | 
 | 	// dependencies, including incomplete ones. | 
 | 	AllPackages map[*types.Package]*PackageInfo | 
 |  | 
 | 	// importMap is the canonical mapping of package paths to | 
 | 	// packages.  It contains all Imported initial packages, but not | 
 | 	// Created ones, and all imported dependencies. | 
 | 	importMap map[string]*types.Package | 
 | } | 
 |  | 
 | // PackageInfo holds the ASTs and facts derived by the type-checker | 
 | // for a single package. | 
 | // | 
 | // Not mutated once exposed via the API. | 
 | type PackageInfo struct { | 
 | 	Pkg                   *types.Package | 
 | 	Importable            bool        // true if 'import "Pkg.Path()"' would resolve to this | 
 | 	TransitivelyErrorFree bool        // true if Pkg and all its dependencies are free of errors | 
 | 	Files                 []*ast.File // syntax trees for the package's files | 
 | 	Errors                []error     // non-nil if the package had errors | 
 | 	types.Info                        // type-checker deductions. | 
 | 	dir                   string      // package directory | 
 |  | 
 | 	checker   *types.Checker // transient type-checker state | 
 | 	errorFunc func(error) | 
 | } | 
 |  | 
 | func (info *PackageInfo) String() string { return info.Pkg.Path() } | 
 |  | 
 | func (info *PackageInfo) appendError(err error) { | 
 | 	if info.errorFunc != nil { | 
 | 		info.errorFunc(err) | 
 | 	} else { | 
 | 		fmt.Fprintln(os.Stderr, err) | 
 | 	} | 
 | 	info.Errors = append(info.Errors, err) | 
 | } | 
 |  | 
 | func (conf *Config) fset() *token.FileSet { | 
 | 	if conf.Fset == nil { | 
 | 		conf.Fset = token.NewFileSet() | 
 | 	} | 
 | 	return conf.Fset | 
 | } | 
 |  | 
 | // ParseFile is a convenience function (intended for testing) that invokes | 
 | // the parser using the Config's FileSet, which is initialized if nil. | 
 | // | 
 | // src specifies the parser input as a string, []byte, or io.Reader, and | 
 | // filename is its apparent name.  If src is nil, the contents of | 
 | // filename are read from the file system. | 
 | func (conf *Config) ParseFile(filename string, src interface{}) (*ast.File, error) { | 
 | 	// TODO(adonovan): use conf.build() etc like parseFiles does. | 
 | 	return parser.ParseFile(conf.fset(), filename, src, conf.ParserMode) | 
 | } | 
 |  | 
 | // FromArgsUsage is a partial usage message that applications calling | 
 | // FromArgs may wish to include in their -help output. | 
 | const FromArgsUsage = ` | 
 | <args> is a list of arguments denoting a set of initial packages. | 
 | It may take one of two forms: | 
 |  | 
 | 1. A list of *.go source files. | 
 |  | 
 |    All of the specified files are loaded, parsed and type-checked | 
 |    as a single package.  All the files must belong to the same directory. | 
 |  | 
 | 2. A list of import paths, each denoting a package. | 
 |  | 
 |    The package's directory is found relative to the $GOROOT and | 
 |    $GOPATH using similar logic to 'go build', and the *.go files in | 
 |    that directory are loaded, parsed and type-checked as a single | 
 |    package. | 
 |  | 
 |    In addition, all *_test.go files in the directory are then loaded | 
 |    and parsed.  Those files whose package declaration equals that of | 
 |    the non-*_test.go files are included in the primary package.  Test | 
 |    files whose package declaration ends with "_test" are type-checked | 
 |    as another package, the 'external' test package, so that a single | 
 |    import path may denote two packages.  (Whether this behaviour is | 
 |    enabled is tool-specific, and may depend on additional flags.) | 
 |  | 
 | A '--' argument terminates the list of packages. | 
 | ` | 
 |  | 
 | // FromArgs interprets args as a set of initial packages to load from | 
 | // source and updates the configuration.  It returns the list of | 
 | // unconsumed arguments. | 
 | // | 
 | // It is intended for use in command-line interfaces that require a | 
 | // set of initial packages to be specified; see FromArgsUsage message | 
 | // for details. | 
 | // | 
 | // Only superficial errors are reported at this stage; errors dependent | 
 | // on I/O are detected during Load. | 
 | func (conf *Config) FromArgs(args []string, xtest bool) ([]string, error) { | 
 | 	var rest []string | 
 | 	for i, arg := range args { | 
 | 		if arg == "--" { | 
 | 			rest = args[i+1:] | 
 | 			args = args[:i] | 
 | 			break // consume "--" and return the remaining args | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if len(args) > 0 && strings.HasSuffix(args[0], ".go") { | 
 | 		// Assume args is a list of a *.go files | 
 | 		// denoting a single ad hoc package. | 
 | 		for _, arg := range args { | 
 | 			if !strings.HasSuffix(arg, ".go") { | 
 | 				return nil, fmt.Errorf("named files must be .go files: %s", arg) | 
 | 			} | 
 | 		} | 
 | 		conf.CreateFromFilenames("", args...) | 
 | 	} else { | 
 | 		// Assume args are directories each denoting a | 
 | 		// package and (perhaps) an external test, iff xtest. | 
 | 		for _, arg := range args { | 
 | 			if xtest { | 
 | 				conf.ImportWithTests(arg) | 
 | 			} else { | 
 | 				conf.Import(arg) | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return rest, nil | 
 | } | 
 |  | 
 | // CreateFromFilenames is a convenience function that adds | 
 | // a conf.CreatePkgs entry to create a package of the specified *.go | 
 | // files. | 
 | func (conf *Config) CreateFromFilenames(path string, filenames ...string) { | 
 | 	conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Filenames: filenames}) | 
 | } | 
 |  | 
 | // CreateFromFiles is a convenience function that adds a conf.CreatePkgs | 
 | // entry to create package of the specified path and parsed files. | 
 | func (conf *Config) CreateFromFiles(path string, files ...*ast.File) { | 
 | 	conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Files: files}) | 
 | } | 
 |  | 
 | // ImportWithTests is a convenience function that adds path to | 
 | // ImportPkgs, the set of initial source packages located relative to | 
 | // $GOPATH.  The package will be augmented by any *_test.go files in | 
 | // its directory that contain a "package x" (not "package x_test") | 
 | // declaration. | 
 | // | 
 | // In addition, if any *_test.go files contain a "package x_test" | 
 | // declaration, an additional package comprising just those files will | 
 | // be added to CreatePkgs. | 
 | func (conf *Config) ImportWithTests(path string) { conf.addImport(path, true) } | 
 |  | 
 | // Import is a convenience function that adds path to ImportPkgs, the | 
 | // set of initial packages that will be imported from source. | 
 | func (conf *Config) Import(path string) { conf.addImport(path, false) } | 
 |  | 
 | func (conf *Config) addImport(path string, tests bool) { | 
 | 	if path == "C" { | 
 | 		return // ignore; not a real package | 
 | 	} | 
 | 	if conf.ImportPkgs == nil { | 
 | 		conf.ImportPkgs = make(map[string]bool) | 
 | 	} | 
 | 	conf.ImportPkgs[path] = conf.ImportPkgs[path] || tests | 
 | } | 
 |  | 
 | // PathEnclosingInterval returns the PackageInfo and ast.Node that | 
 | // contain source interval [start, end), and all the node's ancestors | 
 | // up to the AST root.  It searches all ast.Files of all packages in prog. | 
 | // exact is defined as for astutil.PathEnclosingInterval. | 
 | // | 
 | // The zero value is returned if not found. | 
 | func (prog *Program) PathEnclosingInterval(start, end token.Pos) (pkg *PackageInfo, path []ast.Node, exact bool) { | 
 | 	for _, info := range prog.AllPackages { | 
 | 		for _, f := range info.Files { | 
 | 			if f.Pos() == token.NoPos { | 
 | 				// This can happen if the parser saw | 
 | 				// too many errors and bailed out. | 
 | 				// (Use parser.AllErrors to prevent that.) | 
 | 				continue | 
 | 			} | 
 | 			if !tokenFileContainsPos(prog.Fset.File(f.Pos()), start) { | 
 | 				continue | 
 | 			} | 
 | 			if path, exact := astutil.PathEnclosingInterval(f, start, end); path != nil { | 
 | 				return info, path, exact | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	return nil, nil, false | 
 | } | 
 |  | 
 | // InitialPackages returns a new slice containing the set of initial | 
 | // packages (Created + Imported) in unspecified order. | 
 | func (prog *Program) InitialPackages() []*PackageInfo { | 
 | 	infos := make([]*PackageInfo, 0, len(prog.Created)+len(prog.Imported)) | 
 | 	infos = append(infos, prog.Created...) | 
 | 	for _, info := range prog.Imported { | 
 | 		infos = append(infos, info) | 
 | 	} | 
 | 	return infos | 
 | } | 
 |  | 
 | // Package returns the ASTs and results of type checking for the | 
 | // specified package. | 
 | func (prog *Program) Package(path string) *PackageInfo { | 
 | 	if info, ok := prog.AllPackages[prog.importMap[path]]; ok { | 
 | 		return info | 
 | 	} | 
 | 	for _, info := range prog.Created { | 
 | 		if path == info.Pkg.Path() { | 
 | 			return info | 
 | 		} | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | // ---------- Implementation ---------- | 
 |  | 
 | // importer holds the working state of the algorithm. | 
 | type importer struct { | 
 | 	conf  *Config   // the client configuration | 
 | 	start time.Time // for logging | 
 |  | 
 | 	progMu sync.Mutex // guards prog | 
 | 	prog   *Program   // the resulting program | 
 |  | 
 | 	// findpkg is a memoization of FindPackage. | 
 | 	findpkgMu sync.Mutex // guards findpkg | 
 | 	findpkg   map[findpkgKey]*findpkgValue | 
 |  | 
 | 	importedMu sync.Mutex             // guards imported | 
 | 	imported   map[string]*importInfo // all imported packages (incl. failures) by import path | 
 |  | 
 | 	// import dependency graph: graph[x][y] => x imports y | 
 | 	// | 
 | 	// Since non-importable packages cannot be cyclic, we ignore | 
 | 	// their imports, thus we only need the subgraph over importable | 
 | 	// packages.  Nodes are identified by their import paths. | 
 | 	graphMu sync.Mutex | 
 | 	graph   map[string]map[string]bool | 
 | } | 
 |  | 
 | type findpkgKey struct { | 
 | 	importPath string | 
 | 	fromDir    string | 
 | 	mode       build.ImportMode | 
 | } | 
 |  | 
 | type findpkgValue struct { | 
 | 	ready chan struct{} // closed to broadcast readiness | 
 | 	bp    *build.Package | 
 | 	err   error | 
 | } | 
 |  | 
 | // importInfo tracks the success or failure of a single import. | 
 | // | 
 | // Upon completion, exactly one of info and err is non-nil: | 
 | // info on successful creation of a package, err otherwise. | 
 | // A successful package may still contain type errors. | 
 | type importInfo struct { | 
 | 	path     string        // import path | 
 | 	info     *PackageInfo  // results of typechecking (including errors) | 
 | 	complete chan struct{} // closed to broadcast that info is set. | 
 | } | 
 |  | 
 | // awaitCompletion blocks until ii is complete, | 
 | // i.e. the info field is safe to inspect. | 
 | func (ii *importInfo) awaitCompletion() { | 
 | 	<-ii.complete // wait for close | 
 | } | 
 |  | 
 | // Complete marks ii as complete. | 
 | // Its info and err fields will not be subsequently updated. | 
 | func (ii *importInfo) Complete(info *PackageInfo) { | 
 | 	if info == nil { | 
 | 		panic("info == nil") | 
 | 	} | 
 | 	ii.info = info | 
 | 	close(ii.complete) | 
 | } | 
 |  | 
 | type importError struct { | 
 | 	path string // import path | 
 | 	err  error  // reason for failure to create a package | 
 | } | 
 |  | 
 | // Load creates the initial packages specified by conf.{Create,Import}Pkgs, | 
 | // loading their dependencies packages as needed. | 
 | // | 
 | // On success, Load returns a Program containing a PackageInfo for | 
 | // each package.  On failure, it returns an error. | 
 | // | 
 | // If AllowErrors is true, Load will return a Program even if some | 
 | // packages contained I/O, parser or type errors, or if dependencies | 
 | // were missing.  (Such errors are accessible via PackageInfo.Errors.  If | 
 | // false, Load will fail if any package had an error. | 
 | // | 
 | // It is an error if no packages were loaded. | 
 | func (conf *Config) Load() (*Program, error) { | 
 | 	// Create a simple default error handler for parse/type errors. | 
 | 	if conf.TypeChecker.Error == nil { | 
 | 		conf.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) } | 
 | 	} | 
 |  | 
 | 	// Set default working directory for relative package references. | 
 | 	if conf.Cwd == "" { | 
 | 		var err error | 
 | 		conf.Cwd, err = os.Getwd() | 
 | 		if err != nil { | 
 | 			return nil, err | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// Install default FindPackage hook using go/build logic. | 
 | 	if conf.FindPackage == nil { | 
 | 		conf.FindPackage = (*build.Context).Import | 
 | 	} | 
 |  | 
 | 	prog := &Program{ | 
 | 		Fset:        conf.fset(), | 
 | 		Imported:    make(map[string]*PackageInfo), | 
 | 		importMap:   make(map[string]*types.Package), | 
 | 		AllPackages: make(map[*types.Package]*PackageInfo), | 
 | 	} | 
 |  | 
 | 	imp := importer{ | 
 | 		conf:     conf, | 
 | 		prog:     prog, | 
 | 		findpkg:  make(map[findpkgKey]*findpkgValue), | 
 | 		imported: make(map[string]*importInfo), | 
 | 		start:    time.Now(), | 
 | 		graph:    make(map[string]map[string]bool), | 
 | 	} | 
 |  | 
 | 	// -- loading proper (concurrent phase) -------------------------------- | 
 |  | 
 | 	var errpkgs []string // packages that contained errors | 
 |  | 
 | 	// Load the initially imported packages and their dependencies, | 
 | 	// in parallel. | 
 | 	// No vendor check on packages imported from the command line. | 
 | 	infos, importErrors := imp.importAll("", conf.Cwd, conf.ImportPkgs, ignoreVendor) | 
 | 	for _, ie := range importErrors { | 
 | 		conf.TypeChecker.Error(ie.err) // failed to create package | 
 | 		errpkgs = append(errpkgs, ie.path) | 
 | 	} | 
 | 	for _, info := range infos { | 
 | 		prog.Imported[info.Pkg.Path()] = info | 
 | 	} | 
 |  | 
 | 	// Augment the designated initial packages by their tests. | 
 | 	// Dependencies are loaded in parallel. | 
 | 	var xtestPkgs []*build.Package | 
 | 	for importPath, augment := range conf.ImportPkgs { | 
 | 		if !augment { | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		// No vendor check on packages imported from command line. | 
 | 		bp, err := imp.findPackage(importPath, conf.Cwd, ignoreVendor) | 
 | 		if err != nil { | 
 | 			// Package not found, or can't even parse package declaration. | 
 | 			// Already reported by previous loop; ignore it. | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		// Needs external test package? | 
 | 		if len(bp.XTestGoFiles) > 0 { | 
 | 			xtestPkgs = append(xtestPkgs, bp) | 
 | 		} | 
 |  | 
 | 		// Consult the cache using the canonical package path. | 
 | 		path := bp.ImportPath | 
 | 		imp.importedMu.Lock() // (unnecessary, we're sequential here) | 
 | 		ii, ok := imp.imported[path] | 
 | 		// Paranoid checks added due to issue #11012. | 
 | 		if !ok { | 
 | 			// Unreachable. | 
 | 			// The previous loop called importAll and thus | 
 | 			// startLoad for each path in ImportPkgs, which | 
 | 			// populates imp.imported[path] with a non-zero value. | 
 | 			panic(fmt.Sprintf("imported[%q] not found", path)) | 
 | 		} | 
 | 		if ii == nil { | 
 | 			// Unreachable. | 
 | 			// The ii values in this loop are the same as in | 
 | 			// the previous loop, which enforced the invariant | 
 | 			// that at least one of ii.err and ii.info is non-nil. | 
 | 			panic(fmt.Sprintf("imported[%q] == nil", path)) | 
 | 		} | 
 | 		if ii.info == nil { | 
 | 			// Unreachable. | 
 | 			// awaitCompletion has the postcondition | 
 | 			// ii.info != nil. | 
 | 			panic(fmt.Sprintf("imported[%q].info = nil", path)) | 
 | 		} | 
 | 		info := ii.info | 
 | 		imp.importedMu.Unlock() | 
 |  | 
 | 		// Parse the in-package test files. | 
 | 		files, errs := imp.conf.parsePackageFiles(bp, 't') | 
 | 		for _, err := range errs { | 
 | 			info.appendError(err) | 
 | 		} | 
 |  | 
 | 		// The test files augmenting package P cannot be imported, | 
 | 		// but may import packages that import P, | 
 | 		// so we must disable the cycle check. | 
 | 		imp.addFiles(info, files, false) | 
 | 	} | 
 |  | 
 | 	createPkg := func(path, dir string, files []*ast.File, errs []error) { | 
 | 		info := imp.newPackageInfo(path, dir) | 
 | 		for _, err := range errs { | 
 | 			info.appendError(err) | 
 | 		} | 
 |  | 
 | 		// Ad hoc packages are non-importable, | 
 | 		// so no cycle check is needed. | 
 | 		// addFiles loads dependencies in parallel. | 
 | 		imp.addFiles(info, files, false) | 
 | 		prog.Created = append(prog.Created, info) | 
 | 	} | 
 |  | 
 | 	// Create packages specified by conf.CreatePkgs. | 
 | 	for _, cp := range conf.CreatePkgs { | 
 | 		files, errs := parseFiles(conf.fset(), conf.build(), nil, conf.Cwd, cp.Filenames, conf.ParserMode) | 
 | 		files = append(files, cp.Files...) | 
 |  | 
 | 		path := cp.Path | 
 | 		if path == "" { | 
 | 			if len(files) > 0 { | 
 | 				path = files[0].Name.Name | 
 | 			} else { | 
 | 				path = "(unnamed)" | 
 | 			} | 
 | 		} | 
 |  | 
 | 		dir := conf.Cwd | 
 | 		if len(files) > 0 && files[0].Pos().IsValid() { | 
 | 			dir = filepath.Dir(conf.fset().File(files[0].Pos()).Name()) | 
 | 		} | 
 | 		createPkg(path, dir, files, errs) | 
 | 	} | 
 |  | 
 | 	// Create external test packages. | 
 | 	sort.Sort(byImportPath(xtestPkgs)) | 
 | 	for _, bp := range xtestPkgs { | 
 | 		files, errs := imp.conf.parsePackageFiles(bp, 'x') | 
 | 		createPkg(bp.ImportPath+"_test", bp.Dir, files, errs) | 
 | 	} | 
 |  | 
 | 	// -- finishing up (sequential) ---------------------------------------- | 
 |  | 
 | 	if len(prog.Imported)+len(prog.Created) == 0 { | 
 | 		return nil, errors.New("no initial packages were loaded") | 
 | 	} | 
 |  | 
 | 	// Create infos for indirectly imported packages. | 
 | 	// e.g. incomplete packages without syntax, loaded from export data. | 
 | 	for _, obj := range prog.importMap { | 
 | 		info := prog.AllPackages[obj] | 
 | 		if info == nil { | 
 | 			prog.AllPackages[obj] = &PackageInfo{Pkg: obj, Importable: true} | 
 | 		} else { | 
 | 			// finished | 
 | 			info.checker = nil | 
 | 			info.errorFunc = nil | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if !conf.AllowErrors { | 
 | 		// Report errors in indirectly imported packages. | 
 | 		for _, info := range prog.AllPackages { | 
 | 			if len(info.Errors) > 0 { | 
 | 				errpkgs = append(errpkgs, info.Pkg.Path()) | 
 | 			} | 
 | 		} | 
 | 		if errpkgs != nil { | 
 | 			var more string | 
 | 			if len(errpkgs) > 3 { | 
 | 				more = fmt.Sprintf(" and %d more", len(errpkgs)-3) | 
 | 				errpkgs = errpkgs[:3] | 
 | 			} | 
 | 			return nil, fmt.Errorf("couldn't load packages due to errors: %s%s", | 
 | 				strings.Join(errpkgs, ", "), more) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	markErrorFreePackages(prog.AllPackages) | 
 |  | 
 | 	return prog, nil | 
 | } | 
 |  | 
 | type byImportPath []*build.Package | 
 |  | 
 | func (b byImportPath) Len() int           { return len(b) } | 
 | func (b byImportPath) Less(i, j int) bool { return b[i].ImportPath < b[j].ImportPath } | 
 | func (b byImportPath) Swap(i, j int)      { b[i], b[j] = b[j], b[i] } | 
 |  | 
 | // markErrorFreePackages sets the TransitivelyErrorFree flag on all | 
 | // applicable packages. | 
 | func markErrorFreePackages(allPackages map[*types.Package]*PackageInfo) { | 
 | 	// Build the transpose of the import graph. | 
 | 	importedBy := make(map[*types.Package]map[*types.Package]bool) | 
 | 	for P := range allPackages { | 
 | 		for _, Q := range P.Imports() { | 
 | 			clients, ok := importedBy[Q] | 
 | 			if !ok { | 
 | 				clients = make(map[*types.Package]bool) | 
 | 				importedBy[Q] = clients | 
 | 			} | 
 | 			clients[P] = true | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// Find all packages reachable from some error package. | 
 | 	reachable := make(map[*types.Package]bool) | 
 | 	var visit func(*types.Package) | 
 | 	visit = func(p *types.Package) { | 
 | 		if !reachable[p] { | 
 | 			reachable[p] = true | 
 | 			for q := range importedBy[p] { | 
 | 				visit(q) | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	for _, info := range allPackages { | 
 | 		if len(info.Errors) > 0 { | 
 | 			visit(info.Pkg) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// Mark the others as "transitively error-free". | 
 | 	for _, info := range allPackages { | 
 | 		if !reachable[info.Pkg] { | 
 | 			info.TransitivelyErrorFree = true | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // build returns the effective build context. | 
 | func (conf *Config) build() *build.Context { | 
 | 	if conf.Build != nil { | 
 | 		return conf.Build | 
 | 	} | 
 | 	return &build.Default | 
 | } | 
 |  | 
 | // parsePackageFiles enumerates the files belonging to package path, | 
 | // then loads, parses and returns them, plus a list of I/O or parse | 
 | // errors that were encountered. | 
 | // | 
 | // 'which' indicates which files to include: | 
 | // | 
 | //	'g': include non-test *.go source files (GoFiles + processed CgoFiles) | 
 | //	't': include in-package *_test.go source files (TestGoFiles) | 
 | //	'x': include external *_test.go source files. (XTestGoFiles) | 
 | func (conf *Config) parsePackageFiles(bp *build.Package, which rune) ([]*ast.File, []error) { | 
 | 	if bp.ImportPath == "unsafe" { | 
 | 		return nil, nil | 
 | 	} | 
 | 	var filenames []string | 
 | 	switch which { | 
 | 	case 'g': | 
 | 		filenames = bp.GoFiles | 
 | 	case 't': | 
 | 		filenames = bp.TestGoFiles | 
 | 	case 'x': | 
 | 		filenames = bp.XTestGoFiles | 
 | 	default: | 
 | 		panic(which) | 
 | 	} | 
 |  | 
 | 	files, errs := parseFiles(conf.fset(), conf.build(), conf.DisplayPath, bp.Dir, filenames, conf.ParserMode) | 
 |  | 
 | 	// Preprocess CgoFiles and parse the outputs (sequentially). | 
 | 	if which == 'g' && bp.CgoFiles != nil { | 
 | 		cgofiles, err := cgo.ProcessFiles(bp, conf.fset(), conf.DisplayPath, conf.ParserMode) | 
 | 		if err != nil { | 
 | 			errs = append(errs, err) | 
 | 		} else { | 
 | 			files = append(files, cgofiles...) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return files, errs | 
 | } | 
 |  | 
 | // doImport imports the package denoted by path. | 
 | // It implements the types.Importer signature. | 
 | // | 
 | // It returns an error if a package could not be created | 
 | // (e.g. go/build or parse error), but type errors are reported via | 
 | // the types.Config.Error callback (the first of which is also saved | 
 | // in the package's PackageInfo). | 
 | // | 
 | // Idempotent. | 
 | func (imp *importer) doImport(from *PackageInfo, to string) (*types.Package, error) { | 
 | 	if to == "C" { | 
 | 		// This should be unreachable, but ad hoc packages are | 
 | 		// not currently subject to cgo preprocessing. | 
 | 		// See https://golang.org/issue/11627. | 
 | 		return nil, fmt.Errorf(`the loader doesn't cgo-process ad hoc packages like %q; see Go issue 11627`, | 
 | 			from.Pkg.Path()) | 
 | 	} | 
 |  | 
 | 	bp, err := imp.findPackage(to, from.dir, 0) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 |  | 
 | 	// The standard unsafe package is handled specially, | 
 | 	// and has no PackageInfo. | 
 | 	if bp.ImportPath == "unsafe" { | 
 | 		return types.Unsafe, nil | 
 | 	} | 
 |  | 
 | 	// Look for the package in the cache using its canonical path. | 
 | 	path := bp.ImportPath | 
 | 	imp.importedMu.Lock() | 
 | 	ii := imp.imported[path] | 
 | 	imp.importedMu.Unlock() | 
 | 	if ii == nil { | 
 | 		panic("internal error: unexpected import: " + path) | 
 | 	} | 
 | 	if ii.info != nil { | 
 | 		return ii.info.Pkg, nil | 
 | 	} | 
 |  | 
 | 	// Import of incomplete package: this indicates a cycle. | 
 | 	fromPath := from.Pkg.Path() | 
 | 	if cycle := imp.findPath(path, fromPath); cycle != nil { | 
 | 		// Normalize cycle: start from alphabetically largest node. | 
 | 		pos, start := -1, "" | 
 | 		for i, s := range cycle { | 
 | 			if pos < 0 || s > start { | 
 | 				pos, start = i, s | 
 | 			} | 
 | 		} | 
 | 		cycle = append(cycle, cycle[:pos]...)[pos:] // rotate cycle to start from largest | 
 | 		cycle = append(cycle, cycle[0])             // add start node to end to show cycliness | 
 | 		return nil, fmt.Errorf("import cycle: %s", strings.Join(cycle, " -> ")) | 
 | 	} | 
 |  | 
 | 	panic("internal error: import of incomplete (yet acyclic) package: " + fromPath) | 
 | } | 
 |  | 
 | // findPackage locates the package denoted by the importPath in the | 
 | // specified directory. | 
 | func (imp *importer) findPackage(importPath, fromDir string, mode build.ImportMode) (*build.Package, error) { | 
 | 	// We use a non-blocking duplicate-suppressing cache (gopl.io ยง9.7) | 
 | 	// to avoid holding the lock around FindPackage. | 
 | 	key := findpkgKey{importPath, fromDir, mode} | 
 | 	imp.findpkgMu.Lock() | 
 | 	v, ok := imp.findpkg[key] | 
 | 	if ok { | 
 | 		// cache hit | 
 | 		imp.findpkgMu.Unlock() | 
 |  | 
 | 		<-v.ready // wait for entry to become ready | 
 | 	} else { | 
 | 		// Cache miss: this goroutine becomes responsible for | 
 | 		// populating the map entry and broadcasting its readiness. | 
 | 		v = &findpkgValue{ready: make(chan struct{})} | 
 | 		imp.findpkg[key] = v | 
 | 		imp.findpkgMu.Unlock() | 
 |  | 
 | 		ioLimit <- true | 
 | 		v.bp, v.err = imp.conf.FindPackage(imp.conf.build(), importPath, fromDir, mode) | 
 | 		<-ioLimit | 
 |  | 
 | 		if _, ok := v.err.(*build.NoGoError); ok { | 
 | 			v.err = nil // empty directory is not an error | 
 | 		} | 
 |  | 
 | 		close(v.ready) // broadcast ready condition | 
 | 	} | 
 | 	return v.bp, v.err | 
 | } | 
 |  | 
 | // importAll loads, parses, and type-checks the specified packages in | 
 | // parallel and returns their completed importInfos in unspecified order. | 
 | // | 
 | // fromPath is the package path of the importing package, if it is | 
 | // importable, "" otherwise.  It is used for cycle detection. | 
 | // | 
 | // fromDir is the directory containing the import declaration that | 
 | // caused these imports. | 
 | func (imp *importer) importAll(fromPath, fromDir string, imports map[string]bool, mode build.ImportMode) (infos []*PackageInfo, errors []importError) { | 
 | 	if fromPath != "" { | 
 | 		// We're loading a set of imports. | 
 | 		// | 
 | 		// We must record graph edges from the importing package | 
 | 		// to its dependencies, and check for cycles. | 
 | 		imp.graphMu.Lock() | 
 | 		deps, ok := imp.graph[fromPath] | 
 | 		if !ok { | 
 | 			deps = make(map[string]bool) | 
 | 			imp.graph[fromPath] = deps | 
 | 		} | 
 | 		for importPath := range imports { | 
 | 			deps[importPath] = true | 
 | 		} | 
 | 		imp.graphMu.Unlock() | 
 | 	} | 
 |  | 
 | 	var pending []*importInfo | 
 | 	for importPath := range imports { | 
 | 		if fromPath != "" { | 
 | 			if cycle := imp.findPath(importPath, fromPath); cycle != nil { | 
 | 				// Cycle-forming import: we must not check it | 
 | 				// since it would deadlock. | 
 | 				if trace { | 
 | 					fmt.Fprintf(os.Stderr, "import cycle: %q\n", cycle) | 
 | 				} | 
 | 				continue | 
 | 			} | 
 | 		} | 
 | 		bp, err := imp.findPackage(importPath, fromDir, mode) | 
 | 		if err != nil { | 
 | 			errors = append(errors, importError{ | 
 | 				path: importPath, | 
 | 				err:  err, | 
 | 			}) | 
 | 			continue | 
 | 		} | 
 | 		pending = append(pending, imp.startLoad(bp)) | 
 | 	} | 
 |  | 
 | 	for _, ii := range pending { | 
 | 		ii.awaitCompletion() | 
 | 		infos = append(infos, ii.info) | 
 | 	} | 
 |  | 
 | 	return infos, errors | 
 | } | 
 |  | 
 | // findPath returns an arbitrary path from 'from' to 'to' in the import | 
 | // graph, or nil if there was none. | 
 | func (imp *importer) findPath(from, to string) []string { | 
 | 	imp.graphMu.Lock() | 
 | 	defer imp.graphMu.Unlock() | 
 |  | 
 | 	seen := make(map[string]bool) | 
 | 	var search func(stack []string, importPath string) []string | 
 | 	search = func(stack []string, importPath string) []string { | 
 | 		if !seen[importPath] { | 
 | 			seen[importPath] = true | 
 | 			stack = append(stack, importPath) | 
 | 			if importPath == to { | 
 | 				return stack | 
 | 			} | 
 | 			for x := range imp.graph[importPath] { | 
 | 				if p := search(stack, x); p != nil { | 
 | 					return p | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 		return nil | 
 | 	} | 
 | 	return search(make([]string, 0, 20), from) | 
 | } | 
 |  | 
 | // startLoad initiates the loading, parsing and type-checking of the | 
 | // specified package and its dependencies, if it has not already begun. | 
 | // | 
 | // It returns an importInfo, not necessarily in a completed state.  The | 
 | // caller must call awaitCompletion() before accessing its info field. | 
 | // | 
 | // startLoad is concurrency-safe and idempotent. | 
 | func (imp *importer) startLoad(bp *build.Package) *importInfo { | 
 | 	path := bp.ImportPath | 
 | 	imp.importedMu.Lock() | 
 | 	ii, ok := imp.imported[path] | 
 | 	if !ok { | 
 | 		ii = &importInfo{path: path, complete: make(chan struct{})} | 
 | 		imp.imported[path] = ii | 
 | 		go func() { | 
 | 			info := imp.load(bp) | 
 | 			ii.Complete(info) | 
 | 		}() | 
 | 	} | 
 | 	imp.importedMu.Unlock() | 
 |  | 
 | 	return ii | 
 | } | 
 |  | 
 | // load implements package loading by parsing Go source files | 
 | // located by go/build. | 
 | func (imp *importer) load(bp *build.Package) *PackageInfo { | 
 | 	info := imp.newPackageInfo(bp.ImportPath, bp.Dir) | 
 | 	info.Importable = true | 
 | 	files, errs := imp.conf.parsePackageFiles(bp, 'g') | 
 | 	for _, err := range errs { | 
 | 		info.appendError(err) | 
 | 	} | 
 |  | 
 | 	imp.addFiles(info, files, true) | 
 |  | 
 | 	imp.progMu.Lock() | 
 | 	imp.prog.importMap[bp.ImportPath] = info.Pkg | 
 | 	imp.progMu.Unlock() | 
 |  | 
 | 	return info | 
 | } | 
 |  | 
 | // addFiles adds and type-checks the specified files to info, loading | 
 | // their dependencies if needed.  The order of files determines the | 
 | // package initialization order.  It may be called multiple times on the | 
 | // same package.  Errors are appended to the info.Errors field. | 
 | // | 
 | // cycleCheck determines whether the imports within files create | 
 | // dependency edges that should be checked for potential cycles. | 
 | func (imp *importer) addFiles(info *PackageInfo, files []*ast.File, cycleCheck bool) { | 
 | 	// Ensure the dependencies are loaded, in parallel. | 
 | 	var fromPath string | 
 | 	if cycleCheck { | 
 | 		fromPath = info.Pkg.Path() | 
 | 	} | 
 | 	// TODO(adonovan): opt: make the caller do scanImports. | 
 | 	// Callers with a build.Package can skip it. | 
 | 	imp.importAll(fromPath, info.dir, scanImports(files), 0) | 
 |  | 
 | 	if trace { | 
 | 		fmt.Fprintf(os.Stderr, "%s: start %q (%d)\n", | 
 | 			time.Since(imp.start), info.Pkg.Path(), len(files)) | 
 | 	} | 
 |  | 
 | 	// Don't call checker.Files on Unsafe, even with zero files, | 
 | 	// because it would mutate the package, which is a global. | 
 | 	if info.Pkg == types.Unsafe { | 
 | 		if len(files) > 0 { | 
 | 			panic(`"unsafe" package contains unexpected files`) | 
 | 		} | 
 | 	} else { | 
 | 		// Ignore the returned (first) error since we | 
 | 		// already collect them all in the PackageInfo. | 
 | 		info.checker.Files(files) | 
 | 		info.Files = append(info.Files, files...) | 
 | 	} | 
 |  | 
 | 	if imp.conf.AfterTypeCheck != nil { | 
 | 		imp.conf.AfterTypeCheck(info, files) | 
 | 	} | 
 |  | 
 | 	if trace { | 
 | 		fmt.Fprintf(os.Stderr, "%s: stop %q\n", | 
 | 			time.Since(imp.start), info.Pkg.Path()) | 
 | 	} | 
 | } | 
 |  | 
 | func (imp *importer) newPackageInfo(path, dir string) *PackageInfo { | 
 | 	var pkg *types.Package | 
 | 	if path == "unsafe" { | 
 | 		pkg = types.Unsafe | 
 | 	} else { | 
 | 		pkg = types.NewPackage(path, "") | 
 | 	} | 
 | 	info := &PackageInfo{ | 
 | 		Pkg: pkg, | 
 | 		Info: 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), | 
 | 		}, | 
 | 		errorFunc: imp.conf.TypeChecker.Error, | 
 | 		dir:       dir, | 
 | 	} | 
 | 	typeparams.InitInstanceInfo(&info.Info) | 
 |  | 
 | 	// Copy the types.Config so we can vary it across PackageInfos. | 
 | 	tc := imp.conf.TypeChecker | 
 | 	tc.IgnoreFuncBodies = false | 
 | 	if f := imp.conf.TypeCheckFuncBodies; f != nil { | 
 | 		tc.IgnoreFuncBodies = !f(path) | 
 | 	} | 
 | 	tc.Importer = closure{imp, info} | 
 | 	tc.Error = info.appendError // appendError wraps the user's Error function | 
 |  | 
 | 	info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info) | 
 | 	imp.progMu.Lock() | 
 | 	imp.prog.AllPackages[pkg] = info | 
 | 	imp.progMu.Unlock() | 
 | 	return info | 
 | } | 
 |  | 
 | type closure struct { | 
 | 	imp  *importer | 
 | 	info *PackageInfo | 
 | } | 
 |  | 
 | func (c closure) Import(to string) (*types.Package, error) { return c.imp.doImport(c.info, to) } |