@@ -176,10 +176,13 @@ func findGoModFiles(root string) []string {
176176 return util .FindAllFilesWithName (root , "go.mod" , "vendor" )
177177}
178178
179+ // A regular expression for the Go toolchain version syntax.
180+ var toolchainVersionRe * regexp.Regexp = regexp .MustCompile (`(?m)^([0-9]+\.[0-9]+\.[0-9]+)$` )
181+
179182// Given a list of `go.mod` file paths, try to parse them all. The resulting array of `GoModule` objects
180183// will be the same length as the input array and the objects will contain at least the `go.mod` path.
181184// If parsing the corresponding file is successful, then the parsed contents will also be available.
182- func LoadGoModules (goModFilePaths []string ) []* GoModule {
185+ func LoadGoModules (emitDiagnostics bool , goModFilePaths []string ) []* GoModule {
183186 results := make ([]* GoModule , len (goModFilePaths ))
184187
185188 for i , goModFilePath := range goModFilePaths {
@@ -201,6 +204,25 @@ func LoadGoModules(goModFilePaths []string) []*GoModule {
201204 }
202205
203206 results [i ].Module = modFile
207+
208+ // If this `go.mod` file specifies a Go language version, that version is `1.21` or greater, and
209+ // there is no `toolchain` directive, check that it is a valid Go toolchain version. Otherwise,
210+ // `go` commands which try to download the right version of the Go toolchain will fail. We detect
211+ // this situation and emit a diagnostic.
212+ if modFile .Toolchain == nil && modFile .Go != nil &&
213+ ! toolchainVersionRe .Match ([]byte (modFile .Go .Version )) && semver .Compare ("v" + modFile .Go .Version , "v1.21.0" ) >= 0 {
214+ diagnostics .EmitInvalidToolchainVersion (goModFilePath , modFile .Go .Version )
215+
216+ modPath := filepath .Dir (goModFilePath )
217+
218+ log .Printf (
219+ "`%s` is not a valid toolchain version, trying to install it explicitly using the canonical representation in `%s`." ,
220+ modFile .Go .Version ,
221+ modPath ,
222+ )
223+
224+ toolchain .InstallVersion (modPath , modFile .Go .Version )
225+ }
204226 }
205227
206228 return results
@@ -209,7 +231,7 @@ func LoadGoModules(goModFilePaths []string) []*GoModule {
209231// Given a path to a `go.work` file, this function attempts to parse the `go.work` file. If unsuccessful,
210232// we attempt to discover `go.mod` files within subdirectories of the directory containing the `go.work`
211233// file ourselves.
212- func discoverWorkspace (workFilePath string ) GoWorkspace {
234+ func discoverWorkspace (emitDiagnostics bool , workFilePath string ) GoWorkspace {
213235 log .Printf ("Loading %s...\n " , workFilePath )
214236 baseDir := filepath .Dir (workFilePath )
215237 workFileSrc , err := os .ReadFile (workFilePath )
@@ -223,7 +245,7 @@ func discoverWorkspace(workFilePath string) GoWorkspace {
223245
224246 return GoWorkspace {
225247 BaseDir : baseDir ,
226- Modules : LoadGoModules (goModFilePaths ),
248+ Modules : LoadGoModules (emitDiagnostics , goModFilePaths ),
227249 DepMode : GoGetWithModules ,
228250 ModMode : getModMode (GoGetWithModules , baseDir ),
229251 }
@@ -240,7 +262,7 @@ func discoverWorkspace(workFilePath string) GoWorkspace {
240262
241263 return GoWorkspace {
242264 BaseDir : baseDir ,
243- Modules : LoadGoModules (goModFilePaths ),
265+ Modules : LoadGoModules (emitDiagnostics , goModFilePaths ),
244266 DepMode : GoGetWithModules ,
245267 ModMode : getModMode (GoGetWithModules , baseDir ),
246268 }
@@ -263,7 +285,7 @@ func discoverWorkspace(workFilePath string) GoWorkspace {
263285 return GoWorkspace {
264286 BaseDir : baseDir ,
265287 WorkspaceFile : workFile ,
266- Modules : LoadGoModules (goModFilePaths ),
288+ Modules : LoadGoModules (emitDiagnostics , goModFilePaths ),
267289 DepMode : GoGetWithModules ,
268290 ModMode : ModReadonly , // Workspaces only support "readonly"
269291 }
@@ -286,7 +308,7 @@ func discoverWorkspaces(emitDiagnostics bool) []GoWorkspace {
286308 for i , goModFile := range goModFiles {
287309 results [i ] = GoWorkspace {
288310 BaseDir : filepath .Dir (goModFile ),
289- Modules : LoadGoModules ([]string {goModFile }),
311+ Modules : LoadGoModules (emitDiagnostics , []string {goModFile }),
290312 DepMode : GoGetWithModules ,
291313 ModMode : getModMode (GoGetWithModules , filepath .Dir (goModFile )),
292314 }
@@ -303,7 +325,7 @@ func discoverWorkspaces(emitDiagnostics bool) []GoWorkspace {
303325
304326 results := make ([]GoWorkspace , len (goWorkFiles ))
305327 for i , workFilePath := range goWorkFiles {
306- results [i ] = discoverWorkspace (workFilePath )
328+ results [i ] = discoverWorkspace (emitDiagnostics , workFilePath )
307329 }
308330
309331 // Add all stray `go.mod` files (i.e. those not referenced by `go.work` files)
@@ -335,7 +357,7 @@ func discoverWorkspaces(emitDiagnostics bool) []GoWorkspace {
335357 log .Printf ("Module %s is not referenced by any go.work file; adding it separately.\n " , goModFile )
336358 results = append (results , GoWorkspace {
337359 BaseDir : filepath .Dir (goModFile ),
338- Modules : LoadGoModules ([]string {goModFile }),
360+ Modules : LoadGoModules (emitDiagnostics , []string {goModFile }),
339361 DepMode : GoGetWithModules ,
340362 ModMode : getModMode (GoGetWithModules , filepath .Dir (goModFile )),
341363 })
0 commit comments