-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
gopls/internal/cache: fix crash in snapshot.Analyze with patch versions
Fix the same crash as golang/go#66195, this time in Analyze: don't set invalid Go versions on the types.Config. The largest part of this change was writing a realistic test, since the lack of a test for golang/go#66195 caused us to miss this additional location. It was possible to create a test that worked, by using a flag to select a go1.21 binary location. For this test, it was required to move a couple additional integration test preconditions into integration.Main (otherwise, the test would be skipped due to the modified environment). Fixes golang/go#66636 Change-Id: I24385474d4a6ebf6b7e9ae8f20948564bad3f55e Reviewed-on: https://go-review.googlesource.com/c/tools/+/576135 Auto-Submit: Robert Findley <[email protected]> Reviewed-by: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
- Loading branch information
Showing
6 changed files
with
116 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1632,19 +1632,7 @@ func (b *typeCheckBatch) typesConfig(ctx context.Context, inputs typeCheckInputs | |
|
||
if inputs.goVersion != "" { | ||
goVersion := "go" + inputs.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. | ||
// | ||
// Prior to go/[email protected] the precondition was stricter: | ||
// no patch version. That's not a problem when also using go1.20 list, | ||
// as it would reject go.mod files containing a patch version, but | ||
// go/[email protected] will panic on go.mod versions that are returned | ||
// by go1.21 list, hence the need for the extra check. | ||
if goVersionRx.MatchString(goVersion) && | ||
(slices.Contains(build.Default.ReleaseTags, "go1.21") || | ||
strings.Count(goVersion, ".") < 2) { // no patch version | ||
if validGoVersion(goVersion) { | ||
cfg.GoVersion = goVersion | ||
} | ||
} | ||
|
@@ -1655,6 +1643,26 @@ func (b *typeCheckBatch) typesConfig(ctx context.Context, inputs typeCheckInputs | |
return cfg | ||
} | ||
|
||
// validGoVersion reports whether goVersion is a valid Go version for go/types. | ||
// types.NewChecker panics if GoVersion is invalid. | ||
// | ||
// Note that, prior to go1.21, go/types required exactly two components to the | ||
// version number. For example, go types would panic with the Go version | ||
// go1.21.1. validGoVersion handles this case when built with go1.20 or earlier. | ||
func validGoVersion(goVersion string) bool { | ||
if !goVersionRx.MatchString(goVersion) { | ||
return false // malformed version string | ||
} | ||
|
||
// TODO(rfindley): remove once we no longer support building gopls with Go | ||
// 1.20 or earlier. | ||
if !slices.Contains(build.Default.ReleaseTags, "go1.21") && strings.Count(goVersion, ".") >= 2 { | ||
return false // unsupported patch version | ||
} | ||
|
||
return true | ||
} | ||
|
||
// depsErrors creates diagnostics for each metadata error (e.g. import cycle). | ||
// These may be attached to import declarations in the transitive source files | ||
// of pkg, or to 'requires' declarations in the package's go.mod file. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
gopls/internal/test/integration/workspace/goversion_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Copyright 2024 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. | ||
|
||
package workspace | ||
|
||
import ( | ||
"flag" | ||
"os" | ||
"runtime" | ||
"testing" | ||
|
||
. "golang.org/x/tools/gopls/internal/test/integration" | ||
) | ||
|
||
var go121bin = flag.String("go121bin", "", "bin directory containing go 1.21 or later") | ||
|
||
// TODO(golang/go#65917): delete this test once we no longer support building | ||
// gopls with older Go versions. | ||
func TestCanHandlePatchVersions(t *testing.T) { | ||
// This test verifies the fixes for golang/go#66195 and golang/go#66636 -- | ||
// that gopls does not crash when encountering a go version with a patch | ||
// number in the go.mod file. | ||
// | ||
// This is tricky to test, because the regression requires that gopls is | ||
// built with an older go version, and then the environment is upgraded to | ||
// have a more recent go. To set up this scenario, the test requires a path | ||
// to a bin directory containing go1.21 or later. | ||
if *go121bin == "" { | ||
t.Skip("-go121bin directory is not set") | ||
} | ||
|
||
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" { | ||
t.Skip("requires linux or darwin") // for PATH separator | ||
} | ||
|
||
path := os.Getenv("PATH") | ||
t.Setenv("PATH", *go121bin+":"+path) | ||
|
||
const files = ` | ||
-- go.mod -- | ||
module example.com/bar | ||
go 1.21.1 | ||
-- p.go -- | ||
package bar | ||
type I interface { string } | ||
` | ||
|
||
WithOptions( | ||
EnvVars{ | ||
"PATH": path, | ||
}, | ||
).Run(t, files, func(t *testing.T, env *Env) { | ||
env.OpenFile("p.go") | ||
env.AfterChange( | ||
NoDiagnostics(ForFile("p.go")), | ||
) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters