loader.go (33671B)
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 ioLimit <- true 742 cgofiles, err := cgo.ProcessFiles(bp, conf.fset(), conf.DisplayPath, conf.ParserMode) 743 <-ioLimit 744 if err != nil { 745 errs = append(errs, err) 746 } else { 747 files = append(files, cgofiles...) 748 } 749 } 750 751 return files, errs 752 } 753 754 // doImport imports the package denoted by path. 755 // It implements the types.Importer signature. 756 // 757 // It returns an error if a package could not be created 758 // (e.g. go/build or parse error), but type errors are reported via 759 // the types.Config.Error callback (the first of which is also saved 760 // in the package's PackageInfo). 761 // 762 // Idempotent. 763 func (imp *importer) doImport(from *PackageInfo, to string) (*types.Package, error) { 764 if to == "C" { 765 // This should be unreachable, but ad hoc packages are 766 // not currently subject to cgo preprocessing. 767 // See https://golang.org/issue/11627. 768 return nil, fmt.Errorf(`the loader doesn't cgo-process ad hoc packages like %q; see Go issue 11627`, 769 from.Pkg.Path()) 770 } 771 772 bp, err := imp.findPackage(to, from.dir, 0) 773 if err != nil { 774 return nil, err 775 } 776 777 // The standard unsafe package is handled specially, 778 // and has no PackageInfo. 779 if bp.ImportPath == "unsafe" { 780 return types.Unsafe, nil 781 } 782 783 // Look for the package in the cache using its canonical path. 784 path := bp.ImportPath 785 imp.importedMu.Lock() 786 ii := imp.imported[path] 787 imp.importedMu.Unlock() 788 if ii == nil { 789 panic("internal error: unexpected import: " + path) 790 } 791 if ii.info != nil { 792 return ii.info.Pkg, nil 793 } 794 795 // Import of incomplete package: this indicates a cycle. 796 fromPath := from.Pkg.Path() 797 if cycle := imp.findPath(path, fromPath); cycle != nil { 798 // Normalize cycle: start from alphabetically largest node. 799 pos, start := -1, "" 800 for i, s := range cycle { 801 if pos < 0 || s > start { 802 pos, start = i, s 803 } 804 } 805 cycle = append(cycle, cycle[:pos]...)[pos:] // rotate cycle to start from largest 806 cycle = append(cycle, cycle[0]) // add start node to end to show cycliness 807 return nil, fmt.Errorf("import cycle: %s", strings.Join(cycle, " -> ")) 808 } 809 810 panic("internal error: import of incomplete (yet acyclic) package: " + fromPath) 811 } 812 813 // findPackage locates the package denoted by the importPath in the 814 // specified directory. 815 func (imp *importer) findPackage(importPath, fromDir string, mode build.ImportMode) (*build.Package, error) { 816 // We use a non-blocking duplicate-suppressing cache (gopl.io ยง9.7) 817 // to avoid holding the lock around FindPackage. 818 key := findpkgKey{importPath, fromDir, mode} 819 imp.findpkgMu.Lock() 820 v, ok := imp.findpkg[key] 821 if ok { 822 // cache hit 823 imp.findpkgMu.Unlock() 824 825 <-v.ready // wait for entry to become ready 826 } else { 827 // Cache miss: this goroutine becomes responsible for 828 // populating the map entry and broadcasting its readiness. 829 v = &findpkgValue{ready: make(chan struct{})} 830 imp.findpkg[key] = v 831 imp.findpkgMu.Unlock() 832 833 ioLimit <- true 834 v.bp, v.err = imp.conf.FindPackage(imp.conf.build(), importPath, fromDir, mode) 835 <-ioLimit 836 837 if _, ok := v.err.(*build.NoGoError); ok { 838 v.err = nil // empty directory is not an error 839 } 840 841 close(v.ready) // broadcast ready condition 842 } 843 return v.bp, v.err 844 } 845 846 // importAll loads, parses, and type-checks the specified packages in 847 // parallel and returns their completed importInfos in unspecified order. 848 // 849 // fromPath is the package path of the importing package, if it is 850 // importable, "" otherwise. It is used for cycle detection. 851 // 852 // fromDir is the directory containing the import declaration that 853 // caused these imports. 854 func (imp *importer) importAll(fromPath, fromDir string, imports map[string]bool, mode build.ImportMode) (infos []*PackageInfo, errors []importError) { 855 if fromPath != "" { 856 // We're loading a set of imports. 857 // 858 // We must record graph edges from the importing package 859 // to its dependencies, and check for cycles. 860 imp.graphMu.Lock() 861 deps, ok := imp.graph[fromPath] 862 if !ok { 863 deps = make(map[string]bool) 864 imp.graph[fromPath] = deps 865 } 866 for importPath := range imports { 867 deps[importPath] = true 868 } 869 imp.graphMu.Unlock() 870 } 871 872 var pending []*importInfo 873 for importPath := range imports { 874 if fromPath != "" { 875 if cycle := imp.findPath(importPath, fromPath); cycle != nil { 876 // Cycle-forming import: we must not check it 877 // since it would deadlock. 878 if trace { 879 fmt.Fprintf(os.Stderr, "import cycle: %q\n", cycle) 880 } 881 continue 882 } 883 } 884 bp, err := imp.findPackage(importPath, fromDir, mode) 885 if err != nil { 886 errors = append(errors, importError{ 887 path: importPath, 888 err: err, 889 }) 890 continue 891 } 892 pending = append(pending, imp.startLoad(bp)) 893 } 894 895 for _, ii := range pending { 896 ii.awaitCompletion() 897 infos = append(infos, ii.info) 898 } 899 900 return infos, errors 901 } 902 903 // findPath returns an arbitrary path from 'from' to 'to' in the import 904 // graph, or nil if there was none. 905 func (imp *importer) findPath(from, to string) []string { 906 imp.graphMu.Lock() 907 defer imp.graphMu.Unlock() 908 909 seen := make(map[string]bool) 910 var search func(stack []string, importPath string) []string 911 search = func(stack []string, importPath string) []string { 912 if !seen[importPath] { 913 seen[importPath] = true 914 stack = append(stack, importPath) 915 if importPath == to { 916 return stack 917 } 918 for x := range imp.graph[importPath] { 919 if p := search(stack, x); p != nil { 920 return p 921 } 922 } 923 } 924 return nil 925 } 926 return search(make([]string, 0, 20), from) 927 } 928 929 // startLoad initiates the loading, parsing and type-checking of the 930 // specified package and its dependencies, if it has not already begun. 931 // 932 // It returns an importInfo, not necessarily in a completed state. The 933 // caller must call awaitCompletion() before accessing its info field. 934 // 935 // startLoad is concurrency-safe and idempotent. 936 func (imp *importer) startLoad(bp *build.Package) *importInfo { 937 path := bp.ImportPath 938 imp.importedMu.Lock() 939 ii, ok := imp.imported[path] 940 if !ok { 941 ii = &importInfo{path: path, complete: make(chan struct{})} 942 imp.imported[path] = ii 943 go func() { 944 info := imp.load(bp) 945 ii.Complete(info) 946 }() 947 } 948 imp.importedMu.Unlock() 949 950 return ii 951 } 952 953 // load implements package loading by parsing Go source files 954 // located by go/build. 955 func (imp *importer) load(bp *build.Package) *PackageInfo { 956 info := imp.newPackageInfo(bp.ImportPath, bp.Dir) 957 info.Importable = true 958 files, errs := imp.conf.parsePackageFiles(bp, 'g') 959 for _, err := range errs { 960 info.appendError(err) 961 } 962 963 imp.addFiles(info, files, true) 964 965 imp.progMu.Lock() 966 imp.prog.importMap[bp.ImportPath] = info.Pkg 967 imp.progMu.Unlock() 968 969 return info 970 } 971 972 // addFiles adds and type-checks the specified files to info, loading 973 // their dependencies if needed. The order of files determines the 974 // package initialization order. It may be called multiple times on the 975 // same package. Errors are appended to the info.Errors field. 976 // 977 // cycleCheck determines whether the imports within files create 978 // dependency edges that should be checked for potential cycles. 979 func (imp *importer) addFiles(info *PackageInfo, files []*ast.File, cycleCheck bool) { 980 // Ensure the dependencies are loaded, in parallel. 981 var fromPath string 982 if cycleCheck { 983 fromPath = info.Pkg.Path() 984 } 985 // TODO(adonovan): opt: make the caller do scanImports. 986 // Callers with a build.Package can skip it. 987 imp.importAll(fromPath, info.dir, scanImports(files), 0) 988 989 if trace { 990 fmt.Fprintf(os.Stderr, "%s: start %q (%d)\n", 991 time.Since(imp.start), info.Pkg.Path(), len(files)) 992 } 993 994 // Don't call checker.Files on Unsafe, even with zero files, 995 // because it would mutate the package, which is a global. 996 if info.Pkg == types.Unsafe { 997 if len(files) > 0 { 998 panic(`"unsafe" package contains unexpected files`) 999 } 1000 } else { 1001 // Ignore the returned (first) error since we 1002 // already collect them all in the PackageInfo. 1003 info.checker.Files(files) 1004 info.Files = append(info.Files, files...) 1005 } 1006 1007 if imp.conf.AfterTypeCheck != nil { 1008 imp.conf.AfterTypeCheck(info, files) 1009 } 1010 1011 if trace { 1012 fmt.Fprintf(os.Stderr, "%s: stop %q\n", 1013 time.Since(imp.start), info.Pkg.Path()) 1014 } 1015 } 1016 1017 func (imp *importer) newPackageInfo(path, dir string) *PackageInfo { 1018 var pkg *types.Package 1019 if path == "unsafe" { 1020 pkg = types.Unsafe 1021 } else { 1022 pkg = types.NewPackage(path, "") 1023 } 1024 info := &PackageInfo{ 1025 Pkg: pkg, 1026 Info: types.Info{ 1027 Types: make(map[ast.Expr]types.TypeAndValue), 1028 Defs: make(map[*ast.Ident]types.Object), 1029 Uses: make(map[*ast.Ident]types.Object), 1030 Implicits: make(map[ast.Node]types.Object), 1031 Instances: make(map[*ast.Ident]types.Instance), 1032 Scopes: make(map[ast.Node]*types.Scope), 1033 Selections: make(map[*ast.SelectorExpr]*types.Selection), 1034 FileVersions: make(map[*ast.File]string), 1035 }, 1036 errorFunc: imp.conf.TypeChecker.Error, 1037 dir: dir, 1038 } 1039 1040 // Copy the types.Config so we can vary it across PackageInfos. 1041 tc := imp.conf.TypeChecker 1042 tc.IgnoreFuncBodies = false 1043 if f := imp.conf.TypeCheckFuncBodies; f != nil { 1044 tc.IgnoreFuncBodies = !f(path) 1045 } 1046 tc.Importer = closure{imp, info} 1047 tc.Error = info.appendError // appendError wraps the user's Error function 1048 1049 info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info) 1050 imp.progMu.Lock() 1051 imp.prog.AllPackages[pkg] = info 1052 imp.progMu.Unlock() 1053 return info 1054 } 1055 1056 type closure struct { 1057 imp *importer 1058 info *PackageInfo 1059 } 1060 1061 func (c closure) Import(to string) (*types.Package, error) { return c.imp.doImport(c.info, to) }