GoPLS Viewer

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