Skip to content

Commit 55d718e

Browse files
committed
go/analysis/passes/buildtag: report invalid go versions in build tags.
Report invalid Go versions within build tags. Additionally reports likely misspellings of Go version tags. Fixes golang/go#64127 Change-Id: I9b698c94c7da29aafbe45b4c90d58c0bfe8efa1d Reviewed-on: https://go-review.googlesource.com/c/tools/+/597576 Reviewed-by: Alan Donovan <adonovan@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
1 parent 8b51d66 commit 55d718e

File tree

11 files changed

+143
-1
lines changed

11 files changed

+143
-1
lines changed

go/analysis/passes/buildtag/buildtag.go

+53
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
"golang.org/x/tools/go/analysis"
1717
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
18+
"golang.org/x/tools/internal/versions"
1819
)
1920

2021
const Doc = "check //go:build and // +build directives"
@@ -264,6 +265,8 @@ func (check *checker) goBuildLine(pos token.Pos, line string) {
264265
return
265266
}
266267

268+
check.tags(pos, x)
269+
267270
if check.goBuild == nil {
268271
check.goBuild = x
269272
}
@@ -323,6 +326,8 @@ func (check *checker) plusBuildLine(pos token.Pos, line string) {
323326
check.crossCheck = false
324327
return
325328
}
329+
check.tags(pos, y)
330+
326331
if check.plusBuild == nil {
327332
check.plusBuild = y
328333
} else {
@@ -363,3 +368,51 @@ func (check *checker) finish() {
363368
return
364369
}
365370
}
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+
}

go/analysis/passes/buildtag/buildtag_test.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"golang.org/x/tools/go/analysis"
1111
"golang.org/x/tools/go/analysis/analysistest"
1212
"golang.org/x/tools/go/analysis/passes/buildtag"
13+
"golang.org/x/tools/internal/versions"
1314
)
1415

1516
func Test(t *testing.T) {
@@ -30,5 +31,9 @@ func Test(t *testing.T) {
3031

3132
return buildtag.Analyzer.Run(pass)
3233
}
33-
analysistest.Run(t, analysistest.TestData(), &analyzer, "a")
34+
patterns := []string{"a"}
35+
if versions.ConstraintGoVersion != nil {
36+
patterns = append(patterns, "b")
37+
}
38+
analysistest.Run(t, analysistest.TestData(), &analyzer, patterns...)
3439
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// want +3 `invalid go version \"go1.20.1\" in build constraint`
6+
// want +1 `invalid go version \"go1.20.1\" in build constraint`
7+
//go:build go1.20.1
8+
// +build go1.20.1
9+
10+
package b
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// This file is intentionally so its build tags always match.
6+
7+
package b
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// want +1 `invalid go version \"go120\" in build constraint`
6+
//go:build go120
7+
8+
package b
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// want +1 `invalid go version \"go1..20\" in build constraint`
6+
//go:build go1..20
7+
8+
package b
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// want +1 `invalid go version \"go.20\" in build constraint`
6+
//go:build go.20
7+
8+
package b
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// want +1 `invalid go version \"g1.20\" in build constraint`
6+
//go:build g1.20
7+
8+
package b
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// want +1 `invalid go version \"go20\" in build constraint`
6+
//go:build go20
7+
8+
package b

internal/versions/constraint.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package versions
6+
7+
import "go/build/constraint"
8+
9+
// ConstraintGoVersion is constraint.GoVersion (if built with go1.21+).
10+
// Otherwise nil.
11+
//
12+
// Deprecate once x/tools is after go1.21.
13+
var ConstraintGoVersion func(x constraint.Expr) string

internal/versions/constraint_go121.go

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build go1.21
6+
// +build go1.21
7+
8+
package versions
9+
10+
import "go/build/constraint"
11+
12+
func init() {
13+
ConstraintGoVersion = constraint.GoVersion
14+
}

0 commit comments

Comments
 (0)