internal/lsp/cache: remove support for invalid metadata
Remove support for keeping track of invalid metadata, as discussed in
golang/go#55333.
Fixes golang/go#55333
Change-Id: I7727f43e2cffef610096d20af4898f326c5a8450
Reviewed-on: https://go-review.googlesource.com/c/tools/+/447741
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
diff --git a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.go
index 25da922..1f3622a 100644
--- a/gopls/internal/lsp/cache/check.go
+++ b/gopls/internal/lsp/cache/check.go
@@ -46,7 +46,7 @@
promise *memoize.Promise // [typeCheckResult]
// m is the metadata associated with the package.
- m *KnownMetadata
+ m *Metadata
// key is the hashed key for the package.
//
@@ -105,12 +105,8 @@
// Don't use invalid metadata for dependencies if the top-level
// metadata is valid. We only load top-level packages, so if the
// top-level is valid, all of its dependencies should be as well.
- if err != nil || m.Valid && !depHandle.m.Valid {
- if err != nil {
- event.Error(ctx, fmt.Sprintf("%s: no dep handle for %s", id, depID), err, source.SnapshotLabels(s)...)
- } else {
- event.Log(ctx, fmt.Sprintf("%s: invalid dep handle for %s", id, depID), source.SnapshotLabels(s)...)
- }
+ if err != nil {
+ event.Error(ctx, fmt.Sprintf("%s: no dep handle for %s", id, depID), err, source.SnapshotLabels(s)...)
// This check ensures we break out of the slow
// buildPackageHandle recursion quickly when
@@ -136,7 +132,7 @@
// syntax trees are available from (*pkg).File(URI).
// TODO(adonovan): consider parsing them on demand?
// The need should be rare.
- goFiles, compiledGoFiles, err := readGoFiles(ctx, s, m.Metadata)
+ goFiles, compiledGoFiles, err := readGoFiles(ctx, s, m)
if err != nil {
return nil, err
}
@@ -146,7 +142,7 @@
experimentalKey := s.View().Options().ExperimentalPackageCacheKey
phKey := computePackageKey(m.ID, compiledGoFiles, m, depKey, mode, experimentalKey)
promise, release := s.store.Promise(phKey, func(ctx context.Context, arg interface{}) interface{} {
- pkg, err := typeCheckImpl(ctx, arg.(*snapshot), goFiles, compiledGoFiles, m.Metadata, mode, deps)
+ pkg, err := typeCheckImpl(ctx, arg.(*snapshot), goFiles, compiledGoFiles, m, mode, deps)
return typeCheckResult{pkg, err}
})
@@ -166,7 +162,7 @@
// packageHandle to the handles for parsing its files and the
// handles for type-checking its immediate deps, at which
// point there will be no need to even access s.meta.)
- if s.meta.metadata[ph.m.ID].Metadata != ph.m.Metadata {
+ if s.meta.metadata[ph.m.ID] != ph.m {
return nil, fmt.Errorf("stale metadata for %s", ph.m.ID)
}
@@ -174,7 +170,7 @@
if prev, ok := s.packages.Get(packageKey); ok {
prevPH := prev.(*packageHandle)
release()
- if prevPH.m.Metadata != ph.m.Metadata {
+ if prevPH.m != ph.m {
return nil, bug.Errorf("existing package handle does not match for %s", ph.m.ID)
}
return prevPH, nil
@@ -225,7 +221,7 @@
// computePackageKey returns a key representing the act of type checking
// a package named id containing the specified files, metadata, and
// combined dependency hash.
-func computePackageKey(id PackageID, files []source.FileHandle, m *KnownMetadata, depsKey source.Hash, mode source.ParseMode, experimentalKey bool) packageHandleKey {
+func computePackageKey(id PackageID, files []source.FileHandle, m *Metadata, depsKey source.Hash, mode source.ParseMode, experimentalKey bool) packageHandleKey {
// TODO(adonovan): opt: no need to materalize the bytes; hash them directly.
// Also, use field separators to avoid spurious collisions.
b := bytes.NewBuffer(nil)
diff --git a/gopls/internal/lsp/cache/debug.go b/gopls/internal/lsp/cache/debug.go
index d665b01..fd82aff 100644
--- a/gopls/internal/lsp/cache/debug.go
+++ b/gopls/internal/lsp/cache/debug.go
@@ -47,7 +47,7 @@
for _, id := range ids {
pkgPath := s.workspacePackages[id]
- m, ok := s.meta.metadata[id]
- debugf(" %s:%s (metadata: %t; valid: %t)", id, pkgPath, ok, m.Valid)
+ _, ok := s.meta.metadata[id]
+ debugf(" %s:%s (metadata: %t)", id, pkgPath, ok)
}
}
diff --git a/gopls/internal/lsp/cache/graph.go b/gopls/internal/lsp/cache/graph.go
index 83336a2..be5a061 100644
--- a/gopls/internal/lsp/cache/graph.go
+++ b/gopls/internal/lsp/cache/graph.go
@@ -15,7 +15,7 @@
// graph of Go packages, as obtained from go/packages.
type metadataGraph struct {
// metadata maps package IDs to their associated metadata.
- metadata map[PackageID]*KnownMetadata
+ metadata map[PackageID]*Metadata
// importedBy maps package IDs to the list of packages that import them.
importedBy map[PackageID][]PackageID
@@ -27,12 +27,12 @@
// Clone creates a new metadataGraph, applying the given updates to the
// receiver.
-func (g *metadataGraph) Clone(updates map[PackageID]*KnownMetadata) *metadataGraph {
+func (g *metadataGraph) Clone(updates map[PackageID]*Metadata) *metadataGraph {
if len(updates) == 0 {
// Optimization: since the graph is immutable, we can return the receiver.
return g
}
- result := &metadataGraph{metadata: make(map[PackageID]*KnownMetadata, len(g.metadata))}
+ result := &metadataGraph{metadata: make(map[PackageID]*Metadata, len(g.metadata))}
// Copy metadata.
for id, m := range g.metadata {
result.metadata[id] = m
@@ -74,51 +74,25 @@
}
// Sort and filter file associations.
- //
- // We choose the first non-empty set of package associations out of the
- // following. For simplicity, call a non-command-line-arguments package a
- // "real" package.
- //
- // 1: valid real packages
- // 2: a valid command-line-arguments package
- // 3: invalid real packages
- // 4: an invalid command-line-arguments package
for uri, ids := range g.ids {
sort.Slice(ids, func(i, j int) bool {
- // 1. valid packages appear earlier.
- validi := g.metadata[ids[i]].Valid
- validj := g.metadata[ids[j]].Valid
- if validi != validj {
- return validi
- }
-
- // 2. command-line-args packages appear later.
cli := source.IsCommandLineArguments(ids[i])
clj := source.IsCommandLineArguments(ids[j])
if cli != clj {
return clj
}
- // 3. packages appear in name order.
+ // 2. packages appear in name order.
return ids[i] < ids[j]
})
// Choose the best IDs for each URI, according to the following rules:
// - If there are any valid real packages, choose them.
// - Else, choose the first valid command-line-argument package, if it exists.
- // - Else, keep using all the invalid metadata.
//
// TODO(rfindley): it might be better to track all IDs here, and exclude
// them later in PackagesForFile, but this is the existing behavior.
- hasValidMetadata := false
for i, id := range ids {
- m := g.metadata[id]
- if m.Valid {
- hasValidMetadata = true
- } else if hasValidMetadata {
- g.ids[uri] = ids[:i]
- break
- }
// If we've seen *anything* prior to command-line arguments package, take
// it. Note that ids[0] may itself be command-line-arguments.
if i > 0 && source.IsCommandLineArguments(id) {
@@ -134,7 +108,7 @@
//
// If includeInvalid is false, the algorithm ignores packages with invalid
// metadata (including those in the given list of ids).
-func (g *metadataGraph) reverseTransitiveClosure(includeInvalid bool, ids ...PackageID) map[PackageID]bool {
+func (g *metadataGraph) reverseTransitiveClosure(ids ...PackageID) map[PackageID]bool {
seen := make(map[PackageID]bool)
var visitAll func([]PackageID)
visitAll = func(ids []PackageID) {
@@ -144,7 +118,7 @@
}
m := g.metadata[id]
// Only use invalid metadata if we support it.
- if m == nil || !(m.Valid || includeInvalid) {
+ if m == nil {
continue
}
seen[id] = true
diff --git a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go
index dcd343a..0498264 100644
--- a/gopls/internal/lsp/cache/load.go
+++ b/gopls/internal/lsp/cache/load.go
@@ -157,7 +157,7 @@
moduleErrs := make(map[string][]packages.Error) // module path -> errors
filterer := buildFilterer(s.view.rootURI.Filename(), s.view.gomodcache, s.view.Options())
- newMetadata := make(map[PackageID]*KnownMetadata)
+ newMetadata := make(map[PackageID]*Metadata)
for _, pkg := range pkgs {
// The Go command returns synthetic list results for module queries that
// encountered module errors.
@@ -222,10 +222,10 @@
//
// TODO(rfindley): perform a sanity check that metadata matches here. If not,
// we have an invalidation bug elsewhere.
- updates := make(map[PackageID]*KnownMetadata)
+ updates := make(map[PackageID]*Metadata)
var updatedIDs []PackageID
for _, m := range newMetadata {
- if existing := s.meta.metadata[m.ID]; existing == nil || !existing.Valid {
+ if existing := s.meta.metadata[m.ID]; existing == nil {
updates[m.ID] = m
updatedIDs = append(updatedIDs, m.ID)
delete(s.shouldLoad, m.ID)
@@ -238,7 +238,7 @@
//
// Note that the original metadata is being invalidated here, so we use the
// original metadata graph to compute the reverse closure.
- invalidatedPackages := s.meta.reverseTransitiveClosure(true, updatedIDs...)
+ invalidatedPackages := s.meta.reverseTransitiveClosure(updatedIDs...)
s.meta = s.meta.Clone(updates)
s.resetIsActivePackageLocked()
@@ -475,7 +475,7 @@
// buildMetadata populates the updates map with metadata updates to
// apply, based on the given pkg. It recurs through pkg.Imports to ensure that
// metadata exists for all dependencies.
-func buildMetadata(ctx context.Context, pkg *packages.Package, cfg *packages.Config, query []string, updates map[PackageID]*KnownMetadata, path []PackageID) error {
+func buildMetadata(ctx context.Context, pkg *packages.Package, cfg *packages.Config, query []string, updates map[PackageID]*Metadata, path []PackageID) error {
// Allow for multiple ad-hoc packages in the workspace (see #47584).
pkgPath := PackagePath(pkg.PkgPath)
id := PackageID(pkg.ID)
@@ -507,18 +507,15 @@
}
// Recreate the metadata rather than reusing it to avoid locking.
- m := &KnownMetadata{
- Metadata: &Metadata{
- ID: id,
- PkgPath: pkgPath,
- Name: PackageName(pkg.Name),
- ForTest: PackagePath(packagesinternal.GetForTest(pkg)),
- TypesSizes: pkg.TypesSizes,
- Config: cfg,
- Module: pkg.Module,
- depsErrors: packagesinternal.GetDepsErrors(pkg),
- },
- Valid: true,
+ m := &Metadata{
+ ID: id,
+ PkgPath: pkgPath,
+ Name: PackageName(pkg.Name),
+ ForTest: PackagePath(packagesinternal.GetForTest(pkg)),
+ TypesSizes: pkg.TypesSizes,
+ Config: cfg,
+ Module: pkg.Module,
+ depsErrors: packagesinternal.GetDepsErrors(pkg),
}
updates[id] = m
@@ -650,7 +647,7 @@
// the snapshot s.
//
// s.mu must be held while calling this function.
-func containsOpenFileLocked(s *snapshot, m *KnownMetadata) bool {
+func containsOpenFileLocked(s *snapshot, m *Metadata) bool {
uris := map[span.URI]struct{}{}
for _, uri := range m.CompiledGoFiles {
uris[uri] = struct{}{}
@@ -700,14 +697,7 @@
func computeWorkspacePackagesLocked(s *snapshot, meta *metadataGraph) map[PackageID]PackagePath {
workspacePackages := make(map[PackageID]PackagePath)
for _, m := range meta.metadata {
- // Don't consider invalid packages to be workspace packages. Doing so can
- // result in type-checking and diagnosing packages that no longer exist,
- // which can lead to memory leaks and confusing errors.
- if !m.Valid {
- continue
- }
-
- if !containsPackageLocked(s, m.Metadata) {
+ if !containsPackageLocked(s, m) {
continue
}
@@ -748,12 +738,12 @@
// function returns false.
//
// If m is not a command-line-arguments package, this is trivially true.
-func allFilesHaveRealPackages(g *metadataGraph, m *KnownMetadata) bool {
+func allFilesHaveRealPackages(g *metadataGraph, m *Metadata) bool {
n := len(m.CompiledGoFiles)
checkURIs:
for _, uri := range append(m.CompiledGoFiles[0:n:n], m.GoFiles...) {
for _, id := range g.ids[uri] {
- if !source.IsCommandLineArguments(id) && (g.metadata[id].Valid || !m.Valid) {
+ if !source.IsCommandLineArguments(id) {
continue checkURIs
}
}
diff --git a/gopls/internal/lsp/cache/metadata.go b/gopls/internal/lsp/cache/metadata.go
index 03037f0..135623f 100644
--- a/gopls/internal/lsp/cache/metadata.go
+++ b/gopls/internal/lsp/cache/metadata.go
@@ -86,12 +86,3 @@
func (m *Metadata) ModuleInfo() *packages.Module {
return m.Module
}
-
-// KnownMetadata is a wrapper around metadata that tracks its validity.
-type KnownMetadata struct {
- *Metadata
-
- // Valid is true if the given metadata is Valid.
- // Invalid metadata can still be used if a metadata reload fails.
- Valid bool
-}
diff --git a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_tidy.go
index cc604c1..fc97ee6 100644
--- a/gopls/internal/lsp/cache/mod_tidy.go
+++ b/gopls/internal/lsp/cache/mod_tidy.go
@@ -196,7 +196,7 @@
}
// Read both lists of files of this package, in parallel.
- goFiles, compiledGoFiles, err := readGoFiles(ctx, snapshot, m.Metadata)
+ goFiles, compiledGoFiles, err := readGoFiles(ctx, snapshot, m)
if err != nil {
return nil, err
}
diff --git a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snapshot.go
index 70c26f2..656d08f 100644
--- a/gopls/internal/lsp/cache/snapshot.go
+++ b/gopls/internal/lsp/cache/snapshot.go
@@ -729,8 +729,6 @@
// If experimentalUseInvalidMetadata is set, this function may return package
// IDs with invalid metadata.
func (s *snapshot) getOrLoadIDsForURI(ctx context.Context, uri span.URI) ([]PackageID, error) {
- useInvalidMetadata := s.useInvalidMetadata()
-
s.mu.Lock()
// Start with the set of package associations derived from the last load.
@@ -741,7 +739,7 @@
for _, id := range ids {
// TODO(rfindley): remove the defensiveness here. s.meta.metadata[id] must
// exist.
- if m, ok := s.meta.metadata[id]; ok && m.Valid {
+ if _, ok := s.meta.metadata[id]; ok {
hasValidID = true
}
if len(s.shouldLoad[id]) > 0 {
@@ -757,16 +755,6 @@
s.mu.Unlock()
- // Special case: if experimentalUseInvalidMetadata is set and we have any
- // ids, just return them.
- //
- // This is arguably wrong: if the metadata is invalid we should try reloading
- // it. However, this was the pre-existing behavior, and
- // experimentalUseInvalidMetadata will be removed in a future release.
- if !shouldLoad && useInvalidMetadata && len(ids) > 0 {
- return ids, nil
- }
-
// Reload if loading is likely to improve the package associations for uri:
// - uri is not contained in any valid packages
// - ...or one of the packages containing uri is marked 'shouldLoad'
@@ -800,28 +788,19 @@
s.mu.Lock()
ids = s.meta.ids[uri]
- if !useInvalidMetadata {
- var validIDs []PackageID
- for _, id := range ids {
- // TODO(rfindley): remove the defensiveness here as well.
- if m, ok := s.meta.metadata[id]; ok && m.Valid {
- validIDs = append(validIDs, id)
- }
+ var validIDs []PackageID
+ for _, id := range ids {
+ // TODO(rfindley): remove the defensiveness here as well.
+ if _, ok := s.meta.metadata[id]; ok {
+ validIDs = append(validIDs, id)
}
- ids = validIDs
}
+ ids = validIDs
s.mu.Unlock()
return ids, nil
}
-// Only use invalid metadata for Go versions >= 1.13. Go 1.12 and below has
-// issues with overlays that will cause confusing error messages if we reuse
-// old metadata.
-func (s *snapshot) useInvalidMetadata() bool {
- return s.view.goversion >= 13 && s.view.Options().ExperimentalUseInvalidMetadata
-}
-
func (s *snapshot) GetReverseDependencies(ctx context.Context, id PackageID) ([]source.Package, error) {
if err := s.awaitLoaded(ctx); err != nil {
return nil, err
@@ -829,7 +808,7 @@
s.mu.Lock()
meta := s.meta
s.mu.Unlock()
- ids := meta.reverseTransitiveClosure(s.useInvalidMetadata(), id)
+ ids := meta.reverseTransitiveClosure(id)
// Make sure to delete the original package ID from the map.
delete(ids, id)
@@ -1216,9 +1195,7 @@
var meta []source.Metadata
for _, m := range s.meta.metadata {
- if m.Valid {
- meta = append(meta, m)
- }
+ meta = append(meta, m)
}
return meta, nil
}
@@ -1273,7 +1250,7 @@
return match
}
-func (s *snapshot) getMetadata(id PackageID) *KnownMetadata {
+func (s *snapshot) getMetadata(id PackageID) *Metadata {
s.mu.Lock()
defer s.mu.Unlock()
@@ -1319,7 +1296,7 @@
return true
}
for _, id := range ids {
- if m, ok := s.meta.metadata[id]; ok && m.Valid {
+ if _, ok := s.meta.metadata[id]; ok {
return false
}
}
@@ -1409,19 +1386,8 @@
func (s *snapshot) awaitLoaded(ctx context.Context) error {
loadErr := s.awaitLoadedAllErrors(ctx)
- s.mu.Lock()
- defer s.mu.Unlock()
-
- // If we still have absolutely no metadata, check if the view failed to
- // initialize and return any errors.
- if s.useInvalidMetadata() && len(s.meta.metadata) > 0 {
- return nil
- }
- for _, m := range s.meta.metadata {
- if m.Valid {
- return nil
- }
- }
+ // TODO(rfindley): eliminate this function as part of simplifying
+ // CriticalErrors.
if loadErr != nil {
return loadErr.MainError
}
@@ -1968,27 +1934,10 @@
result.shouldLoad[k] = v
}
- // TODO(rfindley): consolidate the this workspace mode detection with
- // workspace invalidation.
- workspaceModeChanged := s.workspaceMode() != result.workspaceMode()
-
- // We delete invalid metadata in the following cases:
- // - If we are forcing a reload of metadata.
- // - If the workspace mode has changed, as stale metadata may produce
- // confusing or incorrect diagnostics.
- //
- // TODO(rfindley): we should probably also clear metadata if we are
- // reinitializing the workspace, as otherwise we could leave around a bunch
- // of irrelevant and duplicate metadata (for example, if the module path
- // changed). However, this breaks the "experimentalUseInvalidMetadata"
- // feature, which relies on stale metadata when, for example, a go.mod file
- // is broken via invalid syntax.
- deleteInvalidMetadata := forceReloadMetadata || workspaceModeChanged
-
// Compute which metadata updates are required. We only need to invalidate
// packages directly containing the affected file, and only if it changed in
// a relevant way.
- metadataUpdates := make(map[PackageID]*KnownMetadata)
+ metadataUpdates := make(map[PackageID]*Metadata)
for k, v := range s.meta.metadata {
invalidateMetadata := idsToInvalidate[k]
@@ -2012,20 +1961,10 @@
}
// Check whether the metadata should be deleted.
- if skipID[k] || (invalidateMetadata && deleteInvalidMetadata) {
+ if skipID[k] || invalidateMetadata {
metadataUpdates[k] = nil
continue
}
-
- // Check if the metadata has changed.
- valid := v.Valid && !invalidateMetadata
- if valid != v.Valid {
- // Mark invalidated metadata rather than deleting it outright.
- metadataUpdates[k] = &KnownMetadata{
- Metadata: v.Metadata,
- Valid: valid,
- }
- }
}
// Update metadata, if necessary.
@@ -2042,6 +1981,10 @@
// Don't bother copying the importedBy graph,
// as it changes each time we update metadata.
+ // TODO(rfindley): consolidate the this workspace mode detection with
+ // workspace invalidation.
+ workspaceModeChanged := s.workspaceMode() != result.workspaceMode()
+
// If the snapshot's workspace mode has changed, the packages loaded using
// the previous mode are no longer relevant, so clear them out.
if workspaceModeChanged {