blob: edf62c2cc031a099e490667a7a7cd1197b71c8b6 [file] [log] [blame]
Alan Donovan713699d2013-08-27 18:49:13 -04001// 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
Alan Donovan9c9660e2014-12-15 13:20:21 -05005package loader
Alan Donovancd987a32014-03-11 15:41:57 -04006
Alan Donovan1b6275a2015-04-16 15:42:28 -04007// See doc.go for package documentation and implementation notes.
Alan Donovane8afbfa2014-01-15 21:37:55 -05008
Alan Donovanbe28dbb2013-05-31 16:14:13 -04009import (
Alan Donovan3f2f9a72013-09-06 18:13:57 -040010 "errors"
Alan Donovan2a3a1292013-07-30 14:28:14 -040011 "fmt"
Alan Donovanbe28dbb2013-05-31 16:14:13 -040012 "go/ast"
Alan Donovane2921e12013-09-04 13:15:49 -040013 "go/build"
Alan Donovane8afbfa2014-01-15 21:37:55 -050014 "go/parser"
Alan Donovanbe28dbb2013-05-31 16:14:13 -040015 "go/token"
Alan Donovan542ffc72015-12-29 13:06:30 -050016 "go/types"
Alan Donovan2a3a1292013-07-30 14:28:14 -040017 "os"
Alan Donovan6c803ab2016-11-28 16:48:17 -050018 "path/filepath"
Alan Donovan0dda50d2015-01-30 13:30:23 -050019 "sort"
Alan Donovan3f2f9a72013-09-06 18:13:57 -040020 "strings"
Alan Donovan9c9660e2014-12-15 13:20:21 -050021 "sync"
22 "time"
Alan Donovanbe28dbb2013-05-31 16:14:13 -040023
Peter Collingbourne4f8578d2015-01-08 18:32:51 -080024 "golang.org/x/tools/go/ast/astutil"
Michael Matloob1f253522018-08-03 12:55:38 -040025 "golang.org/x/tools/go/internal/cgo"
Tim King33002ea2022-02-14 21:42:10 +000026 "golang.org/x/tools/internal/typeparams"
Alan Donovanbe28dbb2013-05-31 16:14:13 -040027)
28
Alan Donovan10712092016-01-08 13:18:57 -050029var ignoreVendor build.ImportMode
30
Alan Donovan9c9660e2014-12-15 13:20:21 -050031const trace = false // show timing info for type-checking
32
Alan Donovan9c57c192015-04-14 16:56:02 -040033// Config specifies the configuration for loading a whole program from
34// Go source code.
Alan Donovan3d82e7e2014-01-09 14:11:54 -050035// The zero value for Config is a ready-to-use default configuration.
Alan Donovan8a9eca12013-07-19 11:02:27 -040036type Config struct {
Alan Donovane8afbfa2014-01-15 21:37:55 -050037 // Fset is the file set for the parser to use when loading the
Alan Donovan0dda50d2015-01-30 13:30:23 -050038 // program. If nil, it may be lazily initialized by any
Alan Donovane8afbfa2014-01-15 21:37:55 -050039 // method of Config.
40 Fset *token.FileSet
41
Alan Donovan24146772014-03-27 12:50:26 -040042 // ParserMode specifies the mode to be used by the parser when
43 // loading source packages.
44 ParserMode parser.Mode
45
Alan Donovanbe28dbb2013-05-31 16:14:13 -040046 // TypeChecker contains options relating to the type checker.
Alan Donovanf1198742013-12-13 10:04:55 -050047 //
48 // The supplied IgnoreFuncBodies is not used; the effective
49 // value comes from the TypeCheckFuncBodies func below.
Alan Donovan9c57c192015-04-14 16:56:02 -040050 // The supplied Import function is not used either.
Robert Griesemer40a278e2013-07-18 17:07:44 -070051 TypeChecker types.Config
Alan Donovanbe28dbb2013-05-31 16:14:13 -040052
Alan Donovand6e83e52015-12-18 21:02:28 -050053 // TypeCheckFuncBodies is a predicate over package paths.
54 // A package for which the predicate is false will
Alan Donovanf1198742013-12-13 10:04:55 -050055 // have its package-level declarations type checked, but not
56 // its function bodies; this can be used to quickly load
57 // dependencies from source. If nil, all func bodies are type
58 // checked.
Alan Donovand6e83e52015-12-18 21:02:28 -050059 TypeCheckFuncBodies func(path string) bool
Alan Donovanf1198742013-12-13 10:04:55 -050060
Alan Donovan3d82e7e2014-01-09 14:11:54 -050061 // If Build is non-nil, it is used to locate source packages.
62 // Otherwise &build.Default is used.
Alan Donovan7746b672014-06-11 13:16:51 -040063 //
64 // By default, cgo is invoked to preprocess Go files that
65 // import the fake package "C". This behaviour can be
66 // disabled by setting CGO_ENABLED=0 in the environment prior
67 // to startup, or by setting Build.CgoEnabled=false.
Alan Donovane2921e12013-09-04 13:15:49 -040068 Build *build.Context
Alan Donovane8afbfa2014-01-15 21:37:55 -050069
Alan Donovan8cc1c752015-03-12 14:33:01 -040070 // The current directory, used for resolving relative package
71 // references such as "./go/loader". If empty, os.Getwd will be
72 // used instead.
73 Cwd string
74
Alan Donovane3dc58c2014-03-14 16:17:53 -040075 // If DisplayPath is non-nil, it is used to transform each
76 // file name obtained from Build.Import(). This can be used
77 // to prevent a virtualized build.Config's file names from
78 // leaking into the user interface.
79 DisplayPath func(path string) string
80
Alan Donovanf0ff5112014-06-13 11:32:46 -040081 // If AllowErrors is true, Load will return a Program even
82 // if some of the its packages contained I/O, parser or type
83 // errors; such errors are accessible via PackageInfo.Errors. If
84 // false, Load will fail if any package had an error.
85 AllowErrors bool
Alan Donovaned45af72014-02-18 19:43:14 -050086
Alan Donovane8afbfa2014-01-15 21:37:55 -050087 // CreatePkgs specifies a list of non-importable initial
Alan Donovan0dda50d2015-01-30 13:30:23 -050088 // packages to create. The resulting packages will appear in
89 // the corresponding elements of the Program.Created slice.
90 CreatePkgs []PkgSpec
Alan Donovane8afbfa2014-01-15 21:37:55 -050091
Alan Donovand6e83e52015-12-18 21:02:28 -050092 // ImportPkgs specifies a set of initial packages to load.
93 // The map keys are package paths.
Alan Donovan0dda50d2015-01-30 13:30:23 -050094 //
95 // The map value indicates whether to load tests. If true, Load
96 // will add and type-check two lists of files to the package:
97 // non-test files followed by in-package *_test.go files. In
98 // addition, it will append the external test package (if any)
99 // to Program.Created.
Alan Donovane8afbfa2014-01-15 21:37:55 -0500100 ImportPkgs map[string]bool
Peter Collingbourned3e75672014-12-28 11:32:27 -0800101
Alan Donovan51931f82015-02-20 11:23:27 -0500102 // FindPackage is called during Load to create the build.Package
Alan Donovand6e83e52015-12-18 21:02:28 -0500103 // for a given import path from a given directory.
Alan Donovan5a2875a2016-01-12 12:34:15 -0500104 // If FindPackage is nil, (*build.Context).Import is used.
105 // A client may use this hook to adapt to a proprietary build
106 // system that does not follow the "go build" layout
107 // conventions, for example.
Alan Donovan51931f82015-02-20 11:23:27 -0500108 //
109 // It must be safe to call concurrently from multiple goroutines.
Kirill Smelkovc2d03f42017-07-03 17:18:56 +0300110 FindPackage func(ctxt *build.Context, importPath, fromDir string, mode build.ImportMode) (*build.Package, error)
Alan Donovan2336c532016-02-21 18:45:02 -0500111
112 // AfterTypeCheck is called immediately after a list of files
113 // has been type-checked and appended to info.Files.
114 //
115 // This optional hook function is the earliest opportunity for
116 // the client to observe the output of the type checker,
117 // which may be useful to reduce analysis latency when loading
118 // a large program.
119 //
120 // The function is permitted to modify info.Info, for instance
121 // to clear data structures that are no longer needed, which can
122 // dramatically reduce peak memory consumption.
123 //
124 // The function may be called twice for the same PackageInfo:
125 // once for the files of the package and again for the
126 // in-package test files.
127 //
128 // It must be safe to call concurrently from multiple goroutines.
129 AfterTypeCheck func(info *PackageInfo, files []*ast.File)
Alan Donovanbe28dbb2013-05-31 16:14:13 -0400130}
131
Alan Donovan0dda50d2015-01-30 13:30:23 -0500132// A PkgSpec specifies a non-importable package to be created by Load.
133// Files are processed first, but typically only one of Files and
134// Filenames is provided. The path needn't be globally unique.
135//
Alan Donovan6c803ab2016-11-28 16:48:17 -0500136// For vendoring purposes, the package's directory is the one that
137// contains the first file.
Alan Donovan0dda50d2015-01-30 13:30:23 -0500138type PkgSpec struct {
Alan Donovand6e83e52015-12-18 21:02:28 -0500139 Path string // package path ("" => use package declaration)
Alan Donovan0dda50d2015-01-30 13:30:23 -0500140 Files []*ast.File // ASTs of already-parsed files
141 Filenames []string // names of files to be parsed
Alan Donovan0dcaae12014-01-22 09:59:19 -0500142}
143
Alan Donovan9c57c192015-04-14 16:56:02 -0400144// A Program is a Go program loaded from source as specified by a Config.
Alan Donovane8afbfa2014-01-15 21:37:55 -0500145type Program struct {
146 Fset *token.FileSet // the file set for this program
147
Alan Donovan0dda50d2015-01-30 13:30:23 -0500148 // Created[i] contains the initial package whose ASTs or
149 // filenames were supplied by Config.CreatePkgs[i], followed by
150 // the external test package, if any, of each package in
151 // Config.ImportPkgs ordered by ImportPath.
Alan Donovan9d2ff752015-10-05 10:55:06 -0400152 //
153 // NOTE: these files must not import "C". Cgo preprocessing is
154 // only performed on imported packages, not ad hoc packages.
155 //
156 // TODO(adonovan): we need to copy and adapt the logic of
157 // goFilesPackage (from $GOROOT/src/cmd/go/build.go) and make
158 // Config.Import and Config.Create methods return the same kind
159 // of entity, essentially a build.Package.
160 // Perhaps we can even reuse that type directly.
Alan Donovane8afbfa2014-01-15 21:37:55 -0500161 Created []*PackageInfo
162
163 // Imported contains the initially imported packages,
164 // as specified by Config.ImportPkgs.
165 Imported map[string]*PackageInfo
166
Alan Donovane8afbfa2014-01-15 21:37:55 -0500167 // AllPackages contains the PackageInfo of every package
168 // encountered by Load: all initial packages and all
169 // dependencies, including incomplete ones.
170 AllPackages map[*types.Package]*PackageInfo
Alan Donovan1b6275a2015-04-16 15:42:28 -0400171
Alan Donovand6e83e52015-12-18 21:02:28 -0500172 // importMap is the canonical mapping of package paths to
Alan Donovan9c57c192015-04-14 16:56:02 -0400173 // packages. It contains all Imported initial packages, but not
174 // Created ones, and all imported dependencies.
Alan Donovan1b6275a2015-04-16 15:42:28 -0400175 importMap map[string]*types.Package
Alan Donovan3d82e7e2014-01-09 14:11:54 -0500176}
177
Alan Donovancd619932014-07-17 15:10:29 -0400178// PackageInfo holds the ASTs and facts derived by the type-checker
179// for a single package.
180//
181// Not mutated once exposed via the API.
Alan Donovancd619932014-07-17 15:10:29 -0400182type 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.
Alan Donovand6e83e52015-12-18 21:02:28 -0500189 dir string // package directory
Alan Donovancd619932014-07-17 15:10:29 -0400190
Alan Donovan3bbc6302014-08-07 12:50:15 -0400191 checker *types.Checker // transient type-checker state
192 errorFunc func(error)
Alan Donovancd619932014-07-17 15:10:29 -0400193}
194
195func (info *PackageInfo) String() string { return info.Pkg.Path() }
196
Alan Donovan3bbc6302014-08-07 12:50:15 -0400197func (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
Alan Donovane8afbfa2014-01-15 21:37:55 -0500206func (conf *Config) fset() *token.FileSet {
207 if conf.Fset == nil {
208 conf.Fset = token.NewFileSet()
Alan Donovanf1198742013-12-13 10:04:55 -0500209 }
Alan Donovane8afbfa2014-01-15 21:37:55 -0500210 return conf.Fset
Alan Donovanbe28dbb2013-05-31 16:14:13 -0400211}
212
Alan Donovan0dda50d2015-01-30 13:30:23 -0500213// 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.
Alan Donovan24146772014-03-27 12:50:26 -0400219func (conf *Config) ParseFile(filename string, src interface{}) (*ast.File, error) {
Alan Donovanb8d26f52014-11-13 12:34:25 -0500220 // TODO(adonovan): use conf.build() etc like parseFiles does.
Alan Donovan24146772014-03-27 12:50:26 -0400221 return parser.ParseFile(conf.fset(), filename, src, conf.ParserMode)
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400222}
223
Alan Donovane8afbfa2014-01-15 21:37:55 -0500224// FromArgsUsage is a partial usage message that applications calling
225// FromArgs may wish to include in their -help output.
226const FromArgsUsage = `
Alan Donovan9cce4752013-10-10 12:37:49 -0400227<args> is a list of arguments denoting a set of initial packages.
Alan Donovan08fadac2014-02-11 16:52:16 -0500228It may take one of two forms:
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400229
Alan Donovan08fadac2014-02-11 16:52:16 -05002301. A list of *.go source files.
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400231
232 All of the specified files are loaded, parsed and type-checked
Alan Donovan08fadac2014-02-11 16:52:16 -0500233 as a single package. All the files must belong to the same directory.
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400234
Alan Donovan08fadac2014-02-11 16:52:16 -05002352. A list of import paths, each denoting a package.
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400236
237 The package's directory is found relative to the $GOROOT and
238 $GOPATH using similar logic to 'go build', and the *.go files in
Alan Donovan9cce4752013-10-10 12:37:49 -0400239 that directory are loaded, parsed and type-checked as a single
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400240 package.
241
242 In addition, all *_test.go files in the directory are then loaded
243 and parsed. Those files whose package declaration equals that of
244 the non-*_test.go files are included in the primary package. Test
245 files whose package declaration ends with "_test" are type-checked
246 as another package, the 'external' test package, so that a single
Alan Donovan08fadac2014-02-11 16:52:16 -0500247 import path may denote two packages. (Whether this behaviour is
248 enabled is tool-specific, and may depend on additional flags.)
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400249
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400250A '--' argument terminates the list of packages.
251`
252
Alan Donovane8afbfa2014-01-15 21:37:55 -0500253// FromArgs interprets args as a set of initial packages to load from
254// source and updates the configuration. It returns the list of
255// unconsumed arguments.
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400256//
257// It is intended for use in command-line interfaces that require a
Alan Donovane8afbfa2014-01-15 21:37:55 -0500258// set of initial packages to be specified; see FromArgsUsage message
259// for details.
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400260//
Alan Donovan0dda50d2015-01-30 13:30:23 -0500261// Only superficial errors are reported at this stage; errors dependent
262// on I/O are detected during Load.
Alan Donovan0dda50d2015-01-30 13:30:23 -0500263func (conf *Config) FromArgs(args []string, xtest bool) ([]string, error) {
264 var rest []string
Alan Donovan08fadac2014-02-11 16:52:16 -0500265 for i, arg := range args {
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400266 if arg == "--" {
Alan Donovan08fadac2014-02-11 16:52:16 -0500267 rest = args[i+1:]
268 args = args[:i]
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400269 break // consume "--" and return the remaining args
270 }
Alan Donovan08fadac2014-02-11 16:52:16 -0500271 }
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400272
Alan Donovan08fadac2014-02-11 16:52:16 -0500273 if len(args) > 0 && strings.HasSuffix(args[0], ".go") {
274 // Assume args is a list of a *.go files
Alan Donovan9c57c192015-04-14 16:56:02 -0400275 // denoting a single ad hoc package.
Alan Donovan08fadac2014-02-11 16:52:16 -0500276 for _, arg := range args {
277 if !strings.HasSuffix(arg, ".go") {
278 return nil, fmt.Errorf("named files must be .go files: %s", arg)
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400279 }
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400280 }
Alan Donovan0dda50d2015-01-30 13:30:23 -0500281 conf.CreateFromFilenames("", args...)
Alan Donovan08fadac2014-02-11 16:52:16 -0500282 } 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 {
Alan Donovan0dda50d2015-01-30 13:30:23 -0500287 conf.ImportWithTests(arg)
Alan Donovan08fadac2014-02-11 16:52:16 -0500288 } else {
289 conf.Import(arg)
290 }
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400291 }
Alan Donovane8afbfa2014-01-15 21:37:55 -0500292 }
Alan Donovan08fadac2014-02-11 16:52:16 -0500293
Alan Donovan0dda50d2015-01-30 13:30:23 -0500294 return rest, nil
Alan Donovane8afbfa2014-01-15 21:37:55 -0500295}
296
Alan Donovan0dda50d2015-01-30 13:30:23 -0500297// CreateFromFilenames is a convenience function that adds
298// a conf.CreatePkgs entry to create a package of the specified *.go
299// files.
Alan Donovan0dda50d2015-01-30 13:30:23 -0500300func (conf *Config) CreateFromFilenames(path string, filenames ...string) {
301 conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Filenames: filenames})
Alan Donovane8afbfa2014-01-15 21:37:55 -0500302}
303
Alan Donovan0dda50d2015-01-30 13:30:23 -0500304// CreateFromFiles is a convenience function that adds a conf.CreatePkgs
Alan Donovan0dcaae12014-01-22 09:59:19 -0500305// entry to create package of the specified path and parsed files.
Alan Donovan0dcaae12014-01-22 09:59:19 -0500306func (conf *Config) CreateFromFiles(path string, files ...*ast.File) {
Alan Donovan0dda50d2015-01-30 13:30:23 -0500307 conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Files: files})
Alan Donovane8afbfa2014-01-15 21:37:55 -0500308}
309
310// ImportWithTests is a convenience function that adds path to
311// ImportPkgs, the set of initial source packages located relative to
312// $GOPATH. The package will be augmented by any *_test.go files in
313// its directory that contain a "package x" (not "package x_test")
314// declaration.
315//
Alan Donovan0dcaae12014-01-22 09:59:19 -0500316// In addition, if any *_test.go files contain a "package x_test"
Alan Donovane8afbfa2014-01-15 21:37:55 -0500317// declaration, an additional package comprising just those files will
318// be added to CreatePkgs.
Alan Donovan0dda50d2015-01-30 13:30:23 -0500319func (conf *Config) ImportWithTests(path string) { conf.addImport(path, true) }
Alan Donovane8afbfa2014-01-15 21:37:55 -0500320
321// Import is a convenience function that adds path to ImportPkgs, the
322// set of initial packages that will be imported from source.
Alan Donovan0dda50d2015-01-30 13:30:23 -0500323func (conf *Config) Import(path string) { conf.addImport(path, false) }
324
325func (conf *Config) addImport(path string, tests bool) {
Alan Donovanae186f52016-01-08 14:46:14 -0500326 if path == "C" {
Alan Donovane8afbfa2014-01-15 21:37:55 -0500327 return // ignore; not a real package
328 }
329 if conf.ImportPkgs == nil {
330 conf.ImportPkgs = make(map[string]bool)
331 }
Alan Donovan0dda50d2015-01-30 13:30:23 -0500332 conf.ImportPkgs[path] = conf.ImportPkgs[path] || tests
Alan Donovanbe28dbb2013-05-31 16:14:13 -0400333}
Alan Donovanfb3d8622013-12-09 09:36:29 -0500334
335// PathEnclosingInterval returns the PackageInfo and ast.Node that
336// contain source interval [start, end), and all the node's ancestors
Alan Donovane8afbfa2014-01-15 21:37:55 -0500337// up to the AST root. It searches all ast.Files of all packages in prog.
338// exact is defined as for astutil.PathEnclosingInterval.
Alan Donovanfb3d8622013-12-09 09:36:29 -0500339//
Alan Donovaned45af72014-02-18 19:43:14 -0500340// The zero value is returned if not found.
Alan Donovane8afbfa2014-01-15 21:37:55 -0500341func (prog *Program) PathEnclosingInterval(start, end token.Pos) (pkg *PackageInfo, path []ast.Node, exact bool) {
342 for _, info := range prog.AllPackages {
Alan Donovanfb3d8622013-12-09 09:36:29 -0500343 for _, f := range info.Files {
Alan Donovan3d184722015-05-20 15:58:44 -0400344 if f.Pos() == token.NoPos {
345 // This can happen if the parser saw
346 // too many errors and bailed out.
347 // (Use parser.AllErrors to prevent that.)
348 continue
349 }
Alan Donovane8afbfa2014-01-15 21:37:55 -0500350 if !tokenFileContainsPos(prog.Fset.File(f.Pos()), start) {
Alan Donovanfb3d8622013-12-09 09:36:29 -0500351 continue
352 }
353 if path, exact := astutil.PathEnclosingInterval(f, start, end); path != nil {
354 return info, path, exact
355 }
356 }
357 }
358 return nil, nil, false
359}
360
Alan Donovane8afbfa2014-01-15 21:37:55 -0500361// InitialPackages returns a new slice containing the set of initial
362// packages (Created + Imported) in unspecified order.
Alan Donovane8afbfa2014-01-15 21:37:55 -0500363func (prog *Program) InitialPackages() []*PackageInfo {
364 infos := make([]*PackageInfo, 0, len(prog.Created)+len(prog.Imported))
365 infos = append(infos, prog.Created...)
366 for _, info := range prog.Imported {
367 infos = append(infos, info)
368 }
369 return infos
370}
371
Alan Donovan1b6275a2015-04-16 15:42:28 -0400372// Package returns the ASTs and results of type checking for the
373// specified package.
374func (prog *Program) Package(path string) *PackageInfo {
375 if info, ok := prog.AllPackages[prog.importMap[path]]; ok {
376 return info
377 }
378 for _, info := range prog.Created {
379 if path == info.Pkg.Path() {
380 return info
381 }
382 }
383 return nil
384}
385
Alan Donovane8afbfa2014-01-15 21:37:55 -0500386// ---------- Implementation ----------
387
388// importer holds the working state of the algorithm.
389type importer struct {
Alan Donovan9c9660e2014-12-15 13:20:21 -0500390 conf *Config // the client configuration
Alan Donovan9c9660e2014-12-15 13:20:21 -0500391 start time.Time // for logging
392
Alan Donovan9c57c192015-04-14 16:56:02 -0400393 progMu sync.Mutex // guards prog
394 prog *Program // the resulting program
Alan Donovan9c9660e2014-12-15 13:20:21 -0500395
Alan Donovand6e83e52015-12-18 21:02:28 -0500396 // findpkg is a memoization of FindPackage.
Alan Donovan0f870d42016-01-06 11:34:23 -0500397 findpkgMu sync.Mutex // guards findpkg
Alan Donovan5a2875a2016-01-12 12:34:15 -0500398 findpkg map[findpkgKey]*findpkgValue
Alan Donovand6e83e52015-12-18 21:02:28 -0500399
Alan Donovan9c57c192015-04-14 16:56:02 -0400400 importedMu sync.Mutex // guards imported
Alan Donovan9c9660e2014-12-15 13:20:21 -0500401 imported map[string]*importInfo // all imported packages (incl. failures) by import path
402
403 // import dependency graph: graph[x][y] => x imports y
404 //
405 // Since non-importable packages cannot be cyclic, we ignore
406 // their imports, thus we only need the subgraph over importable
407 // packages. Nodes are identified by their import paths.
408 graphMu sync.Mutex
409 graph map[string]map[string]bool
Alan Donovane8afbfa2014-01-15 21:37:55 -0500410}
411
Alan Donovan0f870d42016-01-06 11:34:23 -0500412type findpkgKey struct {
413 importPath string
414 fromDir string
415 mode build.ImportMode
416}
417
Alan Donovand6e83e52015-12-18 21:02:28 -0500418type findpkgValue struct {
Alan Donovan5a2875a2016-01-12 12:34:15 -0500419 ready chan struct{} // closed to broadcast readiness
420 bp *build.Package
421 err error
Alan Donovand6e83e52015-12-18 21:02:28 -0500422}
423
Alan Donovane8afbfa2014-01-15 21:37:55 -0500424// importInfo tracks the success or failure of a single import.
Alan Donovan9c9660e2014-12-15 13:20:21 -0500425//
426// Upon completion, exactly one of info and err is non-nil:
427// info on successful creation of a package, err otherwise.
428// A successful package may still contain type errors.
Alan Donovane8afbfa2014-01-15 21:37:55 -0500429type importInfo struct {
Alan Donovand6e83e52015-12-18 21:02:28 -0500430 path string // import path
431 info *PackageInfo // results of typechecking (including errors)
432 complete chan struct{} // closed to broadcast that info is set.
Alan Donovan9c9660e2014-12-15 13:20:21 -0500433}
434
435// awaitCompletion blocks until ii is complete,
Alan Donovand6e83e52015-12-18 21:02:28 -0500436// i.e. the info field is safe to inspect.
Alan Donovan9c9660e2014-12-15 13:20:21 -0500437func (ii *importInfo) awaitCompletion() {
Alan Donovand6e83e52015-12-18 21:02:28 -0500438 <-ii.complete // wait for close
Alan Donovan9c9660e2014-12-15 13:20:21 -0500439}
440
441// Complete marks ii as complete.
442// Its info and err fields will not be subsequently updated.
Alan Donovand6e83e52015-12-18 21:02:28 -0500443func (ii *importInfo) Complete(info *PackageInfo) {
444 if info == nil {
445 panic("info == nil")
Alan Donovan739a26a2015-07-29 14:08:07 -0400446 }
Alan Donovan9c9660e2014-12-15 13:20:21 -0500447 ii.info = info
Alan Donovand6e83e52015-12-18 21:02:28 -0500448 close(ii.complete)
449}
450
451type importError struct {
452 path string // import path
453 err error // reason for failure to create a package
Alan Donovane8afbfa2014-01-15 21:37:55 -0500454}
455
456// Load creates the initial packages specified by conf.{Create,Import}Pkgs,
457// loading their dependencies packages as needed.
458//
Alan Donovaned45af72014-02-18 19:43:14 -0500459// On success, Load returns a Program containing a PackageInfo for
460// each package. On failure, it returns an error.
461//
Alan Donovanf0ff5112014-06-13 11:32:46 -0400462// If AllowErrors is true, Load will return a Program even if some
463// packages contained I/O, parser or type errors, or if dependencies
464// were missing. (Such errors are accessible via PackageInfo.Errors. If
465// false, Load will fail if any package had an error.
Alan Donovane8afbfa2014-01-15 21:37:55 -0500466//
467// It is an error if no packages were loaded.
Alan Donovane8afbfa2014-01-15 21:37:55 -0500468func (conf *Config) Load() (*Program, error) {
Alan Donovan3bbc6302014-08-07 12:50:15 -0400469 // Create a simple default error handler for parse/type errors.
470 if conf.TypeChecker.Error == nil {
471 conf.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) }
472 }
473
Alan Donovan8cc1c752015-03-12 14:33:01 -0400474 // Set default working directory for relative package references.
475 if conf.Cwd == "" {
476 var err error
477 conf.Cwd, err = os.Getwd()
478 if err != nil {
479 return nil, err
480 }
481 }
482
483 // Install default FindPackage hook using go/build logic.
Alan Donovan51931f82015-02-20 11:23:27 -0500484 if conf.FindPackage == nil {
Alan Donovan5a2875a2016-01-12 12:34:15 -0500485 conf.FindPackage = (*build.Context).Import
Alan Donovan51931f82015-02-20 11:23:27 -0500486 }
487
Alan Donovane8afbfa2014-01-15 21:37:55 -0500488 prog := &Program{
489 Fset: conf.fset(),
490 Imported: make(map[string]*PackageInfo),
Alan Donovan9c57c192015-04-14 16:56:02 -0400491 importMap: make(map[string]*types.Package),
Alan Donovane8afbfa2014-01-15 21:37:55 -0500492 AllPackages: make(map[*types.Package]*PackageInfo),
493 }
494
495 imp := importer{
496 conf: conf,
497 prog: prog,
Alan Donovan5a2875a2016-01-12 12:34:15 -0500498 findpkg: make(map[findpkgKey]*findpkgValue),
Alan Donovane8afbfa2014-01-15 21:37:55 -0500499 imported: make(map[string]*importInfo),
Alan Donovan9c9660e2014-12-15 13:20:21 -0500500 start: time.Now(),
501 graph: make(map[string]map[string]bool),
Alan Donovane8afbfa2014-01-15 21:37:55 -0500502 }
503
Alan Donovan9c9660e2014-12-15 13:20:21 -0500504 // -- loading proper (concurrent phase) --------------------------------
505
Alan Donovan0dda50d2015-01-30 13:30:23 -0500506 var errpkgs []string // packages that contained errors
507
Alan Donovan9c9660e2014-12-15 13:20:21 -0500508 // Load the initially imported packages and their dependencies,
509 // in parallel.
Alan Donovan0f870d42016-01-06 11:34:23 -0500510 // No vendor check on packages imported from the command line.
Alan Donovan10712092016-01-08 13:18:57 -0500511 infos, importErrors := imp.importAll("", conf.Cwd, conf.ImportPkgs, ignoreVendor)
Alan Donovand6e83e52015-12-18 21:02:28 -0500512 for _, ie := range importErrors {
513 conf.TypeChecker.Error(ie.err) // failed to create package
514 errpkgs = append(errpkgs, ie.path)
515 }
516 for _, info := range infos {
517 prog.Imported[info.Pkg.Path()] = info
Alan Donovane8afbfa2014-01-15 21:37:55 -0500518 }
519
Alan Donovan0dda50d2015-01-30 13:30:23 -0500520 // Augment the designated initial packages by their tests.
Alan Donovan9c9660e2014-12-15 13:20:21 -0500521 // Dependencies are loaded in parallel.
Alan Donovan0dda50d2015-01-30 13:30:23 -0500522 var xtestPkgs []*build.Package
Alan Donovand6e83e52015-12-18 21:02:28 -0500523 for importPath, augment := range conf.ImportPkgs {
Alan Donovan0dda50d2015-01-30 13:30:23 -0500524 if !augment {
525 continue
Alan Donovancd987a32014-03-11 15:41:57 -0400526 }
Alan Donovan0dda50d2015-01-30 13:30:23 -0500527
Alan Donovan0f870d42016-01-06 11:34:23 -0500528 // No vendor check on packages imported from command line.
Alan Donovan10712092016-01-08 13:18:57 -0500529 bp, err := imp.findPackage(importPath, conf.Cwd, ignoreVendor)
Alan Donovan0dda50d2015-01-30 13:30:23 -0500530 if err != nil {
531 // Package not found, or can't even parse package declaration.
532 // Already reported by previous loop; ignore it.
533 continue
534 }
535
536 // Needs external test package?
537 if len(bp.XTestGoFiles) > 0 {
538 xtestPkgs = append(xtestPkgs, bp)
539 }
540
Alan Donovand6e83e52015-12-18 21:02:28 -0500541 // Consult the cache using the canonical package path.
542 path := bp.ImportPath
Alan Donovan739a26a2015-07-29 14:08:07 -0400543 imp.importedMu.Lock() // (unnecessary, we're sequential here)
544 ii, ok := imp.imported[path]
545 // Paranoid checks added due to issue #11012.
546 if !ok {
547 // Unreachable.
Alan Donovand6e83e52015-12-18 21:02:28 -0500548 // The previous loop called importAll and thus
Alan Donovan739a26a2015-07-29 14:08:07 -0400549 // startLoad for each path in ImportPkgs, which
550 // populates imp.imported[path] with a non-zero value.
551 panic(fmt.Sprintf("imported[%q] not found", path))
552 }
553 if ii == nil {
554 // Unreachable.
555 // The ii values in this loop are the same as in
556 // the previous loop, which enforced the invariant
557 // that at least one of ii.err and ii.info is non-nil.
558 panic(fmt.Sprintf("imported[%q] == nil", path))
559 }
Alan Donovan739a26a2015-07-29 14:08:07 -0400560 if ii.info == nil {
561 // Unreachable.
Alan Donovand6e83e52015-12-18 21:02:28 -0500562 // awaitCompletion has the postcondition
563 // ii.info != nil.
Alan Donovan739a26a2015-07-29 14:08:07 -0400564 panic(fmt.Sprintf("imported[%q].info = nil", path))
565 }
566 info := ii.info
Alan Donovan0dda50d2015-01-30 13:30:23 -0500567 imp.importedMu.Unlock()
568
569 // Parse the in-package test files.
570 files, errs := imp.conf.parsePackageFiles(bp, 't')
571 for _, err := range errs {
572 info.appendError(err)
573 }
574
575 // The test files augmenting package P cannot be imported,
576 // but may import packages that import P,
577 // so we must disable the cycle check.
578 imp.addFiles(info, files, false)
Alan Donovancd987a32014-03-11 15:41:57 -0400579 }
580
Alan Donovan6c803ab2016-11-28 16:48:17 -0500581 createPkg := func(path, dir string, files []*ast.File, errs []error) {
582 info := imp.newPackageInfo(path, dir)
Alan Donovan0dda50d2015-01-30 13:30:23 -0500583 for _, err := range errs {
584 info.appendError(err)
585 }
586
Alan Donovan9c57c192015-04-14 16:56:02 -0400587 // Ad hoc packages are non-importable,
Alan Donovan0dda50d2015-01-30 13:30:23 -0500588 // so no cycle check is needed.
589 // addFiles loads dependencies in parallel.
590 imp.addFiles(info, files, false)
Alan Donovancd987a32014-03-11 15:41:57 -0400591 prog.Created = append(prog.Created, info)
Alan Donovane8afbfa2014-01-15 21:37:55 -0500592 }
593
Alan Donovan0dda50d2015-01-30 13:30:23 -0500594 // Create packages specified by conf.CreatePkgs.
595 for _, cp := range conf.CreatePkgs {
Alan Donovanb56ef302017-02-07 15:33:46 -0500596 files, errs := parseFiles(conf.fset(), conf.build(), nil, conf.Cwd, cp.Filenames, conf.ParserMode)
Alan Donovan0dda50d2015-01-30 13:30:23 -0500597 files = append(files, cp.Files...)
598
599 path := cp.Path
Alan Donovan8913eae2015-02-27 17:05:06 -0500600 if path == "" {
601 if len(files) > 0 {
602 path = files[0].Name.Name
603 } else {
604 path = "(unnamed)"
605 }
Alan Donovan0dda50d2015-01-30 13:30:23 -0500606 }
Alan Donovan6c803ab2016-11-28 16:48:17 -0500607
Alan Donovanb56ef302017-02-07 15:33:46 -0500608 dir := conf.Cwd
Alan Donovan6c803ab2016-11-28 16:48:17 -0500609 if len(files) > 0 && files[0].Pos().IsValid() {
610 dir = filepath.Dir(conf.fset().File(files[0].Pos()).Name())
611 }
612 createPkg(path, dir, files, errs)
Alan Donovan0dda50d2015-01-30 13:30:23 -0500613 }
614
615 // Create external test packages.
616 sort.Sort(byImportPath(xtestPkgs))
617 for _, bp := range xtestPkgs {
618 files, errs := imp.conf.parsePackageFiles(bp, 'x')
Alan Donovan6c803ab2016-11-28 16:48:17 -0500619 createPkg(bp.ImportPath+"_test", bp.Dir, files, errs)
Alan Donovan0dda50d2015-01-30 13:30:23 -0500620 }
621
Alan Donovan9c9660e2014-12-15 13:20:21 -0500622 // -- finishing up (sequential) ----------------------------------------
623
Alan Donovane8afbfa2014-01-15 21:37:55 -0500624 if len(prog.Imported)+len(prog.Created) == 0 {
Alan Donovan0dda50d2015-01-30 13:30:23 -0500625 return nil, errors.New("no initial packages were loaded")
Alan Donovane8afbfa2014-01-15 21:37:55 -0500626 }
627
628 // Create infos for indirectly imported packages.
629 // e.g. incomplete packages without syntax, loaded from export data.
Alan Donovan1b6275a2015-04-16 15:42:28 -0400630 for _, obj := range prog.importMap {
Alan Donovancd987a32014-03-11 15:41:57 -0400631 info := prog.AllPackages[obj]
632 if info == nil {
Alan Donovane8afbfa2014-01-15 21:37:55 -0500633 prog.AllPackages[obj] = &PackageInfo{Pkg: obj, Importable: true}
Alan Donovancd987a32014-03-11 15:41:57 -0400634 } else {
Alan Donovan3bbc6302014-08-07 12:50:15 -0400635 // finished
636 info.checker = nil
637 info.errorFunc = nil
Alan Donovane8afbfa2014-01-15 21:37:55 -0500638 }
639 }
640
Alan Donovanf0ff5112014-06-13 11:32:46 -0400641 if !conf.AllowErrors {
Alan Donovaned45af72014-02-18 19:43:14 -0500642 // Report errors in indirectly imported packages.
Alan Donovaned45af72014-02-18 19:43:14 -0500643 for _, info := range prog.AllPackages {
Alan Donovanf0ff5112014-06-13 11:32:46 -0400644 if len(info.Errors) > 0 {
Alan Donovaned45af72014-02-18 19:43:14 -0500645 errpkgs = append(errpkgs, info.Pkg.Path())
646 }
647 }
648 if errpkgs != nil {
Alan Donovanf13ba782014-08-14 12:10:34 -0400649 var more string
650 if len(errpkgs) > 3 {
651 more = fmt.Sprintf(" and %d more", len(errpkgs)-3)
652 errpkgs = errpkgs[:3]
653 }
654 return nil, fmt.Errorf("couldn't load packages due to errors: %s%s",
655 strings.Join(errpkgs, ", "), more)
Alan Donovaned45af72014-02-18 19:43:14 -0500656 }
657 }
658
659 markErrorFreePackages(prog.AllPackages)
660
Alan Donovane8afbfa2014-01-15 21:37:55 -0500661 return prog, nil
662}
663
Alan Donovan0dda50d2015-01-30 13:30:23 -0500664type byImportPath []*build.Package
665
666func (b byImportPath) Len() int { return len(b) }
667func (b byImportPath) Less(i, j int) bool { return b[i].ImportPath < b[j].ImportPath }
668func (b byImportPath) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
669
Alan Donovaned45af72014-02-18 19:43:14 -0500670// markErrorFreePackages sets the TransitivelyErrorFree flag on all
671// applicable packages.
672func markErrorFreePackages(allPackages map[*types.Package]*PackageInfo) {
673 // Build the transpose of the import graph.
674 importedBy := make(map[*types.Package]map[*types.Package]bool)
675 for P := range allPackages {
676 for _, Q := range P.Imports() {
677 clients, ok := importedBy[Q]
678 if !ok {
679 clients = make(map[*types.Package]bool)
680 importedBy[Q] = clients
681 }
682 clients[P] = true
683 }
684 }
685
686 // Find all packages reachable from some error package.
687 reachable := make(map[*types.Package]bool)
688 var visit func(*types.Package)
689 visit = func(p *types.Package) {
690 if !reachable[p] {
691 reachable[p] = true
692 for q := range importedBy[p] {
693 visit(q)
694 }
695 }
696 }
697 for _, info := range allPackages {
Alan Donovanf0ff5112014-06-13 11:32:46 -0400698 if len(info.Errors) > 0 {
Alan Donovaned45af72014-02-18 19:43:14 -0500699 visit(info.Pkg)
700 }
701 }
702
703 // Mark the others as "transitively error-free".
704 for _, info := range allPackages {
705 if !reachable[info.Pkg] {
706 info.TransitivelyErrorFree = true
707 }
708 }
709}
710
Alan Donovane8afbfa2014-01-15 21:37:55 -0500711// build returns the effective build context.
712func (conf *Config) build() *build.Context {
713 if conf.Build != nil {
714 return conf.Build
715 }
716 return &build.Default
717}
718
Alan Donovane3dc58c2014-03-14 16:17:53 -0400719// parsePackageFiles enumerates the files belonging to package path,
Alan Donovanf0ff5112014-06-13 11:32:46 -0400720// then loads, parses and returns them, plus a list of I/O or parse
721// errors that were encountered.
Alan Donovane3dc58c2014-03-14 16:17:53 -0400722//
723// 'which' indicates which files to include:
Alan Donovane3dc58c2014-03-14 16:17:53 -0400724//
Russ Coxd5f48fc2022-04-11 23:03:04 -0400725// 'g': include non-test *.go source files (GoFiles + processed CgoFiles)
726// 't': include in-package *_test.go source files (TestGoFiles)
727// 'x': include external *_test.go source files. (XTestGoFiles)
Alan Donovanf0ff5112014-06-13 11:32:46 -0400728func (conf *Config) parsePackageFiles(bp *build.Package, which rune) ([]*ast.File, []error) {
Alan Donovan02f19282016-02-04 11:15:46 -0500729 if bp.ImportPath == "unsafe" {
Alan Donovanae186f52016-01-08 14:46:14 -0500730 return nil, nil
731 }
Alan Donovane3dc58c2014-03-14 16:17:53 -0400732 var filenames []string
733 switch which {
734 case 'g':
735 filenames = bp.GoFiles
736 case 't':
737 filenames = bp.TestGoFiles
738 case 'x':
739 filenames = bp.XTestGoFiles
740 default:
741 panic(which)
742 }
Alan Donovan7746b672014-06-11 13:16:51 -0400743
Alan Donovanf0ff5112014-06-13 11:32:46 -0400744 files, errs := parseFiles(conf.fset(), conf.build(), conf.DisplayPath, bp.Dir, filenames, conf.ParserMode)
Alan Donovan7746b672014-06-11 13:16:51 -0400745
746 // Preprocess CgoFiles and parse the outputs (sequentially).
747 if which == 'g' && bp.CgoFiles != nil {
Michael Matloob62181fa2018-08-07 15:25:39 -0400748 cgofiles, err := cgo.ProcessFiles(bp, conf.fset(), conf.DisplayPath, conf.ParserMode)
Alan Donovan7746b672014-06-11 13:16:51 -0400749 if err != nil {
Alan Donovanf0ff5112014-06-13 11:32:46 -0400750 errs = append(errs, err)
751 } else {
752 files = append(files, cgofiles...)
Alan Donovan7746b672014-06-11 13:16:51 -0400753 }
Alan Donovan7746b672014-06-11 13:16:51 -0400754 }
755
Alan Donovanf0ff5112014-06-13 11:32:46 -0400756 return files, errs
Alan Donovane3dc58c2014-03-14 16:17:53 -0400757}
758
Alan Donovane8afbfa2014-01-15 21:37:55 -0500759// doImport imports the package denoted by path.
760// It implements the types.Importer signature.
761//
Alan Donovane8afbfa2014-01-15 21:37:55 -0500762// It returns an error if a package could not be created
763// (e.g. go/build or parse error), but type errors are reported via
764// the types.Config.Error callback (the first of which is also saved
765// in the package's PackageInfo).
766//
767// Idempotent.
Alan Donovan9c9660e2014-12-15 13:20:21 -0500768func (imp *importer) doImport(from *PackageInfo, to string) (*types.Package, error) {
Alan Donovanc5ca59a2015-07-14 13:57:11 -0400769 if to == "C" {
770 // This should be unreachable, but ad hoc packages are
771 // not currently subject to cgo preprocessing.
Alan Donovan22934f02018-12-04 17:08:00 -0500772 // See https://golang.org/issue/11627.
Alan Donovanc5ca59a2015-07-14 13:57:11 -0400773 return nil, fmt.Errorf(`the loader doesn't cgo-process ad hoc packages like %q; see Go issue 11627`,
774 from.Pkg.Path())
775 }
Alan Donovane8afbfa2014-01-15 21:37:55 -0500776
Alan Donovan10712092016-01-08 13:18:57 -0500777 bp, err := imp.findPackage(to, from.dir, 0)
Alan Donovand6e83e52015-12-18 21:02:28 -0500778 if err != nil {
779 return nil, err
780 }
781
Alan Donovanae186f52016-01-08 14:46:14 -0500782 // The standard unsafe package is handled specially,
783 // and has no PackageInfo.
Alan Donovan02f19282016-02-04 11:15:46 -0500784 if bp.ImportPath == "unsafe" {
Alan Donovanae186f52016-01-08 14:46:14 -0500785 return types.Unsafe, nil
786 }
787
Alan Donovand6e83e52015-12-18 21:02:28 -0500788 // Look for the package in the cache using its canonical path.
789 path := bp.ImportPath
Alan Donovan9c9660e2014-12-15 13:20:21 -0500790 imp.importedMu.Lock()
Alan Donovand6e83e52015-12-18 21:02:28 -0500791 ii := imp.imported[path]
Alan Donovan9c9660e2014-12-15 13:20:21 -0500792 imp.importedMu.Unlock()
793 if ii == nil {
Alan Donovand6e83e52015-12-18 21:02:28 -0500794 panic("internal error: unexpected import: " + path)
Alan Donovan9c9660e2014-12-15 13:20:21 -0500795 }
796 if ii.info != nil {
797 return ii.info.Pkg, nil
Alan Donovane8afbfa2014-01-15 21:37:55 -0500798 }
799
Alan Donovan9c9660e2014-12-15 13:20:21 -0500800 // Import of incomplete package: this indicates a cycle.
801 fromPath := from.Pkg.Path()
Alan Donovand6e83e52015-12-18 21:02:28 -0500802 if cycle := imp.findPath(path, fromPath); cycle != nil {
Michael Matloob9e48ab12019-07-12 17:14:28 -0400803 // Normalize cycle: start from alphabetically largest node.
804 pos, start := -1, ""
805 for i, s := range cycle {
806 if pos < 0 || s > start {
807 pos, start = i, s
808 }
809 }
810 cycle = append(cycle, cycle[:pos]...)[pos:] // rotate cycle to start from largest
811 cycle = append(cycle, cycle[0]) // add start node to end to show cycliness
Alan Donovan9c9660e2014-12-15 13:20:21 -0500812 return nil, fmt.Errorf("import cycle: %s", strings.Join(cycle, " -> "))
813 }
Alan Donovane8afbfa2014-01-15 21:37:55 -0500814
Alan Donovan9c9660e2014-12-15 13:20:21 -0500815 panic("internal error: import of incomplete (yet acyclic) package: " + fromPath)
Alan Donovane8afbfa2014-01-15 21:37:55 -0500816}
817
Alan Donovand6e83e52015-12-18 21:02:28 -0500818// findPackage locates the package denoted by the importPath in the
819// specified directory.
Alan Donovan0f870d42016-01-06 11:34:23 -0500820func (imp *importer) findPackage(importPath, fromDir string, mode build.ImportMode) (*build.Package, error) {
Alan Donovan5a2875a2016-01-12 12:34:15 -0500821 // We use a non-blocking duplicate-suppressing cache (gopl.io §9.7)
822 // to avoid holding the lock around FindPackage.
Alan Donovan0f870d42016-01-06 11:34:23 -0500823 key := findpkgKey{importPath, fromDir, mode}
Alan Donovand6e83e52015-12-18 21:02:28 -0500824 imp.findpkgMu.Lock()
Alan Donovand6e83e52015-12-18 21:02:28 -0500825 v, ok := imp.findpkg[key]
Alan Donovan5a2875a2016-01-12 12:34:15 -0500826 if ok {
827 // cache hit
828 imp.findpkgMu.Unlock()
829
830 <-v.ready // wait for entry to become ready
831 } else {
832 // Cache miss: this goroutine becomes responsible for
833 // populating the map entry and broadcasting its readiness.
834 v = &findpkgValue{ready: make(chan struct{})}
Alan Donovand6e83e52015-12-18 21:02:28 -0500835 imp.findpkg[key] = v
Alan Donovan5a2875a2016-01-12 12:34:15 -0500836 imp.findpkgMu.Unlock()
837
838 ioLimit <- true
839 v.bp, v.err = imp.conf.FindPackage(imp.conf.build(), importPath, fromDir, mode)
840 <-ioLimit
841
842 if _, ok := v.err.(*build.NoGoError); ok {
843 v.err = nil // empty directory is not an error
844 }
845
846 close(v.ready) // broadcast ready condition
Alan Donovand6e83e52015-12-18 21:02:28 -0500847 }
848 return v.bp, v.err
849}
850
851// importAll loads, parses, and type-checks the specified packages in
Alan Donovan9c9660e2014-12-15 13:20:21 -0500852// parallel and returns their completed importInfos in unspecified order.
Alan Donovane8afbfa2014-01-15 21:37:55 -0500853//
Alan Donovand6e83e52015-12-18 21:02:28 -0500854// fromPath is the package path of the importing package, if it is
Alan Donovan9c9660e2014-12-15 13:20:21 -0500855// importable, "" otherwise. It is used for cycle detection.
856//
Alan Donovand6e83e52015-12-18 21:02:28 -0500857// fromDir is the directory containing the import declaration that
858// caused these imports.
Alan Donovan0f870d42016-01-06 11:34:23 -0500859func (imp *importer) importAll(fromPath, fromDir string, imports map[string]bool, mode build.ImportMode) (infos []*PackageInfo, errors []importError) {
Alan Donovan9c9660e2014-12-15 13:20:21 -0500860 if fromPath != "" {
861 // We're loading a set of imports.
862 //
863 // We must record graph edges from the importing package
864 // to its dependencies, and check for cycles.
865 imp.graphMu.Lock()
866 deps, ok := imp.graph[fromPath]
867 if !ok {
868 deps = make(map[string]bool)
869 imp.graph[fromPath] = deps
870 }
Russ Cox06713c22021-02-18 15:04:08 -0500871 for importPath := range imports {
872 deps[importPath] = true
Alan Donovan9c9660e2014-12-15 13:20:21 -0500873 }
874 imp.graphMu.Unlock()
875 }
876
Russ Cox06713c22021-02-18 15:04:08 -0500877 var pending []*importInfo
878 for importPath := range imports {
Alan Donovan9c9660e2014-12-15 13:20:21 -0500879 if fromPath != "" {
Russ Cox06713c22021-02-18 15:04:08 -0500880 if cycle := imp.findPath(importPath, fromPath); cycle != nil {
881 // Cycle-forming import: we must not check it
882 // since it would deadlock.
Alan Donovan9c9660e2014-12-15 13:20:21 -0500883 if trace {
Dominik Honnef361bcb22016-03-26 06:16:12 +0100884 fmt.Fprintf(os.Stderr, "import cycle: %q\n", cycle)
Alan Donovan9c9660e2014-12-15 13:20:21 -0500885 }
886 continue
887 }
888 }
Russ Cox06713c22021-02-18 15:04:08 -0500889 bp, err := imp.findPackage(importPath, fromDir, mode)
890 if err != nil {
891 errors = append(errors, importError{
892 path: importPath,
893 err: err,
894 })
895 continue
896 }
897 pending = append(pending, imp.startLoad(bp))
898 }
899
900 for _, ii := range pending {
Alan Donovan9c9660e2014-12-15 13:20:21 -0500901 ii.awaitCompletion()
Alan Donovand6e83e52015-12-18 21:02:28 -0500902 infos = append(infos, ii.info)
Alan Donovan9c9660e2014-12-15 13:20:21 -0500903 }
Alan Donovand6e83e52015-12-18 21:02:28 -0500904
905 return infos, errors
Alan Donovan9c9660e2014-12-15 13:20:21 -0500906}
907
908// findPath returns an arbitrary path from 'from' to 'to' in the import
909// graph, or nil if there was none.
910func (imp *importer) findPath(from, to string) []string {
911 imp.graphMu.Lock()
912 defer imp.graphMu.Unlock()
913
914 seen := make(map[string]bool)
915 var search func(stack []string, importPath string) []string
916 search = func(stack []string, importPath string) []string {
917 if !seen[importPath] {
918 seen[importPath] = true
919 stack = append(stack, importPath)
920 if importPath == to {
921 return stack
922 }
923 for x := range imp.graph[importPath] {
924 if p := search(stack, x); p != nil {
925 return p
926 }
927 }
928 }
929 return nil
930 }
931 return search(make([]string, 0, 20), from)
932}
933
934// startLoad initiates the loading, parsing and type-checking of the
935// specified package and its dependencies, if it has not already begun.
936//
937// It returns an importInfo, not necessarily in a completed state. The
Alan Donovand6e83e52015-12-18 21:02:28 -0500938// caller must call awaitCompletion() before accessing its info field.
Alan Donovan9c9660e2014-12-15 13:20:21 -0500939//
940// startLoad is concurrency-safe and idempotent.
Alan Donovand6e83e52015-12-18 21:02:28 -0500941func (imp *importer) startLoad(bp *build.Package) *importInfo {
942 path := bp.ImportPath
Alan Donovan9c9660e2014-12-15 13:20:21 -0500943 imp.importedMu.Lock()
Alan Donovane8afbfa2014-01-15 21:37:55 -0500944 ii, ok := imp.imported[path]
945 if !ok {
Alan Donovand6e83e52015-12-18 21:02:28 -0500946 ii = &importInfo{path: path, complete: make(chan struct{})}
Alan Donovane8afbfa2014-01-15 21:37:55 -0500947 imp.imported[path] = ii
Alan Donovan9c57c192015-04-14 16:56:02 -0400948 go func() {
Alan Donovand6e83e52015-12-18 21:02:28 -0500949 info := imp.load(bp)
950 ii.Complete(info)
Alan Donovan9c57c192015-04-14 16:56:02 -0400951 }()
Alan Donovane8afbfa2014-01-15 21:37:55 -0500952 }
Alan Donovan9c9660e2014-12-15 13:20:21 -0500953 imp.importedMu.Unlock()
Alan Donovane8afbfa2014-01-15 21:37:55 -0500954
Alan Donovan9c9660e2014-12-15 13:20:21 -0500955 return ii
956}
957
Alan Donovan9c57c192015-04-14 16:56:02 -0400958// load implements package loading by parsing Go source files
Alan Donovancd987a32014-03-11 15:41:57 -0400959// located by go/build.
Alan Donovand6e83e52015-12-18 21:02:28 -0500960func (imp *importer) load(bp *build.Package) *PackageInfo {
961 info := imp.newPackageInfo(bp.ImportPath, bp.Dir)
Alan Donovan9c9660e2014-12-15 13:20:21 -0500962 info.Importable = true
Alan Donovanf0ff5112014-06-13 11:32:46 -0400963 files, errs := imp.conf.parsePackageFiles(bp, 'g')
Alan Donovan3bbc6302014-08-07 12:50:15 -0400964 for _, err := range errs {
965 info.appendError(err)
966 }
Alan Donovan9c9660e2014-12-15 13:20:21 -0500967
968 imp.addFiles(info, files, true)
969
Alan Donovan9c57c192015-04-14 16:56:02 -0400970 imp.progMu.Lock()
Alan Donovand6e83e52015-12-18 21:02:28 -0500971 imp.prog.importMap[bp.ImportPath] = info.Pkg
Alan Donovan9c57c192015-04-14 16:56:02 -0400972 imp.progMu.Unlock()
Alan Donovan9c9660e2014-12-15 13:20:21 -0500973
Alan Donovand6e83e52015-12-18 21:02:28 -0500974 return info
Alan Donovane8afbfa2014-01-15 21:37:55 -0500975}
976
Alan Donovan9c9660e2014-12-15 13:20:21 -0500977// addFiles adds and type-checks the specified files to info, loading
978// their dependencies if needed. The order of files determines the
979// package initialization order. It may be called multiple times on the
980// same package. Errors are appended to the info.Errors field.
Alan Donovane8afbfa2014-01-15 21:37:55 -0500981//
Alan Donovan9c9660e2014-12-15 13:20:21 -0500982// cycleCheck determines whether the imports within files create
983// dependency edges that should be checked for potential cycles.
Alan Donovan9c9660e2014-12-15 13:20:21 -0500984func (imp *importer) addFiles(info *PackageInfo, files []*ast.File, cycleCheck bool) {
Alan Donovan9c9660e2014-12-15 13:20:21 -0500985 // Ensure the dependencies are loaded, in parallel.
986 var fromPath string
987 if cycleCheck {
988 fromPath = info.Pkg.Path()
989 }
Alan Donovand6e83e52015-12-18 21:02:28 -0500990 // TODO(adonovan): opt: make the caller do scanImports.
991 // Callers with a build.Package can skip it.
Alan Donovan10712092016-01-08 13:18:57 -0500992 imp.importAll(fromPath, info.dir, scanImports(files), 0)
Alan Donovan9c9660e2014-12-15 13:20:21 -0500993
994 if trace {
995 fmt.Fprintf(os.Stderr, "%s: start %q (%d)\n",
996 time.Since(imp.start), info.Pkg.Path(), len(files))
997 }
998
Alan Donovan63c64812017-06-19 13:33:23 -0400999 // Don't call checker.Files on Unsafe, even with zero files,
1000 // because it would mutate the package, which is a global.
1001 if info.Pkg == types.Unsafe {
1002 if len(files) > 0 {
1003 panic(`"unsafe" package contains unexpected files`)
1004 }
1005 } else {
1006 // Ignore the returned (first) error since we
1007 // already collect them all in the PackageInfo.
1008 info.checker.Files(files)
1009 info.Files = append(info.Files, files...)
Alan Donovan7a49e422017-03-02 16:31:04 -05001010 }
1011
Alan Donovan2336c532016-02-21 18:45:02 -05001012 if imp.conf.AfterTypeCheck != nil {
1013 imp.conf.AfterTypeCheck(info, files)
1014 }
Alan Donovan9c9660e2014-12-15 13:20:21 -05001015
1016 if trace {
1017 fmt.Fprintf(os.Stderr, "%s: stop %q\n",
1018 time.Since(imp.start), info.Pkg.Path())
1019 }
Alan Donovancd987a32014-03-11 15:41:57 -04001020}
1021
Alan Donovand6e83e52015-12-18 21:02:28 -05001022func (imp *importer) newPackageInfo(path, dir string) *PackageInfo {
Alan Donovan7a49e422017-03-02 16:31:04 -05001023 var pkg *types.Package
1024 if path == "unsafe" {
1025 pkg = types.Unsafe
1026 } else {
1027 pkg = types.NewPackage(path, "")
1028 }
Alan Donovane8afbfa2014-01-15 21:37:55 -05001029 info := &PackageInfo{
Alan Donovancd987a32014-03-11 15:41:57 -04001030 Pkg: pkg,
Alan Donovane8afbfa2014-01-15 21:37:55 -05001031 Info: types.Info{
Alan Donovan64ec2062014-01-28 16:46:24 -05001032 Types: make(map[ast.Expr]types.TypeAndValue),
Alan Donovanced954c2014-02-27 13:21:59 -05001033 Defs: make(map[*ast.Ident]types.Object),
1034 Uses: make(map[*ast.Ident]types.Object),
Alan Donovane8afbfa2014-01-15 21:37:55 -05001035 Implicits: make(map[ast.Node]types.Object),
1036 Scopes: make(map[ast.Node]*types.Scope),
1037 Selections: make(map[*ast.SelectorExpr]*types.Selection),
1038 },
Alan Donovan3bbc6302014-08-07 12:50:15 -04001039 errorFunc: imp.conf.TypeChecker.Error,
Alan Donovand6e83e52015-12-18 21:02:28 -05001040 dir: dir,
Alan Donovane8afbfa2014-01-15 21:37:55 -05001041 }
Tim King33002ea2022-02-14 21:42:10 +00001042 typeparams.InitInstanceInfo(&info.Info)
Alan Donovanf0ff5112014-06-13 11:32:46 -04001043
Alan Donovan3bbc6302014-08-07 12:50:15 -04001044 // Copy the types.Config so we can vary it across PackageInfos.
Alan Donovanf0ff5112014-06-13 11:32:46 -04001045 tc := imp.conf.TypeChecker
1046 tc.IgnoreFuncBodies = false
1047 if f := imp.conf.TypeCheckFuncBodies; f != nil {
1048 tc.IgnoreFuncBodies = !f(path)
1049 }
Alan Donovan542ffc72015-12-29 13:06:30 -05001050 tc.Importer = closure{imp, info}
Alan Donovan3bbc6302014-08-07 12:50:15 -04001051 tc.Error = info.appendError // appendError wraps the user's Error function
Alan Donovanf0ff5112014-06-13 11:32:46 -04001052
Alan Donovancd987a32014-03-11 15:41:57 -04001053 info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info)
Alan Donovan9c57c192015-04-14 16:56:02 -04001054 imp.progMu.Lock()
Alan Donovancd987a32014-03-11 15:41:57 -04001055 imp.prog.AllPackages[pkg] = info
Alan Donovan9c57c192015-04-14 16:56:02 -04001056 imp.progMu.Unlock()
Alan Donovane8afbfa2014-01-15 21:37:55 -05001057 return info
Alan Donovanfb3d8622013-12-09 09:36:29 -05001058}
Alan Donovan542ffc72015-12-29 13:06:30 -05001059
1060type closure struct {
1061 imp *importer
1062 info *PackageInfo
1063}
1064
1065func (c closure) Import(to string) (*types.Package, error) { return c.imp.doImport(c.info, to) }