Skip to content

Commit 8bf61b8

Browse files
findleyrgopherbot
authored andcommitted
gopls/internal/cache: fix module resolver cache refreshing
An apparent bad merge in CL 561235 caused the critical component-- clearing the resolver for a new scan--to be dropped. Fix this, so that the imports state is actually refreshed asynchronously. It is surprising that this was not reported, though I see perhaps two related comments in survey results. Most likely adding a new import is infrequent enough that users were simply confused, or learned to restart gopls (alas). Also, add more instrumentation that would help debug golang/go#67289. For golang/go#67289 Change-Id: I50d70a470eb393caf9e0b41856997942372b216f Reviewed-on: https://go-review.googlesource.com/c/tools/+/590377 Auto-Submit: Robert Findley <rfindley@google.com> Reviewed-by: Alan Donovan <adonovan@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
1 parent 7522327 commit 8bf61b8

File tree

4 files changed

+36
-33
lines changed

4 files changed

+36
-33
lines changed

gopls/internal/cache/imports.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,15 @@ type importsState struct {
119119
// newImportsState constructs a new imports state for running goimports
120120
// functions via [runProcessEnvFunc].
121121
//
122-
// The returned state will automatically refresh itself following a call to
123-
// runProcessEnvFunc.
122+
// The returned state will automatically refresh itself following a delay.
124123
func newImportsState(backgroundCtx context.Context, modCache *sharedModCache, env *imports.ProcessEnv) *importsState {
125124
s := &importsState{
126125
ctx: backgroundCtx,
127126
modCache: modCache,
128127
processEnv: env,
129128
}
130129
s.refreshTimer = newRefreshTimer(s.refreshProcessEnv)
130+
s.refreshTimer.schedule()
131131
return s
132132
}
133133

@@ -210,20 +210,22 @@ func (s *importsState) refreshProcessEnv() {
210210
resolver, err := s.processEnv.GetResolver()
211211
s.mu.Unlock()
212212
if err != nil {
213+
event.Error(s.ctx, "failed to get import resolver", err)
213214
return
214215
}
215216

216217
event.Log(s.ctx, "background imports cache refresh starting")
218+
resolver2 := resolver.ClearForNewScan()
217219

218220
// Prime the new resolver before updating the processEnv, so that gopls
219221
// doesn't wait on an unprimed cache.
220-
if err := imports.PrimeCache(context.Background(), resolver); err == nil {
222+
if err := imports.PrimeCache(context.Background(), resolver2); err == nil {
221223
event.Log(ctx, fmt.Sprintf("background refresh finished after %v", time.Since(start)))
222224
} else {
223225
event.Log(ctx, fmt.Sprintf("background refresh finished after %v", time.Since(start)), keys.Err.Of(err))
224226
}
225227

226228
s.mu.Lock()
227-
s.processEnv.UpdateResolver(resolver)
229+
s.processEnv.UpdateResolver(resolver2)
228230
s.mu.Unlock()
229231
}

internal/gocommand/invoke.go

+12-5
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,14 @@ func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io
200200
return
201201
}
202202

203-
func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
204-
log := i.Logf
205-
if log == nil {
206-
log = func(string, ...interface{}) {}
203+
// logf logs if i.Logf is non-nil.
204+
func (i *Invocation) logf(format string, args ...any) {
205+
if i.Logf != nil {
206+
i.Logf(format, args...)
207207
}
208+
}
208209

210+
func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
209211
goArgs := []string{i.Verb}
210212

211213
appendModFile := func() {
@@ -277,7 +279,12 @@ func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
277279
cmd.Dir = i.WorkingDir
278280
}
279281

280-
defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now())
282+
debugStr := cmdDebugStr(cmd)
283+
i.logf("starting %v", debugStr)
284+
start := time.Now()
285+
defer func() {
286+
i.logf("%s for %v", time.Since(start), debugStr)
287+
}()
281288

282289
return runCmdContext(ctx, cmd)
283290
}

internal/imports/fix.go

+16-18
Original file line numberDiff line numberDiff line change
@@ -365,9 +365,7 @@ func (p *pass) load() ([]*ImportFix, bool) {
365365
if p.loadRealPackageNames {
366366
err := p.loadPackageNames(append(imports, p.candidates...))
367367
if err != nil {
368-
if p.env.Logf != nil {
369-
p.env.Logf("loading package names: %v", err)
370-
}
368+
p.env.logf("loading package names: %v", err)
371369
return nil, false
372370
}
373371
}
@@ -580,9 +578,7 @@ func getFixes(ctx context.Context, fset *token.FileSet, f *ast.File, filename st
580578
return nil, err
581579
}
582580
srcDir := filepath.Dir(abs)
583-
if env.Logf != nil {
584-
env.Logf("fixImports(filename=%q), abs=%q, srcDir=%q ...", filename, abs, srcDir)
585-
}
581+
env.logf("fixImports(filename=%q), abs=%q, srcDir=%q ...", filename, abs, srcDir)
586582

587583
// First pass: looking only at f, and using the naive algorithm to
588584
// derive package names from import paths, see if the file is already
@@ -1014,16 +1010,26 @@ func (e *ProcessEnv) GetResolver() (Resolver, error) {
10141010
// already know the view type.
10151011
if len(e.Env["GOMOD"]) == 0 && len(e.Env["GOWORK"]) == 0 {
10161012
e.resolver = newGopathResolver(e)
1013+
e.logf("created gopath resolver")
10171014
} else if r, err := newModuleResolver(e, e.ModCache); err != nil {
10181015
e.resolverErr = err
1016+
e.logf("failed to create module resolver: %v", err)
10191017
} else {
10201018
e.resolver = Resolver(r)
1019+
e.logf("created module resolver")
10211020
}
10221021
}
10231022

10241023
return e.resolver, e.resolverErr
10251024
}
10261025

1026+
// logf logs if e.Logf is non-nil.
1027+
func (e *ProcessEnv) logf(format string, args ...any) {
1028+
if e.Logf != nil {
1029+
e.Logf(format, args...)
1030+
}
1031+
}
1032+
10271033
// buildContext returns the build.Context to use for matching files.
10281034
//
10291035
// TODO(rfindley): support dynamic GOOS, GOARCH here, when doing cross-platform
@@ -1610,9 +1616,7 @@ func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string, incl
16101616
fullFile := filepath.Join(dir, fi.Name())
16111617
f, err := parser.ParseFile(fset, fullFile, nil, 0)
16121618
if err != nil {
1613-
if env.Logf != nil {
1614-
env.Logf("error parsing %v: %v", fullFile, err)
1615-
}
1619+
env.logf("error parsing %v: %v", fullFile, err)
16161620
continue
16171621
}
16181622
if f.Name.Name == "documentation" {
@@ -1648,9 +1652,7 @@ func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string, incl
16481652
}
16491653
sortSymbols(exports)
16501654

1651-
if env.Logf != nil {
1652-
env.Logf("loaded exports in dir %v (package %v): %v", dir, pkgName, exports)
1653-
}
1655+
env.logf("loaded exports in dir %v (package %v): %v", dir, pkgName, exports)
16541656
return pkgName, exports, nil
16551657
}
16561658

@@ -1710,16 +1712,12 @@ func findImport(ctx context.Context, pass *pass, candidates []pkgDistance, pkgNa
17101712
wg.Done()
17111713
}()
17121714

1713-
if pass.env.Logf != nil {
1714-
pass.env.Logf("loading exports in dir %s (seeking package %s)", c.pkg.dir, pkgName)
1715-
}
1715+
pass.env.logf("loading exports in dir %s (seeking package %s)", c.pkg.dir, pkgName)
17161716
// If we're an x_test, load the package under test's test variant.
17171717
includeTest := strings.HasSuffix(pass.f.Name.Name, "_test") && c.pkg.dir == pass.srcDir
17181718
_, exports, err := resolver.loadExports(ctx, c.pkg, includeTest)
17191719
if err != nil {
1720-
if pass.env.Logf != nil {
1721-
pass.env.Logf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err)
1722-
}
1720+
pass.env.logf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err)
17231721
resc <- nil
17241722
return
17251723
}

internal/imports/mod.go

+2-6
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,7 @@ func (r *ModuleResolver) initAllMods() error {
265265
return err
266266
}
267267
if mod.Dir == "" {
268-
if r.env.Logf != nil {
269-
r.env.Logf("module %v has not been downloaded and will be ignored", mod.Path)
270-
}
268+
r.env.logf("module %v has not been downloaded and will be ignored", mod.Path)
271269
// Can't do anything with a module that's not downloaded.
272270
continue
273271
}
@@ -766,9 +764,7 @@ func (r *ModuleResolver) scanDirForPackage(root gopathwalk.Root, dir string) dir
766764
}
767765
modPath, err := module.UnescapePath(filepath.ToSlash(matches[1]))
768766
if err != nil {
769-
if r.env.Logf != nil {
770-
r.env.Logf("decoding module cache path %q: %v", subdir, err)
771-
}
767+
r.env.logf("decoding module cache path %q: %v", subdir, err)
772768
return directoryPackageInfo{
773769
status: directoryScanned,
774770
err: fmt.Errorf("decoding module cache path %q: %v", subdir, err),

0 commit comments

Comments
 (0)