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