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
5package loader
6
7// See doc.go for package documentation and implementation notes.
8
9import (
10	"errors"
11	"fmt"
12	"go/ast"
13	"go/build"
14	"go/parser"
15	"go/token"
16	"go/types"
17	"os"
18	"path/filepath"
19	"sort"
20	"strings"
21	"sync"
22	"time"
23
24	"golang.org/x/tools/go/ast/astutil"
25	"golang.org/x/tools/go/internal/cgo"
26)
27
28var ignoreVendor build.ImportMode
29
30const trace = false // show timing info for type-checking
31
32// Config specifies the configuration for loading a whole program from
33// Go source code.
34// The zero value for Config is a ready-to-use default configuration.
35type Config struct {
36	// Fset is the file set for the parser to use when loading the
37	// program.  If nil, it may be lazily initialized by any
38	// method of Config.
39	Fset *token.FileSet
40
41	// ParserMode specifies the mode to be used by the parser when
42	// loading source packages.
43	ParserMode parser.Mode
44
45	// TypeChecker contains options relating to the type checker.
46	//
47	// The supplied IgnoreFuncBodies is not used; the effective
48	// value comes from the TypeCheckFuncBodies func below.
49	// The supplied Import function is not used either.
50	TypeChecker types.Config
51
52	// TypeCheckFuncBodies is a predicate over package paths.
53	// A package for which the predicate is false will
54	// have its package-level declarations type checked, but not
55	// its function bodies; this can be used to quickly load
56	// dependencies from source.  If nil, all func bodies are type
57	// checked.
58	TypeCheckFuncBodies func(path string) bool
59
60	// If Build is non-nil, it is used to locate source packages.
61	// Otherwise &build.Default is used.
62	//
63	// By default, cgo is invoked to preprocess Go files that
64	// import the fake package "C".  This behaviour can be
65	// disabled by setting CGO_ENABLED=0 in the environment prior
66	// to startup, or by setting Build.CgoEnabled=false.
67	Build *build.Context
68
69	// The current directory, used for resolving relative package
70	// references such as "./go/loader".  If empty, os.Getwd will be
71	// used instead.
72	Cwd string
73
74	// If DisplayPath is non-nil, it is used to transform each
75	// file name obtained from Build.Import().  This can be used
76	// to prevent a virtualized build.Config's file names from
77	// leaking into the user interface.
78	DisplayPath func(path string) string
79
80	// If AllowErrors is true, Load will return a Program even
81	// if some of the its packages contained I/O, parser or type
82	// errors; such errors are accessible via PackageInfo.Errors.  If
83	// false, Load will fail if any package had an error.
84	AllowErrors bool
85
86	// CreatePkgs specifies a list of non-importable initial
87	// packages to create.  The resulting packages will appear in
88	// the corresponding elements of the Program.Created slice.
89	CreatePkgs []PkgSpec
90
91	// ImportPkgs specifies a set of initial packages to load.
92	// The map keys are package paths.
93	//
94	// The map value indicates whether to load tests.  If true, Load
95	// will add and type-check two lists of files to the package:
96	// non-test files followed by in-package *_test.go files.  In
97	// addition, it will append the external test package (if any)
98	// to Program.Created.
99	ImportPkgs map[string]bool
100
101	// FindPackage is called during Load to create the build.Package
102	// for a given import path from a given directory.
103	// If FindPackage is nil, (*build.Context).Import is used.
104	// A client may use this hook to adapt to a proprietary build
105	// system that does not follow the "go build" layout
106	// conventions, for example.
107	//
108	// It must be safe to call concurrently from multiple goroutines.
109	FindPackage func(ctxt *build.Context, importPath, fromDir string, mode build.ImportMode) (*build.Package, error)
110
111	// AfterTypeCheck is called immediately after a list of files
112	// has been type-checked and appended to info.Files.
113	//
114	// This optional hook function is the earliest opportunity for
115	// the client to observe the output of the type checker,
116	// which may be useful to reduce analysis latency when loading
117	// a large program.
118	//
119	// The function is permitted to modify info.Info, for instance
120	// to clear data structures that are no longer needed, which can
121	// dramatically reduce peak memory consumption.
122	//
123	// The function may be called twice for the same PackageInfo:
124	// once for the files of the package and again for the
125	// in-package test files.
126	//
127	// It must be safe to call concurrently from multiple goroutines.
128	AfterTypeCheck func(info *PackageInfo, files []*ast.File)
129}
130
131// A PkgSpec specifies a non-importable package to be created by Load.
132// Files are processed first, but typically only one of Files and
133// Filenames is provided.  The path needn't be globally unique.
134//
135// For vendoring purposes, the package's directory is the one that
136// contains the first file.
137type PkgSpec struct {
138	Path      string      // package path ("" => use package declaration)
139	Files     []*ast.File // ASTs of already-parsed files
140	Filenames []string    // names of files to be parsed
141}
142
143// A Program is a Go program loaded from source as specified by a Config.
144type Program struct {
145	Fset *token.FileSet // the file set for this program
146
147	// Created[i] contains the initial package whose ASTs or
148	// filenames were supplied by Config.CreatePkgs[i], followed by
149	// the external test package, if any, of each package in
150	// Config.ImportPkgs ordered by ImportPath.
151	//
152	// NOTE: these files must not import "C".  Cgo preprocessing is
153	// only performed on imported packages, not ad hoc packages.
154	//
155	// TODO(adonovan): we need to copy and adapt the logic of
156	// goFilesPackage (from $GOROOT/src/cmd/go/build.go) and make
157	// Config.Import and Config.Create methods return the same kind
158	// of entity, essentially a build.Package.
159	// Perhaps we can even reuse that type directly.
160	Created []*PackageInfo
161
162	// Imported contains the initially imported packages,
163	// as specified by Config.ImportPkgs.
164	Imported map[string]*PackageInfo
165
166	// AllPackages contains the PackageInfo of every package
167	// encountered by Load: all initial packages and all
168	// dependencies, including incomplete ones.
169	AllPackages map[*types.Package]*PackageInfo
170
171	// importMap is the canonical mapping of package paths to
172	// packages.  It contains all Imported initial packages, but not
173	// Created ones, and all imported dependencies.
174	importMap map[string]*types.Package
175}
176
177// PackageInfo holds the ASTs and facts derived by the type-checker
178// for a single package.
179//
180// Not mutated once exposed via the API.
181//
182type PackageInfo struct {
183	Pkg                   *types.Package
184	Importable            bool        // true if 'import "Pkg.Path()"' would resolve to this
185	TransitivelyErrorFree bool        // true if Pkg and all its dependencies are free of errors
186	Files                 []*ast.File // syntax trees for the package's files
187	Errors                []error     // non-nil if the package had errors
188	types.Info                        // type-checker deductions.
189	dir                   string      // package directory
190
191	checker   *types.Checker // transient type-checker state
192	errorFunc func(error)
193}
194
195func (info *PackageInfo) String() string { return info.Pkg.Path() }
196
197func (info *PackageInfo) appendError(err error) {
198	if info.errorFunc != nil {
199		info.errorFunc(err)
200	} else {
201		fmt.Fprintln(os.Stderr, err)
202	}
203	info.Errors = append(info.Errors, err)
204}
205
206func (conf *Config) fset() *token.FileSet {
207	if conf.Fset == nil {
208		conf.Fset = token.NewFileSet()
209	}
210	return conf.Fset
211}
212
213// ParseFile is a convenience function (intended for testing) that invokes
214// the parser using the Config's FileSet, which is initialized if nil.
215//
216// src specifies the parser input as a string, []byte, or io.Reader, and
217// filename is its apparent name.  If src is nil, the contents of
218// filename are read from the file system.
219//
220func (conf *Config) ParseFile(filename string, src interface{}) (*ast.File, error) {
221	// TODO(adonovan): use conf.build() etc like parseFiles does.
222	return parser.ParseFile(conf.fset(), filename, src, conf.ParserMode)
223}
224
225// FromArgsUsage is a partial usage message that applications calling
226// FromArgs may wish to include in their -help output.
227const FromArgsUsage = `
228<args> is a list of arguments denoting a set of initial packages.
229It may take one of two forms:
230
2311. A list of *.go source files.
232
233   All of the specified files are loaded, parsed and type-checked
234   as a single package.  All the files must belong to the same directory.
235
2362. A list of import paths, each denoting a package.
237
238   The package's directory is found relative to the $GOROOT and
239   $GOPATH using similar logic to 'go build', and the *.go files in
240   that directory are loaded, parsed and type-checked as a single
241   package.
242
243   In addition, all *_test.go files in the directory are then loaded
244   and parsed.  Those files whose package declaration equals that of
245   the non-*_test.go files are included in the primary package.  Test
246   files whose package declaration ends with "_test" are type-checked
247   as another package, the 'external' test package, so that a single
248   import path may denote two packages.  (Whether this behaviour is
249   enabled is tool-specific, and may depend on additional flags.)
250
251A '--' argument terminates the list of packages.
252`
253
254// FromArgs interprets args as a set of initial packages to load from
255// source and updates the configuration.  It returns the list of
256// unconsumed arguments.
257//
258// It is intended for use in command-line interfaces that require a
259// set of initial packages to be specified; see FromArgsUsage message
260// for details.
261//
262// Only superficial errors are reported at this stage; errors dependent
263// on I/O are detected during Load.
264//
265func (conf *Config) FromArgs(args []string, xtest bool) ([]string, error) {
266	var rest []string
267	for i, arg := range args {
268		if arg == "--" {
269			rest = args[i+1:]
270			args = args[:i]
271			break // consume "--" and return the remaining args
272		}
273	}
274
275	if len(args) > 0 && strings.HasSuffix(args[0], ".go") {
276		// Assume args is a list of a *.go files
277		// denoting a single ad hoc package.
278		for _, arg := range args {
279			if !strings.HasSuffix(arg, ".go") {
280				return nil, fmt.Errorf("named files must be .go files: %s", arg)
281			}
282		}
283		conf.CreateFromFilenames("", args...)
284	} else {
285		// Assume args are directories each denoting a
286		// package and (perhaps) an external test, iff xtest.
287		for _, arg := range args {
288			if xtest {
289				conf.ImportWithTests(arg)
290			} else {
291				conf.Import(arg)
292			}
293		}
294	}
295
296	return rest, nil
297}
298
299// CreateFromFilenames is a convenience function that adds
300// a conf.CreatePkgs entry to create a package of the specified *.go
301// files.
302//
303func (conf *Config) CreateFromFilenames(path string, filenames ...string) {
304	conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Filenames: filenames})
305}
306
307// CreateFromFiles is a convenience function that adds a conf.CreatePkgs
308// entry to create package of the specified path and parsed files.
309//
310func (conf *Config) CreateFromFiles(path string, files ...*ast.File) {
311	conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Files: files})
312}
313
314// ImportWithTests is a convenience function that adds path to
315// ImportPkgs, the set of initial source packages located relative to
316// $GOPATH.  The package will be augmented by any *_test.go files in
317// its directory that contain a "package x" (not "package x_test")
318// declaration.
319//
320// In addition, if any *_test.go files contain a "package x_test"
321// declaration, an additional package comprising just those files will
322// be added to CreatePkgs.
323//
324func (conf *Config) ImportWithTests(path string) { conf.addImport(path, true) }
325
326// Import is a convenience function that adds path to ImportPkgs, the
327// set of initial packages that will be imported from source.
328//
329func (conf *Config) Import(path string) { conf.addImport(path, false) }
330
331func (conf *Config) addImport(path string, tests bool) {
332	if path == "C" {
333		return // ignore; not a real package
334	}
335	if conf.ImportPkgs == nil {
336		conf.ImportPkgs = make(map[string]bool)
337	}
338	conf.ImportPkgs[path] = conf.ImportPkgs[path] || tests
339}
340
341// PathEnclosingInterval returns the PackageInfo and ast.Node that
342// contain source interval [start, end), and all the node's ancestors
343// up to the AST root.  It searches all ast.Files of all packages in prog.
344// exact is defined as for astutil.PathEnclosingInterval.
345//
346// The zero value is returned if not found.
347//
348func (prog *Program) PathEnclosingInterval(start, end token.Pos) (pkg *PackageInfo, path []ast.Node, exact bool) {
349	for _, info := range prog.AllPackages {
350		for _, f := range info.Files {
351			if f.Pos() == token.NoPos {
352				// This can happen if the parser saw
353				// too many errors and bailed out.
354				// (Use parser.AllErrors to prevent that.)
355				continue
356			}
357			if !tokenFileContainsPos(prog.Fset.File(f.Pos()), start) {
358				continue
359			}
360			if path, exact := astutil.PathEnclosingInterval(f, start, end); path != nil {
361				return info, path, exact
362			}
363		}
364	}
365	return nil, nil, false
366}
367
368// InitialPackages returns a new slice containing the set of initial
369// packages (Created + Imported) in unspecified order.
370//
371func (prog *Program) InitialPackages() []*PackageInfo {
372	infos := make([]*PackageInfo, 0, len(prog.Created)+len(prog.Imported))
373	infos = append(infos, prog.Created...)
374	for _, info := range prog.Imported {
375		infos = append(infos, info)
376	}
377	return infos
378}
379
380// Package returns the ASTs and results of type checking for the
381// specified package.
382func (prog *Program) Package(path string) *PackageInfo {
383	if info, ok := prog.AllPackages[prog.importMap[path]]; ok {
384		return info
385	}
386	for _, info := range prog.Created {
387		if path == info.Pkg.Path() {
388			return info
389		}
390	}
391	return nil
392}
393
394// ---------- Implementation ----------
395
396// importer holds the working state of the algorithm.
397type importer struct {
398	conf  *Config   // the client configuration
399	start time.Time // for logging
400
401	progMu sync.Mutex // guards prog
402	prog   *Program   // the resulting program
403
404	// findpkg is a memoization of FindPackage.
405	findpkgMu sync.Mutex // guards findpkg
406	findpkg   map[findpkgKey]*findpkgValue
407
408	importedMu sync.Mutex             // guards imported
409	imported   map[string]*importInfo // all imported packages (incl. failures) by import path
410
411	// import dependency graph: graph[x][y] => x imports y
412	//
413	// Since non-importable packages cannot be cyclic, we ignore
414	// their imports, thus we only need the subgraph over importable
415	// packages.  Nodes are identified by their import paths.
416	graphMu sync.Mutex
417	graph   map[string]map[string]bool
418}
419
420type findpkgKey struct {
421	importPath string
422	fromDir    string
423	mode       build.ImportMode
424}
425
426type findpkgValue struct {
427	ready chan struct{} // closed to broadcast readiness
428	bp    *build.Package
429	err   error
430}
431
432// importInfo tracks the success or failure of a single import.
433//
434// Upon completion, exactly one of info and err is non-nil:
435// info on successful creation of a package, err otherwise.
436// A successful package may still contain type errors.
437//
438type importInfo struct {
439	path     string        // import path
440	info     *PackageInfo  // results of typechecking (including errors)
441	complete chan struct{} // closed to broadcast that info is set.
442}
443
444// awaitCompletion blocks until ii is complete,
445// i.e. the info field is safe to inspect.
446func (ii *importInfo) awaitCompletion() {
447	<-ii.complete // wait for close
448}
449
450// Complete marks ii as complete.
451// Its info and err fields will not be subsequently updated.
452func (ii *importInfo) Complete(info *PackageInfo) {
453	if info == nil {
454		panic("info == nil")
455	}
456	ii.info = info
457	close(ii.complete)
458}
459
460type importError struct {
461	path string // import path
462	err  error  // reason for failure to create a package
463}
464
465// Load creates the initial packages specified by conf.{Create,Import}Pkgs,
466// loading their dependencies packages as needed.
467//
468// On success, Load returns a Program containing a PackageInfo for
469// each package.  On failure, it returns an error.
470//
471// If AllowErrors is true, Load will return a Program even if some
472// packages contained I/O, parser or type errors, or if dependencies
473// were missing.  (Such errors are accessible via PackageInfo.Errors.  If
474// false, Load will fail if any package had an error.
475//
476// It is an error if no packages were loaded.
477//
478func (conf *Config) Load() (*Program, error) {
479	// Create a simple default error handler for parse/type errors.
480	if conf.TypeChecker.Error == nil {
481		conf.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) }
482	}
483
484	// Set default working directory for relative package references.
485	if conf.Cwd == "" {
486		var err error
487		conf.Cwd, err = os.Getwd()
488		if err != nil {
489			return nil, err
490		}
491	}
492
493	// Install default FindPackage hook using go/build logic.
494	if conf.FindPackage == nil {
495		conf.FindPackage = (*build.Context).Import
496	}
497
498	prog := &Program{
499		Fset:        conf.fset(),
500		Imported:    make(map[string]*PackageInfo),
501		importMap:   make(map[string]*types.Package),
502		AllPackages: make(map[*types.Package]*PackageInfo),
503	}
504
505	imp := importer{
506		conf:     conf,
507		prog:     prog,
508		findpkg:  make(map[findpkgKey]*findpkgValue),
509		imported: make(map[string]*importInfo),
510		start:    time.Now(),
511		graph:    make(map[string]map[string]bool),
512	}
513
514	// -- loading proper (concurrent phase) --------------------------------
515
516	var errpkgs []string // packages that contained errors
517
518	// Load the initially imported packages and their dependencies,
519	// in parallel.
520	// No vendor check on packages imported from the command line.
521	infos, importErrors := imp.importAll("", conf.Cwd, conf.ImportPkgs, ignoreVendor)
522	for _, ie := range importErrors {
523		conf.TypeChecker.Error(ie.err) // failed to create package
524		errpkgs = append(errpkgs, ie.path)
525	}
526	for _, info := range infos {
527		prog.Imported[info.Pkg.Path()] = info
528	}
529
530	// Augment the designated initial packages by their tests.
531	// Dependencies are loaded in parallel.
532	var xtestPkgs []*build.Package
533	for importPath, augment := range conf.ImportPkgs {
534		if !augment {
535			continue
536		}
537
538		// No vendor check on packages imported from command line.
539		bp, err := imp.findPackage(importPath, conf.Cwd, ignoreVendor)
540		if err != nil {
541			// Package not found, or can't even parse package declaration.
542			// Already reported by previous loop; ignore it.
543			continue
544		}
545
546		// Needs external test package?
547		if len(bp.XTestGoFiles) > 0 {
548			xtestPkgs = append(xtestPkgs, bp)
549		}
550
551		// Consult the cache using the canonical package path.
552		path := bp.ImportPath
553		imp.importedMu.Lock() // (unnecessary, we're sequential here)
554		ii, ok := imp.imported[path]
555		// Paranoid checks added due to issue #11012.
556		if !ok {
557			// Unreachable.
558			// The previous loop called importAll and thus
559			// startLoad for each path in ImportPkgs, which
560			// populates imp.imported[path] with a non-zero value.
561			panic(fmt.Sprintf("imported[%q] not found", path))
562		}
563		if ii == nil {
564			// Unreachable.
565			// The ii values in this loop are the same as in
566			// the previous loop, which enforced the invariant
567			// that at least one of ii.err and ii.info is non-nil.
568			panic(fmt.Sprintf("imported[%q] == nil", path))
569		}
570		if ii.info == nil {
571			// Unreachable.
572			// awaitCompletion has the postcondition
573			// ii.info != nil.
574			panic(fmt.Sprintf("imported[%q].info = nil", path))
575		}
576		info := ii.info
577		imp.importedMu.Unlock()
578
579		// Parse the in-package test files.
580		files, errs := imp.conf.parsePackageFiles(bp, 't')
581		for _, err := range errs {
582			info.appendError(err)
583		}
584
585		// The test files augmenting package P cannot be imported,
586		// but may import packages that import P,
587		// so we must disable the cycle check.
588		imp.addFiles(info, files, false)
589	}
590
591	createPkg := func(path, dir string, files []*ast.File, errs []error) {
592		info := imp.newPackageInfo(path, dir)
593		for _, err := range errs {
594			info.appendError(err)
595		}
596
597		// Ad hoc packages are non-importable,
598		// so no cycle check is needed.
599		// addFiles loads dependencies in parallel.
600		imp.addFiles(info, files, false)
601		prog.Created = append(prog.Created, info)
602	}
603
604	// Create packages specified by conf.CreatePkgs.
605	for _, cp := range conf.CreatePkgs {
606		files, errs := parseFiles(conf.fset(), conf.build(), nil, conf.Cwd, cp.Filenames, conf.ParserMode)
607		files = append(files, cp.Files...)
608
609		path := cp.Path
610		if path == "" {
611			if len(files) > 0 {
612				path = files[0].Name.Name
613			} else {
614				path = "(unnamed)"
615			}
616		}
617
618		dir := conf.Cwd
619		if len(files) > 0 && files[0].Pos().IsValid() {
620			dir = filepath.Dir(conf.fset().File(files[0].Pos()).Name())
621		}
622		createPkg(path, dir, files, errs)
623	}
624
625	// Create external test packages.
626	sort.Sort(byImportPath(xtestPkgs))
627	for _, bp := range xtestPkgs {
628		files, errs := imp.conf.parsePackageFiles(bp, 'x')
629		createPkg(bp.ImportPath+"_test", bp.Dir, files, errs)
630	}
631
632	// -- finishing up (sequential) ----------------------------------------
633
634	if len(prog.Imported)+len(prog.Created) == 0 {
635		return nil, errors.New("no initial packages were loaded")
636	}
637
638	// Create infos for indirectly imported packages.
639	// e.g. incomplete packages without syntax, loaded from export data.
640	for _, obj := range prog.importMap {
641		info := prog.AllPackages[obj]
642		if info == nil {
643			prog.AllPackages[obj] = &PackageInfo{Pkg: obj, Importable: true}
644		} else {
645			// finished
646			info.checker = nil
647			info.errorFunc = nil
648		}
649	}
650
651	if !conf.AllowErrors {
652		// Report errors in indirectly imported packages.
653		for _, info := range prog.AllPackages {
654			if len(info.Errors) > 0 {
655				errpkgs = append(errpkgs, info.Pkg.Path())
656			}
657		}
658		if errpkgs != nil {
659			var more string
660			if len(errpkgs) > 3 {
661				more = fmt.Sprintf(" and %d more", len(errpkgs)-3)
662				errpkgs = errpkgs[:3]
663			}
664			return nil, fmt.Errorf("couldn't load packages due to errors: %s%s",
665				strings.Join(errpkgs, ", "), more)
666		}
667	}
668
669	markErrorFreePackages(prog.AllPackages)
670
671	return prog, nil
672}
673
674type byImportPath []*build.Package
675
676func (b byImportPath) Len() int           { return len(b) }
677func (b byImportPath) Less(i, j int) bool { return b[i].ImportPath < b[j].ImportPath }
678func (b byImportPath) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
679
680// markErrorFreePackages sets the TransitivelyErrorFree flag on all
681// applicable packages.
682func markErrorFreePackages(allPackages map[*types.Package]*PackageInfo) {
683	// Build the transpose of the import graph.
684	importedBy := make(map[*types.Package]map[*types.Package]bool)
685	for P := range allPackages {
686		for _, Q := range P.Imports() {
687			clients, ok := importedBy[Q]
688			if !ok {
689				clients = make(map[*types.Package]bool)
690				importedBy[Q] = clients
691			}
692			clients[P] = true
693		}
694	}
695
696	// Find all packages reachable from some error package.
697	reachable := make(map[*types.Package]bool)
698	var visit func(*types.Package)
699	visit = func(p *types.Package) {
700		if !reachable[p] {
701			reachable[p] = true
702			for q := range importedBy[p] {
703				visit(q)
704			}
705		}
706	}
707	for _, info := range allPackages {
708		if len(info.Errors) > 0 {
709			visit(info.Pkg)
710		}
711	}
712
713	// Mark the others as "transitively error-free".
714	for _, info := range allPackages {
715		if !reachable[info.Pkg] {
716			info.TransitivelyErrorFree = true
717		}
718	}
719}
720
721// build returns the effective build context.
722func (conf *Config) build() *build.Context {
723	if conf.Build != nil {
724		return conf.Build
725	}
726	return &build.Default
727}
728
729// parsePackageFiles enumerates the files belonging to package path,
730// then loads, parses and returns them, plus a list of I/O or parse
731// errors that were encountered.
732//
733// 'which' indicates which files to include:
734//    'g': include non-test *.go source files (GoFiles + processed CgoFiles)
735//    't': include in-package *_test.go source files (TestGoFiles)
736//    'x': include external *_test.go source files. (XTestGoFiles)
737//
738func (conf *Config) parsePackageFiles(bp *build.Package, which rune) ([]*ast.File, []error) {
739	if bp.ImportPath == "unsafe" {
740		return nil, nil
741	}
742	var filenames []string
743	switch which {
744	case 'g':
745		filenames = bp.GoFiles
746	case 't':
747		filenames = bp.TestGoFiles
748	case 'x':
749		filenames = bp.XTestGoFiles
750	default:
751		panic(which)
752	}
753
754	files, errs := parseFiles(conf.fset(), conf.build(), conf.DisplayPath, bp.Dir, filenames, conf.ParserMode)
755
756	// Preprocess CgoFiles and parse the outputs (sequentially).
757	if which == 'g' && bp.CgoFiles != nil {
758		cgofiles, err := cgo.ProcessFiles(bp, conf.fset(), conf.DisplayPath, conf.ParserMode)
759		if err != nil {
760			errs = append(errs, err)
761		} else {
762			files = append(files, cgofiles...)
763		}
764	}
765
766	return files, errs
767}
768
769// doImport imports the package denoted by path.
770// It implements the types.Importer signature.
771//
772// It returns an error if a package could not be created
773// (e.g. go/build or parse error), but type errors are reported via
774// the types.Config.Error callback (the first of which is also saved
775// in the package's PackageInfo).
776//
777// Idempotent.
778//
779func (imp *importer) doImport(from *PackageInfo, to string) (*types.Package, error) {
780	if to == "C" {
781		// This should be unreachable, but ad hoc packages are
782		// not currently subject to cgo preprocessing.
783		// See https://golang.org/issue/11627.
784		return nil, fmt.Errorf(`the loader doesn't cgo-process ad hoc packages like %q; see Go issue 11627`,
785			from.Pkg.Path())
786	}
787
788	bp, err := imp.findPackage(to, from.dir, 0)
789	if err != nil {
790		return nil, err
791	}
792
793	// The standard unsafe package is handled specially,
794	// and has no PackageInfo.
795	if bp.ImportPath == "unsafe" {
796		return types.Unsafe, nil
797	}
798
799	// Look for the package in the cache using its canonical path.
800	path := bp.ImportPath
801	imp.importedMu.Lock()
802	ii := imp.imported[path]
803	imp.importedMu.Unlock()
804	if ii == nil {
805		panic("internal error: unexpected import: " + path)
806	}
807	if ii.info != nil {
808		return ii.info.Pkg, nil
809	}
810
811	// Import of incomplete package: this indicates a cycle.
812	fromPath := from.Pkg.Path()
813	if cycle := imp.findPath(path, fromPath); cycle != nil {
814		// Normalize cycle: start from alphabetically largest node.
815		pos, start := -1, ""
816		for i, s := range cycle {
817			if pos < 0 || s > start {
818				pos, start = i, s
819			}
820		}
821		cycle = append(cycle, cycle[:pos]...)[pos:] // rotate cycle to start from largest
822		cycle = append(cycle, cycle[0])             // add start node to end to show cycliness
823		return nil, fmt.Errorf("import cycle: %s", strings.Join(cycle, " -> "))
824	}
825
826	panic("internal error: import of incomplete (yet acyclic) package: " + fromPath)
827}
828
829// findPackage locates the package denoted by the importPath in the
830// specified directory.
831func (imp *importer) findPackage(importPath, fromDir string, mode build.ImportMode) (*build.Package, error) {
832	// We use a non-blocking duplicate-suppressing cache (gopl.io §9.7)
833	// to avoid holding the lock around FindPackage.
834	key := findpkgKey{importPath, fromDir, mode}
835	imp.findpkgMu.Lock()
836	v, ok := imp.findpkg[key]
837	if ok {
838		// cache hit
839		imp.findpkgMu.Unlock()
840
841		<-v.ready // wait for entry to become ready
842	} else {
843		// Cache miss: this goroutine becomes responsible for
844		// populating the map entry and broadcasting its readiness.
845		v = &findpkgValue{ready: make(chan struct{})}
846		imp.findpkg[key] = v
847		imp.findpkgMu.Unlock()
848
849		ioLimit <- true
850		v.bp, v.err = imp.conf.FindPackage(imp.conf.build(), importPath, fromDir, mode)
851		<-ioLimit
852
853		if _, ok := v.err.(*build.NoGoError); ok {
854			v.err = nil // empty directory is not an error
855		}
856
857		close(v.ready) // broadcast ready condition
858	}
859	return v.bp, v.err
860}
861
862// importAll loads, parses, and type-checks the specified packages in
863// parallel and returns their completed importInfos in unspecified order.
864//
865// fromPath is the package path of the importing package, if it is
866// importable, "" otherwise.  It is used for cycle detection.
867//
868// fromDir is the directory containing the import declaration that
869// caused these imports.
870//
871func (imp *importer) importAll(fromPath, fromDir string, imports map[string]bool, mode build.ImportMode) (infos []*PackageInfo, errors []importError) {
872	if fromPath != "" {
873		// We're loading a set of imports.
874		//
875		// We must record graph edges from the importing package
876		// to its dependencies, and check for cycles.
877		imp.graphMu.Lock()
878		deps, ok := imp.graph[fromPath]
879		if !ok {
880			deps = make(map[string]bool)
881			imp.graph[fromPath] = deps
882		}
883		for importPath := range imports {
884			deps[importPath] = true
885		}
886		imp.graphMu.Unlock()
887	}
888
889	var pending []*importInfo
890	for importPath := range imports {
891		if fromPath != "" {
892			if cycle := imp.findPath(importPath, fromPath); cycle != nil {
893				// Cycle-forming import: we must not check it
894				// since it would deadlock.
895				if trace {
896					fmt.Fprintf(os.Stderr, "import cycle: %q\n", cycle)
897				}
898				continue
899			}
900		}
901		bp, err := imp.findPackage(importPath, fromDir, mode)
902		if err != nil {
903			errors = append(errors, importError{
904				path: importPath,
905				err:  err,
906			})
907			continue
908		}
909		pending = append(pending, imp.startLoad(bp))
910	}
911
912	for _, ii := range pending {
913		ii.awaitCompletion()
914		infos = append(infos, ii.info)
915	}
916
917	return infos, errors
918}
919
920// findPath returns an arbitrary path from 'from' to 'to' in the import
921// graph, or nil if there was none.
922func (imp *importer) findPath(from, to string) []string {
923	imp.graphMu.Lock()
924	defer imp.graphMu.Unlock()
925
926	seen := make(map[string]bool)
927	var search func(stack []string, importPath string) []string
928	search = func(stack []string, importPath string) []string {
929		if !seen[importPath] {
930			seen[importPath] = true
931			stack = append(stack, importPath)
932			if importPath == to {
933				return stack
934			}
935			for x := range imp.graph[importPath] {
936				if p := search(stack, x); p != nil {
937					return p
938				}
939			}
940		}
941		return nil
942	}
943	return search(make([]string, 0, 20), from)
944}
945
946// startLoad initiates the loading, parsing and type-checking of the
947// specified package and its dependencies, if it has not already begun.
948//
949// It returns an importInfo, not necessarily in a completed state.  The
950// caller must call awaitCompletion() before accessing its info field.
951//
952// startLoad is concurrency-safe and idempotent.
953//
954func (imp *importer) startLoad(bp *build.Package) *importInfo {
955	path := bp.ImportPath
956	imp.importedMu.Lock()
957	ii, ok := imp.imported[path]
958	if !ok {
959		ii = &importInfo{path: path, complete: make(chan struct{})}
960		imp.imported[path] = ii
961		go func() {
962			info := imp.load(bp)
963			ii.Complete(info)
964		}()
965	}
966	imp.importedMu.Unlock()
967
968	return ii
969}
970
971// load implements package loading by parsing Go source files
972// located by go/build.
973func (imp *importer) load(bp *build.Package) *PackageInfo {
974	info := imp.newPackageInfo(bp.ImportPath, bp.Dir)
975	info.Importable = true
976	files, errs := imp.conf.parsePackageFiles(bp, 'g')
977	for _, err := range errs {
978		info.appendError(err)
979	}
980
981	imp.addFiles(info, files, true)
982
983	imp.progMu.Lock()
984	imp.prog.importMap[bp.ImportPath] = info.Pkg
985	imp.progMu.Unlock()
986
987	return info
988}
989
990// addFiles adds and type-checks the specified files to info, loading
991// their dependencies if needed.  The order of files determines the
992// package initialization order.  It may be called multiple times on the
993// same package.  Errors are appended to the info.Errors field.
994//
995// cycleCheck determines whether the imports within files create
996// dependency edges that should be checked for potential cycles.
997//
998func (imp *importer) addFiles(info *PackageInfo, files []*ast.File, cycleCheck bool) {
999	// Ensure the dependencies are loaded, in parallel.
1000	var fromPath string
1001	if cycleCheck {
1002		fromPath = info.Pkg.Path()
1003	}
1004	// TODO(adonovan): opt: make the caller do scanImports.
1005	// Callers with a build.Package can skip it.
1006	imp.importAll(fromPath, info.dir, scanImports(files), 0)
1007
1008	if trace {
1009		fmt.Fprintf(os.Stderr, "%s: start %q (%d)\n",
1010			time.Since(imp.start), info.Pkg.Path(), len(files))
1011	}
1012
1013	// Don't call checker.Files on Unsafe, even with zero files,
1014	// because it would mutate the package, which is a global.
1015	if info.Pkg == types.Unsafe {
1016		if len(files) > 0 {
1017			panic(`"unsafe" package contains unexpected files`)
1018		}
1019	} else {
1020		// Ignore the returned (first) error since we
1021		// already collect them all in the PackageInfo.
1022		info.checker.Files(files)
1023		info.Files = append(info.Files, files...)
1024	}
1025
1026	if imp.conf.AfterTypeCheck != nil {
1027		imp.conf.AfterTypeCheck(info, files)
1028	}
1029
1030	if trace {
1031		fmt.Fprintf(os.Stderr, "%s: stop %q\n",
1032			time.Since(imp.start), info.Pkg.Path())
1033	}
1034}
1035
1036func (imp *importer) newPackageInfo(path, dir string) *PackageInfo {
1037	var pkg *types.Package
1038	if path == "unsafe" {
1039		pkg = types.Unsafe
1040	} else {
1041		pkg = types.NewPackage(path, "")
1042	}
1043	info := &PackageInfo{
1044		Pkg: pkg,
1045		Info: types.Info{
1046			Types:      make(map[ast.Expr]types.TypeAndValue),
1047			Defs:       make(map[*ast.Ident]types.Object),
1048			Uses:       make(map[*ast.Ident]types.Object),
1049			Implicits:  make(map[ast.Node]types.Object),
1050			Scopes:     make(map[ast.Node]*types.Scope),
1051			Selections: make(map[*ast.SelectorExpr]*types.Selection),
1052		},
1053		errorFunc: imp.conf.TypeChecker.Error,
1054		dir:       dir,
1055	}
1056
1057	// Copy the types.Config so we can vary it across PackageInfos.
1058	tc := imp.conf.TypeChecker
1059	tc.IgnoreFuncBodies = false
1060	if f := imp.conf.TypeCheckFuncBodies; f != nil {
1061		tc.IgnoreFuncBodies = !f(path)
1062	}
1063	tc.Importer = closure{imp, info}
1064	tc.Error = info.appendError // appendError wraps the user's Error function
1065
1066	info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info)
1067	imp.progMu.Lock()
1068	imp.prog.AllPackages[pkg] = info
1069	imp.progMu.Unlock()
1070	return info
1071}
1072
1073type closure struct {
1074	imp  *importer
1075	info *PackageInfo
1076}
1077
1078func (c closure) Import(to string) (*types.Package, error) { return c.imp.doImport(c.info, to) }
1079