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"
|
2019-05-29 12:59:35 -06:00
|
|
|
|
|
|
|
"golang.org/x/tools/go/packages"
|
2019-06-04 20:14:37 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/source"
|
2019-07-14 21:08:10 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/telemetry"
|
2019-05-29 12:59:35 -06:00
|
|
|
"golang.org/x/tools/internal/span"
|
2019-08-13 13:07:39 -06:00
|
|
|
"golang.org/x/tools/internal/telemetry/log"
|
|
|
|
"golang.org/x/tools/internal/telemetry/tag"
|
|
|
|
"golang.org/x/tools/internal/telemetry/trace"
|
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
|
|
|
|
name string
|
2019-11-21 12:54:31 -07:00
|
|
|
goFiles []span.URI
|
2019-11-20 14:15:00 -07:00
|
|
|
compiledGoFiles []span.URI
|
|
|
|
typesSizes types.Sizes
|
|
|
|
errors []packages.Error
|
|
|
|
deps []packageID
|
|
|
|
missingDeps map[packagePath]struct{}
|
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
|
|
|
}
|
|
|
|
|
2019-11-01 14:45:32 -06:00
|
|
|
func (s *snapshot) load(ctx context.Context, scope source.Scope) ([]*metadata, error) {
|
|
|
|
uri := scope.URI()
|
|
|
|
var query string
|
|
|
|
switch scope.(type) {
|
|
|
|
case source.FileURI:
|
|
|
|
query = fmt.Sprintf("file=%s", scope.URI().Filename())
|
|
|
|
case source.DirectoryURI:
|
|
|
|
query = fmt.Sprintf("%s/...", scope.URI().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() == scope.URI().Filename() {
|
|
|
|
query = "./..."
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
panic(fmt.Errorf("unsupported scope type %T", scope))
|
|
|
|
}
|
2019-09-27 11:17:59 -06:00
|
|
|
ctx, done := trace.StartSpan(ctx, "cache.view.load", telemetry.URI.Of(uri))
|
2019-07-09 18:16:21 -06:00
|
|
|
defer done()
|
2019-09-17 09:19:11 -06:00
|
|
|
|
2019-10-01 13:21:06 -06:00
|
|
|
cfg := s.view.Config(ctx)
|
2019-11-01 14:45:32 -06:00
|
|
|
pkgs, err := packages.Load(cfg, query)
|
2019-09-23 18:06:15 -06:00
|
|
|
|
2019-10-16 14:31:27 -06:00
|
|
|
// If the context was canceled, return early.
|
|
|
|
// Otherwise, we might be type-checking an incomplete result.
|
|
|
|
if err == context.Canceled {
|
2019-11-01 14:45:32 -06:00
|
|
|
return nil, errors.Errorf("no metadata for %s: %v", uri, err)
|
2019-10-16 14:31:27 -06:00
|
|
|
}
|
|
|
|
log.Print(ctx, "go/packages.Load", tag.Of("packages", len(pkgs)))
|
2019-11-11 10:56:51 -07:00
|
|
|
if len(pkgs) == 0 {
|
2019-05-23 11:51:56 -06:00
|
|
|
if err == nil {
|
2019-11-30 00:01:00 -07:00
|
|
|
err = errors.Errorf("no packages found for query %s", query)
|
2019-05-29 12:59:35 -06:00
|
|
|
}
|
2019-11-11 10:56:51 -07:00
|
|
|
}
|
|
|
|
if err != nil {
|
2019-07-09 15:52:23 -06:00
|
|
|
return nil, err
|
2019-05-23 11:51:56 -06:00
|
|
|
}
|
2019-12-03 17:56:44 -07:00
|
|
|
return s.updateMetadata(ctx, scope, pkgs, cfg)
|
2019-09-23 18:19:50 -06:00
|
|
|
}
|
2019-09-27 11:17:59 -06:00
|
|
|
|
|
|
|
// shouldLoad reparses a file's package and import declarations to
|
|
|
|
// determine if they have changed.
|
|
|
|
func (c *cache) shouldLoad(ctx context.Context, s *snapshot, originalFH, currentFH source.FileHandle) bool {
|
|
|
|
if originalFH == nil {
|
|
|
|
return true
|
|
|
|
}
|
2019-12-05 14:03:59 -07:00
|
|
|
if originalFH.Identity().Kind == currentFH.Identity().Kind && currentFH.Identity().Kind == source.Mod {
|
|
|
|
return true
|
|
|
|
}
|
2019-09-27 11:17:59 -06:00
|
|
|
|
2019-10-15 16:07:52 -06:00
|
|
|
// Get the original and current parsed files in order to check package name and imports.
|
|
|
|
original, _, _, originalErr := c.ParseGoHandle(originalFH, source.ParseHeader).Parse(ctx)
|
|
|
|
current, _, _, currentErr := c.ParseGoHandle(currentFH, source.ParseHeader).Parse(ctx)
|
|
|
|
if originalErr != nil || currentErr != nil {
|
|
|
|
return (originalErr == nil) != (currentErr == nil)
|
2019-09-27 11:17:59 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the package's metadata has changed. The cases handled are:
|
|
|
|
//
|
|
|
|
// 1. A package's name has changed
|
|
|
|
// 2. A file's imports have changed
|
|
|
|
//
|
|
|
|
if original.Name.Name != current.Name.Name {
|
|
|
|
return true
|
|
|
|
}
|
2019-11-26 15:47:09 -07:00
|
|
|
// If the package's imports have increased, definitely re-run `go list`.
|
|
|
|
if len(original.Imports) < len(current.Imports) {
|
2019-09-27 11:17:59 -06:00
|
|
|
return true
|
|
|
|
}
|
2019-11-26 15:47:09 -07:00
|
|
|
importSet := make(map[string]struct{})
|
|
|
|
for _, importSpec := range original.Imports {
|
|
|
|
importSet[importSpec.Path.Value] = struct{}{}
|
|
|
|
}
|
|
|
|
// If any of the current imports were not in the original imports.
|
|
|
|
for _, importSpec := range current.Imports {
|
|
|
|
if _, ok := importSet[importSpec.Path.Value]; !ok {
|
2019-09-27 11:17:59 -06:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-12-03 17:56:44 -07:00
|
|
|
func (s *snapshot) updateMetadata(ctx context.Context, uri source.Scope, pkgs []*packages.Package, cfg *packages.Config) ([]*metadata, error) {
|
2019-09-27 11:17:59 -06:00
|
|
|
var results []*metadata
|
|
|
|
for _, pkg := range pkgs {
|
|
|
|
log.Print(ctx, "go/packages.Load", tag.Of("package", pkg.PkgPath), tag.Of("files", pkg.CompiledGoFiles))
|
|
|
|
|
|
|
|
// Set the metadata for this package.
|
2019-10-31 16:18:00 -06:00
|
|
|
if err := s.updateImports(ctx, packagePath(pkg.PkgPath), pkg, cfg, map[packageID]struct{}{}); err != nil {
|
2019-12-03 17:56:44 -07:00
|
|
|
return nil, err
|
2019-09-27 11:17:59 -06:00
|
|
|
}
|
|
|
|
m := s.getMetadata(packageID(pkg.ID))
|
|
|
|
if m != nil {
|
|
|
|
results = append(results, m)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rebuild the import graph when the metadata is updated.
|
|
|
|
s.clearAndRebuildImportGraph()
|
|
|
|
|
|
|
|
if len(results) == 0 {
|
2019-12-03 17:56:44 -07:00
|
|
|
return nil, errors.Errorf("no metadata for %s", uri)
|
2019-09-27 11:17:59 -06:00
|
|
|
}
|
2019-12-03 17:56:44 -07:00
|
|
|
return results, nil
|
2019-09-27 11:17:59 -06:00
|
|
|
}
|
|
|
|
|
2019-10-31 16:18:00 -06:00
|
|
|
func (s *snapshot) updateImports(ctx context.Context, pkgPath packagePath, pkg *packages.Package, cfg *packages.Config, seen map[packageID]struct{}) error {
|
|
|
|
id := packageID(pkg.ID)
|
|
|
|
if _, ok := seen[id]; ok {
|
|
|
|
return errors.Errorf("import cycle detected: %q", id)
|
|
|
|
}
|
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,
|
|
|
|
name: pkg.Name,
|
|
|
|
typesSizes: pkg.TypesSizes,
|
|
|
|
errors: pkg.Errors,
|
2019-10-01 13:21:06 -06:00
|
|
|
config: cfg,
|
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 {
|
|
|
|
uri := span.FileURI(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 {
|
|
|
|
uri := span.FileURI(filename)
|
|
|
|
m.goFiles = append(m.goFiles, uri)
|
2019-09-27 11:17:59 -06:00
|
|
|
s.addID(uri, m.id)
|
|
|
|
}
|
|
|
|
|
2019-11-21 12:54:31 -07:00
|
|
|
seen[id] = struct{}{}
|
2019-10-31 16:18:00 -06:00
|
|
|
copied := make(map[packageID]struct{})
|
|
|
|
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
|
|
|
|
}
|
|
|
|
dep := s.getMetadata(importID)
|
|
|
|
if dep == nil {
|
2019-10-31 16:18:00 -06:00
|
|
|
if err := s.updateImports(ctx, importPkgPath, importPkg, cfg, copied); err != nil {
|
2019-09-27 11:17:59 -06:00
|
|
|
log.Error(ctx, "error in dependency", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-10-31 16:18:00 -06:00
|
|
|
|
|
|
|
// Add the metadata to the cache.
|
|
|
|
s.setMetadata(m)
|
|
|
|
|
2019-09-27 11:17:59 -06:00
|
|
|
return nil
|
|
|
|
}
|