Description
Background
Right now the gopls team supports building gopls using the four most recent major Go versions, though technically we still maintain support for Go 1.12+. This is significantly longer than the support window of the Go project itself, which covers only the two most recent major Go versions.
In the past, we've seen evidence (in the form of bug reports or survey results) that our users are supporting older Go versions for their applications/libraries. Whether or not this is a good thing is moot: there were reasons out of our control (e.g. cloud services support windows) that caused our users to support older Go versions. Absent a strong reason to break compatibility, the gopls team continued to maintain compatibility with older Go versions because the only way to provide accurate functionality for an older Go version was to build gopls at that Go version.
This legacy support causes significant friction for the team, notably:
- we maintain additional CI (Kokoro) that runs tests using older Go versions
- we work around issues that only occur with older Go versions
- we will be maintaining a compatibility layer that replicates the functionality of
go.work
at older Go versions - our tests must be tolerant of error messages produced at older Go versions
- some of our analyzers interpret error messages, and so must also be tolerant of error messages produced at older Go versions
- we cannot easily leverage recent language or standard library improvements in our own code
- our feedback cycle with the standard library is ~infinite: we cannot typically wait for fixes to
go/parser
orgo/types
- the dependencies we integrate with (e.g. gofumpt or staticcheck) have (quite reasonably) incompatible support windows
Proposal
We should provide features that make it easier to build gopls with a recent Go version but target development at an earlier Go version. We should then begin the process of dropping support for building gopls at older Go versions. As we do this, we should document and announce the final version of gopls that supports being built at each Go version.
Eventually we should align our support window with the Go project: the two most recent Go versions. This will allow us to turn down our legacy CI.
Supporting a target language version
With Go 1.18, the types.Config.GoVersion
field allows us to specify an accepted language version for go/types
, analogous to the compiler -lang
flag. This removes a key blocker to this proposal.
Specifically, there are three components to providing accurate gopls functionality for older target Go versions:
- providing compiler (=
go/types
) errors based on the target language version (x/tools/gopls: set types.Config.GoVersion based on go.mod #50688) - warning about usage of standard library APIs that were added after the target language version (x/tools/gopls: warn about standard library API usage based on Go version #50689)
- building packages based on the target language version (i.e. based on the correct release tags)
Of these, (1) and (2) are now straightforward, as described by the linked issues. (3) is harder, because AFAIK x/tools/go/packages
(and the go
command) does not provide a way to specify release tags -- they are baked into the go
binary. Issue #42504 would be useful here: gopls could do its own post-processing of build tags.
In the meantime, we can either declare that we won't be able to satisfy (3), or support using a different go
command for go/packages
than was used to build gopls. In principle simply using a different go
version for go list
should work, but may introduce difficult combinatorial problems. Using a different version of go
for go/packages
would actually handle both (2) and (3) above, so we should explore this option.
We should of course update our installation instructions to document this workflow.
Deriving the target language version(s)
We can derive the target language version from the applicable go.mod
, the go
version in PATH
, or from explicit gopls configuration. We may use more than one of these sources, but ideally we would avoid any additional gopls configuration.
Dropping support for older Go versions
We can take the following steps to ease the impact on our users when we drop support for a Go version:
- Announce plans for final versions in our release notes. Specifically, since we try to release approximately monthly, we should endeavor to announce when the next version of gopls will be the final version to support being built at a given Go version.
- Surface this information when upgrading gopls in VS Code.
- Make VS Code aware of the gopls compatibility matrix, so that it doesn't try to upgrade to an unsupported version.
- Explain all of this in our documentation.
CC @golang/tools-team