mirror of https://github.com/golang/go.git
internal/lsp/cache: prune types.Info entries in slice literals
The type checker emits a TypeAndValue entry for (among other things) every constant in a Go file. Normally, that cost is moderate. However, in the case of a large slice literal, it can get out of control very quickly. Imagine a code generator that creates a 2KB byte slice literal; that's 2K TypeAndValue entries, each of which is considerably larger than the 1-3 bytes for the source text. Unfortunately, there are a number of such code generators. Notably, there are such slice literals in proto code, e.g. https://github.com/grpc/grpc-go/blob/master/examples/route_guide/routeguide/route_guide.pb.go#L360 This CL changes the type checking code to remove the TypeAndValue entries for slice literals of basic types after the checker returns. In the extreme case of https://github.com/googleapis/go-genproto, which is nothing but protos, I see a ~40% drop in heap usage. I believe this change is generally safe, but there's no way to guarantee it. I don't think any editor features need to know the type or value of an arbitrary slice element, but it is just barely possible that an analyzer does. Change-Id: Iee1af2369f994597a42fd1dcbf8af20faa43410e Reviewed-on: https://go-review.googlesource.com/c/tools/+/308730 Trust: Heschi Kreinick <heschi@google.com> Run-TryBot: Heschi Kreinick <heschi@google.com> gopls-CI: kokoro <noreply+kokoro@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
1a0c6081c5
commit
07295caad0
|
|
@ -446,7 +446,6 @@ func typeCheck(ctx context.Context, snapshot *snapshot, m *metadata, mode source
|
|||
typesinternal.SetUsesCgo(cfg)
|
||||
|
||||
check := types.NewChecker(cfg, fset, pkg.types, pkg.typesInfo)
|
||||
|
||||
// Type checking errors are handled via the config, so ignore them here.
|
||||
_ = check.Files(files)
|
||||
// If the context was cancelled, we may have returned a ton of transient
|
||||
|
|
@ -455,6 +454,8 @@ func typeCheck(ctx context.Context, snapshot *snapshot, m *metadata, mode source
|
|||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
trimTypesInfo(files, pkg.typesInfo)
|
||||
|
||||
// We don't care about a package's errors unless we have parsed it in full.
|
||||
if mode != source.ParseFull {
|
||||
return pkg, nil
|
||||
|
|
@ -795,3 +796,33 @@ func isValidImport(pkgPath, importPkgPath packagePath) bool {
|
|||
type importerFunc func(path string) (*types.Package, error)
|
||||
|
||||
func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
|
||||
|
||||
func trimTypesInfo(files []*ast.File, info *types.Info) {
|
||||
for _, file := range files {
|
||||
ast.Inspect(file, func(n ast.Node) bool {
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
cl, ok := n.(*ast.CompositeLit)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
// types.Info.Types for long slice/array literals are particularly
|
||||
// expensive. Try to clear them out.
|
||||
if _, ok := cl.Type.(*ast.ArrayType); !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, elt := range cl.Elts {
|
||||
// Only delete information for basic literals; other elements,
|
||||
// like constants and function calls, could still be relevant.
|
||||
if _, ok := elt.(*ast.BasicLit); ok {
|
||||
delete(info.Types, elt)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue