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