From 164402db9dd6fa703883e516c61a536114a42198 Mon Sep 17 00:00:00 2001 From: Heschi Kreinick Date: Fri, 4 Feb 2022 16:50:42 -0500 Subject: [PATCH] internal/lsp/cache: set types.Config.GoVersion Set the language version from the controlling go.mod file's go version, if any. Also verify that we properly surface a diagnostic if the version is invalid. I didn't add any quick fixes. Fixes golang/go#50688. Change-Id: Ic472502d1224a1decb5b989d51110b837020e998 Reviewed-on: https://go-review.googlesource.com/c/tools/+/383394 Trust: Heschi Kreinick Run-TryBot: Heschi Kreinick Reviewed-by: Robert Findley gopls-CI: kokoro TryBot-Result: Gopher Robot --- .../regtest/diagnostics/diagnostics_test.go | 19 +++++++++++++++++++ .../internal/regtest/modfile/modfile_test.go | 17 +++++++++++++++++ internal/lsp/cache/check.go | 12 ++++++++++++ internal/typesinternal/types.go | 2 ++ internal/typesinternal/types_118.go | 19 +++++++++++++++++++ 5 files changed, 69 insertions(+) create mode 100644 internal/typesinternal/types_118.go diff --git a/gopls/internal/regtest/diagnostics/diagnostics_test.go b/gopls/internal/regtest/diagnostics/diagnostics_test.go index 6c02213553b..82da4cd3925 100644 --- a/gopls/internal/regtest/diagnostics/diagnostics_test.go +++ b/gopls/internal/regtest/diagnostics/diagnostics_test.go @@ -2138,3 +2138,22 @@ func main() { ) }) } + +func TestLangVersion(t *testing.T) { + testenv.NeedsGo1Point(t, 18) // Requires types.Config.GoVersion, new in 1.18. + const files = ` +-- go.mod -- +module mod.com + +go 1.12 +-- main.go -- +package main + +const C = 0b10 +` + Run(t, files, func(t *testing.T, env *Env) { + env.Await(env.DiagnosticAtRegexpWithMessage("main.go", `0b10`, "go1.13 or later")) + env.WriteWorkspaceFile("go.mod", "module mod.com \n\ngo 1.13\n") + env.Await(EmptyDiagnostics("main.go")) + }) +} diff --git a/gopls/internal/regtest/modfile/modfile_test.go b/gopls/internal/regtest/modfile/modfile_test.go index 8592a96dc9d..05b7ade2944 100644 --- a/gopls/internal/regtest/modfile/modfile_test.go +++ b/gopls/internal/regtest/modfile/modfile_test.go @@ -1120,3 +1120,20 @@ func main() { ) }) } + +func TestInvalidGoVersion(t *testing.T) { + testenv.NeedsGo1Point(t, 14) // Times out on 1.13 for reasons unclear. Not worth worrying about. + const files = ` +-- go.mod -- +module mod.com + +go foo +-- main.go -- +package main +` + Run(t, files, func(t *testing.T, env *Env) { + env.Await(env.DiagnosticAtRegexpWithMessage("go.mod", `go foo`, "invalid go version")) + env.WriteWorkspaceFile("go.mod", "module mod.com \n\ngo 1.12\n") + env.Await(EmptyDiagnostics("go.mod")) + }) +} diff --git a/internal/lsp/cache/check.go b/internal/lsp/cache/check.go index 4f27ed73790..616381653a9 100644 --- a/internal/lsp/cache/check.go +++ b/internal/lsp/cache/check.go @@ -12,6 +12,7 @@ import ( "go/types" "path" "path/filepath" + "regexp" "sort" "strings" "sync" @@ -420,6 +421,8 @@ func typeCheck(ctx context.Context, snapshot *snapshot, m *Metadata, mode source return pkg, nil } +var goVersionRx = regexp.MustCompile(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`) + func doTypeCheck(ctx context.Context, snapshot *snapshot, m *Metadata, mode source.ParseMode, deps map[PackagePath]*packageHandle, astFilter *unexportedFilter) (*pkg, error) { ctx, done := event.Start(ctx, "cache.typeCheck", tag.Package.Of(string(m.ID))) defer done() @@ -515,6 +518,15 @@ func doTypeCheck(ctx context.Context, snapshot *snapshot, m *Metadata, mode sour return depPkg.types, nil }), } + if pkg.m.Module != nil && pkg.m.Module.GoVersion != "" { + goVersion := "go" + pkg.m.Module.GoVersion + // types.NewChecker panics if GoVersion is invalid. An unparsable mod + // file should probably stop us before we get here, but double check + // just in case. + if goVersionRx.MatchString(goVersion) { + typesinternal.SetGoVersion(cfg, goVersion) + } + } if mode != source.ParseFull { cfg.DisableUnusedImportCheck = true diff --git a/internal/typesinternal/types.go b/internal/typesinternal/types.go index 7c77c2fbc03..ce7d4351b22 100644 --- a/internal/typesinternal/types.go +++ b/internal/typesinternal/types.go @@ -48,3 +48,5 @@ func ReadGo116ErrorData(err types.Error) (code ErrorCode, start, end token.Pos, } return ErrorCode(data[0]), token.Pos(data[1]), token.Pos(data[2]), true } + +var SetGoVersion = func(conf *types.Config, version string) bool { return false } diff --git a/internal/typesinternal/types_118.go b/internal/typesinternal/types_118.go new file mode 100644 index 00000000000..a42b072a67d --- /dev/null +++ b/internal/typesinternal/types_118.go @@ -0,0 +1,19 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.18 +// +build go1.18 + +package typesinternal + +import ( + "go/types" +) + +func init() { + SetGoVersion = func(conf *types.Config, version string) bool { + conf.GoVersion = version + return true + } +}