2019-06-21 15:00:02 -06:00
|
|
|
// Copyright 2019 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2019-05-29 12:59:35 -06:00
|
|
|
package cache
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2019-09-27 11:17:59 -06:00
|
|
|
"go/types"
|
2020-01-27 16:59:13 -07:00
|
|
|
"sort"
|
2020-01-07 19:37:41 -07:00
|
|
|
"strings"
|
2019-05-29 12:59:35 -06:00
|
|
|
|
|
|
|
"golang.org/x/tools/go/packages"
|
2020-04-17 07:32:56 -06:00
|
|
|
"golang.org/x/tools/internal/event"
|
2020-03-10 21:09:39 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/debug/tag"
|
2020-02-07 12:38:36 -07:00
|
|
|
"golang.org/x/tools/internal/lsp/source"
|
2020-01-16 12:32:14 -07:00
|
|
|
"golang.org/x/tools/internal/packagesinternal"
|
2019-05-29 12:59:35 -06:00
|
|
|
"golang.org/x/tools/internal/span"
|
2019-08-06 13:13:11 -06:00
|
|
|
errors "golang.org/x/xerrors"
|
2019-05-29 12:59:35 -06:00
|
|
|
)
|
|
|
|
|
2019-09-27 11:17:59 -06:00
|
|
|
type metadata struct {
|
2019-11-20 14:15:00 -07:00
|
|
|
id packageID
|
|
|
|
pkgPath packagePath
|
2020-06-07 19:50:35 -06:00
|
|
|
name packageName
|
2019-11-21 12:54:31 -07:00
|
|
|
goFiles []span.URI
|
2019-11-20 14:15:00 -07:00
|
|
|
compiledGoFiles []span.URI
|
2020-01-16 12:32:14 -07:00
|
|
|
forTest packagePath
|
2019-11-20 14:15:00 -07:00
|
|
|
typesSizes types.Sizes
|
|
|
|
errors []packages.Error
|
|
|
|
deps []packageID
|
|
|
|
missingDeps map[packagePath]struct{}
|
2020-05-15 15:13:55 -06:00
|
|
|
module *packages.Module
|
2019-10-01 13:21:06 -06:00
|
|
|
|
|
|
|
// config is the *packages.Config associated with the loaded package.
|
|
|
|
config *packages.Config
|
2019-06-04 16:06:55 -06:00
|
|
|
}
|
|
|
|
|
2020-01-30 18:44:00 -07:00
|
|
|
func (s *snapshot) load(ctx context.Context, scopes ...interface{}) error {
|
2020-01-14 11:59:17 -07:00
|
|
|
var query []string
|
2020-01-28 22:08:22 -07:00
|
|
|
var containsDir bool // for logging
|
2020-01-14 22:22:42 -07:00
|
|
|
for _, scope := range scopes {
|
|
|
|
switch scope := scope.(type) {
|
2020-01-27 16:59:13 -07:00
|
|
|
case packagePath:
|
2020-03-23 18:16:00 -06:00
|
|
|
if scope == "command-line-arguments" {
|
|
|
|
panic("attempted to load command-line-arguments")
|
|
|
|
}
|
2020-01-22 17:23:30 -07:00
|
|
|
// The only time we pass package paths is when we're doing a
|
|
|
|
// partial workspace load. In those cases, the paths came back from
|
|
|
|
// go list and should already be GOPATH-vendorized when appropriate.
|
2020-01-14 22:22:42 -07:00
|
|
|
query = append(query, string(scope))
|
|
|
|
case fileURI:
|
|
|
|
query = append(query, fmt.Sprintf("file=%s", span.URI(scope).Filename()))
|
|
|
|
case directoryURI:
|
|
|
|
filename := span.URI(scope).Filename()
|
|
|
|
q := fmt.Sprintf("%s/...", filename)
|
|
|
|
// Simplify the query if it will be run in the requested directory.
|
|
|
|
// This ensures compatibility with Go 1.12 that doesn't allow
|
|
|
|
// <directory>/... in GOPATH mode.
|
|
|
|
if s.view.folder.Filename() == filename {
|
|
|
|
q = "./..."
|
|
|
|
}
|
|
|
|
query = append(query, q)
|
2020-01-23 23:22:47 -07:00
|
|
|
case viewLoadScope:
|
|
|
|
// If we are outside of GOPATH, a module, or some other known
|
|
|
|
// build system, don't load subdirectories.
|
|
|
|
if !s.view.hasValidBuildConfiguration {
|
|
|
|
query = append(query, "./")
|
|
|
|
} else {
|
|
|
|
query = append(query, "./...")
|
|
|
|
}
|
2020-01-22 17:23:30 -07:00
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("unknown scope type %T", scope))
|
2020-01-10 15:18:59 -07:00
|
|
|
}
|
2020-01-28 22:08:22 -07:00
|
|
|
switch scope.(type) {
|
|
|
|
case directoryURI, viewLoadScope:
|
|
|
|
containsDir = true
|
|
|
|
}
|
2019-11-01 14:45:32 -06:00
|
|
|
}
|
2020-01-27 16:59:13 -07:00
|
|
|
sort.Strings(query) // for determinism
|
|
|
|
|
2020-04-20 10:14:12 -06:00
|
|
|
ctx, done := event.Start(ctx, "cache.view.load", tag.Query.Of(query))
|
2019-07-09 18:16:21 -06:00
|
|
|
defer done()
|
2019-09-17 09:19:11 -06:00
|
|
|
|
2020-06-11 16:25:17 -06:00
|
|
|
cfg := s.config(ctx)
|
2020-06-18 10:30:59 -06:00
|
|
|
cleanup := func() {}
|
2020-06-10 23:11:52 -06:00
|
|
|
if s.view.tmpMod {
|
|
|
|
modFH, err := s.GetFile(ctx, s.view.modURI)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var sumFH source.FileHandle
|
|
|
|
if s.view.sumURI != "" {
|
|
|
|
sumFH, err = s.GetFile(ctx, s.view.sumURI)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2020-06-18 10:30:59 -06:00
|
|
|
var tmpURI span.URI
|
|
|
|
tmpURI, cleanup, err = tempModFile(modFH, sumFH)
|
2020-06-10 23:11:52 -06:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-06-18 10:30:59 -06:00
|
|
|
cfg.BuildFlags = append(cfg.BuildFlags, fmt.Sprintf("-modfile=%s", tmpURI.Filename()))
|
2020-06-10 23:11:52 -06:00
|
|
|
}
|
2020-03-23 06:35:36 -06:00
|
|
|
pkgs, err := packages.Load(cfg, query...)
|
2020-06-18 10:30:59 -06:00
|
|
|
cleanup()
|
2019-09-23 18:06:15 -06:00
|
|
|
|
2020-01-13 15:05:54 -07:00
|
|
|
// If the context was canceled, return early. Otherwise, we might be
|
|
|
|
// type-checking an incomplete result. Check the context directly,
|
|
|
|
// because go/packages adds extra information to the error.
|
|
|
|
if ctx.Err() != nil {
|
2020-01-30 18:44:00 -07:00
|
|
|
return ctx.Err()
|
2019-10-16 14:31:27 -06:00
|
|
|
}
|
2020-04-23 21:24:24 -06:00
|
|
|
if err != nil {
|
2020-05-28 19:21:29 -06:00
|
|
|
// Match on common error messages. This is really hacky, but I'm not sure
|
|
|
|
// of any better way. This can be removed when golang/go#39164 is resolved.
|
|
|
|
if strings.Contains(err.Error(), "inconsistent vendoring") {
|
|
|
|
return source.InconsistentVendoring
|
|
|
|
}
|
2020-04-23 21:24:24 -06:00
|
|
|
event.Error(ctx, "go/packages.Load", err, tag.Snapshot.Of(s.ID()), tag.Directory.Of(cfg.Dir), tag.Query.Of(query), tag.PackageCount.Of(len(pkgs)))
|
|
|
|
} else {
|
|
|
|
event.Log(ctx, "go/packages.Load", tag.Snapshot.Of(s.ID()), tag.Directory.Of(cfg.Dir), tag.Query.Of(query), tag.PackageCount.Of(len(pkgs)))
|
|
|
|
}
|
2019-11-11 10:56:51 -07:00
|
|
|
if len(pkgs) == 0 {
|
2020-01-30 18:44:00 -07:00
|
|
|
return err
|
2019-05-23 11:51:56 -06:00
|
|
|
}
|
2020-05-28 19:21:29 -06:00
|
|
|
|
2019-09-27 11:17:59 -06:00
|
|
|
for _, pkg := range pkgs {
|
2020-01-14 22:22:42 -07:00
|
|
|
if !containsDir || s.view.Options().VerboseOutput {
|
2020-04-20 10:14:12 -06:00
|
|
|
event.Log(ctx, "go/packages.Load", tag.Snapshot.Of(s.ID()), tag.PackagePath.Of(pkg.PkgPath), tag.Files.Of(pkg.CompiledGoFiles))
|
2019-12-10 12:29:58 -07:00
|
|
|
}
|
2020-01-27 18:25:48 -07:00
|
|
|
// Ignore packages with no sources, since we will never be able to
|
|
|
|
// correctly invalidate that metadata.
|
|
|
|
if len(pkg.GoFiles) == 0 && len(pkg.CompiledGoFiles) == 0 {
|
2019-12-27 14:44:33 -07:00
|
|
|
continue
|
|
|
|
}
|
2020-01-30 18:52:23 -07:00
|
|
|
// Special case for the builtin package, as it has no dependencies.
|
|
|
|
if pkg.PkgPath == "builtin" {
|
|
|
|
if err := s.view.buildBuiltinPackage(ctx, pkg.GoFiles); err != nil {
|
2020-01-30 18:44:00 -07:00
|
|
|
return err
|
2020-01-30 18:52:23 -07:00
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2020-01-07 19:37:41 -07:00
|
|
|
// Skip test main packages.
|
2020-03-27 10:52:40 -06:00
|
|
|
if isTestMain(pkg, s.view.gocache) {
|
2020-01-07 19:37:41 -07:00
|
|
|
continue
|
|
|
|
}
|
2019-09-27 11:17:59 -06:00
|
|
|
// Set the metadata for this package.
|
2020-01-28 22:08:22 -07:00
|
|
|
m, err := s.setMetadata(ctx, packagePath(pkg.PkgPath), pkg, cfg, map[packageID]struct{}{})
|
|
|
|
if err != nil {
|
2020-01-30 18:44:00 -07:00
|
|
|
return err
|
2019-09-27 11:17:59 -06:00
|
|
|
}
|
2020-02-07 12:38:36 -07:00
|
|
|
if _, err := s.buildPackageHandle(ctx, m.id, source.ParseFull); err != nil {
|
2020-01-30 18:44:00 -07:00
|
|
|
return err
|
2019-09-27 11:17:59 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Rebuild the import graph when the metadata is updated.
|
|
|
|
s.clearAndRebuildImportGraph()
|
|
|
|
|
2020-01-30 18:44:00 -07:00
|
|
|
return nil
|
2019-09-27 11:17:59 -06:00
|
|
|
}
|
|
|
|
|
2020-01-28 22:08:22 -07:00
|
|
|
func (s *snapshot) setMetadata(ctx context.Context, pkgPath packagePath, pkg *packages.Package, cfg *packages.Config, seen map[packageID]struct{}) (*metadata, error) {
|
2019-10-31 16:18:00 -06:00
|
|
|
id := packageID(pkg.ID)
|
|
|
|
if _, ok := seen[id]; ok {
|
2020-01-28 22:08:22 -07:00
|
|
|
return nil, errors.Errorf("import cycle detected: %q", id)
|
2019-10-31 16:18:00 -06:00
|
|
|
}
|
2019-09-27 11:17:59 -06:00
|
|
|
// Recreate the metadata rather than reusing it to avoid locking.
|
|
|
|
m := &metadata{
|
2019-10-31 16:18:00 -06:00
|
|
|
id: id,
|
2019-09-27 11:17:59 -06:00
|
|
|
pkgPath: pkgPath,
|
2020-06-07 19:50:35 -06:00
|
|
|
name: packageName(pkg.Name),
|
2020-01-16 12:32:14 -07:00
|
|
|
forTest: packagePath(packagesinternal.GetForTest(pkg)),
|
2019-09-27 11:17:59 -06:00
|
|
|
typesSizes: pkg.TypesSizes,
|
|
|
|
errors: pkg.Errors,
|
2019-10-01 13:21:06 -06:00
|
|
|
config: cfg,
|
2020-05-15 15:13:55 -06:00
|
|
|
module: pkg.Module,
|
2019-09-27 11:17:59 -06:00
|
|
|
}
|
2019-11-21 12:54:31 -07:00
|
|
|
|
2019-09-27 11:17:59 -06:00
|
|
|
for _, filename := range pkg.CompiledGoFiles {
|
2020-02-12 14:36:46 -07:00
|
|
|
uri := span.URIFromPath(filename)
|
2019-11-20 14:15:00 -07:00
|
|
|
m.compiledGoFiles = append(m.compiledGoFiles, uri)
|
2019-11-21 12:54:31 -07:00
|
|
|
s.addID(uri, m.id)
|
|
|
|
}
|
|
|
|
for _, filename := range pkg.GoFiles {
|
2020-02-12 14:36:46 -07:00
|
|
|
uri := span.URIFromPath(filename)
|
2019-11-21 12:54:31 -07:00
|
|
|
m.goFiles = append(m.goFiles, uri)
|
2019-09-27 11:17:59 -06:00
|
|
|
s.addID(uri, m.id)
|
|
|
|
}
|
|
|
|
|
2020-01-28 22:08:22 -07:00
|
|
|
copied := map[packageID]struct{}{
|
2020-03-27 10:52:40 -06:00
|
|
|
id: {},
|
2020-01-28 22:08:22 -07:00
|
|
|
}
|
2019-10-31 16:18:00 -06:00
|
|
|
for k, v := range seen {
|
|
|
|
copied[k] = v
|
|
|
|
}
|
2019-09-27 11:17:59 -06:00
|
|
|
for importPath, importPkg := range pkg.Imports {
|
|
|
|
importPkgPath := packagePath(importPath)
|
|
|
|
importID := packageID(importPkg.ID)
|
|
|
|
|
|
|
|
m.deps = append(m.deps, importID)
|
|
|
|
|
|
|
|
// Don't remember any imports with significant errors.
|
|
|
|
if importPkgPath != "unsafe" && len(importPkg.CompiledGoFiles) == 0 {
|
|
|
|
if m.missingDeps == nil {
|
|
|
|
m.missingDeps = make(map[packagePath]struct{})
|
|
|
|
}
|
|
|
|
m.missingDeps[importPkgPath] = struct{}{}
|
|
|
|
continue
|
|
|
|
}
|
2020-01-07 19:37:41 -07:00
|
|
|
if s.getMetadata(importID) == nil {
|
2020-01-28 22:08:22 -07:00
|
|
|
if _, err := s.setMetadata(ctx, importPkgPath, importPkg, cfg, copied); err != nil {
|
2020-03-07 19:28:21 -07:00
|
|
|
event.Error(ctx, "error in dependency", err)
|
2019-09-27 11:17:59 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-30 18:52:23 -07:00
|
|
|
|
2019-10-31 16:18:00 -06:00
|
|
|
// Add the metadata to the cache.
|
2020-01-28 22:08:22 -07:00
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
|
|
// TODO: We should make sure not to set duplicate metadata,
|
|
|
|
// and instead panic here. This can be done by making sure not to
|
|
|
|
// reset metadata information for packages we've already seen.
|
2020-01-30 18:52:23 -07:00
|
|
|
if original, ok := s.metadata[m.id]; ok {
|
|
|
|
m = original
|
2020-01-28 22:08:22 -07:00
|
|
|
} else {
|
|
|
|
s.metadata[m.id] = m
|
|
|
|
}
|
2020-01-30 18:52:23 -07:00
|
|
|
|
|
|
|
// Set the workspace packages. If any of the package's files belong to the
|
2020-06-03 20:58:36 -06:00
|
|
|
// view, then the package may be a workspace package.
|
2020-01-30 18:52:23 -07:00
|
|
|
for _, uri := range append(m.compiledGoFiles, m.goFiles...) {
|
2020-06-03 19:13:43 -06:00
|
|
|
if !s.view.contains(uri) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// The package's files are in this view. It may be a workspace package.
|
2020-06-03 20:58:36 -06:00
|
|
|
if strings.Contains(string(uri), "/vendor/") {
|
|
|
|
// Vendored packages are not likely to be interesting to the user.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-06-15 12:51:03 -06:00
|
|
|
switch {
|
|
|
|
case m.forTest == "":
|
2020-06-03 19:13:43 -06:00
|
|
|
// A normal package.
|
|
|
|
s.workspacePackages[m.id] = pkgPath
|
2020-06-15 12:51:03 -06:00
|
|
|
case m.forTest == m.pkgPath, m.forTest+"_test" == m.pkgPath:
|
|
|
|
// The test variant of some workspace package or its x_test.
|
|
|
|
// To load it, we need to load the non-test variant with -test.
|
2020-06-03 19:13:43 -06:00
|
|
|
s.workspacePackages[m.id] = m.forTest
|
|
|
|
default:
|
|
|
|
// A test variant of some intermediate package. We don't care about it.
|
2020-01-30 18:52:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return m, nil
|
2019-09-27 11:17:59 -06:00
|
|
|
}
|
2020-01-07 19:37:41 -07:00
|
|
|
|
2020-03-27 10:52:40 -06:00
|
|
|
func isTestMain(pkg *packages.Package, gocache string) bool {
|
2020-01-07 19:37:41 -07:00
|
|
|
// Test mains must have an import path that ends with ".test".
|
|
|
|
if !strings.HasSuffix(pkg.PkgPath, ".test") {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
// Test main packages are always named "main".
|
|
|
|
if pkg.Name != "main" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
// Test mains always have exactly one GoFile that is in the build cache.
|
|
|
|
if len(pkg.GoFiles) > 1 {
|
|
|
|
return false
|
|
|
|
}
|
2020-01-27 18:25:48 -07:00
|
|
|
if !strings.HasPrefix(pkg.GoFiles[0], gocache) {
|
2020-01-07 19:37:41 -07:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|