Skip to content

Commit 20c1ee7

Browse files
findleyrgopherbot
authored andcommitted
gopls/internal/lsp: warn about Go versions that are too old
Add a showMessage notification when the Go version in PATH is too old. Also delete the unused View.Rebuild method. Updates golang/go#50825 Change-Id: I279a04f021a0f8ddb09fcfe299fbab8d10e8c022 Reviewed-on: https://go-review.googlesource.com/c/tools/+/439836 Run-TryBot: Robert Findley <[email protected]> Auto-Submit: Robert Findley <[email protected]> TryBot-Result: Gopher Robot <[email protected]> gopls-CI: kokoro <[email protected]> Reviewed-by: Hyang-Ah Hana Kim <[email protected]>
1 parent 709f108 commit 20c1ee7

File tree

7 files changed

+134
-21
lines changed

7 files changed

+134
-21
lines changed

gopls/internal/lsp/cache/view.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -317,15 +317,6 @@ func (v *View) SetOptions(ctx context.Context, options *source.Options) (source.
317317
return newView, err
318318
}
319319

320-
func (v *View) Rebuild(ctx context.Context) (source.Snapshot, func(), error) {
321-
newView, err := v.session.updateView(ctx, v, v.Options())
322-
if err != nil {
323-
return nil, func() {}, err
324-
}
325-
snapshot, release := newView.Snapshot(ctx)
326-
return snapshot, release, nil
327-
}
328-
329320
func (s *snapshot) WriteEnv(ctx context.Context, w io.Writer) error {
330321
s.view.optionsMu.Lock()
331322
env := s.view.options.EnvSlice()
@@ -1057,6 +1048,10 @@ func (v *View) SetVulnerabilities(modfile span.URI, vulns []command.Vuln) {
10571048
v.vulns[modfile] = vulns
10581049
}
10591050

1051+
func (v *View) GoVersion() int {
1052+
return v.workspaceInformation.goversion
1053+
}
1054+
10601055
// Copied from
10611056
// https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/str/path.go;l=58;drc=2910c5b4a01a573ebc97744890a07c1a3122c67a
10621057
func globsMatchPath(globs, target string) bool {

gopls/internal/lsp/general.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ func (s *Server) initialized(ctx context.Context, params *protocol.InitializedPa
202202
return err
203203
}
204204
s.pendingFolders = nil
205+
s.checkViewGoVersions()
205206

206207
var registrations []protocol.Registration
207208
if options.ConfigurationSupported && options.DynamicConfigurationSupported {
@@ -223,6 +224,34 @@ func (s *Server) initialized(ctx context.Context, params *protocol.InitializedPa
223224
return nil
224225
}
225226

227+
// OldestSupportedGoVersion is the last X in Go 1.X that we support.
228+
//
229+
// Mutable for testing, since we won't otherwise run CI on unsupported Go
230+
// versions.
231+
var OldestSupportedGoVersion = 16
232+
233+
// checkViewGoVersions checks whether any Go version used by a view is too old,
234+
// raising a showMessage notification if so.
235+
//
236+
// It should be called after views change.
237+
func (s *Server) checkViewGoVersions() {
238+
oldestVersion := -1
239+
for _, view := range s.session.Views() {
240+
viewVersion := view.GoVersion()
241+
if oldestVersion == -1 || viewVersion < oldestVersion {
242+
oldestVersion = viewVersion
243+
}
244+
}
245+
246+
if oldestVersion >= 0 && oldestVersion < OldestSupportedGoVersion {
247+
msg := fmt.Sprintf("Found Go version 1.%d, which is unsupported. Please upgrade to Go 1.%d or later.", oldestVersion, OldestSupportedGoVersion)
248+
s.eventuallyShowMessage(context.Background(), &protocol.ShowMessageParams{
249+
Type: protocol.Error,
250+
Message: msg,
251+
})
252+
}
253+
}
254+
226255
func (s *Server) addFolders(ctx context.Context, folders []protocol.WorkspaceFolder) error {
227256
originalViews := len(s.session.Views())
228257
viewErrors := make(map[span.URI]error)

gopls/internal/lsp/regtest/expectation.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,17 +162,19 @@ func NoOutstandingWork() SimpleExpectation {
162162
}
163163
}
164164

165-
// NoShowMessage asserts that the editor has not received a ShowMessage.
166-
func NoShowMessage() SimpleExpectation {
165+
// NoShownMessage asserts that the editor has not received a ShowMessage.
166+
func NoShownMessage(subString string) SimpleExpectation {
167167
check := func(s State) Verdict {
168-
if len(s.showMessage) == 0 {
169-
return Met
168+
for _, m := range s.showMessage {
169+
if strings.Contains(m.Message, subString) {
170+
return Unmeetable
171+
}
170172
}
171-
return Unmeetable
173+
return Met
172174
}
173175
return SimpleExpectation{
174176
check: check,
175-
description: "no ShowMessage received",
177+
description: fmt.Sprintf("no ShowMessage received containing %q", subString),
176178
}
177179
}
178180

gopls/internal/lsp/source/view.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,11 +253,6 @@ type View interface {
253253
// no longer needed.
254254
Snapshot(ctx context.Context) (Snapshot, func())
255255

256-
// Rebuild rebuilds the current view, replacing the original
257-
// view in its session. It returns a Snapshot and a release
258-
// function that must be called when the Snapshot is no longer needed.
259-
Rebuild(ctx context.Context) (Snapshot, func(), error)
260-
261256
// IsGoPrivatePath reports whether target is a private import path, as identified
262257
// by the GOPRIVATE environment variable.
263258
IsGoPrivatePath(path string) bool
@@ -284,6 +279,9 @@ type View interface {
284279

285280
// FileKind returns the type of a file
286281
FileKind(FileHandle) FileKind
282+
283+
// GoVersion returns the configured Go version for this view.
284+
GoVersion() int
287285
}
288286

289287
// A FileSource maps uris to FileHandles. This abstraction exists both for

gopls/internal/lsp/workspace.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ func (s *Server) didChangeConfiguration(ctx context.Context, _ *protocol.DidChan
6969
}()
7070
}
7171

72+
// An options change may have affected the detected Go version.
73+
s.checkViewGoVersions()
74+
7275
registration := semanticTokenRegistration(options.SemanticTypes, options.SemanticMods)
7376
// Update any session-specific registrations or unregistrations.
7477
if !semanticTokensRegistered && options.SemanticTokens {

gopls/internal/regtest/diagnostics/diagnostics_test.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1356,6 +1356,10 @@ func _() {
13561356
}
13571357

13581358
func TestEnableAllExperiments(t *testing.T) {
1359+
// Before the oldest supported Go version, gopls sends a warning to upgrade
1360+
// Go, which fails the expectation below.
1361+
testenv.NeedsGo1Point(t, lsp.OldestSupportedGoVersion)
1362+
13591363
const mod = `
13601364
-- go.mod --
13611365
module mod.com
@@ -1374,7 +1378,12 @@ func b(c bytes.Buffer) {
13741378
Settings{"allExperiments": true},
13751379
).Run(t, mod, func(t *testing.T, env *Env) {
13761380
// Confirm that the setting doesn't cause any warnings.
1377-
env.Await(NoShowMessage())
1381+
env.Await(
1382+
OnceMet(
1383+
InitialWorkspaceLoad,
1384+
NoShownMessage(""), // empty substring to match any message
1385+
),
1386+
)
13781387
})
13791388
}
13801389

gopls/internal/regtest/workspace/workspace_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@
55
package workspace
66

77
import (
8+
"context"
89
"fmt"
910
"path/filepath"
1011
"strings"
1112
"testing"
1213

1314
"golang.org/x/tools/gopls/internal/hooks"
15+
"golang.org/x/tools/gopls/internal/lsp"
1416
"golang.org/x/tools/gopls/internal/lsp/fake"
1517
"golang.org/x/tools/gopls/internal/lsp/protocol"
1618
"golang.org/x/tools/internal/bug"
19+
"golang.org/x/tools/internal/gocommand"
1720
"golang.org/x/tools/internal/testenv"
1821

1922
. "golang.org/x/tools/gopls/internal/lsp/regtest"
@@ -1234,3 +1237,77 @@ import (
12341237
)
12351238
})
12361239
}
1240+
1241+
// Test that we don't get a version warning when the Go version in PATH is
1242+
// supported.
1243+
func TestOldGoNotification_SupportedVersion(t *testing.T) {
1244+
v := goVersion(t)
1245+
if v < lsp.OldestSupportedGoVersion {
1246+
t.Skipf("go version 1.%d is unsupported", v)
1247+
}
1248+
1249+
Run(t, "", func(t *testing.T, env *Env) {
1250+
env.Await(
1251+
OnceMet(
1252+
InitialWorkspaceLoad,
1253+
NoShownMessage("upgrade"),
1254+
),
1255+
)
1256+
})
1257+
}
1258+
1259+
// Test that we do get a version warning when the Go version in PATH is
1260+
// unsupported, though this test may never execute if we stop running CI at
1261+
// legacy Go versions (see also TestOldGoNotification_Fake)
1262+
func TestOldGoNotification_UnsupportedVersion(t *testing.T) {
1263+
v := goVersion(t)
1264+
if v >= lsp.OldestSupportedGoVersion {
1265+
t.Skipf("go version 1.%d is supported", v)
1266+
}
1267+
1268+
Run(t, "", func(t *testing.T, env *Env) {
1269+
env.Await(
1270+
OnceMet(
1271+
InitialWorkspaceLoad,
1272+
ShownMessage("Please upgrade"),
1273+
),
1274+
)
1275+
})
1276+
}
1277+
1278+
func TestOldGoNotification_Fake(t *testing.T) {
1279+
// Get the Go version from path, and make sure it's unsupported.
1280+
//
1281+
// In the future we'll stop running CI on legacy Go versions. By mutating the
1282+
// oldest supported Go version here, we can at least ensure that the
1283+
// ShowMessage pop-up works.
1284+
ctx := context.Background()
1285+
goversion, err := gocommand.GoVersion(ctx, gocommand.Invocation{}, &gocommand.Runner{})
1286+
if err != nil {
1287+
t.Fatal(err)
1288+
}
1289+
defer func(v int) {
1290+
lsp.OldestSupportedGoVersion = v
1291+
}(lsp.OldestSupportedGoVersion)
1292+
lsp.OldestSupportedGoVersion = goversion + 1
1293+
1294+
Run(t, "", func(t *testing.T, env *Env) {
1295+
env.Await(
1296+
OnceMet(
1297+
InitialWorkspaceLoad,
1298+
ShownMessage("Please upgrade"),
1299+
),
1300+
)
1301+
})
1302+
}
1303+
1304+
// goVersion returns the version of the Go command in PATH.
1305+
func goVersion(t *testing.T) int {
1306+
t.Helper()
1307+
ctx := context.Background()
1308+
goversion, err := gocommand.GoVersion(ctx, gocommand.Invocation{}, &gocommand.Runner{})
1309+
if err != nil {
1310+
t.Fatal(err)
1311+
}
1312+
return goversion
1313+
}

0 commit comments

Comments
 (0)