@@ -176,10 +176,13 @@ func findGoModFiles(root string) []string {
176
176
return util .FindAllFilesWithName (root , "go.mod" , "vendor" )
177
177
}
178
178
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
+
179
182
// Given a list of `go.mod` file paths, try to parse them all. The resulting array of `GoModule` objects
180
183
// will be the same length as the input array and the objects will contain at least the `go.mod` path.
181
184
// 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 {
183
186
results := make ([]* GoModule , len (goModFilePaths ))
184
187
185
188
for i , goModFilePath := range goModFilePaths {
@@ -201,6 +204,25 @@ func LoadGoModules(goModFilePaths []string) []*GoModule {
201
204
}
202
205
203
206
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
+ }
204
226
}
205
227
206
228
return results
@@ -209,7 +231,7 @@ func LoadGoModules(goModFilePaths []string) []*GoModule {
209
231
// Given a path to a `go.work` file, this function attempts to parse the `go.work` file. If unsuccessful,
210
232
// we attempt to discover `go.mod` files within subdirectories of the directory containing the `go.work`
211
233
// file ourselves.
212
- func discoverWorkspace (workFilePath string ) GoWorkspace {
234
+ func discoverWorkspace (emitDiagnostics bool , workFilePath string ) GoWorkspace {
213
235
log .Printf ("Loading %s...\n " , workFilePath )
214
236
baseDir := filepath .Dir (workFilePath )
215
237
workFileSrc , err := os .ReadFile (workFilePath )
@@ -223,7 +245,7 @@ func discoverWorkspace(workFilePath string) GoWorkspace {
223
245
224
246
return GoWorkspace {
225
247
BaseDir : baseDir ,
226
- Modules : LoadGoModules (goModFilePaths ),
248
+ Modules : LoadGoModules (emitDiagnostics , goModFilePaths ),
227
249
DepMode : GoGetWithModules ,
228
250
ModMode : getModMode (GoGetWithModules , baseDir ),
229
251
}
@@ -240,7 +262,7 @@ func discoverWorkspace(workFilePath string) GoWorkspace {
240
262
241
263
return GoWorkspace {
242
264
BaseDir : baseDir ,
243
- Modules : LoadGoModules (goModFilePaths ),
265
+ Modules : LoadGoModules (emitDiagnostics , goModFilePaths ),
244
266
DepMode : GoGetWithModules ,
245
267
ModMode : getModMode (GoGetWithModules , baseDir ),
246
268
}
@@ -263,7 +285,7 @@ func discoverWorkspace(workFilePath string) GoWorkspace {
263
285
return GoWorkspace {
264
286
BaseDir : baseDir ,
265
287
WorkspaceFile : workFile ,
266
- Modules : LoadGoModules (goModFilePaths ),
288
+ Modules : LoadGoModules (emitDiagnostics , goModFilePaths ),
267
289
DepMode : GoGetWithModules ,
268
290
ModMode : ModReadonly , // Workspaces only support "readonly"
269
291
}
@@ -286,7 +308,7 @@ func discoverWorkspaces(emitDiagnostics bool) []GoWorkspace {
286
308
for i , goModFile := range goModFiles {
287
309
results [i ] = GoWorkspace {
288
310
BaseDir : filepath .Dir (goModFile ),
289
- Modules : LoadGoModules ([]string {goModFile }),
311
+ Modules : LoadGoModules (emitDiagnostics , []string {goModFile }),
290
312
DepMode : GoGetWithModules ,
291
313
ModMode : getModMode (GoGetWithModules , filepath .Dir (goModFile )),
292
314
}
@@ -303,7 +325,7 @@ func discoverWorkspaces(emitDiagnostics bool) []GoWorkspace {
303
325
304
326
results := make ([]GoWorkspace , len (goWorkFiles ))
305
327
for i , workFilePath := range goWorkFiles {
306
- results [i ] = discoverWorkspace (workFilePath )
328
+ results [i ] = discoverWorkspace (emitDiagnostics , workFilePath )
307
329
}
308
330
309
331
// Add all stray `go.mod` files (i.e. those not referenced by `go.work` files)
@@ -335,7 +357,7 @@ func discoverWorkspaces(emitDiagnostics bool) []GoWorkspace {
335
357
log .Printf ("Module %s is not referenced by any go.work file; adding it separately.\n " , goModFile )
336
358
results = append (results , GoWorkspace {
337
359
BaseDir : filepath .Dir (goModFile ),
338
- Modules : LoadGoModules ([]string {goModFile }),
360
+ Modules : LoadGoModules (emitDiagnostics , []string {goModFile }),
339
361
DepMode : GoGetWithModules ,
340
362
ModMode : getModMode (GoGetWithModules , filepath .Dir (goModFile )),
341
363
})
0 commit comments