Skip to content

Commit 6dcdf1d

Browse files
committed
internal/lsp/cache: refactor functions that return PackageHandles
Now that we build PackageHandles in load, we can simplify a lot of our logic for rebuilding them and reloading metadata. The only possibly-significant change is the missing imports logic. Now that we always create package handles in load, we don't have to do the extra "sameSet" logic. If the package handle hasn't been invalidated since we last built it, we will keep the benefits of caching it. Otherwise, it will be rebuilt in load. Updates golang/go#36918 Change-Id: Ieb45898d248501e3cebdb95c8b518149ba7498e5 Reviewed-on: https://go-review.googlesource.com/c/tools/+/217157 Run-TryBot: Rebecca Stambler <[email protected]> Reviewed-by: Heschi Kreinick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 009580c commit 6dcdf1d

File tree

2 files changed

+50
-133
lines changed

2 files changed

+50
-133
lines changed

internal/lsp/cache/session.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,9 @@ func (s *session) updateOverlays(ctx context.Context, changes []source.FileModif
402402
// Get the overlays for each change while the session's overlay map is locked.
403403
overlays := make(map[span.URI]*overlay)
404404
for _, c := range changes {
405-
overlays[c.URI] = s.overlays[c.URI]
405+
if o, ok := s.overlays[c.URI]; ok {
406+
overlays[c.URI] = o
407+
}
406408
}
407409
return overlays, nil
408410
}

internal/lsp/cache/snapshot.go

+47-132
Original file line numberDiff line numberDiff line change
@@ -127,142 +127,58 @@ func (s *snapshot) buildOverlay() map[string][]byte {
127127
}
128128

129129
func (s *snapshot) PackageHandles(ctx context.Context, fh source.FileHandle) ([]source.PackageHandle, error) {
130-
// If the file is a go.mod file, go.Packages.Load will always return 0 packages.
131-
if fh.Identity().Kind == source.Mod {
132-
return nil, errors.Errorf("attempting to get PackageHandles of .mod file %s", fh.Identity().URI)
130+
if fh.Identity().Kind != source.Go {
131+
panic(fmt.Sprintf("called PackageHandles on a non-Go FileHandle"))
133132
}
134133

135134
ctx = telemetry.File.With(ctx, fh.Identity().URI)
136-
meta := s.getMetadataForURI(fh.Identity().URI)
137135

138-
phs, err := s.packageHandles(ctx, fh.Identity().URI, meta)
139-
if err != nil {
140-
return nil, err
141-
}
142-
var results []source.PackageHandle
143-
for _, ph := range phs {
144-
results = append(results, ph)
145-
}
146-
return results, nil
147-
}
148-
149-
func (s *snapshot) packageHandle(ctx context.Context, id packageID) (*packageHandle, error) {
150-
m := s.getMetadata(id)
151-
152-
// Don't reload metadata in this function.
153-
// Callers of this function must reload metadata themselves.
154-
if m == nil {
155-
return nil, errors.Errorf("%s has no metadata", id)
156-
}
157-
phs, load, check := s.shouldCheck([]*metadata{m})
158-
if load {
159-
return nil, errors.Errorf("%s needs loading", id)
160-
}
161-
if check {
162-
return s.buildPackageHandle(ctx, m.id, source.ParseFull)
163-
}
164-
var result *packageHandle
165-
for _, ph := range phs {
166-
if ph.m.id == id {
167-
if result != nil {
168-
return nil, errors.Errorf("multiple package handles for the same ID: %s", id)
169-
}
170-
result = ph
171-
}
172-
}
173-
if result == nil {
174-
return nil, errors.Errorf("no PackageHandle for %s", id)
175-
}
176-
return result, nil
177-
}
178-
179-
func (s *snapshot) packageHandles(ctx context.Context, uri span.URI, meta []*metadata) ([]*packageHandle, error) {
180-
// First, determine if we need to reload or recheck the package.
181-
phs, load, check := s.shouldCheck(meta)
182-
if load {
183-
if err := s.load(ctx, fileURI(uri)); err != nil {
136+
// Check if we should reload metadata for the file. We don't invalidate IDs
137+
// (though we should), so the IDs will be a better source of truth than the
138+
// metadata. If there are no IDs for the file, then we should also reload.
139+
ids := s.getIDsForURI(fh.Identity().URI)
140+
reload := len(ids) == 0
141+
for _, id := range ids {
142+
// Reload package metadata if any of the metadata has missing
143+
// dependencies, in case something has changed since the last time we
144+
// reloaded it.
145+
if m := s.getMetadata(id); m == nil || len(m.missingDeps) > 0 {
146+
reload = true
147+
break
148+
}
149+
}
150+
if reload {
151+
if err := s.load(ctx, fileURI(fh.Identity().URI)); err != nil {
184152
return nil, err
185153
}
186-
// TODO(rstambler): Now that we are creating new package handles in
187-
// every load, this isn't really necessary.
188-
newMeta := s.getMetadataForURI(uri)
189-
newMissing := missingImports(newMeta)
190-
if len(newMissing) != 0 {
191-
// Type checking a package with the same missing imports over and over
192-
// is futile. Don't re-check unless something has changed.
193-
check = check && !sameSet(missingImports(meta), newMissing)
194-
}
195-
meta = newMeta
196-
}
197-
var results []*packageHandle
198-
if check {
199-
for _, m := range meta {
200-
ph, err := s.buildPackageHandle(ctx, m.id, source.ParseFull)
201-
if err != nil {
202-
return nil, err
203-
}
204-
results = append(results, ph)
205-
}
206-
} else {
207-
results = phs
208154
}
209-
if len(results) == 0 {
210-
return nil, errors.Errorf("packageHandles: no package handles for %v", uri)
211-
}
212-
return results, nil
213-
}
214-
215-
func missingImports(metadata []*metadata) map[packagePath]struct{} {
216-
result := map[packagePath]struct{}{}
217-
for _, m := range metadata {
218-
for path := range m.missingDeps {
219-
result[path] = struct{}{}
220-
}
221-
}
222-
return result
223-
}
224-
225-
func sameSet(x, y map[packagePath]struct{}) bool {
226-
if len(x) != len(y) {
227-
return false
228-
}
229-
for k := range x {
230-
if _, ok := y[k]; !ok {
231-
return false
155+
// Get the list of IDs from the snapshot again, in case it has changed.
156+
var phs []source.PackageHandle
157+
for _, id := range s.getIDsForURI(fh.Identity().URI) {
158+
ph, err := s.packageHandle(ctx, id, source.ParseFull)
159+
if err != nil {
160+
return nil, err
232161
}
162+
phs = append(phs, ph)
233163
}
234-
return true
164+
return phs, nil
235165
}
236166

237-
// shouldCheck determines if the packages provided by the metadata
238-
// need to be re-loaded or re-type-checked.
239-
func (s *snapshot) shouldCheck(m []*metadata) (phs []*packageHandle, load, check bool) {
240-
// No metadata. Re-load and re-check.
241-
if len(m) == 0 {
242-
return nil, true, true
243-
}
244-
// We expect to see a checked package for each package ID,
245-
// and it should be parsed in full mode.
246-
// If a single PackageHandle is missing, re-check all of them.
247-
// TODO: Optimize this by only checking the necessary packages.
248-
for _, m := range m {
249-
ph := s.getPackage(m.id, source.ParseFull)
250-
if ph == nil {
251-
return nil, false, true
252-
}
253-
phs = append(phs, ph)
167+
// packageHandle returns a PackageHandle for the given ID. It assumes that
168+
// the metadata for the given ID has already been loaded, but if the
169+
// PackageHandle has not been constructed, it will rebuild it.
170+
func (s *snapshot) packageHandle(ctx context.Context, id packageID, mode source.ParseMode) (*packageHandle, error) {
171+
ph := s.getPackage(id, mode)
172+
if ph != nil {
173+
return ph, nil
254174
}
255-
// If the metadata for the package had missing dependencies,
256-
// we _may_ need to re-check. If the missing dependencies haven't changed
257-
// since previous load, we will not check again.
258-
if len(phs) < len(m) {
259-
for _, m := range m {
260-
if len(m.missingDeps) != 0 {
261-
return nil, true, true
262-
}
263-
}
175+
// Don't reload metadata in this function.
176+
// Callers of this function must reload metadata themselves.
177+
m := s.getMetadata(id)
178+
if m == nil {
179+
return nil, errors.Errorf("%s has no metadata", id)
264180
}
265-
return phs, false, false
181+
return s.buildPackageHandle(ctx, m.id, mode)
266182
}
267183

268184
func (s *snapshot) GetReverseDependencies(ctx context.Context, id string) ([]source.PackageHandle, error) {
@@ -277,7 +193,7 @@ func (s *snapshot) GetReverseDependencies(ctx context.Context, id string) ([]sou
277193

278194
var results []source.PackageHandle
279195
for id := range ids {
280-
ph, err := s.packageHandle(ctx, id)
196+
ph, err := s.packageHandle(ctx, id, source.ParseFull)
281197
if err != nil {
282198
return nil, err
283199
}
@@ -362,7 +278,7 @@ func (s *snapshot) WorkspacePackages(ctx context.Context) ([]source.PackageHandl
362278
}
363279
var results []source.PackageHandle
364280
for _, pkgID := range s.workspacePackageIDs() {
365-
ph, err := s.packageHandle(ctx, pkgID)
281+
ph, err := s.packageHandle(ctx, pkgID, source.ParseFull)
366282
if err != nil {
367283
return nil, err
368284
}
@@ -386,7 +302,7 @@ func (s *snapshot) KnownPackages(ctx context.Context) ([]source.PackageHandle, e
386302

387303
var results []source.PackageHandle
388304
for pkgID := range wsPackages {
389-
ph, err := s.packageHandle(ctx, pkgID)
305+
ph, err := s.packageHandle(ctx, pkgID, source.ParseFull)
390306
if err != nil {
391307
return nil, err
392308
}
@@ -486,11 +402,11 @@ func (s *snapshot) addActionHandle(ah *actionHandle) {
486402
s.actions[key] = ah
487403
}
488404

489-
func (s *snapshot) getMetadataForURI(uri span.URI) []*metadata {
405+
func (s *snapshot) getIDsForURI(uri span.URI) []packageID {
490406
s.mu.Lock()
491407
defer s.mu.Unlock()
492408

493-
return s.getMetadataForURILocked(uri)
409+
return s.ids[uri]
494410
}
495411

496412
func (s *snapshot) getMetadataForURILocked(uri span.URI) (metadata []*metadata) {
@@ -711,7 +627,7 @@ func (s *snapshot) clone(ctx context.Context, withoutURIs map[span.URI]source.Fi
711627

712628
// Check if the file's package name or imports have changed,
713629
// and if so, invalidate this file's packages' metadata.
714-
invalidateMetadata := s.shouldLoad(ctx, originalFH, currentFH)
630+
invalidateMetadata := s.shouldInvalidateMetadata(ctx, originalFH, currentFH)
715631

716632
// If a go.mod file's contents have changed, invalidate the metadata
717633
// for all of the packages in the workspace.
@@ -802,9 +718,9 @@ func (s *snapshot) clone(ctx context.Context, withoutURIs map[span.URI]source.Fi
802718
return result
803719
}
804720

805-
// shouldLoad reparses a file's package and import declarations to
721+
// shouldInvalidateMetadata reparses a file's package and import declarations to
806722
// determine if the file requires a metadata reload.
807-
func (s *snapshot) shouldLoad(ctx context.Context, originalFH, currentFH source.FileHandle) bool {
723+
func (s *snapshot) shouldInvalidateMetadata(ctx context.Context, originalFH, currentFH source.FileHandle) bool {
808724
if originalFH == nil {
809725
return currentFH.Identity().Kind == source.Go
810726
}
@@ -823,7 +739,6 @@ func (s *snapshot) shouldLoad(ctx context.Context, originalFH, currentFH source.
823739
if originalErr != nil || currentErr != nil {
824740
return (originalErr == nil) != (currentErr == nil)
825741
}
826-
827742
// Check if the package's metadata has changed. The cases handled are:
828743
// 1. A package's name has changed
829744
// 2. A file's imports have changed

0 commit comments

Comments
 (0)