Skip to content

Commit bc4e384

Browse files
committed
gopls/internal/lsp/cache: fix crash in analysis
Since we removed the 'recover' in analysis, which swept crash bugs under the rug, we've seen a large number of crashes on the builders in which an analyzer crashes while trying to use the result of another analyzer (e.g. the inspector) on the same package. Logging shows that the "same" package is in fact a stale version of the same package. The root cause is that the key for the analysis cache is the analyzer name paired with the "recipe" for the package. However, analysis is not content with an equivalent package: identity matters for ast.Nodes, types.Objects, etc. This change attemps to fix the problem by, in effect, using the identity (not the recipe) of the package as part of the key. It is materialized by adding a store of promises to the pkg, and then using the analysis name as the key within this store. Change-Id: I057807d2781492a685f27c815250c3445e7475f9 Reviewed-on: https://go-review.googlesource.com/c/tools/+/443100 gopls-CI: kokoro <[email protected]> Reviewed-by: Robert Findley <[email protected]> Run-TryBot: Alan Donovan <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent b93a56f commit bc4e384

File tree

2 files changed

+13
-7
lines changed

2 files changed

+13
-7
lines changed

gopls/internal/lsp/cache/analysis.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@ type actionKey struct {
6666
analyzer *analysis.Analyzer
6767
}
6868

69-
type actionHandleKey source.Hash
70-
7169
// An action represents one unit of analysis work: the application of
7270
// one analysis to one package. Actions form a DAG, both within a
7371
// package (as different analyzers are applied, either in sequence or
@@ -162,7 +160,16 @@ func (s *snapshot) actionHandle(ctx context.Context, id PackageID, a *analysis.A
162160
}
163161
}
164162

165-
promise, release := s.store.Promise(buildActionKey(a, ph), func(ctx context.Context, arg interface{}) interface{} {
163+
// The promises are kept in a store on the package,
164+
// so the key need only include the analyzer name.
165+
//
166+
// (Since type-checking and analysis depend on the identity
167+
// of packages--distinct packages produced by the same
168+
// recipe are not fungible--we must in effect use the package
169+
// itself as part of the key. Rather than actually use a pointer
170+
// in the key, we get a simpler object graph if we shard the
171+
// store by packages.)
172+
promise, release := pkg.analyses.Promise(a.Name, func(ctx context.Context, arg interface{}) interface{} {
166173
res, err := actionImpl(ctx, arg.(*snapshot), deps, a, pkg)
167174
return actionResult{res, err}
168175
})
@@ -186,10 +193,6 @@ func (s *snapshot) actionHandle(ctx context.Context, id PackageID, a *analysis.A
186193
return ah, nil
187194
}
188195

189-
func buildActionKey(a *analysis.Analyzer, ph *packageHandle) actionHandleKey {
190-
return actionHandleKey(source.Hashf("%p%s", a, ph.key[:]))
191-
}
192-
193196
func (key actionKey) String() string {
194197
return fmt.Sprintf("%s@%s", key.analyzer, key.pkgid)
195198
}

gopls/internal/lsp/cache/pkg.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"golang.org/x/mod/module"
1414
"golang.org/x/tools/gopls/internal/lsp/source"
1515
"golang.org/x/tools/gopls/internal/span"
16+
"golang.org/x/tools/internal/memoize"
1617
)
1718

1819
// pkg contains the type information needed by the source package.
@@ -30,6 +31,8 @@ type pkg struct {
3031
typesInfo *types.Info
3132
typesSizes types.Sizes
3233
hasFixedFiles bool // if true, AST was sufficiently mangled that we should hide type errors
34+
35+
analyses memoize.Store // maps analyzer.Name to Promise[actionResult]
3336
}
3437

3538
// A loadScope defines a package loading scope for use with go/packages.

0 commit comments

Comments
 (0)