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