github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/internal/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 goimporter "go/importer" 15 "go/parser" 16 "go/token" 17 "go/types" 18 "os" 19 "sort" 20 "strings" 21 "sync" 22 "time" 23 ) 24 25 const trace = false // show timing info for type-checking 26 27 // Config specifies the configuration for loading a whole program from 28 // Go source code. 29 // The zero value for Config is a ready-to-use default configuration. 30 type Config struct { 31 // Fset is the file set for the parser to use when loading the 32 // program. If nil, it may be lazily initialized by any 33 // method of Config. 34 Fset *token.FileSet 35 36 // ParserMode specifies the mode to be used by the parser when 37 // loading source packages. 38 ParserMode parser.Mode 39 40 // TypeChecker contains options relating to the type checker. 41 // 42 // The supplied IgnoreFuncBodies is not used; the effective 43 // value comes from the TypeCheckFuncBodies func below. 44 // The supplied Import function is not used either. 45 TypeChecker types.Config 46 47 // TypeCheckFuncBodies is a predicate over package import 48 // paths. A package for which the predicate is false will 49 // have its package-level declarations type checked, but not 50 // its function bodies; this can be used to quickly load 51 // dependencies from source. If nil, all func bodies are type 52 // checked. 53 TypeCheckFuncBodies func(string) bool 54 55 // If Build is non-nil, it is used to locate source packages. 56 // Otherwise &build.Default is used. 57 // 58 // By default, cgo is invoked to preprocess Go files that 59 // import the fake package "C". This behaviour can be 60 // disabled by setting CGO_ENABLED=0 in the environment prior 61 // to startup, or by setting Build.CgoEnabled=false. 62 Build *build.Context 63 64 // The current directory, used for resolving relative package 65 // references such as "./go/loader". If empty, os.Getwd will be 66 // used instead. 67 Cwd string 68 69 // If DisplayPath is non-nil, it is used to transform each 70 // file name obtained from Build.Import(). This can be used 71 // to prevent a virtualized build.Config's file names from 72 // leaking into the user interface. 73 DisplayPath func(path string) string 74 75 // If AllowErrors is true, Load will return a Program even 76 // if some of the its packages contained I/O, parser or type 77 // errors; such errors are accessible via PackageInfo.Errors. If 78 // false, Load will fail if any package had an error. 79 AllowErrors bool 80 81 // CreatePkgs specifies a list of non-importable initial 82 // packages to create. The resulting packages will appear in 83 // the corresponding elements of the Program.Created slice. 84 CreatePkgs []PkgSpec 85 86 // ImportPkgs specifies a set of initial packages to load from 87 // source. The map keys are package import paths, used to 88 // locate the package relative to $GOROOT. 89 // 90 // The map value indicates whether to load tests. If true, Load 91 // will add and type-check two lists of files to the package: 92 // non-test files followed by in-package *_test.go files. In 93 // addition, it will append the external test package (if any) 94 // to Program.Created. 95 ImportPkgs map[string]bool 96 97 // FindPackage is called during Load to create the build.Package 98 // for a given import path. If nil, a default implementation 99 // based on ctxt.Import is used. A client may use this hook to 100 // adapt to a proprietary build system that does not follow the 101 // "go build" layout conventions, for example. 102 // 103 // It must be safe to call concurrently from multiple goroutines. 104 FindPackage func(ctxt *build.Context, importPath string) (*build.Package, error) 105 } 106 107 // A PkgSpec specifies a non-importable package to be created by Load. 108 // Files are processed first, but typically only one of Files and 109 // Filenames is provided. The path needn't be globally unique. 110 // 111 type PkgSpec struct { 112 Path string // import path ("" => use package declaration) 113 Files []*ast.File // ASTs of already-parsed files 114 Filenames []string // names of files to be parsed 115 } 116 117 // A Program is a Go program loaded from source as specified by a Config. 118 type Program struct { 119 Fset *token.FileSet // the file set for this program 120 121 // Created[i] contains the initial package whose ASTs or 122 // filenames were supplied by Config.CreatePkgs[i], followed by 123 // the external test package, if any, of each package in 124 // Config.ImportPkgs ordered by ImportPath. 125 Created []*PackageInfo 126 127 // Imported contains the initially imported packages, 128 // as specified by Config.ImportPkgs. 129 Imported map[string]*PackageInfo 130 131 // AllPackages contains the PackageInfo of every package 132 // encountered by Load: all initial packages and all 133 // dependencies, including incomplete ones. 134 AllPackages map[*types.Package]*PackageInfo 135 136 // importMap is the canonical mapping of import paths to 137 // packages. It contains all Imported initial packages, but not 138 // Created ones, and all imported dependencies. 139 importMap map[string]*types.Package 140 } 141 142 // PackageInfo holds the ASTs and facts derived by the type-checker 143 // for a single package. 144 // 145 // Not mutated once exposed via the API. 146 // 147 type PackageInfo struct { 148 Pkg *types.Package 149 Importable bool // true if 'import "Pkg.Path()"' would resolve to this 150 TransitivelyErrorFree bool // true if Pkg and all its dependencies are free of errors 151 Files []*ast.File // syntax trees for the package's files 152 Errors []error // non-nil if the package had errors 153 types.Info // type-checker deductions. 154 155 checker *types.Checker // transient type-checker state 156 errorFunc func(error) 157 } 158 159 func (info *PackageInfo) String() string { return info.Pkg.Path() } 160 161 func (info *PackageInfo) appendError(err error) { 162 if info.errorFunc != nil { 163 info.errorFunc(err) 164 } else { 165 fmt.Fprintln(os.Stderr, err) 166 } 167 info.Errors = append(info.Errors, err) 168 } 169 170 func (conf *Config) fset() *token.FileSet { 171 if conf.Fset == nil { 172 conf.Fset = token.NewFileSet() 173 } 174 return conf.Fset 175 } 176 177 // ParseFile is a convenience function (intended for testing) that invokes 178 // the parser using the Config's FileSet, which is initialized if nil. 179 // 180 // src specifies the parser input as a string, []byte, or io.Reader, and 181 // filename is its apparent name. If src is nil, the contents of 182 // filename are read from the file system. 183 // 184 func (conf *Config) ParseFile(filename string, src interface{}) (*ast.File, error) { 185 // TODO(adonovan): use conf.build() etc like parseFiles does. 186 return parser.ParseFile(conf.fset(), filename, src, conf.ParserMode) 187 } 188 189 // FromArgsUsage is a partial usage message that applications calling 190 // FromArgs may wish to include in their -help output. 191 const FromArgsUsage = ` 192 <args> is a list of arguments denoting a set of initial packages. 193 It may take one of two forms: 194 195 1. A list of *.go source files. 196 197 All of the specified files are loaded, parsed and type-checked 198 as a single package. All the files must belong to the same directory. 199 200 2. A list of import paths, each denoting a package. 201 202 The package's directory is found relative to the $GOROOT and 203 $GOPATH using similar logic to 'go build', and the *.go files in 204 that directory are loaded, parsed and type-checked as a single 205 package. 206 207 In addition, all *_test.go files in the directory are then loaded 208 and parsed. Those files whose package declaration equals that of 209 the non-*_test.go files are included in the primary package. Test 210 files whose package declaration ends with "_test" are type-checked 211 as another package, the 'external' test package, so that a single 212 import path may denote two packages. (Whether this behaviour is 213 enabled is tool-specific, and may depend on additional flags.) 214 215 A '--' argument terminates the list of packages. 216 ` 217 218 // FromArgs interprets args as a set of initial packages to load from 219 // source and updates the configuration. It returns the list of 220 // unconsumed arguments. 221 // 222 // It is intended for use in command-line interfaces that require a 223 // set of initial packages to be specified; see FromArgsUsage message 224 // for details. 225 // 226 // Only superficial errors are reported at this stage; errors dependent 227 // on I/O are detected during Load. 228 // 229 func (conf *Config) FromArgs(args []string, xtest bool) ([]string, error) { 230 var rest []string 231 for i, arg := range args { 232 if arg == "--" { 233 rest = args[i+1:] 234 args = args[:i] 235 break // consume "--" and return the remaining args 236 } 237 } 238 239 if len(args) > 0 && strings.HasSuffix(args[0], ".go") { 240 // Assume args is a list of a *.go files 241 // denoting a single ad hoc package. 242 for _, arg := range args { 243 if !strings.HasSuffix(arg, ".go") { 244 return nil, fmt.Errorf("named files must be .go files: %s", arg) 245 } 246 } 247 conf.CreateFromFilenames("", args...) 248 } else { 249 // Assume args are directories each denoting a 250 // package and (perhaps) an external test, iff xtest. 251 for _, arg := range args { 252 if xtest { 253 conf.ImportWithTests(arg) 254 } else { 255 conf.Import(arg) 256 } 257 } 258 } 259 260 return rest, nil 261 } 262 263 // CreateFromFilenames is a convenience function that adds 264 // a conf.CreatePkgs entry to create a package of the specified *.go 265 // files. 266 // 267 func (conf *Config) CreateFromFilenames(path string, filenames ...string) { 268 conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Filenames: filenames}) 269 } 270 271 // CreateFromFiles is a convenience function that adds a conf.CreatePkgs 272 // entry to create package of the specified path and parsed files. 273 // 274 func (conf *Config) CreateFromFiles(path string, files ...*ast.File) { 275 conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Files: files}) 276 } 277 278 // ImportWithTests is a convenience function that adds path to 279 // ImportPkgs, the set of initial source packages located relative to 280 // $GOPATH. The package will be augmented by any *_test.go files in 281 // its directory that contain a "package x" (not "package x_test") 282 // declaration. 283 // 284 // In addition, if any *_test.go files contain a "package x_test" 285 // declaration, an additional package comprising just those files will 286 // be added to CreatePkgs. 287 // 288 func (conf *Config) ImportWithTests(path string) { conf.addImport(path, true) } 289 290 // Import is a convenience function that adds path to ImportPkgs, the 291 // set of initial packages that will be imported from source. 292 // 293 func (conf *Config) Import(path string) { conf.addImport(path, false) } 294 295 func (conf *Config) addImport(path string, tests bool) { 296 if path == "C" || path == "unsafe" { 297 return // ignore; not a real package 298 } 299 if conf.ImportPkgs == nil { 300 conf.ImportPkgs = make(map[string]bool) 301 } 302 conf.ImportPkgs[path] = conf.ImportPkgs[path] || tests 303 } 304 305 // InitialPackages returns a new slice containing the set of initial 306 // packages (Created + Imported) in unspecified order. 307 // 308 func (prog *Program) InitialPackages() []*PackageInfo { 309 infos := make([]*PackageInfo, 0, len(prog.Created)+len(prog.Imported)) 310 infos = append(infos, prog.Created...) 311 for _, info := range prog.Imported { 312 infos = append(infos, info) 313 } 314 return infos 315 } 316 317 // Package returns the ASTs and results of type checking for the 318 // specified package. 319 func (prog *Program) Package(path string) *PackageInfo { 320 if info, ok := prog.AllPackages[prog.importMap[path]]; ok { 321 return info 322 } 323 for _, info := range prog.Created { 324 if path == info.Pkg.Path() { 325 return info 326 } 327 } 328 return nil 329 } 330 331 // ---------- Implementation ---------- 332 333 // importer holds the working state of the algorithm. 334 type importer struct { 335 conf *Config // the client configuration 336 start time.Time // for logging 337 338 progMu sync.Mutex // guards prog 339 prog *Program // the resulting program 340 341 importedMu sync.Mutex // guards imported 342 imported map[string]*importInfo // all imported packages (incl. failures) by import path 343 344 // import dependency graph: graph[x][y] => x imports y 345 // 346 // Since non-importable packages cannot be cyclic, we ignore 347 // their imports, thus we only need the subgraph over importable 348 // packages. Nodes are identified by their import paths. 349 graphMu sync.Mutex 350 graph map[string]map[string]bool 351 } 352 353 // importInfo tracks the success or failure of a single import. 354 // 355 // Upon completion, exactly one of info and err is non-nil: 356 // info on successful creation of a package, err otherwise. 357 // A successful package may still contain type errors. 358 // 359 type importInfo struct { 360 path string // import path 361 mu sync.Mutex // guards the following fields prior to completion 362 info *PackageInfo // results of typechecking (including errors) 363 err error // reason for failure to create a package 364 complete sync.Cond // complete condition is that one of info, err is non-nil. 365 } 366 367 // awaitCompletion blocks until ii is complete, 368 // i.e. the info and err fields are safe to inspect without a lock. 369 // It is concurrency-safe and idempotent. 370 func (ii *importInfo) awaitCompletion() { 371 ii.mu.Lock() 372 for ii.info == nil && ii.err == nil { 373 ii.complete.Wait() 374 } 375 ii.mu.Unlock() 376 } 377 378 // Complete marks ii as complete. 379 // Its info and err fields will not be subsequently updated. 380 func (ii *importInfo) Complete(info *PackageInfo, err error) { 381 ii.mu.Lock() 382 ii.info = info 383 ii.err = err 384 ii.complete.Broadcast() 385 ii.mu.Unlock() 386 } 387 388 // Load creates the initial packages specified by conf.{Create,Import}Pkgs, 389 // loading their dependencies packages as needed. 390 // 391 // On success, Load returns a Program containing a PackageInfo for 392 // each package. On failure, it returns an error. 393 // 394 // If AllowErrors is true, Load will return a Program even if some 395 // packages contained I/O, parser or type errors, or if dependencies 396 // were missing. (Such errors are accessible via PackageInfo.Errors. If 397 // false, Load will fail if any package had an error. 398 // 399 // It is an error if no packages were loaded. 400 // 401 func (conf *Config) Load() (*Program, error) { 402 // Create a simple default error handler for parse/type errors. 403 if conf.TypeChecker.Error == nil { 404 conf.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) } 405 } 406 407 // Set default working directory for relative package references. 408 if conf.Cwd == "" { 409 var err error 410 conf.Cwd, err = os.Getwd() 411 if err != nil { 412 return nil, err 413 } 414 } 415 416 // Install default FindPackage hook using go/build logic. 417 if conf.FindPackage == nil { 418 conf.FindPackage = func(ctxt *build.Context, path string) (*build.Package, error) { 419 // TODO(adonovan): cache calls to build.Import 420 // so we don't do it three times per test package. 421 bp, err := ctxt.Import(path, conf.Cwd, 0) 422 if _, ok := err.(*build.NoGoError); ok { 423 return bp, nil // empty directory is not an error 424 } 425 return bp, err 426 } 427 } 428 429 prog := &Program{ 430 Fset: conf.fset(), 431 Imported: make(map[string]*PackageInfo), 432 importMap: make(map[string]*types.Package), 433 AllPackages: make(map[*types.Package]*PackageInfo), 434 } 435 436 imp := importer{ 437 conf: conf, 438 prog: prog, 439 imported: make(map[string]*importInfo), 440 start: time.Now(), 441 graph: make(map[string]map[string]bool), 442 } 443 444 // -- loading proper (concurrent phase) -------------------------------- 445 446 var errpkgs []string // packages that contained errors 447 448 // Load the initially imported packages and their dependencies, 449 // in parallel. 450 for _, ii := range imp.loadAll("", conf.ImportPkgs) { 451 if ii.err != nil { 452 conf.TypeChecker.Error(ii.err) // failed to create package 453 errpkgs = append(errpkgs, ii.path) 454 continue 455 } 456 prog.Imported[ii.info.Pkg.Path()] = ii.info 457 } 458 459 // Augment the designated initial packages by their tests. 460 // Dependencies are loaded in parallel. 461 var xtestPkgs []*build.Package 462 for path, augment := range conf.ImportPkgs { 463 if !augment { 464 continue 465 } 466 467 bp, err := conf.FindPackage(conf.build(), path) 468 if err != nil { 469 // Package not found, or can't even parse package declaration. 470 // Already reported by previous loop; ignore it. 471 continue 472 } 473 474 // Needs external test package? 475 if len(bp.XTestGoFiles) > 0 { 476 xtestPkgs = append(xtestPkgs, bp) 477 } 478 479 imp.importedMu.Lock() // (unnecessary, we're sequential here) 480 info := imp.imported[path].info // must be non-nil, see above 481 imp.importedMu.Unlock() 482 483 // Parse the in-package test files. 484 files, errs := imp.conf.parsePackageFiles(bp, 't') 485 for _, err := range errs { 486 info.appendError(err) 487 } 488 489 // The test files augmenting package P cannot be imported, 490 // but may import packages that import P, 491 // so we must disable the cycle check. 492 imp.addFiles(info, files, false) 493 } 494 495 createPkg := func(path string, files []*ast.File, errs []error) { 496 info := imp.newPackageInfo(path) 497 for _, err := range errs { 498 info.appendError(err) 499 } 500 501 // Ad hoc packages are non-importable, 502 // so no cycle check is needed. 503 // addFiles loads dependencies in parallel. 504 imp.addFiles(info, files, false) 505 prog.Created = append(prog.Created, info) 506 } 507 508 // Create packages specified by conf.CreatePkgs. 509 for _, cp := range conf.CreatePkgs { 510 files, errs := parseFiles(conf.fset(), conf.build(), nil, ".", cp.Filenames, conf.ParserMode) 511 files = append(files, cp.Files...) 512 513 path := cp.Path 514 if path == "" { 515 if len(files) > 0 { 516 path = files[0].Name.Name 517 } else { 518 path = "(unnamed)" 519 } 520 } 521 createPkg(path, files, errs) 522 } 523 524 // Create external test packages. 525 sort.Sort(byImportPath(xtestPkgs)) 526 for _, bp := range xtestPkgs { 527 files, errs := imp.conf.parsePackageFiles(bp, 'x') 528 createPkg(bp.ImportPath+"_test", files, errs) 529 } 530 531 // -- finishing up (sequential) ---------------------------------------- 532 533 if len(prog.Imported)+len(prog.Created) == 0 { 534 return nil, errors.New("no initial packages were loaded") 535 } 536 537 // Create infos for indirectly imported packages. 538 // e.g. incomplete packages without syntax, loaded from export data. 539 for _, obj := range prog.importMap { 540 info := prog.AllPackages[obj] 541 if info == nil { 542 prog.AllPackages[obj] = &PackageInfo{Pkg: obj, Importable: true} 543 } else { 544 // finished 545 info.checker = nil 546 info.errorFunc = nil 547 } 548 } 549 550 if !conf.AllowErrors { 551 // Report errors in indirectly imported packages. 552 for _, info := range prog.AllPackages { 553 if len(info.Errors) > 0 { 554 errpkgs = append(errpkgs, info.Pkg.Path()) 555 } 556 } 557 if errpkgs != nil { 558 var more string 559 if len(errpkgs) > 3 { 560 more = fmt.Sprintf(" and %d more", len(errpkgs)-3) 561 errpkgs = errpkgs[:3] 562 } 563 return nil, fmt.Errorf("couldn't load packages due to errors: %s%s", 564 strings.Join(errpkgs, ", "), more) 565 } 566 } 567 568 markErrorFreePackages(prog.AllPackages) 569 570 return prog, nil 571 } 572 573 type byImportPath []*build.Package 574 575 func (b byImportPath) Len() int { return len(b) } 576 func (b byImportPath) Less(i, j int) bool { return b[i].ImportPath < b[j].ImportPath } 577 func (b byImportPath) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 578 579 // markErrorFreePackages sets the TransitivelyErrorFree flag on all 580 // applicable packages. 581 func markErrorFreePackages(allPackages map[*types.Package]*PackageInfo) { 582 // Build the transpose of the import graph. 583 importedBy := make(map[*types.Package]map[*types.Package]bool) 584 for P := range allPackages { 585 for _, Q := range P.Imports() { 586 clients, ok := importedBy[Q] 587 if !ok { 588 clients = make(map[*types.Package]bool) 589 importedBy[Q] = clients 590 } 591 clients[P] = true 592 } 593 } 594 595 // Find all packages reachable from some error package. 596 reachable := make(map[*types.Package]bool) 597 var visit func(*types.Package) 598 visit = func(p *types.Package) { 599 if !reachable[p] { 600 reachable[p] = true 601 for q := range importedBy[p] { 602 visit(q) 603 } 604 } 605 } 606 for _, info := range allPackages { 607 if len(info.Errors) > 0 { 608 visit(info.Pkg) 609 } 610 } 611 612 // Mark the others as "transitively error-free". 613 for _, info := range allPackages { 614 if !reachable[info.Pkg] { 615 info.TransitivelyErrorFree = true 616 } 617 } 618 } 619 620 // build returns the effective build context. 621 func (conf *Config) build() *build.Context { 622 if conf.Build != nil { 623 return conf.Build 624 } 625 return &build.Default 626 } 627 628 // parsePackageFiles enumerates the files belonging to package path, 629 // then loads, parses and returns them, plus a list of I/O or parse 630 // errors that were encountered. 631 // 632 // 'which' indicates which files to include: 633 // 'g': include non-test *.go source files (GoFiles + processed CgoFiles) 634 // 't': include in-package *_test.go source files (TestGoFiles) 635 // 'x': include external *_test.go source files. (XTestGoFiles) 636 // 637 func (conf *Config) parsePackageFiles(bp *build.Package, which rune) ([]*ast.File, []error) { 638 var filenames []string 639 switch which { 640 case 'g': 641 filenames = bp.GoFiles 642 case 't': 643 filenames = bp.TestGoFiles 644 case 'x': 645 filenames = bp.XTestGoFiles 646 default: 647 panic(which) 648 } 649 650 files, errs := parseFiles(conf.fset(), conf.build(), conf.DisplayPath, bp.Dir, filenames, conf.ParserMode) 651 652 // Preprocess CgoFiles and parse the outputs (sequentially). 653 if which == 'g' && bp.CgoFiles != nil { 654 cgofiles, err := processCgoFiles(bp, conf.fset(), conf.DisplayPath, conf.ParserMode) 655 if err != nil { 656 errs = append(errs, err) 657 } else { 658 files = append(files, cgofiles...) 659 } 660 } 661 662 return files, errs 663 } 664 665 // loadAll loads, parses, and type-checks the specified packages in 666 // parallel and returns their completed importInfos in unspecified order. 667 // 668 // fromPath is the import path of the importing package, if it is 669 // importable, "" otherwise. It is used for cycle detection. 670 // 671 func (imp *importer) loadAll(fromPath string, paths map[string]bool) []*importInfo { 672 result := make([]*importInfo, 0, len(paths)) 673 for path := range paths { 674 result = append(result, imp.startLoad(path)) 675 } 676 677 if fromPath != "" { 678 // We're loading a set of imports. 679 // 680 // We must record graph edges from the importing package 681 // to its dependencies, and check for cycles. 682 imp.graphMu.Lock() 683 deps, ok := imp.graph[fromPath] 684 if !ok { 685 deps = make(map[string]bool) 686 imp.graph[fromPath] = deps 687 } 688 for path := range paths { 689 deps[path] = true 690 } 691 imp.graphMu.Unlock() 692 } 693 694 for _, ii := range result { 695 if fromPath != "" { 696 if cycle := imp.findPath(ii.path, fromPath); cycle != nil { 697 // Cycle-forming import: we must not await its 698 // completion since it would deadlock. 699 // 700 // We don't record the error in ii since 701 // the error is really associated with the 702 // cycle-forming edge, not the package itself. 703 // (Also it would complicate the 704 // invariants of importPath completion.) 705 if trace { 706 fmt.Fprintln(os.Stderr, "import cycle: %q", cycle) 707 } 708 continue 709 } 710 } 711 ii.awaitCompletion() 712 713 } 714 return result 715 } 716 717 // findPath returns an arbitrary path from 'from' to 'to' in the import 718 // graph, or nil if there was none. 719 func (imp *importer) findPath(from, to string) []string { 720 imp.graphMu.Lock() 721 defer imp.graphMu.Unlock() 722 723 seen := make(map[string]bool) 724 var search func(stack []string, importPath string) []string 725 search = func(stack []string, importPath string) []string { 726 if !seen[importPath] { 727 seen[importPath] = true 728 stack = append(stack, importPath) 729 if importPath == to { 730 return stack 731 } 732 for x := range imp.graph[importPath] { 733 if p := search(stack, x); p != nil { 734 return p 735 } 736 } 737 } 738 return nil 739 } 740 return search(make([]string, 0, 20), from) 741 } 742 743 // startLoad initiates the loading, parsing and type-checking of the 744 // specified package and its dependencies, if it has not already begun. 745 // 746 // It returns an importInfo, not necessarily in a completed state. The 747 // caller must call awaitCompletion() before accessing its info and err 748 // fields. 749 // 750 // startLoad is concurrency-safe and idempotent. 751 // 752 // Precondition: path != "unsafe". 753 // 754 func (imp *importer) startLoad(path string) *importInfo { 755 imp.importedMu.Lock() 756 ii, ok := imp.imported[path] 757 if !ok { 758 ii = &importInfo{path: path} 759 ii.complete.L = &ii.mu 760 imp.imported[path] = ii 761 go func() { 762 ii.Complete(imp.load(path)) 763 }() 764 } 765 imp.importedMu.Unlock() 766 767 return ii 768 } 769 770 // load implements package loading by parsing Go source files 771 // located by go/build. 772 // 773 func (imp *importer) load(path string) (*PackageInfo, error) { 774 bp, err := imp.conf.FindPackage(imp.conf.build(), path) 775 if err != nil { 776 return nil, err // package not found 777 } 778 info := imp.newPackageInfo(bp.ImportPath) 779 info.Importable = true 780 files, errs := imp.conf.parsePackageFiles(bp, 'g') 781 for _, err := range errs { 782 info.appendError(err) 783 } 784 785 imp.addFiles(info, files, true) 786 787 imp.progMu.Lock() 788 imp.prog.importMap[path] = info.Pkg 789 imp.progMu.Unlock() 790 791 return info, nil 792 } 793 794 // addFiles adds and type-checks the specified files to info, loading 795 // their dependencies if needed. The order of files determines the 796 // package initialization order. It may be called multiple times on the 797 // same package. Errors are appended to the info.Errors field. 798 // 799 // cycleCheck determines whether the imports within files create 800 // dependency edges that should be checked for potential cycles. 801 // 802 func (imp *importer) addFiles(info *PackageInfo, files []*ast.File, cycleCheck bool) { 803 info.Files = append(info.Files, files...) 804 805 // Ensure the dependencies are loaded, in parallel. 806 var fromPath string 807 if cycleCheck { 808 fromPath = info.Pkg.Path() 809 } 810 imp.loadAll(fromPath, scanImports(files)) 811 812 if trace { 813 fmt.Fprintf(os.Stderr, "%s: start %q (%d)\n", 814 time.Since(imp.start), info.Pkg.Path(), len(files)) 815 } 816 817 // Ignore the returned (first) error since we 818 // already collect them all in the PackageInfo. 819 info.checker.Files(files) 820 821 if trace { 822 fmt.Fprintf(os.Stderr, "%s: stop %q\n", 823 time.Since(imp.start), info.Pkg.Path()) 824 } 825 } 826 827 func (imp *importer) newPackageInfo(path string) *PackageInfo { 828 pkg := types.NewPackage(path, "") 829 info := &PackageInfo{ 830 Pkg: pkg, 831 Info: types.Info{ 832 Types: make(map[ast.Expr]types.TypeAndValue), 833 Defs: make(map[*ast.Ident]types.Object), 834 Uses: make(map[*ast.Ident]types.Object), 835 Implicits: make(map[ast.Node]types.Object), 836 Scopes: make(map[ast.Node]*types.Scope), 837 Selections: make(map[*ast.SelectorExpr]*types.Selection), 838 }, 839 errorFunc: imp.conf.TypeChecker.Error, 840 } 841 842 // Copy the types.Config so we can vary it across PackageInfos. 843 tc := imp.conf.TypeChecker 844 tc.IgnoreFuncBodies = false 845 if f := imp.conf.TypeCheckFuncBodies; f != nil { 846 tc.IgnoreFuncBodies = !f(path) 847 } 848 tc.Importer = goimporter.Default() 849 tc.Error = info.appendError // appendError wraps the user's Error function 850 851 info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info) 852 imp.progMu.Lock() 853 imp.prog.AllPackages[pkg] = info 854 imp.progMu.Unlock() 855 return info 856 }