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