internal/lsp/cache: move metadata fields to a new metadataGraph type
In preparation for making metadata immutable, move metadata-related
fields to a new MetadataGraph type. Other than instantiating this type
when cloning, this CL contains no functional changes.
For golang/go#45686
Change-Id: I7ad29d1f331ba7e53dad3f012ad7ecdae4f7d4b7
Reviewed-on: https://go-review.googlesource.com/c/tools/+/340730
Run-TryBot: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/internal/lsp/cache/graph.go b/internal/lsp/cache/graph.go
new file mode 100644
index 0000000..f0f8724
--- /dev/null
+++ b/internal/lsp/cache/graph.go
@@ -0,0 +1,33 @@
+// Copyright 2022 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.
+
+package cache
+
+import "golang.org/x/tools/internal/span"
+
+// A metadataGraph holds information about a transtively closed import graph of
+// Go packages, as obtained from go/packages.
+//
+// Currently a new metadata graph is created for each snapshot.
+// TODO(rfindley): make this type immutable, so that it may be shared across
+// snapshots.
+type metadataGraph struct {
+ // ids maps file URIs to package IDs. A single file may belong to multiple
+ // packages due to tests packages.
+ ids map[span.URI][]PackageID
+
+ // metadata maps package IDs to their associated metadata.
+ metadata map[PackageID]*KnownMetadata
+
+ // importedBy maps package IDs to the list of packages that import them.
+ importedBy map[PackageID][]PackageID
+}
+
+func NewMetadataGraph() *metadataGraph {
+ return &metadataGraph{
+ ids: make(map[span.URI][]PackageID),
+ metadata: make(map[PackageID]*KnownMetadata),
+ importedBy: make(map[PackageID][]PackageID),
+ }
+}
diff --git a/internal/lsp/cache/load.go b/internal/lsp/cache/load.go
index 5341d5c..1d4921a 100644
--- a/internal/lsp/cache/load.go
+++ b/internal/lsp/cache/load.go
@@ -495,12 +495,12 @@
// Add the metadata to the cache.
// If we've already set the metadata for this snapshot, reuse it.
- if original, ok := s.metadata[m.ID]; ok && original.Valid {
+ if original, ok := s.meta.metadata[m.ID]; ok && original.Valid {
// Since we've just reloaded, clear out shouldLoad.
original.ShouldLoad = false
m = original.Metadata
} else {
- s.metadata[m.ID] = &KnownMetadata{
+ s.meta.metadata[m.ID] = &KnownMetadata{
Metadata: m,
Valid: true,
}
diff --git a/internal/lsp/cache/session.go b/internal/lsp/cache/session.go
index 5575aca..e018cb3 100644
--- a/internal/lsp/cache/session.go
+++ b/internal/lsp/cache/session.go
@@ -232,12 +232,10 @@
initializeOnce: &sync.Once{},
generation: s.cache.store.Generation(generationName(v, 0)),
packages: make(map[packageKey]*packageHandle),
- ids: make(map[span.URI][]PackageID),
- metadata: make(map[PackageID]*KnownMetadata),
+ meta: NewMetadataGraph(),
files: make(map[span.URI]source.VersionedFileHandle),
goFiles: make(map[parseKey]*parseGoHandle),
symbols: make(map[span.URI]*symbolHandle),
- importedBy: make(map[PackageID][]PackageID),
actions: make(map[actionKey]*actionHandle),
workspacePackages: make(map[PackageID]PackagePath),
unloadableFiles: make(map[span.URI]struct{}),
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index b4fc69f..a219935 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -68,16 +68,8 @@
// builtin pins the AST and package for builtin.go in memory.
builtin span.URI
- // ids maps file URIs to package IDs.
- // It may be invalidated on calls to go/packages.
- ids map[span.URI][]PackageID
-
- // metadata maps file IDs to their associated metadata.
- // It may invalidated on calls to go/packages.
- metadata map[PackageID]*KnownMetadata
-
- // importedBy maps package IDs to the list of packages that import them.
- importedBy map[PackageID][]PackageID
+ // meta holds loaded metadata.
+ meta *metadataGraph
// files maps file URIs to their corresponding FileHandles.
// It may invalidated when a file's content changes.
@@ -716,10 +708,10 @@
func (s *snapshot) getImportedByLocked(id PackageID) []PackageID {
// If we haven't rebuilt the import graph since creating the snapshot.
- if len(s.importedBy) == 0 {
+ if len(s.meta.importedBy) == 0 {
s.rebuildImportGraph()
}
- return s.importedBy[id]
+ return s.meta.importedBy[id]
}
func (s *snapshot) clearAndRebuildImportGraph() {
@@ -727,14 +719,14 @@
defer s.mu.Unlock()
// Completely invalidate the original map.
- s.importedBy = make(map[PackageID][]PackageID)
+ s.meta.importedBy = make(map[PackageID][]PackageID)
s.rebuildImportGraph()
}
func (s *snapshot) rebuildImportGraph() {
- for id, m := range s.metadata {
+ for id, m := range s.meta.metadata {
for _, importID := range m.Deps {
- s.importedBy[importID] = append(s.importedBy[importID], id)
+ s.meta.importedBy[importID] = append(s.meta.importedBy[importID], id)
}
}
}
@@ -789,7 +781,7 @@
defer func() {
seen[id] = active
}()
- m, ok := s.metadata[id]
+ m, ok := s.meta.metadata[id]
if !ok {
return false
}
@@ -1045,7 +1037,7 @@
// workspace packages first.
ids := s.workspacePackageIDs()
s.mu.Lock()
- for id := range s.metadata {
+ for id := range s.meta.metadata {
if _, ok := s.workspacePackages[id]; ok {
continue
}
@@ -1177,14 +1169,14 @@
s.mu.Lock()
defer s.mu.Unlock()
- return s.ids[uri]
+ return s.meta.ids[uri]
}
func (s *snapshot) getMetadata(id PackageID) *KnownMetadata {
s.mu.Lock()
defer s.mu.Unlock()
- return s.metadata[id]
+ return s.meta.metadata[id]
}
func (s *snapshot) shouldLoad(scope interface{}) bool {
@@ -1194,7 +1186,7 @@
switch scope := scope.(type) {
case PackagePath:
var meta *KnownMetadata
- for _, m := range s.metadata {
+ for _, m := range s.meta.metadata {
if m.PkgPath != scope {
continue
}
@@ -1206,12 +1198,12 @@
return false
case fileURI:
uri := span.URI(scope)
- ids := s.ids[uri]
+ ids := s.meta.ids[uri]
if len(ids) == 0 {
return true
}
for _, id := range ids {
- m, ok := s.metadata[id]
+ m, ok := s.meta.metadata[id]
if !ok || m.ShouldLoad {
return true
}
@@ -1229,7 +1221,7 @@
switch scope := scope.(type) {
case PackagePath:
var meta *KnownMetadata
- for _, m := range s.metadata {
+ for _, m := range s.meta.metadata {
if m.PkgPath == scope {
meta = m
}
@@ -1240,12 +1232,12 @@
meta.ShouldLoad = false
case fileURI:
uri := span.URI(scope)
- ids := s.ids[uri]
+ ids := s.meta.ids[uri]
if len(ids) == 0 {
return
}
for _, id := range ids {
- if m, ok := s.metadata[id]; ok {
+ if m, ok := s.meta.metadata[id]; ok {
m.ShouldLoad = false
}
}
@@ -1255,12 +1247,12 @@
// noValidMetadataForURILocked reports whether there is any valid metadata for
// the given URI.
func (s *snapshot) noValidMetadataForURILocked(uri span.URI) bool {
- ids, ok := s.ids[uri]
+ ids, ok := s.meta.ids[uri]
if !ok {
return true
}
for _, id := range ids {
- if m, ok := s.metadata[id]; ok && m.Valid {
+ if m, ok := s.meta.metadata[id]; ok && m.Valid {
return false
}
}
@@ -1276,7 +1268,7 @@
}
func (s *snapshot) noValidMetadataForIDLocked(id PackageID) bool {
- m := s.metadata[id]
+ m := s.meta.metadata[id]
return m == nil || !m.Valid
}
@@ -1289,7 +1281,7 @@
for uri := range uris {
// Collect the new set of IDs, preserving any valid existing IDs.
newIDs := []PackageID{id}
- for _, existingID := range s.ids[uri] {
+ for _, existingID := range s.meta.ids[uri] {
// Don't set duplicates of the same ID.
if existingID == id {
continue
@@ -1302,7 +1294,7 @@
}
// If the metadata for an existing ID is invalid, and we are
// setting metadata for a new, valid ID--don't preserve the old ID.
- if m, ok := s.metadata[existingID]; !ok || !m.Valid {
+ if m, ok := s.meta.metadata[existingID]; !ok || !m.Valid {
continue
}
newIDs = append(newIDs, existingID)
@@ -1310,7 +1302,7 @@
sort.Slice(newIDs, func(i, j int) bool {
return newIDs[i] < newIDs[j]
})
- s.ids[uri] = newIDs
+ s.meta.ids[uri] = newIDs
}
}
@@ -1396,10 +1388,10 @@
// If we still have absolutely no metadata, check if the view failed to
// initialize and return any errors.
- if s.useInvalidMetadata() && len(s.metadata) > 0 {
+ if s.useInvalidMetadata() && len(s.meta.metadata) > 0 {
return nil
}
- for _, m := range s.metadata {
+ for _, m := range s.meta.metadata {
if m.Valid {
return nil
}
@@ -1523,10 +1515,10 @@
func (s *snapshot) reloadWorkspace(ctx context.Context) error {
// See which of the workspace packages are missing metadata.
s.mu.Lock()
- missingMetadata := len(s.workspacePackages) == 0 || len(s.metadata) == 0
+ missingMetadata := len(s.workspacePackages) == 0 || len(s.meta.metadata) == 0
pkgPathSet := map[PackagePath]struct{}{}
for id, pkgPath := range s.workspacePackages {
- if m, ok := s.metadata[id]; ok && m.Valid {
+ if m, ok := s.meta.metadata[id]; ok && m.Valid {
continue
}
missingMetadata = true
@@ -1670,10 +1662,10 @@
// Check that every go file for a workspace package is identified as
// belonging to that workspace package.
for wsID := range s.workspacePackages {
- if m, ok := s.metadata[wsID]; ok {
+ if m, ok := s.meta.metadata[wsID]; ok {
for _, uri := range m.GoFiles {
found := false
- for _, id := range s.ids[uri] {
+ for _, id := range s.meta.ids[uri] {
if id == wsID {
found = true
break
@@ -1723,9 +1715,7 @@
builtin: s.builtin,
initializeOnce: s.initializeOnce,
initializedErr: s.initializedErr,
- ids: make(map[span.URI][]PackageID, len(s.ids)),
- importedBy: make(map[PackageID][]PackageID, len(s.importedBy)),
- metadata: make(map[PackageID]*KnownMetadata, len(s.metadata)),
+ meta: NewMetadataGraph(),
packages: make(map[packageKey]*packageHandle, len(s.packages)),
actions: make(map[actionKey]*actionHandle, len(s.actions)),
files: make(map[span.URI]source.VersionedFileHandle, len(s.files)),
@@ -1813,7 +1803,7 @@
// Invalidate all package metadata if the workspace module has changed.
if workspaceReload {
- for k := range s.metadata {
+ for k := range s.meta.metadata {
directIDs[k] = true
}
}
@@ -1843,7 +1833,7 @@
anyImportDeleted = anyImportDeleted || importDeleted
// Mark all of the package IDs containing the given file.
- filePackageIDs := invalidatedPackageIDs(uri, s.ids, pkgFileChanged)
+ filePackageIDs := invalidatedPackageIDs(uri, s.meta.ids, pkgFileChanged)
if pkgFileChanged {
for id := range filePackageIDs {
changedPkgFiles[id] = struct{}{}
@@ -1892,7 +1882,7 @@
// from an unparseable state to a parseable state, as we don't have a
// starting point to compare with.
if anyImportDeleted {
- for id, metadata := range s.metadata {
+ for id, metadata := range s.meta.metadata {
if len(metadata.Errors) > 0 {
directIDs[id] = true
}
@@ -1952,7 +1942,7 @@
continue
}
// The file has been deleted.
- if ids, ok := s.ids[c.fileHandle.URI()]; ok {
+ if ids, ok := s.meta.ids[c.fileHandle.URI()]; ok {
for _, id := range ids {
skipID[id] = true
}
@@ -1968,7 +1958,7 @@
return
}
reachableID[id] = true
- m, ok := s.metadata[id]
+ m, ok := s.meta.metadata[id]
if !ok {
return
}
@@ -1984,7 +1974,7 @@
// metadata will be reloaded in future calls to load.
deleteInvalidMetadata := forceReloadMetadata || workspaceModeChanged
idsInSnapshot := map[PackageID]bool{} // track all known IDs
- for uri, ids := range s.ids {
+ for uri, ids := range s.meta.ids {
var resultIDs []PackageID
for _, id := range ids {
if skipID[id] || deleteInvalidMetadata && idsToInvalidate[id] {
@@ -1998,12 +1988,12 @@
idsInSnapshot[id] = true
resultIDs = append(resultIDs, id)
}
- result.ids[uri] = resultIDs
+ result.meta.ids[uri] = resultIDs
}
// Copy the package metadata. We only need to invalidate packages directly
// containing the affected file, and only if it changed in a relevant way.
- for k, v := range s.metadata {
+ for k, v := range s.meta.metadata {
if !idsInSnapshot[k] {
// Delete metadata for IDs that are no longer reachable from files
// in the snapshot.
@@ -2011,7 +2001,7 @@
}
invalidateMetadata := idsToInvalidate[k]
// Mark invalidated metadata rather than deleting it outright.
- result.metadata[k] = &KnownMetadata{
+ result.meta.metadata[k] = &KnownMetadata{
Metadata: v.Metadata,
Valid: v.Valid && !invalidateMetadata,
ShouldLoad: v.ShouldLoad || invalidateMetadata,
@@ -2032,9 +2022,9 @@
// If all the files we know about in a package have been deleted,
// the package is gone and we should no longer try to load it.
- if m := s.metadata[id]; m != nil {
+ if m := s.meta.metadata[id]; m != nil {
hasFiles := false
- for _, uri := range s.metadata[id].GoFiles {
+ for _, uri := range s.meta.metadata[id].GoFiles {
// For internal tests, we need _test files, not just the normal
// ones. External tests only have _test files, but we can check
// them anyway.