github.com/jhump/golang-x-tools@v0.0.0-20220218190644-4958d6d39439/go/loader/doc.go (about) 1 // Copyright 2015 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 loads a complete Go program from source code, parsing 6 // and type-checking the initial packages plus their transitive closure 7 // of dependencies. The ASTs and the derived facts are retained for 8 // later use. 9 // 10 // Deprecated: This is an older API and does not have support 11 // for modules. Use golang.org/x/tools/go/packages instead. 12 // 13 // The package defines two primary types: Config, which specifies a 14 // set of initial packages to load and various other options; and 15 // Program, which is the result of successfully loading the packages 16 // specified by a configuration. 17 // 18 // The configuration can be set directly, but *Config provides various 19 // convenience methods to simplify the common cases, each of which can 20 // be called any number of times. Finally, these are followed by a 21 // call to Load() to actually load and type-check the program. 22 // 23 // var conf loader.Config 24 // 25 // // Use the command-line arguments to specify 26 // // a set of initial packages to load from source. 27 // // See FromArgsUsage for help. 28 // rest, err := conf.FromArgs(os.Args[1:], wantTests) 29 // 30 // // Parse the specified files and create an ad hoc package with path "foo". 31 // // All files must have the same 'package' declaration. 32 // conf.CreateFromFilenames("foo", "foo.go", "bar.go") 33 // 34 // // Create an ad hoc package with path "foo" from 35 // // the specified already-parsed files. 36 // // All ASTs must have the same 'package' declaration. 37 // conf.CreateFromFiles("foo", parsedFiles) 38 // 39 // // Add "runtime" to the set of packages to be loaded. 40 // conf.Import("runtime") 41 // 42 // // Adds "fmt" and "fmt_test" to the set of packages 43 // // to be loaded. "fmt" will include *_test.go files. 44 // conf.ImportWithTests("fmt") 45 // 46 // // Finally, load all the packages specified by the configuration. 47 // prog, err := conf.Load() 48 // 49 // See examples_test.go for examples of API usage. 50 // 51 // 52 // CONCEPTS AND TERMINOLOGY 53 // 54 // The WORKSPACE is the set of packages accessible to the loader. The 55 // workspace is defined by Config.Build, a *build.Context. The 56 // default context treats subdirectories of $GOROOT and $GOPATH as 57 // packages, but this behavior may be overridden. 58 // 59 // An AD HOC package is one specified as a set of source files on the 60 // command line. In the simplest case, it may consist of a single file 61 // such as $GOROOT/src/net/http/triv.go. 62 // 63 // EXTERNAL TEST packages are those comprised of a set of *_test.go 64 // files all with the same 'package foo_test' declaration, all in the 65 // same directory. (go/build.Package calls these files XTestFiles.) 66 // 67 // An IMPORTABLE package is one that can be referred to by some import 68 // spec. Every importable package is uniquely identified by its 69 // PACKAGE PATH or just PATH, a string such as "fmt", "encoding/json", 70 // or "cmd/vendor/golang.org/x/arch/x86/x86asm". A package path 71 // typically denotes a subdirectory of the workspace. 72 // 73 // An import declaration uses an IMPORT PATH to refer to a package. 74 // Most import declarations use the package path as the import path. 75 // 76 // Due to VENDORING (https://golang.org/s/go15vendor), the 77 // interpretation of an import path may depend on the directory in which 78 // it appears. To resolve an import path to a package path, go/build 79 // must search the enclosing directories for a subdirectory named 80 // "vendor". 81 // 82 // ad hoc packages and external test packages are NON-IMPORTABLE. The 83 // path of an ad hoc package is inferred from the package 84 // declarations of its files and is therefore not a unique package key. 85 // For example, Config.CreatePkgs may specify two initial ad hoc 86 // packages, both with path "main". 87 // 88 // An AUGMENTED package is an importable package P plus all the 89 // *_test.go files with same 'package foo' declaration as P. 90 // (go/build.Package calls these files TestFiles.) 91 // 92 // The INITIAL packages are those specified in the configuration. A 93 // DEPENDENCY is a package loaded to satisfy an import in an initial 94 // package or another dependency. 95 // 96 package loader 97 98 // IMPLEMENTATION NOTES 99 // 100 // 'go test', in-package test files, and import cycles 101 // --------------------------------------------------- 102 // 103 // An external test package may depend upon members of the augmented 104 // package that are not in the unaugmented package, such as functions 105 // that expose internals. (See bufio/export_test.go for an example.) 106 // So, the loader must ensure that for each external test package 107 // it loads, it also augments the corresponding non-test package. 108 // 109 // The import graph over n unaugmented packages must be acyclic; the 110 // import graph over n-1 unaugmented packages plus one augmented 111 // package must also be acyclic. ('go test' relies on this.) But the 112 // import graph over n augmented packages may contain cycles. 113 // 114 // First, all the (unaugmented) non-test packages and their 115 // dependencies are imported in the usual way; the loader reports an 116 // error if it detects an import cycle. 117 // 118 // Then, each package P for which testing is desired is augmented by 119 // the list P' of its in-package test files, by calling 120 // (*types.Checker).Files. This arrangement ensures that P' may 121 // reference definitions within P, but P may not reference definitions 122 // within P'. Furthermore, P' may import any other package, including 123 // ones that depend upon P, without an import cycle error. 124 // 125 // Consider two packages A and B, both of which have lists of 126 // in-package test files we'll call A' and B', and which have the 127 // following import graph edges: 128 // B imports A 129 // B' imports A 130 // A' imports B 131 // This last edge would be expected to create an error were it not 132 // for the special type-checking discipline above. 133 // Cycles of size greater than two are possible. For example: 134 // compress/bzip2/bzip2_test.go (package bzip2) imports "io/ioutil" 135 // io/ioutil/tempfile_test.go (package ioutil) imports "regexp" 136 // regexp/exec_test.go (package regexp) imports "compress/bzip2" 137 // 138 // 139 // Concurrency 140 // ----------- 141 // 142 // Let us define the import dependency graph as follows. Each node is a 143 // list of files passed to (Checker).Files at once. Many of these lists 144 // are the production code of an importable Go package, so those nodes 145 // are labelled by the package's path. The remaining nodes are 146 // ad hoc packages and lists of in-package *_test.go files that augment 147 // an importable package; those nodes have no label. 148 // 149 // The edges of the graph represent import statements appearing within a 150 // file. An edge connects a node (a list of files) to the node it 151 // imports, which is importable and thus always labelled. 152 // 153 // Loading is controlled by this dependency graph. 154 // 155 // To reduce I/O latency, we start loading a package's dependencies 156 // asynchronously as soon as we've parsed its files and enumerated its 157 // imports (scanImports). This performs a preorder traversal of the 158 // import dependency graph. 159 // 160 // To exploit hardware parallelism, we type-check unrelated packages in 161 // parallel, where "unrelated" means not ordered by the partial order of 162 // the import dependency graph. 163 // 164 // We use a concurrency-safe non-blocking cache (importer.imported) to 165 // record the results of type-checking, whether success or failure. An 166 // entry is created in this cache by startLoad the first time the 167 // package is imported. The first goroutine to request an entry becomes 168 // responsible for completing the task and broadcasting completion to 169 // subsequent requestors, which block until then. 170 // 171 // Type checking occurs in (parallel) postorder: we cannot type-check a 172 // set of files until we have loaded and type-checked all of their 173 // immediate dependencies (and thus all of their transitive 174 // dependencies). If the input were guaranteed free of import cycles, 175 // this would be trivial: we could simply wait for completion of the 176 // dependencies and then invoke the typechecker. 177 // 178 // But as we saw in the 'go test' section above, some cycles in the 179 // import graph over packages are actually legal, so long as the 180 // cycle-forming edge originates in the in-package test files that 181 // augment the package. This explains why the nodes of the import 182 // dependency graph are not packages, but lists of files: the unlabelled 183 // nodes avoid the cycles. Consider packages A and B where B imports A 184 // and A's in-package tests AT import B. The naively constructed import 185 // graph over packages would contain a cycle (A+AT) --> B --> (A+AT) but 186 // the graph over lists of files is AT --> B --> A, where AT is an 187 // unlabelled node. 188 // 189 // Awaiting completion of the dependencies in a cyclic graph would 190 // deadlock, so we must materialize the import dependency graph (as 191 // importer.graph) and check whether each import edge forms a cycle. If 192 // x imports y, and the graph already contains a path from y to x, then 193 // there is an import cycle, in which case the processing of x must not 194 // wait for the completion of processing of y. 195 // 196 // When the type-checker makes a callback (doImport) to the loader for a 197 // given import edge, there are two possible cases. In the normal case, 198 // the dependency has already been completely type-checked; doImport 199 // does a cache lookup and returns it. In the cyclic case, the entry in 200 // the cache is still necessarily incomplete, indicating a cycle. We 201 // perform the cycle check again to obtain the error message, and return 202 // the error. 203 // 204 // The result of using concurrency is about a 2.5x speedup for stdlib_test.