@@ -15,6 +15,7 @@ import (
15
15
16
16
"golang.org/x/tools/go/analysis"
17
17
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
18
+ "golang.org/x/tools/internal/versions"
18
19
)
19
20
20
21
const Doc = "check //go:build and // +build directives"
@@ -264,6 +265,8 @@ func (check *checker) goBuildLine(pos token.Pos, line string) {
264
265
return
265
266
}
266
267
268
+ check .tags (pos , x )
269
+
267
270
if check .goBuild == nil {
268
271
check .goBuild = x
269
272
}
@@ -323,6 +326,8 @@ func (check *checker) plusBuildLine(pos token.Pos, line string) {
323
326
check .crossCheck = false
324
327
return
325
328
}
329
+ check .tags (pos , y )
330
+
326
331
if check .plusBuild == nil {
327
332
check .plusBuild = y
328
333
} else {
@@ -363,3 +368,51 @@ func (check *checker) finish() {
363
368
return
364
369
}
365
370
}
371
+
372
+ // tags reports issues in go versions in tags within the expression e.
373
+ func (check * checker ) tags (pos token.Pos , e constraint.Expr ) {
374
+ // Check that constraint.GoVersion is meaningful (>= go1.21).
375
+ if versions .ConstraintGoVersion == nil {
376
+ return
377
+ }
378
+
379
+ // Use Eval to visit each tag.
380
+ _ = e .Eval (func (tag string ) bool {
381
+ if malformedGoTag (tag ) {
382
+ check .pass .Reportf (pos , "invalid go version %q in build constraint" , tag )
383
+ }
384
+ return false // result is immaterial as Eval does not short-circuit
385
+ })
386
+ }
387
+
388
+ // malformedGoTag returns true if a tag is likely to be a malformed
389
+ // go version constraint.
390
+ func malformedGoTag (tag string ) bool {
391
+ // Not a go version?
392
+ if ! strings .HasPrefix (tag , "go1" ) {
393
+ // Check for close misspellings of the "go1." prefix.
394
+ for _ , pre := range []string {"go." , "g1." , "go" } {
395
+ suffix := strings .TrimPrefix (tag , pre )
396
+ if suffix != tag {
397
+ if valid , ok := validTag ("go1." + suffix ); ok && valid {
398
+ return true
399
+ }
400
+ }
401
+ }
402
+ return false
403
+ }
404
+
405
+ // The tag starts with "go1" so it is almost certainly a GoVersion.
406
+ // Report it if it is not a valid build constraint.
407
+ valid , ok := validTag (tag )
408
+ return ok && ! valid
409
+ }
410
+
411
+ // validTag returns (valid, ok) where valid reports when a tag is valid,
412
+ // and ok reports determining if the tag is valid succeeded.
413
+ func validTag (tag string ) (valid bool , ok bool ) {
414
+ if versions .ConstraintGoVersion != nil {
415
+ return versions .ConstraintGoVersion (& constraint.TagExpr {Tag : tag }) != "" , true
416
+ }
417
+ return false , false
418
+ }
0 commit comments