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