1 // Copyright 2019 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.
19 "golang.org/x/mod/module"
20 "golang.org/x/tools/go/packages"
21 "golang.org/x/tools/internal/event"
22 "golang.org/x/tools/internal/lsp/debug/tag"
23 "golang.org/x/tools/internal/lsp/source"
24 "golang.org/x/tools/internal/memoize"
25 "golang.org/x/tools/internal/span"
26 "golang.org/x/tools/internal/typesinternal"
27 errors "golang.org/x/xerrors"
30 type packageHandleKey string
32 type packageHandle struct {
33 handle *memoize.Handle
35 goFiles, compiledGoFiles []*parseGoHandle
37 // mode is the mode the files were parsed in.
40 // m is the metadata associated with the package.
43 // key is the hashed key for the package.
47 func (ph *packageHandle) packageKey() packageKey {
54 // packageData contains the data produced by type-checking a package.
55 type packageData struct {
60 // buildPackageHandle returns a packageHandle for a given package and mode.
61 func (s *snapshot) buildPackageHandle(ctx context.Context, id packageID, mode source.ParseMode) (*packageHandle, error) {
62 if ph := s.getPackage(id, mode); ph != nil {
66 // Build the packageHandle for this ID and its dependencies.
67 ph, deps, err := s.buildKey(ctx, id, mode)
72 // Do not close over the packageHandle or the snapshot in the Bind function.
73 // This creates a cycle, which causes the finalizers to never run on the handles.
74 // The possible cycles are:
76 // packageHandle.h.function -> packageHandle
77 // packageHandle.h.function -> snapshot -> packageHandle
83 h := s.generation.Bind(key, func(ctx context.Context, arg memoize.Arg) interface{} {
84 snapshot := arg.(*snapshot)
86 // Begin loading the direct dependencies, in parallel.
88 for _, dep := range deps {
90 go func(dep *packageHandle) {
91 dep.check(ctx, snapshot)
96 data := &packageData{}
97 data.pkg, data.err = typeCheck(ctx, snapshot, m, mode, deps)
98 // Make sure that the workers above have finished before we return,
99 // especially in case of cancellation.
106 // Cache the handle in the snapshot. If a package handle has already
107 // been cached, addPackage will return the cached value. This is fine,
108 // since the original package handle above will have no references and be
109 // garbage collected.
110 ph = s.addPackageHandle(ph)
115 // buildKey computes the key for a given packageHandle.
116 func (s *snapshot) buildKey(ctx context.Context, id packageID, mode source.ParseMode) (*packageHandle, map[packagePath]*packageHandle, error) {
117 m := s.getMetadata(id)
119 return nil, nil, errors.Errorf("no metadata for %s", id)
121 goFiles, err := s.parseGoHandles(ctx, m.goFiles, mode)
125 compiledGoFiles, err := s.parseGoHandles(ctx, m.compiledGoFiles, mode)
129 ph := &packageHandle{
132 compiledGoFiles: compiledGoFiles,
135 // Make sure all of the depList are sorted.
136 depList := append([]packageID{}, m.deps...)
137 sort.Slice(depList, func(i, j int) bool {
138 return depList[i] < depList[j]
141 deps := make(map[packagePath]*packageHandle)
143 // Begin computing the key by getting the depKeys for all dependencies.
144 var depKeys []packageHandleKey
145 for _, depID := range depList {
146 depHandle, err := s.buildPackageHandle(ctx, depID, s.workspaceParseMode(depID))
148 event.Error(ctx, fmt.Sprintf("%s: no dep handle for %s", id, depID), err, tag.Snapshot.Of(s.id))
149 if ctx.Err() != nil {
150 return nil, nil, ctx.Err()
152 // One bad dependency should not prevent us from checking the entire package.
153 // Add a special key to mark a bad dependency.
154 depKeys = append(depKeys, packageHandleKey(fmt.Sprintf("%s import not found", id)))
157 deps[depHandle.m.pkgPath] = depHandle
158 depKeys = append(depKeys, depHandle.key)
160 experimentalKey := s.View().Options().ExperimentalPackageCacheKey
161 ph.key = checkPackageKey(ctx, ph.m.id, compiledGoFiles, m.config, depKeys, mode, experimentalKey)
165 func (s *snapshot) workspaceParseMode(id packageID) source.ParseMode {
166 if _, ws := s.isWorkspacePackage(id); ws {
167 return source.ParseFull
169 return source.ParseExported
173 func checkPackageKey(ctx context.Context, id packageID, pghs []*parseGoHandle, cfg *packages.Config, deps []packageHandleKey, mode source.ParseMode, experimentalKey bool) packageHandleKey {
174 b := bytes.NewBuffer(nil)
175 b.WriteString(string(id))
176 if !experimentalKey {
177 // cfg was used to produce the other hashed inputs (package ID, parsed Go
178 // files, and deps). It should not otherwise affect the inputs to the type
179 // checker, so this experiment omits it. This should increase cache hits on
180 // the daemon as cfg contains the environment and working directory.
181 b.WriteString(hashConfig(cfg))
183 b.WriteByte(byte(mode))
184 for _, dep := range deps {
185 b.WriteString(string(dep))
187 for _, cgf := range pghs {
188 b.WriteString(cgf.file.FileIdentity().String())
190 return packageHandleKey(hashContents(b.Bytes()))
193 // hashEnv returns a hash of the snapshot's configuration.
194 func hashEnv(s *snapshot) string {
195 s.view.optionsMu.Lock()
196 env := s.view.options.EnvSlice()
197 s.view.optionsMu.Unlock()
200 for _, e := range env {
203 return hashContents(b.Bytes())
206 // hashConfig returns the hash for the *packages.Config.
207 func hashConfig(config *packages.Config) string {
208 b := bytes.NewBuffer(nil)
210 // Dir, Mode, Env, BuildFlags are the parts of the config that can change.
211 b.WriteString(config.Dir)
212 b.WriteString(string(rune(config.Mode)))
214 for _, e := range config.Env {
217 for _, f := range config.BuildFlags {
220 return hashContents(b.Bytes())
223 func (ph *packageHandle) Check(ctx context.Context, s source.Snapshot) (source.Package, error) {
224 return ph.check(ctx, s.(*snapshot))
227 func (ph *packageHandle) check(ctx context.Context, s *snapshot) (*pkg, error) {
228 v, err := ph.handle.Get(ctx, s.generation, s)
232 data := v.(*packageData)
233 return data.pkg, data.err
236 func (ph *packageHandle) CompiledGoFiles() []span.URI {
237 return ph.m.compiledGoFiles
240 func (ph *packageHandle) ID() string {
241 return string(ph.m.id)
244 func (ph *packageHandle) cached(g *memoize.Generation) (*pkg, error) {
245 v := ph.handle.Cached(g)
247 return nil, errors.Errorf("no cached type information for %s", ph.m.pkgPath)
249 data := v.(*packageData)
250 return data.pkg, data.err
253 func (s *snapshot) parseGoHandles(ctx context.Context, files []span.URI, mode source.ParseMode) ([]*parseGoHandle, error) {
254 pghs := make([]*parseGoHandle, 0, len(files))
255 for _, uri := range files {
256 fh, err := s.GetFile(ctx, uri)
260 pghs = append(pghs, s.parseGoHandle(ctx, fh, mode))
265 func typeCheck(ctx context.Context, snapshot *snapshot, m *metadata, mode source.ParseMode, deps map[packagePath]*packageHandle) (*pkg, error) {
266 ctx, done := event.Start(ctx, "cache.importer.typeCheck", tag.Package.Of(string(m.id)))
269 var rawErrors []error
270 for _, err := range m.errors {
271 rawErrors = append(rawErrors, err)
274 fset := snapshot.view.session.cache.fset
278 goFiles: make([]*source.ParsedGoFile, len(m.goFiles)),
279 compiledGoFiles: make([]*source.ParsedGoFile, len(m.compiledGoFiles)),
280 imports: make(map[packagePath]*pkg),
281 typesSizes: m.typesSizes,
282 typesInfo: &types.Info{
283 Types: make(map[ast.Expr]types.TypeAndValue),
284 Defs: make(map[*ast.Ident]types.Object),
285 Uses: make(map[*ast.Ident]types.Object),
286 Implicits: make(map[ast.Node]types.Object),
287 Selections: make(map[*ast.SelectorExpr]*types.Selection),
288 Scopes: make(map[ast.Node]*types.Scope),
291 // If this is a replaced module in the workspace, the version is
292 // meaningless, and we don't want clients to access it.
294 version := m.module.Version
295 if source.IsWorkspaceModuleVersion(version) {
298 pkg.version = &module.Version{
304 files = make([]*ast.File, len(m.compiledGoFiles))
305 parseErrors = make([]error, len(m.compiledGoFiles))
306 actualErrors = make([]error, len(m.compiledGoFiles))
312 for i, cgf := range m.compiledGoFiles {
314 go func(i int, cgf span.URI) {
316 fh, err := snapshot.GetFile(ctx, cgf)
318 actualErrors[i] = err
321 pgh := snapshot.parseGoHandle(ctx, fh, mode)
322 pgf, fixed, err := snapshot.parseGo(ctx, pgh)
324 actualErrors[i] = err
327 pkg.compiledGoFiles[i] = pgf
328 files[i], parseErrors[i], actualErrors[i] = pgf.File, pgf.ParseErr, err
331 skipTypeErrors = skipTypeErrors || fixed
335 for i, gf := range m.goFiles {
337 // We need to parse the non-compiled go files, but we don't care about their errors.
338 go func(i int, gf span.URI) {
340 fh, err := snapshot.GetFile(ctx, gf)
344 pgf, _ := snapshot.ParseGo(ctx, fh, mode)
349 for _, err := range actualErrors {
355 for _, e := range parseErrors {
357 rawErrors = append(rawErrors, e)
362 for _, f := range files {
370 // Use the default type information for the unsafe package.
371 if pkg.m.pkgPath == "unsafe" {
372 pkg.types = types.Unsafe
373 // Don't type check Unsafe: it's unnecessary, and doing so exposes a data
374 // race to Unsafe.completed.
376 } else if len(files) == 0 { // not the unsafe package, no parsed files
377 // Try to attach errors messages to the file as much as possible.
379 for _, e := range rawErrors {
380 srcErr, err := sourceError(ctx, snapshot, pkg, e)
385 pkg.errors = append(pkg.errors, srcErr)
390 return nil, errors.Errorf("no parsed files for package %s, expected: %v, list errors: %v", pkg.m.pkgPath, pkg.compiledGoFiles, rawErrors)
392 pkg.types = types.NewPackage(string(m.pkgPath), string(m.name))
395 cfg := &types.Config{
396 Error: func(e error) {
397 // If we have fixed parse errors in any of the files,
398 // we should hide type errors, as they may be completely nonsensical.
402 rawErrors = append(rawErrors, e)
404 Importer: importerFunc(func(pkgPath string) (*types.Package, error) {
405 // If the context was cancelled, we should abort.
406 if ctx.Err() != nil {
407 return nil, ctx.Err()
409 dep := resolveImportPath(pkgPath, pkg, deps)
411 return nil, snapshot.missingPkgError(pkgPath)
413 if !isValidImport(m.pkgPath, dep.m.pkgPath) {
414 return nil, errors.Errorf("invalid use of internal package %s", pkgPath)
416 depPkg, err := dep.check(ctx, snapshot)
420 pkg.imports[depPkg.m.pkgPath] = depPkg
421 return depPkg.types, nil
424 // We want to type check cgo code if go/types supports it.
425 // We passed typecheckCgo to go/packages when we Loaded.
426 typesinternal.SetUsesCgo(cfg)
428 check := types.NewChecker(cfg, fset, pkg.types, pkg.typesInfo)
430 // Type checking errors are handled via the config, so ignore them here.
431 _ = check.Files(files)
432 // If the context was cancelled, we may have returned a ton of transient
433 // errors to the type checker. Swallow them.
434 if ctx.Err() != nil {
435 return nil, ctx.Err()
438 // We don't care about a package's errors unless we have parsed it in full.
439 if mode == source.ParseFull {
440 expandErrors(rawErrors)
441 for _, e := range rawErrors {
442 srcErr, err := sourceError(ctx, snapshot, pkg, e)
444 event.Error(ctx, "unable to compute error positions", err, tag.Package.Of(pkg.ID()))
447 pkg.errors = append(pkg.errors, srcErr)
448 if err, ok := e.(extendedError); ok {
449 pkg.typeErrors = append(pkg.typeErrors, err.primary)
457 // missingPkgError returns an error message for a missing package that varies
458 // based on the user's workspace mode.
459 func (s *snapshot) missingPkgError(pkgPath string) error {
460 if s.workspaceMode()&moduleMode != 0 {
461 return fmt.Errorf("no required module provides package %q", pkgPath)
463 gorootSrcPkg := filepath.FromSlash(filepath.Join(s.view.goroot, "src", pkgPath))
465 var b strings.Builder
466 b.WriteString(fmt.Sprintf("cannot find package %q in any of \n\t%s (from $GOROOT)", pkgPath, gorootSrcPkg))
468 for _, gopath := range strings.Split(s.view.gopath, ":") {
469 gopathSrcPkg := filepath.FromSlash(filepath.Join(gopath, "src", pkgPath))
470 b.WriteString(fmt.Sprintf("\n\t%s (from $GOPATH)", gopathSrcPkg))
472 return errors.New(b.String())
475 type extendedError struct {
477 secondaries []types.Error
480 func (e extendedError) Error() string {
481 return e.primary.Error()
484 // expandErrors duplicates "secondary" errors by mapping them to their main
485 // error. Some errors returned by the type checker are followed by secondary
486 // errors which give more information about the error. These are errors in
487 // their own right, and they are marked by starting with \t. For instance, when
488 // there is a multiply-defined function, the secondary error points back to the
489 // definition first noticed.
491 // This code associates the secondary error with its primary error, which can
492 // then be used as RelatedInformation when the error becomes a diagnostic.
493 func expandErrors(errs []error) []error {
494 for i := 0; i < len(errs); {
495 e, ok := errs[i].(types.Error)
500 enew := extendedError{
504 for ; j < len(errs); j++ {
505 spl, ok := errs[j].(types.Error)
506 if !ok || len(spl.Msg) == 0 || spl.Msg[0] != '\t' {
509 enew.secondaries = append(enew.secondaries, spl)
517 // resolveImportPath resolves an import path in pkg to a package from deps.
518 // It should produce the same results as resolveImportPath:
519 // https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/load/pkg.go;drc=641918ee09cb44d282a30ee8b66f99a0b63eaef9;l=990.
520 func resolveImportPath(importPath string, pkg *pkg, deps map[packagePath]*packageHandle) *packageHandle {
521 if dep := deps[packagePath(importPath)]; dep != nil {
524 // We may be in GOPATH mode, in which case we need to check vendor dirs.
525 searchDir := path.Dir(pkg.PkgPath())
527 vdir := packagePath(path.Join(searchDir, "vendor", importPath))
528 if vdep := deps[vdir]; vdep != nil {
532 // Search until Dir doesn't take us anywhere new, e.g. "." or "/".
533 next := path.Dir(searchDir)
534 if searchDir == next {
540 // Vendor didn't work. Let's try minimal module compatibility mode.
541 // In MMC, the packagePath is the canonical (.../vN/...) path, which
542 // is hard to calculate. But the go command has already resolved the ID
543 // to the non-versioned path, and we can take advantage of that.
544 for _, dep := range deps {
545 if dep.ID() == importPath {
552 func isValidImport(pkgPath, importPkgPath packagePath) bool {
553 i := strings.LastIndex(string(importPkgPath), "/internal/")
557 if pkgPath == "command-line-arguments" {
560 return strings.HasPrefix(string(pkgPath), string(importPkgPath[:i]))
563 // An importFunc is an implementation of the single-method
564 // types.Importer interface based on a function value.
565 type importerFunc func(path string) (*types.Package, error)
567 func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }