Skip to content

Commit 4bdbdca

Browse files
committed
[gopls-release-branch.0.15] internal/check: filter out too-new Go versions for type checking
NOTE: Patched for the release branch to use versions.Compare rather than versions.Before. The type checker produces an error if the Go version is too new. When compiled with Go 1.21, this error is silently dropped on the floor and the type checked package is empty, due to golang/go##66525. Guard against this very problematic failure mode by filtering out Go versions that are too new. We should also produce a diagnostic, but that is more complicated and covered by golang/go#61673. Also: fix a bug where sandbox cleanup would fail due to being run with a non-local toolchain. Updates golang/go#66677 Updates golang/go#66730 Change-Id: Ia66f17c195382c9c55cf0ef883e898553ce950e3 Reviewed-on: https://go-review.googlesource.com/c/tools/+/576678 Reviewed-by: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> (cherry picked from commit de6db98) Reviewed-on: https://go-review.googlesource.com/c/tools/+/577302
1 parent 1202974 commit 4bdbdca

File tree

5 files changed

+95
-2
lines changed

5 files changed

+95
-2
lines changed

gopls/internal/cache/check.go

+17
Original file line numberDiff line numberDiff line change
@@ -1652,6 +1652,10 @@ func validGoVersion(goVersion string) bool {
16521652
return false // malformed version string
16531653
}
16541654

1655+
if relVer := releaseVersion(); relVer != "" && versions.Compare(relVer, goVersion) < 0 {
1656+
return false // 'go list' is too new for go/types
1657+
}
1658+
16551659
// TODO(rfindley): remove once we no longer support building gopls with Go
16561660
// 1.20 or earlier.
16571661
if !slices.Contains(build.Default.ReleaseTags, "go1.21") && strings.Count(goVersion, ".") >= 2 {
@@ -1661,6 +1665,19 @@ func validGoVersion(goVersion string) bool {
16611665
return true
16621666
}
16631667

1668+
// releaseVersion reports the Go language version used to compile gopls, or ""
1669+
// if it cannot be determined.
1670+
func releaseVersion() string {
1671+
if len(build.Default.ReleaseTags) > 0 {
1672+
v := build.Default.ReleaseTags[len(build.Default.ReleaseTags)-1]
1673+
var dummy int
1674+
if _, err := fmt.Sscanf(v, "go1.%d", &dummy); err == nil {
1675+
return v
1676+
}
1677+
}
1678+
return ""
1679+
}
1680+
16641681
// depsErrors creates diagnostics for each metadata error (e.g. import cycle).
16651682
// These may be attached to import declarations in the transitive source files
16661683
// of pkg, or to 'requires' declarations in the package's go.mod file.

gopls/internal/test/integration/fake/sandbox.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,9 @@ func (sb *Sandbox) GoVersion(ctx context.Context) (int, error) {
289289
func (sb *Sandbox) Close() error {
290290
var goCleanErr error
291291
if sb.gopath != "" {
292-
goCleanErr = sb.RunGoCommand(context.Background(), "", "clean", []string{"-modcache"}, nil, false)
292+
// Important: run this command in RootDir so that it doesn't interact with
293+
// any toolchain downloads that may occur
294+
goCleanErr = sb.RunGoCommand(context.Background(), sb.RootDir(), "clean", []string{"-modcache"}, nil, false)
293295
}
294296
err := robustio.RemoveAll(sb.rootdir)
295297
if err != nil || goCleanErr != nil {

gopls/internal/test/integration/workspace/goversion_test.go

+64
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ package workspace
77
import (
88
"flag"
99
"os"
10+
"os/exec"
1011
"runtime"
12+
"strings"
1113
"testing"
1214

1315
. "golang.org/x/tools/gopls/internal/test/integration"
16+
"golang.org/x/tools/internal/testenv"
1417
)
1518

1619
var go121bin = flag.String("go121bin", "", "bin directory containing go 1.21 or later")
@@ -60,3 +63,64 @@ type I interface { string }
6063
)
6164
})
6265
}
66+
67+
func TestTypeCheckingFutureVersions(t *testing.T) {
68+
// This test checks the regression in golang/go#66677, where go/types fails
69+
// silently when the language version is 1.22.
70+
//
71+
// It does this by recreating the scenario of a toolchain upgrade to 1.22, as
72+
// reported in the issue. For this to work, the test must be able to download
73+
// toolchains from proxy.golang.org.
74+
//
75+
// This is really only a problem for Go 1.21, because with Go 1.23, the bug
76+
// is fixed, and starting with 1.23 we're going to *require* 1.23 to build
77+
// gopls.
78+
//
79+
// TODO(golang/go#65917): delete this test after Go 1.23 is released and
80+
// gopls requires the latest Go to build.
81+
testenv.SkipAfterGo1Point(t, 21)
82+
83+
if testing.Short() {
84+
t.Skip("skipping with -short, as this test uses the network")
85+
}
86+
87+
// If go 1.22.2 is already available in the module cache, reuse it rather
88+
// than downloading it anew.
89+
out, err := exec.Command("go", "env", "GOPATH").Output()
90+
if err != nil {
91+
t.Fatal(err)
92+
}
93+
gopath := strings.TrimSpace(string(out)) // use the ambient 1.22.2 toolchain if available
94+
95+
const files = `
96+
-- go.mod --
97+
module example.com/foo
98+
99+
go 1.22.2
100+
101+
-- main.go --
102+
package main
103+
104+
func main() {
105+
x := 1
106+
}
107+
`
108+
109+
WithOptions(
110+
Modes(Default), // slow test, only run in one mode
111+
EnvVars{
112+
"GOPATH": gopath,
113+
"GOTOOLCHAIN": "", // not local
114+
"GOPROXY": "https://proxy.golang.org",
115+
"GOSUMDB": "sum.golang.org",
116+
},
117+
).Run(t, files, func(t *testing.T, env *Env) {
118+
env.OpenFile("main.go")
119+
env.AfterChange(
120+
Diagnostics(
121+
env.AtRegexp("main.go", "x"),
122+
WithMessage("not used"),
123+
),
124+
)
125+
})
126+
}

gopls/internal/test/marker/doc.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ treatment by the test runner:
4343
4444
- "flags": this file is treated as a whitespace-separated list of flags
4545
that configure the MarkerTest instance. Supported flags:
46-
-min_go=go1.20 sets the minimum Go version for the test;
46+
-{min,max}_go=go1.20 sets the {min,max}imum Go version for the test
47+
(inclusive)
4748
-cgo requires that CGO_ENABLED is set and the cgo tool is available
4849
-write_sumfile=a,b,c instructs the test runner to generate go.sum files
4950
in these directories before running the test.

gopls/internal/test/marker/marker_test.go

+9
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@ func Test(t *testing.T) {
129129
}
130130
testenv.NeedsGo1Point(t, go1point)
131131
}
132+
if test.maxGoVersion != "" {
133+
var go1point int
134+
if _, err := fmt.Sscanf(test.maxGoVersion, "go1.%d", &go1point); err != nil {
135+
t.Fatalf("parsing -max_go version: %v", err)
136+
}
137+
testenv.SkipAfterGo1Point(t, go1point)
138+
}
132139
if test.cgo {
133140
testenv.NeedsTool(t, "cgo")
134141
}
@@ -500,6 +507,7 @@ type markerTest struct {
500507

501508
// Parsed flags values.
502509
minGoVersion string
510+
maxGoVersion string
503511
cgo bool
504512
writeGoSum []string // comma separated dirs to write go sum for
505513
skipGOOS []string // comma separated GOOS values to skip
@@ -513,6 +521,7 @@ type markerTest struct {
513521
func (t *markerTest) flagSet() *flag.FlagSet {
514522
flags := flag.NewFlagSet(t.name, flag.ContinueOnError)
515523
flags.StringVar(&t.minGoVersion, "min_go", "", "if set, the minimum go1.X version required for this test")
524+
flags.StringVar(&t.maxGoVersion, "max_go", "", "if set, the maximum go1.X version required for this test")
516525
flags.BoolVar(&t.cgo, "cgo", false, "if set, requires cgo (both the cgo tool and CGO_ENABLED=1)")
517526
flags.Var((*stringListValue)(&t.writeGoSum), "write_sumfile", "if set, write the sumfile for these directories")
518527
flags.Var((*stringListValue)(&t.skipGOOS), "skip_goos", "if set, skip this test on these GOOS values")

0 commit comments

Comments
 (0)