Skip to content

Go: remove invalid toolchain version diagnostics #19370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
# and that we actually get are too big, the build fails on CI.
BAZEL := $(shell bash -c "which bazel")

rtjo ?= none

all: gen extractor

EXTRACTOR_PACK_OUT = extractor-pack
Expand Down
2 changes: 1 addition & 1 deletion go/extractor/cli/go-autobuilder/go-autobuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ func installDependencies(workspace project.GoWorkspace) {
} else {
if workspace.Modules == nil {
project.InitGoModForLegacyProject(workspace.BaseDir)
workspace.Modules = project.LoadGoModules(true, []string{filepath.Join(workspace.BaseDir, "go.mod")})
workspace.Modules = project.LoadGoModules([]string{filepath.Join(workspace.BaseDir, "go.mod")})
}

// get dependencies for all modules
Expand Down
15 changes: 0 additions & 15 deletions go/extractor/diagnostics/diagnostics.go
Original file line number Diff line number Diff line change
Expand Up @@ -508,18 +508,3 @@ func EmitExtractionFailedForProjects(path []string) {
noLocation,
)
}

func EmitInvalidToolchainVersion(goModPath string, version string) {
emitDiagnostic(
"go/autobuilder/invalid-go-toolchain-version",
"Invalid Go toolchain version",
strings.Join([]string{
"As of Go 1.21, toolchain versions [must use the 1.N.P syntax](https://go.dev/doc/toolchain#version).",
fmt.Sprintf("`%s` in `%s` does not match this syntax and there is no additional `toolchain` directive, which may cause some `go` commands to fail.", version, goModPath),
},
"\n\n"),
severityWarning,
fullVisibility,
&locationStruct{File: goModPath},
)
}
34 changes: 8 additions & 26 deletions go/extractor/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,20 +192,10 @@ func findGoModFiles(root string) []string {
return util.FindAllFilesWithName(root, "go.mod", util.SkipVendorChecks...)
}

// A regular expression for the Go toolchain version syntax.
var toolchainVersionRe *regexp.Regexp = regexp.MustCompile(`(?m)^([0-9]+\.[0-9]+(\.[0-9]+|rc[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)
}

// Given a list of `go.mod` file paths, try to parse them all. The resulting array of `GoModule` objects
// will be the same length as the input array and the objects will contain at least the `go.mod` path.
// If parsing the corresponding file is successful, then the parsed contents will also be available.
func LoadGoModules(emitDiagnostics bool, goModFilePaths []string) []*GoModule {
func LoadGoModules(goModFilePaths []string) []*GoModule {
results := make([]*GoModule, len(goModFilePaths))

for i, goModFilePath := range goModFilePaths {
Expand All @@ -227,14 +217,6 @@ func LoadGoModules(emitDiagnostics bool, goModFilePaths []string) []*GoModule {
}

results[i].Module = modFile

// If this `go.mod` file specifies a Go language version, that version is `1.21` or greater, and
// 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) {
diagnostics.EmitInvalidToolchainVersion(goModFilePath, modFile.Go.Version)
}
}

return results
Expand All @@ -243,7 +225,7 @@ func LoadGoModules(emitDiagnostics bool, goModFilePaths []string) []*GoModule {
// Given a path to a `go.work` file, this function attempts to parse the `go.work` file. If unsuccessful,
// we attempt to discover `go.mod` files within subdirectories of the directory containing the `go.work`
// file ourselves.
func discoverWorkspace(emitDiagnostics bool, workFilePath string) GoWorkspace {
func discoverWorkspace(workFilePath string) GoWorkspace {
log.Printf("Loading %s...\n", workFilePath)
baseDir := filepath.Dir(workFilePath)
workFileSrc, err := os.ReadFile(workFilePath)
Expand All @@ -257,7 +239,7 @@ func discoverWorkspace(emitDiagnostics bool, workFilePath string) GoWorkspace {

return GoWorkspace{
BaseDir: baseDir,
Modules: LoadGoModules(emitDiagnostics, goModFilePaths),
Modules: LoadGoModules(goModFilePaths),
DepMode: GoGetWithModules,
ModMode: getModMode(GoGetWithModules, baseDir),
}
Expand All @@ -274,7 +256,7 @@ func discoverWorkspace(emitDiagnostics bool, workFilePath string) GoWorkspace {

return GoWorkspace{
BaseDir: baseDir,
Modules: LoadGoModules(emitDiagnostics, goModFilePaths),
Modules: LoadGoModules(goModFilePaths),
DepMode: GoGetWithModules,
ModMode: getModMode(GoGetWithModules, baseDir),
}
Expand All @@ -297,7 +279,7 @@ func discoverWorkspace(emitDiagnostics bool, workFilePath string) GoWorkspace {
return GoWorkspace{
BaseDir: baseDir,
WorkspaceFile: workFile,
Modules: LoadGoModules(emitDiagnostics, goModFilePaths),
Modules: LoadGoModules(goModFilePaths),
DepMode: GoGetWithModules,
ModMode: ModReadonly, // Workspaces only support "readonly"
}
Expand Down Expand Up @@ -325,7 +307,7 @@ func discoverWorkspaces(emitDiagnostics bool) []GoWorkspace {
for i, goModFile := range goModFiles {
results[i] = GoWorkspace{
BaseDir: filepath.Dir(goModFile),
Modules: LoadGoModules(emitDiagnostics, []string{goModFile}),
Modules: LoadGoModules([]string{goModFile}),
DepMode: GoGetWithModules,
ModMode: getModMode(GoGetWithModules, filepath.Dir(goModFile)),
}
Expand All @@ -342,7 +324,7 @@ func discoverWorkspaces(emitDiagnostics bool) []GoWorkspace {

results := make([]GoWorkspace, len(goWorkFiles))
for i, workFilePath := range goWorkFiles {
results[i] = discoverWorkspace(emitDiagnostics, workFilePath)
results[i] = discoverWorkspace(workFilePath)
}

// Add all stray `go.mod` files (i.e. those not referenced by `go.work` files)
Expand Down Expand Up @@ -374,7 +356,7 @@ func discoverWorkspaces(emitDiagnostics bool) []GoWorkspace {
log.Printf("Module %s is not referenced by any go.work file; adding it separately.\n", goModFile)
results = append(results, GoWorkspace{
BaseDir: filepath.Dir(goModFile),
Modules: LoadGoModules(emitDiagnostics, []string{goModFile}),
Modules: LoadGoModules([]string{goModFile}),
DepMode: GoGetWithModules,
ModMode: getModMode(GoGetWithModules, filepath.Dir(goModFile)),
})
Expand Down
29 changes: 0 additions & 29 deletions go/extractor/project/project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,35 +39,6 @@ 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) {
invalid := []string{
"go 1.21\n",
"go 1.22\n",
}

for _, v := range invalid {
if !testHasInvalidToolchainVersion(t, v) {
t.Errorf("Expected testHasInvalidToolchainVersion(\"%s\") to be true, but got false", v)
}
}

valid := []string{
"go 1.20\n",
"go 1.21.1\n",
"go 1.22\n\ntoolchain go1.22.0\n",
}

for _, v := range valid {
if testHasInvalidToolchainVersion(t, v) {
t.Errorf("Expected testHasInvalidToolchainVersion(\"%s\") to be false, but got true", v)
}
}
}

func parseWorkFile(t *testing.T, contents string) *modfile.WorkFile {
workFile, err := modfile.ParseWork("go.work", []byte(contents), nil)

Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

17 changes: 0 additions & 17 deletions go/ql/integration-tests/go-version-bump/diagnostics.expected
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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",
Expand Down