gopls/v0.12.0
This release contains a major rewrite of the way gopls computes and stores package information, with the goal of reducing memory usage and allowing gopls to scale to larger repositories. This change can also significantly reduce startup time when workspaces are reopened, as gopls now uses a file-based cache to persist data across sessions. With these optimizations, gopls is finally able to fully analyze dependencies using the golang.org/x/tools/go/analysis framework, resulting in improved accuracy for analysis diagnostics.
You can install this release with go install
:
go install golang.org/x/tools/gopls@v0.12.0
Support changes
As gopls matures, we're trying to simplify its configuration so that gopls Just Works in more scenarios, and so that we have fewer configuration combinations to test. This means that we will be gradually deprecating settings that affect the core behavior of gopls.
Removed experimental configuration options
As announced in the v0.10.0 release notes, this release removes support for the experimentalWorkspaceModule
and experimentalWatchedFileDelay
settings. The experimentalPackageCacheKey
setting is also removed, as it is irrelevant in the new design.
The experimentalWorkspaceModule
setting in particular may still be in use by some users. This setting has been superseded by built-in support for multi-module workspaces in the go
command, via Go workspaces. To get the equivalent behavior in gopls@v0.12.0, please create a go.work
file in your workspace using all desired modules. To use all modules in your workspace, run:
go work use -r .
Dropped support for Go 1.13-1.15, deprecated support for Go 1.16-1.17
As announced in the v0.10.0 release notes, this release drops support for Go 1.13-1.15, and in fact does not build with these Go versions.
Additionally, gopls@v0.12.x
will be the final sequence of versions supporting Go 1.16-1.17, and therefore displays a deprecation notice when used with these Go versions.
Supported operating systems
Given that our users are almost entirely on Linux, Windows, or Darwin, we are discussing narrowing our support to focus on those operating systems, in golang/go#59981.
Performance improvements
The banner feature of this release is an internal redesign that significantly improves the way gopls scales in larger codebases. Performance, particularly memory usage, has long been a pain point for our users.
Reduced memory usage
Previous releases of gopls held typed syntax trees for all packages, in memory, all the time. With this release, these large data structures are ephemeral: as soon as they are constructed, an index of information derived from them is saved persistently to a file-based cache, and the data structures are recycled. The index for each package includes the locations of declaring and referring identifiers; the set of exported declarations and their types; the method sets of each interface; and any diagnostics and facts (see below) produced during analysis. The index holds all the information needed to serve global-scope LSP queries such as “references”, “implementations”, and so on.
Moving package information to a file-based cache greatly reduces the amount of RAM gopls uses, by almost an order of magnitude in larger projects. The table below shows the reductions in steady-state memory usage for three open-source Go repositories.
Project | Packages | In-use bytes v0.11.0 | v0.12.0 | Change |
---|---|---|---|---|
gopls | 405 | 497MB | 232MB | -53% |
kubernetes | 3137 | 3090MB | 832MB | -73% |
google-cloud-go + submods | 7657 | 5039MB | 863MB | -83% |
Improved invalidation
Internally, gopls now resembles an incremental build system such as go build
, which recompiles only the packages that have changed due to edits since the last build. If neither the inputs nor the configuration have changed, gopls can reuse the result previously computed.
Nonetheless, a change to a low-level package may in principle affect a large number of other packages. In practice though, the effects of many common changes, such as adding a statement within a function body, are local. So, gopls computes symbol dependencies at a much finer grain than the import dependency graph so that it can safely avoid invalidating packages that cannot be affected by local changes to their dependencies.
Gopls also now employs a number of more modest optimizations that may be characterized as “do less”. For example, many operations previously requested type information that wasn’t needed in most cases, or requested parsed syntax trees when raw file content would do. We have reduced the demand for unnecessary but computationally expensive steps.
How well is gopls performing for you?
We expect this release to make gopls faster for all users, but it involves a lot of new machinery. We’d love to hear how well it works for you and how it affects performance in your workspace. Has it improved the way you work? Are there ways it could do better? Let us know by filling out our survey or reporting an issue.
The gopls stats
command provides some metrics that may be useful when filing issues to help us understand performance problems and bugs. Please review its output before sharing, or use the -anon
flag if you prefer not to share any fields that might identify you or the names of your files.
New features
Improved static analysis
This release includes a new driver for golang.org/x/tools/go/analysis static checkers (as used in go vet
) that supports “facts”. Facts are deductions made during analysis of one package that may be assumed during analysis of another package. When the printf format-string checker encounters a function MyPrintf
that delegates to fmt.Sprintf
, it records this information as a fact about MyPrintf. This fact enables argument checking for calls to MyPrintf too, so gopls will emit a diagnostic for any call to MyPrintf with arguments of the wrong type for the format string.
Here’s an example from gopls’ own code base. The Hashf
function, defined in package source
, is a wrapper around fmt.Sprintf
.
The code below illustrates a call to source.Hashf with a formatting mistake, which gopls reports:
Gopls’ support for analysis facts thus enables it to discover and report many more diagnostics about your code than in previous releases.
Other features
Quick fixes to help manage go.work files
Gopls now provides better error messages when modules are missing from the go.work file, and offers quick fixes to use missing modules. (golang/go#53880)
Inlay hints for go.mod version differences
Gopls now provides an inlay hint to signal version differences in go.mod files resulting from minimal version selection across other modules in the go.work
file. (golang/go#57026)
A new gopls stats
subcommand
Gopls now has a stats
subcommand to print information about the current workspace, including metrics that may be relevant to scaling and performance. This command is intended to help users report information about their workspace. The -anon
flag suppresses potentially identifying information such as file paths. (golang/go#59673)
Hover over hex/binary literals
Gopls now offers additional hover information for hex or binary literals (golang/go#47453).
Hover/definition support for go:linkname
directives
The "unsafe" gophers out there can hover and jump to function definitions inside go:linkname directives. Thank you to @vikblom for contributing this feature! (golang/go#57312)
Support for textDocument/selectionRange
Gopls now supports the textDocument/selectionRange request, which allows selecting surrounding syntax. Thank you to @rneatherway for contributing this feature! (golang/go#36679)
A new directive
analyzer
To support the backward compatibility proposal, a new directive
analyzer diagnoses problems with Go toolchain directives such as //go:debug
.
Configuration changes
The symbolScope
setting
A new symbolScope
setting controls the scope of workspace/symbol
requests.
Previously, workspace/symbol
results would include symbols in dependencies, including results outside the workspace folder and even standard library results. When "symbolScope"
is set to "workspace"
, only results in a module contained in the workspace are included.
Which behavior do you prefer? Comment on golang/go#60494 if you have an opinion.
The subdirWatchPatterns
setting
The "subdirWatchPatterns"
setting is a new internal setting that controls the registration of file watch patterns for subdirectories. See the "Freezing on save..." section below for details.
Bug fixes
See the GitHub milestone for a full list of fixed bugs.
Freezing on save when editing large repos with VS Code+Mac
This release fixes a severe but rare bug where gopls would appear to hang after a save with the VS Code popup "waiting for code actions". We've received this report several times in the past (example 1, 2, 3), but could not reproduce with gopls alone.
It turns out that this bug was reproducible only on Mac+VS Code, in repositories with thousands of folders. Under these conditions, VS Code itself would freeze for minutes following two saves in short succession. This appears to be related to the internal file watching implementation in VS Code, and was resolved by changing the way gopls requests directory watchers.
Gopls must ask VS Code to watch directories, because of a limitation of the VS Code LSP client (microsoft/vscode#109754). For other clients, simply requesting the watch pattern **/*.go
is sufficient to receive all relevant file notifications. Furthermore, certain other clients (e.g. coc.nvim) perform poorly with many watchers. For this reason, gopls only requests watch patterns for each subdirectory if it detects the VS Code client via the clientInfo.name field.
This is the first time we've specialized gopls' behavior to any particular client, and it is an unsatisfactory solution. For this reason, we've added a new internal setting: "subdirWatchPatterns"
. When set to "on"
, this setting causes gopls to request watch patterns for each subdirectory. When set to "off"
, gopls does not request these patterns. When set to "auto"
(the default), gopls requests these patterns only for VS Code.
Don’t tolerate bugs
We’ve fixed a lot of bugs in this release, but more importantly we’ve solved some structural problems that should make it easier for us to identify and fix other bugs in future. In the past, some users have told us that they have stopped bothering to report bugs because gopls is too unpredictable. If you’re one of them, please break your habit. We don’t tolerate bugs, and nor should you.
What’s next
The v0.12.0 release was unique, in that we had to make a large number of risky and complicated changes to core aspect of gopls. Going forward, we expect to be back to a more regular cadence of minor releases every ~3 months, as described in our release policy.
For the v0.13.0 release, we are continuing our efforts to make gopls Just Work in more scenarios. Specifically, we will be focusing on "zero config" gopls (golang/go#57979), so that gopls behaves the same no matter which folder is opened. This will also pave the way for improved handling of build tags (golang/go#29202), in a later release.
Furthermore, we want to fix our treatment of case-sensitive file paths, which is currently buggy on Windows (golang/go#57081).
Longer term, some other features and improvements we'd like to bring to future releases of gopls include:
- Improved refactoring: gopls already supports some refactorings such as Rename Identifier and Extract Function. We'd like to add more, including the most requested one, Inline Function Call.
- Further memory reduction: despite the improvements of this release, there's still room to do better. For example, for read
consistency, gopls holds all source file content in memory. This is unnecessary for read-only files such as those in GOMODCACHE, and even for writable files given clever caching. - Godoc integration: https://pkg.go.dev is great for viewing documentation of published packages, but it would be nice if it were easier to review your API documentation work in progress to see how it will look. We'd like to integrate with a local doc viewer for easier review. (#55861)
- Better documentation: gopls's documentation can be out of date and hard to find. We'd like to make it easier for users to get
started with gopls and get the most out of it, no matter what editor they use, by making our documentation easier to navigate.
Thank you to our contributors!
@adonovan, @alexandear, @ansaba, @bcmills, @cuishuang, @cuonglm, @findleyr, @fviernau, @glepnir, @gopherbot, @hyangah, @kortschak, @matloob, @pjweinbgo, @prattmic, @rneatherway, @rsc, @ShoshinNikita, @suzmue, @sywhang, @timothy-king, @vikblom, @walles