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