diff --git a/go/extractor/project/project.go b/go/extractor/project/project.go index c870e6a5458a..441546542e92 100644 --- a/go/extractor/project/project.go +++ b/go/extractor/project/project.go @@ -195,11 +195,24 @@ func findGoModFiles(root string) []string { // A regular expression for the Go toolchain version syntax. var toolchainVersionRe *regexp.Regexp = regexp.MustCompile(`(?m)^([0-9]+\.[0-9]+\.[0-9]+)$`) -// Returns true if the `go.mod` file specifies a Go language version, that version is `1.21` or greater, and -// there is no `toolchain` directive, and the Go language version is not a valid toolchain version. -func hasInvalidToolchainVersion(modFile *modfile.File) bool { - return modFile.Toolchain == nil && modFile.Go != nil && - !toolchainVersionRe.Match([]byte(modFile.Go.Version)) && util.NewSemVer(modFile.Go.Version).IsAtLeast(toolchain.V1_21) +// Returns true if the `go.mod` file specifies a Go language version which is not of the format that +// is expected by the Go 1.21 and Go 1.22 toolchains for toolchain versions, and there is no +// explicit toolchain version declared. +func hasInvalidToolchainVersion(installedToolchainVersion util.SemVer, modFile *modfile.File) bool { + if modFile.Toolchain != nil { + // There is an explicit toolchain directive, so it doesn't matter what format the + // Go language version is in, since it will not be used as a fallback toolchain version. + return false + } else if modFile.Go != nil && !toolchainVersionRe.Match([]byte(modFile.Go.Version)) { + // There's no explicit toolchain directive, but we have a language version which + // does not match the toolchain version format in Go 1.21 and Go 1.22. + // This is a problem if the installed Go toolchain is within that version range + // as it will try to use the language version as the toolchain version. + return util.NewSemVer(modFile.Go.Version).IsAtLeast(toolchain.V1_21) && + installedToolchainVersion.IsAtLeast(toolchain.V1_21) && + installedToolchainVersion.IsOlderThan(toolchain.V1_23) + } + return false } // Given a list of `go.mod` file paths, try to parse them all. The resulting array of `GoModule` objects @@ -232,7 +245,7 @@ func LoadGoModules(emitDiagnostics bool, goModFilePaths []string) []*GoModule { // there is no `toolchain` directive, check that it is a valid Go toolchain version. Otherwise, // `go` commands which try to download the right version of the Go toolchain will fail. We detect // this situation and emit a diagnostic. - if hasInvalidToolchainVersion(modFile) { + if hasInvalidToolchainVersion(util.NewSemVer(toolchain.GetEnvGoVersion()), modFile) { diagnostics.EmitInvalidToolchainVersion(goModFilePath, modFile.Go.Version) } } diff --git a/go/extractor/project/project_test.go b/go/extractor/project/project_test.go index 149a9723ec29..fe17ccb7535e 100644 --- a/go/extractor/project/project_test.go +++ b/go/extractor/project/project_test.go @@ -39,18 +39,20 @@ func parseModFile(t *testing.T, contents string) *modfile.File { return modFile } -func testHasInvalidToolchainVersion(t *testing.T, contents string) bool { - return hasInvalidToolchainVersion(parseModFile(t, contents)) +func testHasInvalidToolchainVersion(t *testing.T, installedToolchainVersion util.SemVer, contents string) bool { + return hasInvalidToolchainVersion(installedToolchainVersion, parseModFile(t, contents)) } func TestHasInvalidToolchainVersion(t *testing.T) { + installedToolchainVersion := util.NewSemVer("1.21") + invalid := []string{ "go 1.21\n", "go 1.22\n", } for _, v := range invalid { - if !testHasInvalidToolchainVersion(t, v) { + if !testHasInvalidToolchainVersion(t, installedToolchainVersion, v) { t.Errorf("Expected testHasInvalidToolchainVersion(\"%s\") to be true, but got false", v) } } @@ -62,7 +64,7 @@ func TestHasInvalidToolchainVersion(t *testing.T) { } for _, v := range valid { - if testHasInvalidToolchainVersion(t, v) { + if testHasInvalidToolchainVersion(t, installedToolchainVersion, v) { t.Errorf("Expected testHasInvalidToolchainVersion(\"%s\") to be false, but got true", v) } } diff --git a/go/extractor/toolchain/toolchain.go b/go/extractor/toolchain/toolchain.go index 119e3782f6f6..123694c4c586 100644 --- a/go/extractor/toolchain/toolchain.go +++ b/go/extractor/toolchain/toolchain.go @@ -17,6 +17,7 @@ var V1_14 = util.NewSemVer("v1.14.0") var V1_16 = util.NewSemVer("v1.16.0") var V1_18 = util.NewSemVer("v1.18.0") var V1_21 = util.NewSemVer("v1.21.0") +var V1_23 = util.NewSemVer("v1.23.0") // Check if Go is installed in the environment. func IsInstalled() bool { diff --git a/go/ql/integration-tests/diagnostics/invalid-toolchain-version/diagnostics.expected b/go/ql/integration-tests/diagnostics/invalid-toolchain-version/diagnostics.expected index 2e5d732e53e7..56d774b7037c 100644 --- a/go/ql/integration-tests/diagnostics/invalid-toolchain-version/diagnostics.expected +++ b/go/ql/integration-tests/diagnostics/invalid-toolchain-version/diagnostics.expected @@ -1,20 +1,3 @@ -{ - "location": { - "file": "go.mod" - }, - "markdownMessage": "As of Go 1.21, toolchain versions [must use the 1.N.P syntax](https://go.dev/doc/toolchain#version).\n\n`1.21` in `go.mod` does not match this syntax and there is no additional `toolchain` directive, which may cause some `go` commands to fail.", - "severity": "warning", - "source": { - "extractorName": "go", - "id": "go/autobuilder/invalid-go-toolchain-version", - "name": "Invalid Go toolchain version" - }, - "visibility": { - "cliSummaryTable": true, - "statusPage": true, - "telemetry": true - } -} { "markdownMessage": "A single `go.mod` file was found.\n\n`go.mod`", "severity": "note", diff --git a/go/ql/integration-tests/go-version-bump/diagnostics.expected b/go/ql/integration-tests/go-version-bump/diagnostics.expected index 2e5d732e53e7..56d774b7037c 100644 --- a/go/ql/integration-tests/go-version-bump/diagnostics.expected +++ b/go/ql/integration-tests/go-version-bump/diagnostics.expected @@ -1,20 +1,3 @@ -{ - "location": { - "file": "go.mod" - }, - "markdownMessage": "As of Go 1.21, toolchain versions [must use the 1.N.P syntax](https://go.dev/doc/toolchain#version).\n\n`1.21` in `go.mod` does not match this syntax and there is no additional `toolchain` directive, which may cause some `go` commands to fail.", - "severity": "warning", - "source": { - "extractorName": "go", - "id": "go/autobuilder/invalid-go-toolchain-version", - "name": "Invalid Go toolchain version" - }, - "visibility": { - "cliSummaryTable": true, - "statusPage": true, - "telemetry": true - } -} { "markdownMessage": "A single `go.mod` file was found.\n\n`go.mod`", "severity": "note", diff --git a/go/ql/integration-tests/resolve-build-environment/newer-go-needed/diagnostics.expected b/go/ql/integration-tests/resolve-build-environment/newer-go-needed/diagnostics.expected index b64c1397938d..56d774b7037c 100644 --- a/go/ql/integration-tests/resolve-build-environment/newer-go-needed/diagnostics.expected +++ b/go/ql/integration-tests/resolve-build-environment/newer-go-needed/diagnostics.expected @@ -1,20 +1,3 @@ -{ - "location": { - "file": "go.mod" - }, - "markdownMessage": "As of Go 1.21, toolchain versions [must use the 1.N.P syntax](https://go.dev/doc/toolchain#version).\n\n`1.22` in `go.mod` does not match this syntax and there is no additional `toolchain` directive, which may cause some `go` commands to fail.", - "severity": "warning", - "source": { - "extractorName": "go", - "id": "go/autobuilder/invalid-go-toolchain-version", - "name": "Invalid Go toolchain version" - }, - "visibility": { - "cliSummaryTable": true, - "statusPage": true, - "telemetry": true - } -} { "markdownMessage": "A single `go.mod` file was found.\n\n`go.mod`", "severity": "note",