go/loader: API examples

Also: make it easier to find packages.  Hide the importMap field
(never used) and expose a (*Program).Package method which searches
importMap and then Created.

Also: move huge comments into doc.go.

Change-Id: Iad96a12524b7c41ad9acd1e806af23171e71fa7c
Reviewed-on: https://go-review.googlesource.com/9030
Reviewed-by: Robert Griesemer <gri@golang.org>
diff --git a/go/loader/loader.go b/go/loader/loader.go
index 4dd8485..41df151 100644
--- a/go/loader/loader.go
+++ b/go/loader/loader.go
@@ -2,189 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package loader loads, parses and type-checks packages of Go code
-// plus their transitive closure, and retains both the ASTs and the
-// derived facts.
-//
-// THIS INTERFACE IS EXPERIMENTAL AND IS LIKELY TO CHANGE.
-//
-// The package defines two primary types: Config, which specifies a
-// set of initial packages to load and various other options; and
-// Program, which is the result of successfully loading the packages
-// specified by a configuration.
-//
-// The configuration can be set directly, but *Config provides various
-// convenience methods to simplify the common cases, each of which can
-// be called any number of times.  Finally, these are followed by a
-// call to Load() to actually load and type-check the program.
-//
-//      var conf loader.Config
-//
-//      // Use the command-line arguments to specify
-//      // a set of initial packages to load from source.
-//      // See FromArgsUsage for help.
-//      rest, err := conf.FromArgs(os.Args[1:], wantTests)
-//
-//      // Parse the specified files and create an ad-hoc package with path "foo".
-//      // All files must have the same 'package' declaration.
-//      conf.CreateFromFilenames("foo", "foo.go", "bar.go")
-//
-//      // Create an ad-hoc package with path "foo" from
-//      // the specified already-parsed files.
-//      // All ASTs must have the same 'package' declaration.
-//      conf.CreateFromFiles("foo", parsedFiles)
-//
-//      // Add "runtime" to the set of packages to be loaded.
-//      conf.Import("runtime")
-//
-//      // Adds "fmt" and "fmt_test" to the set of packages
-//      // to be loaded.  "fmt" will include *_test.go files.
-//      conf.ImportWithTests("fmt")
-//
-//      // Finally, load all the packages specified by the configuration.
-//      prog, err := conf.Load()
-//
-//
-// CONCEPTS AND TERMINOLOGY
-//
-// An AD-HOC package is one specified as a set of source files on the
-// command line.  In the simplest case, it may consist of a single file
-// such as $GOROOT/src/net/http/triv.go.
-//
-// EXTERNAL TEST packages are those comprised of a set of *_test.go
-// files all with the same 'package foo_test' declaration, all in the
-// same directory.  (go/build.Package calls these files XTestFiles.)
-//
-// An IMPORTABLE package is one that can be referred to by some import
-// spec.  The Path() of each importable package is unique within a
-// Program.
-//
-// Ad-hoc packages and external test packages are NON-IMPORTABLE.  The
-// Path() of an ad-hoc package is inferred from the package
-// declarations of its files and is therefore not a unique package key.
-// For example, Config.CreatePkgs may specify two initial ad-hoc
-// packages both called "main".
-//
-// An AUGMENTED package is an importable package P plus all the
-// *_test.go files with same 'package foo' declaration as P.
-// (go/build.Package calls these files TestFiles.)
-//
-// The INITIAL packages are those specified in the configuration.  A
-// DEPENDENCY is a package loaded to satisfy an import in an initial
-// package or another dependency.
-//
 package loader
 
-// 'go test', in-package test files, and import cycles
-// ---------------------------------------------------
-//
-// An external test package may depend upon members of the augmented
-// package that are not in the unaugmented package, such as functions
-// that expose internals.  (See bufio/export_test.go for an example.)
-// So, the loader must ensure that for each external test package
-// it loads, it also augments the corresponding non-test package.
-//
-// The import graph over n unaugmented packages must be acyclic; the
-// import graph over n-1 unaugmented packages plus one augmented
-// package must also be acyclic.  ('go test' relies on this.)  But the
-// import graph over n augmented packages may contain cycles.
-//
-// First, all the (unaugmented) non-test packages and their
-// dependencies are imported in the usual way; the loader reports an
-// error if it detects an import cycle.
-//
-// Then, each package P for which testing is desired is augmented by
-// the list P' of its in-package test files, by calling
-// (*types.Checker).Files.  This arrangement ensures that P' may
-// reference definitions within P, but P may not reference definitions
-// within P'.  Furthermore, P' may import any other package, including
-// ones that depend upon P, without an import cycle error.
-//
-// Consider two packages A and B, both of which have lists of
-// in-package test files we'll call A' and B', and which have the
-// following import graph edges:
-//    B  imports A
-//    B' imports A
-//    A' imports B
-// This last edge would be expected to create an error were it not
-// for the special type-checking discipline above.
-// Cycles of size greater than two are possible.  For example:
-//   compress/bzip2/bzip2_test.go (package bzip2)  imports "io/ioutil"
-//   io/ioutil/tempfile_test.go   (package ioutil) imports "regexp"
-//   regexp/exec_test.go          (package regexp) imports "compress/bzip2"
-//
-//
-// Concurrency
-// -----------
-//
-// Let us define the import dependency graph as follows.  Each node is a
-// list of files passed to (Checker).Files at once.  Many of these lists
-// are the production code of an importable Go package, so those nodes
-// are labelled by the package's import path.  The remaining nodes are
-// ad-hoc packages and lists of in-package *_test.go files that augment
-// an importable package; those nodes have no label.
-//
-// The edges of the graph represent import statements appearing within a
-// file.  An edge connects a node (a list of files) to the node it
-// imports, which is importable and thus always labelled.
-//
-// Loading is controlled by this dependency graph.
-//
-// To reduce I/O latency, we start loading a package's dependencies
-// asynchronously as soon as we've parsed its files and enumerated its
-// imports (scanImports).  This performs a preorder traversal of the
-// import dependency graph.
-//
-// To exploit hardware parallelism, we type-check unrelated packages in
-// parallel, where "unrelated" means not ordered by the partial order of
-// the import dependency graph.
-//
-// We use a concurrency-safe blocking cache (importer.imported) to
-// record the results of type-checking, whether success or failure.  An
-// entry is created in this cache by startLoad the first time the
-// package is imported.  The first goroutine to request an entry becomes
-// responsible for completing the task and broadcasting completion to
-// subsequent requestors, which block until then.
-//
-// Type checking occurs in (parallel) postorder: we cannot type-check a
-// set of files until we have loaded and type-checked all of their
-// immediate dependencies (and thus all of their transitive
-// dependencies). If the input were guaranteed free of import cycles,
-// this would be trivial: we could simply wait for completion of the
-// dependencies and then invoke the typechecker.
-//
-// But as we saw in the 'go test' section above, some cycles in the
-// import graph over packages are actually legal, so long as the
-// cycle-forming edge originates in the in-package test files that
-// augment the package.  This explains why the nodes of the import
-// dependency graph are not packages, but lists of files: the unlabelled
-// nodes avoid the cycles.  Consider packages A and B where B imports A
-// and A's in-package tests AT import B.  The naively constructed import
-// graph over packages would contain a cycle (A+AT) --> B --> (A+AT) but
-// the graph over lists of files is AT --> B --> A, where AT is an
-// unlabelled node.
-//
-// Awaiting completion of the dependencies in a cyclic graph would
-// deadlock, so we must materialize the import dependency graph (as
-// importer.graph) and check whether each import edge forms a cycle.  If
-// x imports y, and the graph already contains a path from y to x, then
-// there is an import cycle, in which case the processing of x must not
-// wait for the completion of processing of y.
-//
-// When the type-checker makes a callback (doImport) to the loader for a
-// given import edge, there are two possible cases.  In the normal case,
-// the dependency has already been completely type-checked; doImport
-// does a cache lookup and returns it.  In the cyclic case, the entry in
-// the cache is still necessarily incomplete, indicating a cycle.  We
-// perform the cycle check again to obtain the error message, and return
-// the error.
-//
-// The result of using concurrency is about a 2.5x speedup for stdlib_test.
-
-// TODO(adonovan):
-// - cache the calls to build.Import so we don't do it three times per
-//   test package.
-// - Thorough overhaul of package documentation.
+// See doc.go for package documentation and implementation notes.
 
 import (
 	"errors"
@@ -353,16 +173,16 @@
 	// as specified by Config.ImportPkgs.
 	Imported map[string]*PackageInfo
 
-	// ImportMap is the canonical mapping of import paths to
-	// packages used by the type-checker (Config.TypeChecker.Packages).
-	// It contains all Imported initial packages, but not Created
-	// ones, and all imported dependencies.
-	ImportMap map[string]*types.Package
-
 	// AllPackages contains the PackageInfo of every package
 	// encountered by Load: all initial packages and all
 	// dependencies, including incomplete ones.
 	AllPackages map[*types.Package]*PackageInfo
+
+	// importMap is the canonical mapping of import paths to
+	// packages used by the type-checker.
+	// It contains all Imported initial packages, but not Created
+	// ones, and all imported dependencies.
+	importMap map[string]*types.Package
 }
 
 // PackageInfo holds the ASTs and facts derived by the type-checker
@@ -561,6 +381,20 @@
 	return infos
 }
 
+// Package returns the ASTs and results of type checking for the
+// specified package.
+func (prog *Program) Package(path string) *PackageInfo {
+	if info, ok := prog.AllPackages[prog.importMap[path]]; ok {
+		return info
+	}
+	for _, info := range prog.Created {
+		if path == info.Pkg.Path() {
+			return info
+		}
+	}
+	return nil
+}
+
 // ---------- Implementation ----------
 
 // importer holds the working state of the algorithm.
@@ -661,6 +495,8 @@
 	// Install default FindPackage hook using go/build logic.
 	if conf.FindPackage == nil {
 		conf.FindPackage = func(ctxt *build.Context, path string) (*build.Package, error) {
+			// TODO(adonovan): cache calls to build.Import
+			// so we don't do it three times per test package.
 			bp, err := ctxt.Import(path, conf.Cwd, 0)
 			if _, ok := err.(*build.NoGoError); ok {
 				return bp, nil // empty directory is not an error
@@ -672,7 +508,7 @@
 	prog := &Program{
 		Fset:        conf.fset(),
 		Imported:    make(map[string]*PackageInfo),
-		ImportMap:   conf.TypeChecker.Packages,
+		importMap:   conf.TypeChecker.Packages,
 		AllPackages: make(map[*types.Package]*PackageInfo),
 	}
 
@@ -779,7 +615,7 @@
 
 	// Create infos for indirectly imported packages.
 	// e.g. incomplete packages without syntax, loaded from export data.
-	for _, obj := range prog.ImportMap {
+	for _, obj := range prog.importMap {
 		info := prog.AllPackages[obj]
 		if info == nil {
 			prog.AllPackages[obj] = &PackageInfo{Pkg: obj, Importable: true}