Skip to content

Commit 9611592

Browse files
committed
internal/lsp/cache: fix load race, refactor
As far as I can tell, the code I removed in from load did roughly nothing -- returning nil metadata didn't suppress type checking as I think was intended. Throwing away the metadata also created the race in Pull the check for missing import changes up to PackageHandles, where it is non-racy and can cause type checking to be skipped. Simplify and refactor. Fixes golang/go#35951. Change-Id: Id4b32b86569afb36863aaf982616b2b3727b0e83 Reviewed-on: https://go-review.googlesource.com/c/tools/+/209737 Run-TryBot: Heschi Kreinick <[email protected]> Reviewed-by: Rebecca Stambler <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent b1451cf commit 9611592

File tree

2 files changed

+38
-62
lines changed

2 files changed

+38
-62
lines changed

internal/lsp/cache/load.go

Lines changed: 5 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -71,48 +71,7 @@ func (s *snapshot) load(ctx context.Context, scope source.Scope) ([]*metadata, e
7171
if err != nil {
7272
return nil, err
7373
}
74-
m, prevMissingImports, err := s.updateMetadata(ctx, scope, pkgs, cfg)
75-
if err != nil {
76-
return nil, err
77-
}
78-
meta, err := validateMetadata(ctx, m, prevMissingImports)
79-
if err != nil {
80-
return nil, err
81-
}
82-
return meta, nil
83-
}
84-
85-
func validateMetadata(ctx context.Context, metadata []*metadata, prevMissingImports map[packageID]map[packagePath]struct{}) ([]*metadata, error) {
86-
// If we saw incorrect metadata for this package previously, don't bother rechecking it.
87-
for _, m := range metadata {
88-
if len(m.missingDeps) > 0 {
89-
prev, ok := prevMissingImports[m.id]
90-
// There are missing imports that we previously hadn't seen before.
91-
if !ok {
92-
return metadata, nil
93-
}
94-
// The set of missing imports has changed.
95-
if !sameSet(prev, m.missingDeps) {
96-
return metadata, nil
97-
}
98-
} else {
99-
// There are no missing imports.
100-
return metadata, nil
101-
}
102-
}
103-
return nil, nil
104-
}
105-
106-
func sameSet(x, y map[packagePath]struct{}) bool {
107-
if len(x) != len(y) {
108-
return false
109-
}
110-
for k := range x {
111-
if _, ok := y[k]; !ok {
112-
return false
113-
}
114-
}
115-
return true
74+
return s.updateMetadata(ctx, scope, pkgs, cfg)
11675
}
11776

11877
// shouldLoad reparses a file's package and import declarations to
@@ -150,7 +109,7 @@ func (c *cache) shouldLoad(ctx context.Context, s *snapshot, originalFH, current
150109
return false
151110
}
152111

153-
func (s *snapshot) updateMetadata(ctx context.Context, uri source.Scope, pkgs []*packages.Package, cfg *packages.Config) ([]*metadata, map[packageID]map[packagePath]struct{}, error) {
112+
func (s *snapshot) updateMetadata(ctx context.Context, uri source.Scope, pkgs []*packages.Package, cfg *packages.Config) ([]*metadata, error) {
154113
// Clear metadata since we are re-running go/packages.
155114
var m []*metadata
156115
switch uri.(type) {
@@ -165,20 +124,14 @@ func (s *snapshot) updateMetadata(ctx context.Context, uri source.Scope, pkgs []
165124
default:
166125
panic(fmt.Errorf("unsupported Scope type %T", uri))
167126
}
168-
prevMissingImports := make(map[packageID]map[packagePath]struct{})
169-
for _, m := range m {
170-
if len(m.missingDeps) > 0 {
171-
prevMissingImports[m.id] = m.missingDeps
172-
}
173-
}
174127

175128
var results []*metadata
176129
for _, pkg := range pkgs {
177130
log.Print(ctx, "go/packages.Load", tag.Of("package", pkg.PkgPath), tag.Of("files", pkg.CompiledGoFiles))
178131

179132
// Set the metadata for this package.
180133
if err := s.updateImports(ctx, packagePath(pkg.PkgPath), pkg, cfg, map[packageID]struct{}{}); err != nil {
181-
return nil, nil, err
134+
return nil, err
182135
}
183136
m := s.getMetadata(packageID(pkg.ID))
184137
if m != nil {
@@ -190,9 +143,9 @@ func (s *snapshot) updateMetadata(ctx context.Context, uri source.Scope, pkgs []
190143
s.clearAndRebuildImportGraph()
191144

192145
if len(results) == 0 {
193-
return nil, nil, errors.Errorf("no metadata for %s", uri)
146+
return nil, errors.Errorf("no metadata for %s", uri)
194147
}
195-
return results, prevMissingImports, nil
148+
return results, nil
196149
}
197150

198151
func (s *snapshot) updateImports(ctx context.Context, pkgPath packagePath, pkg *packages.Package, cfg *packages.Config, seen map[packageID]struct{}) error {

internal/lsp/cache/snapshot.go

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,28 +63,28 @@ func (s *snapshot) View() source.View {
6363

6464
func (s *snapshot) PackageHandles(ctx context.Context, fh source.FileHandle) ([]source.PackageHandle, error) {
6565
ctx = telemetry.File.With(ctx, fh.Identity().URI)
66-
67-
metadata := s.getMetadataForURI(fh.Identity().URI)
68-
66+
meta := s.getMetadataForURI(fh.Identity().URI)
6967
// Determine if we need to type-check the package.
70-
phs, load, check := s.shouldCheck(metadata)
68+
phs, load, check := s.shouldCheck(meta)
7169

7270
// We may need to re-load package metadata.
7371
// We only need to this if it has been invalidated, and is therefore unvailable.
7472
if load {
75-
var err error
76-
m, err := s.load(ctx, source.FileURI(fh.Identity().URI))
73+
newMeta, err := s.load(ctx, source.FileURI(fh.Identity().URI))
7774
if err != nil {
7875
return nil, err
7976
}
80-
// If metadata was returned, from the load call, use it.
81-
if m != nil {
82-
metadata = m
77+
newMissing := missingImports(newMeta)
78+
if len(newMissing) != 0 {
79+
// Type checking a package with the same missing imports over and over
80+
// is futile. Don't re-check unless something has changed.
81+
check = check && !sameSet(missingImports(meta), newMissing)
8382
}
83+
meta = newMeta
8484
}
8585
if check {
8686
var results []source.PackageHandle
87-
for _, m := range metadata {
87+
for _, m := range meta {
8888
ph, err := s.packageHandle(ctx, m.id, source.ParseFull)
8989
if err != nil {
9090
return nil, err
@@ -96,9 +96,32 @@ func (s *snapshot) PackageHandles(ctx context.Context, fh source.FileHandle) ([]
9696
if len(phs) == 0 {
9797
return nil, errors.Errorf("no CheckPackageHandles for %s", fh.Identity().URI)
9898
}
99+
99100
return phs, nil
100101
}
101102

103+
func missingImports(metadata []*metadata) map[packagePath]struct{} {
104+
result := map[packagePath]struct{}{}
105+
for _, m := range metadata {
106+
for path := range m.missingDeps {
107+
result[path] = struct{}{}
108+
}
109+
}
110+
return result
111+
}
112+
113+
func sameSet(x, y map[packagePath]struct{}) bool {
114+
if len(x) != len(y) {
115+
return false
116+
}
117+
for k := range x {
118+
if _, ok := y[k]; !ok {
119+
return false
120+
}
121+
}
122+
return true
123+
}
124+
102125
func (s *snapshot) PackageHandle(ctx context.Context, id string) (source.PackageHandle, error) {
103126
ctx = telemetry.Package.With(ctx, id)
104127

0 commit comments

Comments
 (0)