github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/internal/loader/loader.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package loader
     6  
     7  // See doc.go for package documentation and implementation notes.
     8  
     9  import (
    10  	"errors"
    11  	"fmt"
    12  	"go/ast"
    13  	"go/build"
    14  	goimporter "go/importer"
    15  	"go/parser"
    16  	"go/token"
    17  	"go/types"
    18  	"os"
    19  	"sort"
    20  	"strings"
    21  	"sync"
    22  	"time"
    23  )
    24  
    25  const trace = false // show timing info for type-checking
    26  
    27  // Config specifies the configuration for loading a whole program from
    28  // Go source code.
    29  // The zero value for Config is a ready-to-use default configuration.
    30  type Config struct {
    31  	// Fset is the file set for the parser to use when loading the
    32  	// program.  If nil, it may be lazily initialized by any
    33  	// method of Config.
    34  	Fset *token.FileSet
    35  
    36  	// ParserMode specifies the mode to be used by the parser when
    37  	// loading source packages.
    38  	ParserMode parser.Mode
    39  
    40  	// TypeChecker contains options relating to the type checker.
    41  	//
    42  	// The supplied IgnoreFuncBodies is not used; the effective
    43  	// value comes from the TypeCheckFuncBodies func below.
    44  	// The supplied Import function is not used either.
    45  	TypeChecker types.Config
    46  
    47  	// TypeCheckFuncBodies is a predicate over package import
    48  	// paths.  A package for which the predicate is false will
    49  	// have its package-level declarations type checked, but not
    50  	// its function bodies; this can be used to quickly load
    51  	// dependencies from source.  If nil, all func bodies are type
    52  	// checked.
    53  	TypeCheckFuncBodies func(string) bool
    54  
    55  	// If Build is non-nil, it is used to locate source packages.
    56  	// Otherwise &build.Default is used.
    57  	//
    58  	// By default, cgo is invoked to preprocess Go files that
    59  	// import the fake package "C".  This behaviour can be
    60  	// disabled by setting CGO_ENABLED=0 in the environment prior
    61  	// to startup, or by setting Build.CgoEnabled=false.
    62  	Build *build.Context
    63  
    64  	// The current directory, used for resolving relative package
    65  	// references such as "./go/loader".  If empty, os.Getwd will be
    66  	// used instead.
    67  	Cwd string
    68  
    69  	// If DisplayPath is non-nil, it is used to transform each
    70  	// file name obtained from Build.Import().  This can be used
    71  	// to prevent a virtualized build.Config's file names from
    72  	// leaking into the user interface.
    73  	DisplayPath func(path string) string
    74  
    75  	// If AllowErrors is true, Load will return a Program even
    76  	// if some of the its packages contained I/O, parser or type
    77  	// errors; such errors are accessible via PackageInfo.Errors.  If
    78  	// false, Load will fail if any package had an error.
    79  	AllowErrors bool
    80  
    81  	// CreatePkgs specifies a list of non-importable initial
    82  	// packages to create.  The resulting packages will appear in
    83  	// the corresponding elements of the Program.Created slice.
    84  	CreatePkgs []PkgSpec
    85  
    86  	// ImportPkgs specifies a set of initial packages to load from
    87  	// source.  The map keys are package import paths, used to
    88  	// locate the package relative to $GOROOT.
    89  	//
    90  	// The map value indicates whether to load tests.  If true, Load
    91  	// will add and type-check two lists of files to the package:
    92  	// non-test files followed by in-package *_test.go files.  In
    93  	// addition, it will append the external test package (if any)
    94  	// to Program.Created.
    95  	ImportPkgs map[string]bool
    96  
    97  	// FindPackage is called during Load to create the build.Package
    98  	// for a given import path.  If nil, a default implementation
    99  	// based on ctxt.Import is used.  A client may use this hook to
   100  	// adapt to a proprietary build system that does not follow the
   101  	// "go build" layout conventions, for example.
   102  	//
   103  	// It must be safe to call concurrently from multiple goroutines.
   104  	FindPackage func(ctxt *build.Context, importPath string) (*build.Package, error)
   105  }
   106  
   107  // A PkgSpec specifies a non-importable package to be created by Load.
   108  // Files are processed first, but typically only one of Files and
   109  // Filenames is provided.  The path needn't be globally unique.
   110  //
   111  type PkgSpec struct {
   112  	Path      string      // import path ("" => use package declaration)
   113  	Files     []*ast.File // ASTs of already-parsed files
   114  	Filenames []string    // names of files to be parsed
   115  }
   116  
   117  // A Program is a Go program loaded from source as specified by a Config.
   118  type Program struct {
   119  	Fset *token.FileSet // the file set for this program
   120  
   121  	// Created[i] contains the initial package whose ASTs or
   122  	// filenames were supplied by Config.CreatePkgs[i], followed by
   123  	// the external test package, if any, of each package in
   124  	// Config.ImportPkgs ordered by ImportPath.
   125  	Created []*PackageInfo
   126  
   127  	// Imported contains the initially imported packages,
   128  	// as specified by Config.ImportPkgs.
   129  	Imported map[string]*PackageInfo
   130  
   131  	// AllPackages contains the PackageInfo of every package
   132  	// encountered by Load: all initial packages and all
   133  	// dependencies, including incomplete ones.
   134  	AllPackages map[*types.Package]*PackageInfo
   135  
   136  	// importMap is the canonical mapping of import paths to
   137  	// packages.  It contains all Imported initial packages, but not
   138  	// Created ones, and all imported dependencies.
   139  	importMap map[string]*types.Package
   140  }
   141  
   142  // PackageInfo holds the ASTs and facts derived by the type-checker
   143  // for a single package.
   144  //
   145  // Not mutated once exposed via the API.
   146  //
   147  type PackageInfo struct {
   148  	Pkg                   *types.Package
   149  	Importable            bool        // true if 'import "Pkg.Path()"' would resolve to this
   150  	TransitivelyErrorFree bool        // true if Pkg and all its dependencies are free of errors
   151  	Files                 []*ast.File // syntax trees for the package's files
   152  	Errors                []error     // non-nil if the package had errors
   153  	types.Info                        // type-checker deductions.
   154  
   155  	checker   *types.Checker // transient type-checker state
   156  	errorFunc func(error)
   157  }
   158  
   159  func (info *PackageInfo) String() string { return info.Pkg.Path() }
   160  
   161  func (info *PackageInfo) appendError(err error) {
   162  	if info.errorFunc != nil {
   163  		info.errorFunc(err)
   164  	} else {
   165  		fmt.Fprintln(os.Stderr, err)
   166  	}
   167  	info.Errors = append(info.Errors, err)
   168  }
   169  
   170  func (conf *Config) fset() *token.FileSet {
   171  	if conf.Fset == nil {
   172  		conf.Fset = token.NewFileSet()
   173  	}
   174  	return conf.Fset
   175  }
   176  
   177  // ParseFile is a convenience function (intended for testing) that invokes
   178  // the parser using the Config's FileSet, which is initialized if nil.
   179  //
   180  // src specifies the parser input as a string, []byte, or io.Reader, and
   181  // filename is its apparent name.  If src is nil, the contents of
   182  // filename are read from the file system.
   183  //
   184  func (conf *Config) ParseFile(filename string, src interface{}) (*ast.File, error) {
   185  	// TODO(adonovan): use conf.build() etc like parseFiles does.
   186  	return parser.ParseFile(conf.fset(), filename, src, conf.ParserMode)
   187  }
   188  
   189  // FromArgsUsage is a partial usage message that applications calling
   190  // FromArgs may wish to include in their -help output.
   191  const FromArgsUsage = `
   192  <args> is a list of arguments denoting a set of initial packages.
   193  It may take one of two forms:
   194  
   195  1. A list of *.go source files.
   196  
   197     All of the specified files are loaded, parsed and type-checked
   198     as a single package.  All the files must belong to the same directory.
   199  
   200  2. A list of import paths, each denoting a package.
   201  
   202     The package's directory is found relative to the $GOROOT and
   203     $GOPATH using similar logic to 'go build', and the *.go files in
   204     that directory are loaded, parsed and type-checked as a single
   205     package.
   206  
   207     In addition, all *_test.go files in the directory are then loaded
   208     and parsed.  Those files whose package declaration equals that of
   209     the non-*_test.go files are included in the primary package.  Test
   210     files whose package declaration ends with "_test" are type-checked
   211     as another package, the 'external' test package, so that a single
   212     import path may denote two packages.  (Whether this behaviour is
   213     enabled is tool-specific, and may depend on additional flags.)
   214  
   215  A '--' argument terminates the list of packages.
   216  `
   217  
   218  // FromArgs interprets args as a set of initial packages to load from
   219  // source and updates the configuration.  It returns the list of
   220  // unconsumed arguments.
   221  //
   222  // It is intended for use in command-line interfaces that require a
   223  // set of initial packages to be specified; see FromArgsUsage message
   224  // for details.
   225  //
   226  // Only superficial errors are reported at this stage; errors dependent
   227  // on I/O are detected during Load.
   228  //
   229  func (conf *Config) FromArgs(args []string, xtest bool) ([]string, error) {
   230  	var rest []string
   231  	for i, arg := range args {
   232  		if arg == "--" {
   233  			rest = args[i+1:]
   234  			args = args[:i]
   235  			break // consume "--" and return the remaining args
   236  		}
   237  	}
   238  
   239  	if len(args) > 0 && strings.HasSuffix(args[0], ".go") {
   240  		// Assume args is a list of a *.go files
   241  		// denoting a single ad hoc package.
   242  		for _, arg := range args {
   243  			if !strings.HasSuffix(arg, ".go") {
   244  				return nil, fmt.Errorf("named files must be .go files: %s", arg)
   245  			}
   246  		}
   247  		conf.CreateFromFilenames("", args...)
   248  	} else {
   249  		// Assume args are directories each denoting a
   250  		// package and (perhaps) an external test, iff xtest.
   251  		for _, arg := range args {
   252  			if xtest {
   253  				conf.ImportWithTests(arg)
   254  			} else {
   255  				conf.Import(arg)
   256  			}
   257  		}
   258  	}
   259  
   260  	return rest, nil
   261  }
   262  
   263  // CreateFromFilenames is a convenience function that adds
   264  // a conf.CreatePkgs entry to create a package of the specified *.go
   265  // files.
   266  //
   267  func (conf *Config) CreateFromFilenames(path string, filenames ...string) {
   268  	conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Filenames: filenames})
   269  }
   270  
   271  // CreateFromFiles is a convenience function that adds a conf.CreatePkgs
   272  // entry to create package of the specified path and parsed files.
   273  //
   274  func (conf *Config) CreateFromFiles(path string, files ...*ast.File) {
   275  	conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Files: files})
   276  }
   277  
   278  // ImportWithTests is a convenience function that adds path to
   279  // ImportPkgs, the set of initial source packages located relative to
   280  // $GOPATH.  The package will be augmented by any *_test.go files in
   281  // its directory that contain a "package x" (not "package x_test")
   282  // declaration.
   283  //
   284  // In addition, if any *_test.go files contain a "package x_test"
   285  // declaration, an additional package comprising just those files will
   286  // be added to CreatePkgs.
   287  //
   288  func (conf *Config) ImportWithTests(path string) { conf.addImport(path, true) }
   289  
   290  // Import is a convenience function that adds path to ImportPkgs, the
   291  // set of initial packages that will be imported from source.
   292  //
   293  func (conf *Config) Import(path string) { conf.addImport(path, false) }
   294  
   295  func (conf *Config) addImport(path string, tests bool) {
   296  	if path == "C" || path == "unsafe" {
   297  		return // ignore; not a real package
   298  	}
   299  	if conf.ImportPkgs == nil {
   300  		conf.ImportPkgs = make(map[string]bool)
   301  	}
   302  	conf.ImportPkgs[path] = conf.ImportPkgs[path] || tests
   303  }
   304  
   305  // InitialPackages returns a new slice containing the set of initial
   306  // packages (Created + Imported) in unspecified order.
   307  //
   308  func (prog *Program) InitialPackages() []*PackageInfo {
   309  	infos := make([]*PackageInfo, 0, len(prog.Created)+len(prog.Imported))
   310  	infos = append(infos, prog.Created...)
   311  	for _, info := range prog.Imported {
   312  		infos = append(infos, info)
   313  	}
   314  	return infos
   315  }
   316  
   317  // Package returns the ASTs and results of type checking for the
   318  // specified package.
   319  func (prog *Program) Package(path string) *PackageInfo {
   320  	if info, ok := prog.AllPackages[prog.importMap[path]]; ok {
   321  		return info
   322  	}
   323  	for _, info := range prog.Created {
   324  		if path == info.Pkg.Path() {
   325  			return info
   326  		}
   327  	}
   328  	return nil
   329  }
   330  
   331  // ---------- Implementation ----------
   332  
   333  // importer holds the working state of the algorithm.
   334  type importer struct {
   335  	conf  *Config   // the client configuration
   336  	start time.Time // for logging
   337  
   338  	progMu sync.Mutex // guards prog
   339  	prog   *Program   // the resulting program
   340  
   341  	importedMu sync.Mutex             // guards imported
   342  	imported   map[string]*importInfo // all imported packages (incl. failures) by import path
   343  
   344  	// import dependency graph: graph[x][y] => x imports y
   345  	//
   346  	// Since non-importable packages cannot be cyclic, we ignore
   347  	// their imports, thus we only need the subgraph over importable
   348  	// packages.  Nodes are identified by their import paths.
   349  	graphMu sync.Mutex
   350  	graph   map[string]map[string]bool
   351  }
   352  
   353  // importInfo tracks the success or failure of a single import.
   354  //
   355  // Upon completion, exactly one of info and err is non-nil:
   356  // info on successful creation of a package, err otherwise.
   357  // A successful package may still contain type errors.
   358  //
   359  type importInfo struct {
   360  	path     string       // import path
   361  	mu       sync.Mutex   // guards the following fields prior to completion
   362  	info     *PackageInfo // results of typechecking (including errors)
   363  	err      error        // reason for failure to create a package
   364  	complete sync.Cond    // complete condition is that one of info, err is non-nil.
   365  }
   366  
   367  // awaitCompletion blocks until ii is complete,
   368  // i.e. the info and err fields are safe to inspect without a lock.
   369  // It is concurrency-safe and idempotent.
   370  func (ii *importInfo) awaitCompletion() {
   371  	ii.mu.Lock()
   372  	for ii.info == nil && ii.err == nil {
   373  		ii.complete.Wait()
   374  	}
   375  	ii.mu.Unlock()
   376  }
   377  
   378  // Complete marks ii as complete.
   379  // Its info and err fields will not be subsequently updated.
   380  func (ii *importInfo) Complete(info *PackageInfo, err error) {
   381  	ii.mu.Lock()
   382  	ii.info = info
   383  	ii.err = err
   384  	ii.complete.Broadcast()
   385  	ii.mu.Unlock()
   386  }
   387  
   388  // Load creates the initial packages specified by conf.{Create,Import}Pkgs,
   389  // loading their dependencies packages as needed.
   390  //
   391  // On success, Load returns a Program containing a PackageInfo for
   392  // each package.  On failure, it returns an error.
   393  //
   394  // If AllowErrors is true, Load will return a Program even if some
   395  // packages contained I/O, parser or type errors, or if dependencies
   396  // were missing.  (Such errors are accessible via PackageInfo.Errors.  If
   397  // false, Load will fail if any package had an error.
   398  //
   399  // It is an error if no packages were loaded.
   400  //
   401  func (conf *Config) Load() (*Program, error) {
   402  	// Create a simple default error handler for parse/type errors.
   403  	if conf.TypeChecker.Error == nil {
   404  		conf.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) }
   405  	}
   406  
   407  	// Set default working directory for relative package references.
   408  	if conf.Cwd == "" {
   409  		var err error
   410  		conf.Cwd, err = os.Getwd()
   411  		if err != nil {
   412  			return nil, err
   413  		}
   414  	}
   415  
   416  	// Install default FindPackage hook using go/build logic.
   417  	if conf.FindPackage == nil {
   418  		conf.FindPackage = func(ctxt *build.Context, path string) (*build.Package, error) {
   419  			// TODO(adonovan): cache calls to build.Import
   420  			// so we don't do it three times per test package.
   421  			bp, err := ctxt.Import(path, conf.Cwd, 0)
   422  			if _, ok := err.(*build.NoGoError); ok {
   423  				return bp, nil // empty directory is not an error
   424  			}
   425  			return bp, err
   426  		}
   427  	}
   428  
   429  	prog := &Program{
   430  		Fset:        conf.fset(),
   431  		Imported:    make(map[string]*PackageInfo),
   432  		importMap:   make(map[string]*types.Package),
   433  		AllPackages: make(map[*types.Package]*PackageInfo),
   434  	}
   435  
   436  	imp := importer{
   437  		conf:     conf,
   438  		prog:     prog,
   439  		imported: make(map[string]*importInfo),
   440  		start:    time.Now(),
   441  		graph:    make(map[string]map[string]bool),
   442  	}
   443  
   444  	// -- loading proper (concurrent phase) --------------------------------
   445  
   446  	var errpkgs []string // packages that contained errors
   447  
   448  	// Load the initially imported packages and their dependencies,
   449  	// in parallel.
   450  	for _, ii := range imp.loadAll("", conf.ImportPkgs) {
   451  		if ii.err != nil {
   452  			conf.TypeChecker.Error(ii.err) // failed to create package
   453  			errpkgs = append(errpkgs, ii.path)
   454  			continue
   455  		}
   456  		prog.Imported[ii.info.Pkg.Path()] = ii.info
   457  	}
   458  
   459  	// Augment the designated initial packages by their tests.
   460  	// Dependencies are loaded in parallel.
   461  	var xtestPkgs []*build.Package
   462  	for path, augment := range conf.ImportPkgs {
   463  		if !augment {
   464  			continue
   465  		}
   466  
   467  		bp, err := conf.FindPackage(conf.build(), path)
   468  		if err != nil {
   469  			// Package not found, or can't even parse package declaration.
   470  			// Already reported by previous loop; ignore it.
   471  			continue
   472  		}
   473  
   474  		// Needs external test package?
   475  		if len(bp.XTestGoFiles) > 0 {
   476  			xtestPkgs = append(xtestPkgs, bp)
   477  		}
   478  
   479  		imp.importedMu.Lock()           // (unnecessary, we're sequential here)
   480  		info := imp.imported[path].info // must be non-nil, see above
   481  		imp.importedMu.Unlock()
   482  
   483  		// Parse the in-package test files.
   484  		files, errs := imp.conf.parsePackageFiles(bp, 't')
   485  		for _, err := range errs {
   486  			info.appendError(err)
   487  		}
   488  
   489  		// The test files augmenting package P cannot be imported,
   490  		// but may import packages that import P,
   491  		// so we must disable the cycle check.
   492  		imp.addFiles(info, files, false)
   493  	}
   494  
   495  	createPkg := func(path string, files []*ast.File, errs []error) {
   496  		info := imp.newPackageInfo(path)
   497  		for _, err := range errs {
   498  			info.appendError(err)
   499  		}
   500  
   501  		// Ad hoc packages are non-importable,
   502  		// so no cycle check is needed.
   503  		// addFiles loads dependencies in parallel.
   504  		imp.addFiles(info, files, false)
   505  		prog.Created = append(prog.Created, info)
   506  	}
   507  
   508  	// Create packages specified by conf.CreatePkgs.
   509  	for _, cp := range conf.CreatePkgs {
   510  		files, errs := parseFiles(conf.fset(), conf.build(), nil, ".", cp.Filenames, conf.ParserMode)
   511  		files = append(files, cp.Files...)
   512  
   513  		path := cp.Path
   514  		if path == "" {
   515  			if len(files) > 0 {
   516  				path = files[0].Name.Name
   517  			} else {
   518  				path = "(unnamed)"
   519  			}
   520  		}
   521  		createPkg(path, files, errs)
   522  	}
   523  
   524  	// Create external test packages.
   525  	sort.Sort(byImportPath(xtestPkgs))
   526  	for _, bp := range xtestPkgs {
   527  		files, errs := imp.conf.parsePackageFiles(bp, 'x')
   528  		createPkg(bp.ImportPath+"_test", files, errs)
   529  	}
   530  
   531  	// -- finishing up (sequential) ----------------------------------------
   532  
   533  	if len(prog.Imported)+len(prog.Created) == 0 {
   534  		return nil, errors.New("no initial packages were loaded")
   535  	}
   536  
   537  	// Create infos for indirectly imported packages.
   538  	// e.g. incomplete packages without syntax, loaded from export data.
   539  	for _, obj := range prog.importMap {
   540  		info := prog.AllPackages[obj]
   541  		if info == nil {
   542  			prog.AllPackages[obj] = &PackageInfo{Pkg: obj, Importable: true}
   543  		} else {
   544  			// finished
   545  			info.checker = nil
   546  			info.errorFunc = nil
   547  		}
   548  	}
   549  
   550  	if !conf.AllowErrors {
   551  		// Report errors in indirectly imported packages.
   552  		for _, info := range prog.AllPackages {
   553  			if len(info.Errors) > 0 {
   554  				errpkgs = append(errpkgs, info.Pkg.Path())
   555  			}
   556  		}
   557  		if errpkgs != nil {
   558  			var more string
   559  			if len(errpkgs) > 3 {
   560  				more = fmt.Sprintf(" and %d more", len(errpkgs)-3)
   561  				errpkgs = errpkgs[:3]
   562  			}
   563  			return nil, fmt.Errorf("couldn't load packages due to errors: %s%s",
   564  				strings.Join(errpkgs, ", "), more)
   565  		}
   566  	}
   567  
   568  	markErrorFreePackages(prog.AllPackages)
   569  
   570  	return prog, nil
   571  }
   572  
   573  type byImportPath []*build.Package
   574  
   575  func (b byImportPath) Len() int           { return len(b) }
   576  func (b byImportPath) Less(i, j int) bool { return b[i].ImportPath < b[j].ImportPath }
   577  func (b byImportPath) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
   578  
   579  // markErrorFreePackages sets the TransitivelyErrorFree flag on all
   580  // applicable packages.
   581  func markErrorFreePackages(allPackages map[*types.Package]*PackageInfo) {
   582  	// Build the transpose of the import graph.
   583  	importedBy := make(map[*types.Package]map[*types.Package]bool)
   584  	for P := range allPackages {
   585  		for _, Q := range P.Imports() {
   586  			clients, ok := importedBy[Q]
   587  			if !ok {
   588  				clients = make(map[*types.Package]bool)
   589  				importedBy[Q] = clients
   590  			}
   591  			clients[P] = true
   592  		}
   593  	}
   594  
   595  	// Find all packages reachable from some error package.
   596  	reachable := make(map[*types.Package]bool)
   597  	var visit func(*types.Package)
   598  	visit = func(p *types.Package) {
   599  		if !reachable[p] {
   600  			reachable[p] = true
   601  			for q := range importedBy[p] {
   602  				visit(q)
   603  			}
   604  		}
   605  	}
   606  	for _, info := range allPackages {
   607  		if len(info.Errors) > 0 {
   608  			visit(info.Pkg)
   609  		}
   610  	}
   611  
   612  	// Mark the others as "transitively error-free".
   613  	for _, info := range allPackages {
   614  		if !reachable[info.Pkg] {
   615  			info.TransitivelyErrorFree = true
   616  		}
   617  	}
   618  }
   619  
   620  // build returns the effective build context.
   621  func (conf *Config) build() *build.Context {
   622  	if conf.Build != nil {
   623  		return conf.Build
   624  	}
   625  	return &build.Default
   626  }
   627  
   628  // parsePackageFiles enumerates the files belonging to package path,
   629  // then loads, parses and returns them, plus a list of I/O or parse
   630  // errors that were encountered.
   631  //
   632  // 'which' indicates which files to include:
   633  //    'g': include non-test *.go source files (GoFiles + processed CgoFiles)
   634  //    't': include in-package *_test.go source files (TestGoFiles)
   635  //    'x': include external *_test.go source files. (XTestGoFiles)
   636  //
   637  func (conf *Config) parsePackageFiles(bp *build.Package, which rune) ([]*ast.File, []error) {
   638  	var filenames []string
   639  	switch which {
   640  	case 'g':
   641  		filenames = bp.GoFiles
   642  	case 't':
   643  		filenames = bp.TestGoFiles
   644  	case 'x':
   645  		filenames = bp.XTestGoFiles
   646  	default:
   647  		panic(which)
   648  	}
   649  
   650  	files, errs := parseFiles(conf.fset(), conf.build(), conf.DisplayPath, bp.Dir, filenames, conf.ParserMode)
   651  
   652  	// Preprocess CgoFiles and parse the outputs (sequentially).
   653  	if which == 'g' && bp.CgoFiles != nil {
   654  		cgofiles, err := processCgoFiles(bp, conf.fset(), conf.DisplayPath, conf.ParserMode)
   655  		if err != nil {
   656  			errs = append(errs, err)
   657  		} else {
   658  			files = append(files, cgofiles...)
   659  		}
   660  	}
   661  
   662  	return files, errs
   663  }
   664  
   665  // loadAll loads, parses, and type-checks the specified packages in
   666  // parallel and returns their completed importInfos in unspecified order.
   667  //
   668  // fromPath is the import path of the importing package, if it is
   669  // importable, "" otherwise.  It is used for cycle detection.
   670  //
   671  func (imp *importer) loadAll(fromPath string, paths map[string]bool) []*importInfo {
   672  	result := make([]*importInfo, 0, len(paths))
   673  	for path := range paths {
   674  		result = append(result, imp.startLoad(path))
   675  	}
   676  
   677  	if fromPath != "" {
   678  		// We're loading a set of imports.
   679  		//
   680  		// We must record graph edges from the importing package
   681  		// to its dependencies, and check for cycles.
   682  		imp.graphMu.Lock()
   683  		deps, ok := imp.graph[fromPath]
   684  		if !ok {
   685  			deps = make(map[string]bool)
   686  			imp.graph[fromPath] = deps
   687  		}
   688  		for path := range paths {
   689  			deps[path] = true
   690  		}
   691  		imp.graphMu.Unlock()
   692  	}
   693  
   694  	for _, ii := range result {
   695  		if fromPath != "" {
   696  			if cycle := imp.findPath(ii.path, fromPath); cycle != nil {
   697  				// Cycle-forming import: we must not await its
   698  				// completion since it would deadlock.
   699  				//
   700  				// We don't record the error in ii since
   701  				// the error is really associated with the
   702  				// cycle-forming edge, not the package itself.
   703  				// (Also it would complicate the
   704  				// invariants of importPath completion.)
   705  				if trace {
   706  					fmt.Fprintln(os.Stderr, "import cycle: %q", cycle)
   707  				}
   708  				continue
   709  			}
   710  		}
   711  		ii.awaitCompletion()
   712  
   713  	}
   714  	return result
   715  }
   716  
   717  // findPath returns an arbitrary path from 'from' to 'to' in the import
   718  // graph, or nil if there was none.
   719  func (imp *importer) findPath(from, to string) []string {
   720  	imp.graphMu.Lock()
   721  	defer imp.graphMu.Unlock()
   722  
   723  	seen := make(map[string]bool)
   724  	var search func(stack []string, importPath string) []string
   725  	search = func(stack []string, importPath string) []string {
   726  		if !seen[importPath] {
   727  			seen[importPath] = true
   728  			stack = append(stack, importPath)
   729  			if importPath == to {
   730  				return stack
   731  			}
   732  			for x := range imp.graph[importPath] {
   733  				if p := search(stack, x); p != nil {
   734  					return p
   735  				}
   736  			}
   737  		}
   738  		return nil
   739  	}
   740  	return search(make([]string, 0, 20), from)
   741  }
   742  
   743  // startLoad initiates the loading, parsing and type-checking of the
   744  // specified package and its dependencies, if it has not already begun.
   745  //
   746  // It returns an importInfo, not necessarily in a completed state.  The
   747  // caller must call awaitCompletion() before accessing its info and err
   748  // fields.
   749  //
   750  // startLoad is concurrency-safe and idempotent.
   751  //
   752  // Precondition: path != "unsafe".
   753  //
   754  func (imp *importer) startLoad(path string) *importInfo {
   755  	imp.importedMu.Lock()
   756  	ii, ok := imp.imported[path]
   757  	if !ok {
   758  		ii = &importInfo{path: path}
   759  		ii.complete.L = &ii.mu
   760  		imp.imported[path] = ii
   761  		go func() {
   762  			ii.Complete(imp.load(path))
   763  		}()
   764  	}
   765  	imp.importedMu.Unlock()
   766  
   767  	return ii
   768  }
   769  
   770  // load implements package loading by parsing Go source files
   771  // located by go/build.
   772  //
   773  func (imp *importer) load(path string) (*PackageInfo, error) {
   774  	bp, err := imp.conf.FindPackage(imp.conf.build(), path)
   775  	if err != nil {
   776  		return nil, err // package not found
   777  	}
   778  	info := imp.newPackageInfo(bp.ImportPath)
   779  	info.Importable = true
   780  	files, errs := imp.conf.parsePackageFiles(bp, 'g')
   781  	for _, err := range errs {
   782  		info.appendError(err)
   783  	}
   784  
   785  	imp.addFiles(info, files, true)
   786  
   787  	imp.progMu.Lock()
   788  	imp.prog.importMap[path] = info.Pkg
   789  	imp.progMu.Unlock()
   790  
   791  	return info, nil
   792  }
   793  
   794  // addFiles adds and type-checks the specified files to info, loading
   795  // their dependencies if needed.  The order of files determines the
   796  // package initialization order.  It may be called multiple times on the
   797  // same package.  Errors are appended to the info.Errors field.
   798  //
   799  // cycleCheck determines whether the imports within files create
   800  // dependency edges that should be checked for potential cycles.
   801  //
   802  func (imp *importer) addFiles(info *PackageInfo, files []*ast.File, cycleCheck bool) {
   803  	info.Files = append(info.Files, files...)
   804  
   805  	// Ensure the dependencies are loaded, in parallel.
   806  	var fromPath string
   807  	if cycleCheck {
   808  		fromPath = info.Pkg.Path()
   809  	}
   810  	imp.loadAll(fromPath, scanImports(files))
   811  
   812  	if trace {
   813  		fmt.Fprintf(os.Stderr, "%s: start %q (%d)\n",
   814  			time.Since(imp.start), info.Pkg.Path(), len(files))
   815  	}
   816  
   817  	// Ignore the returned (first) error since we
   818  	// already collect them all in the PackageInfo.
   819  	info.checker.Files(files)
   820  
   821  	if trace {
   822  		fmt.Fprintf(os.Stderr, "%s: stop %q\n",
   823  			time.Since(imp.start), info.Pkg.Path())
   824  	}
   825  }
   826  
   827  func (imp *importer) newPackageInfo(path string) *PackageInfo {
   828  	pkg := types.NewPackage(path, "")
   829  	info := &PackageInfo{
   830  		Pkg: pkg,
   831  		Info: types.Info{
   832  			Types:      make(map[ast.Expr]types.TypeAndValue),
   833  			Defs:       make(map[*ast.Ident]types.Object),
   834  			Uses:       make(map[*ast.Ident]types.Object),
   835  			Implicits:  make(map[ast.Node]types.Object),
   836  			Scopes:     make(map[ast.Node]*types.Scope),
   837  			Selections: make(map[*ast.SelectorExpr]*types.Selection),
   838  		},
   839  		errorFunc: imp.conf.TypeChecker.Error,
   840  	}
   841  
   842  	// Copy the types.Config so we can vary it across PackageInfos.
   843  	tc := imp.conf.TypeChecker
   844  	tc.IgnoreFuncBodies = false
   845  	if f := imp.conf.TypeCheckFuncBodies; f != nil {
   846  		tc.IgnoreFuncBodies = !f(path)
   847  	}
   848  	tc.Importer = goimporter.Default()
   849  	tc.Error = info.appendError // appendError wraps the user's Error function
   850  
   851  	info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info)
   852  	imp.progMu.Lock()
   853  	imp.prog.AllPackages[pkg] = info
   854  	imp.progMu.Unlock()
   855  	return info
   856  }