Skip to content

Commit 3124968

Browse files
committed
cmd/fix: add buildtag fix
Now that Go 1.17 is out and Go 1.15 is unsupported, removing // +build lines can be done safely: in the worst case, if code is compiled using Go 1.16 the toolchain will detect the presence of a //go:build without // +build and fail the build. (It will not silently choose the wrong files.) Note that +build lines will continue to work in Go sources forever. This just provides a mechanism for users who are done with Go 1.16 to remove them easily, by running "go fix". Also update for new generics AST. For #41184. Fixes #48978. Change-Id: I11a432c319e5abd05ad68dda9ccd7a7fdcc8bbb8 Reviewed-on: https://go-review.googlesource.com/c/go/+/240611 Trust: Russ Cox <[email protected]> Run-TryBot: Russ Cox <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Bryan C. Mills <[email protected]>
1 parent 6bd0e7f commit 3124968

File tree

7 files changed

+171
-12
lines changed

7 files changed

+171
-12
lines changed

src/cmd/fix/buildtag.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2020 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 main
6+
7+
import (
8+
"go/ast"
9+
"strings"
10+
)
11+
12+
func init() {
13+
register(buildtagFix)
14+
}
15+
16+
const buildtagGoVersionCutoff = 1_18
17+
18+
var buildtagFix = fix{
19+
name: "buildtag",
20+
date: "2021-08-25",
21+
f: buildtag,
22+
desc: `Remove +build comments from modules using Go 1.18 or later`,
23+
}
24+
25+
func buildtag(f *ast.File) bool {
26+
if goVersion < buildtagGoVersionCutoff {
27+
return false
28+
}
29+
30+
// File is already gofmt-ed, so we know that if there are +build lines,
31+
// they are in a comment group that starts with a //go:build line followed
32+
// by a blank line. While we cannot delete comments from an AST and
33+
// expect consistent output in general, this specific case - deleting only
34+
// some lines from a comment block - does format correctly.
35+
fixed := false
36+
for _, g := range f.Comments {
37+
sawGoBuild := false
38+
for i, c := range g.List {
39+
if strings.HasPrefix(c.Text, "//go:build ") {
40+
sawGoBuild = true
41+
}
42+
if sawGoBuild && strings.HasPrefix(c.Text, "// +build ") {
43+
g.List = g.List[:i]
44+
fixed = true
45+
break
46+
}
47+
}
48+
}
49+
50+
return fixed
51+
}

src/cmd/fix/buildtag_test.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2020 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 main
6+
7+
func init() {
8+
addTestCases(buildtagTests, buildtag)
9+
}
10+
11+
var buildtagTests = []testCase{
12+
{
13+
Name: "buildtag.oldGo",
14+
Version: 1_10,
15+
In: `//go:build yes
16+
// +build yes
17+
18+
package main
19+
`,
20+
},
21+
{
22+
Name: "buildtag.new",
23+
Version: 1_99,
24+
In: `//go:build yes
25+
// +build yes
26+
27+
package main
28+
`,
29+
Out: `//go:build yes
30+
31+
package main
32+
`,
33+
},
34+
}

src/cmd/fix/fix.go

+9
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
125125
case *ast.IndexExpr:
126126
walkBeforeAfter(&n.X, before, after)
127127
walkBeforeAfter(&n.Index, before, after)
128+
case *ast.IndexListExpr:
129+
walkBeforeAfter(&n.X, before, after)
130+
walkBeforeAfter(&n.Indices, before, after)
128131
case *ast.SliceExpr:
129132
walkBeforeAfter(&n.X, before, after)
130133
if n.Low != nil {
@@ -156,6 +159,9 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
156159
case *ast.StructType:
157160
walkBeforeAfter(&n.Fields, before, after)
158161
case *ast.FuncType:
162+
if n.TypeParams != nil {
163+
walkBeforeAfter(&n.TypeParams, before, after)
164+
}
159165
walkBeforeAfter(&n.Params, before, after)
160166
if n.Results != nil {
161167
walkBeforeAfter(&n.Results, before, after)
@@ -231,6 +237,9 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
231237
walkBeforeAfter(&n.Values, before, after)
232238
walkBeforeAfter(&n.Names, before, after)
233239
case *ast.TypeSpec:
240+
if n.TypeParams != nil {
241+
walkBeforeAfter(&n.TypeParams, before, after)
242+
}
234243
walkBeforeAfter(&n.Type, before, after)
235244

236245
case *ast.BadDecl:

src/cmd/fix/main.go

+27-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"os"
1919
"path/filepath"
2020
"sort"
21+
"strconv"
2122
"strings"
2223

2324
"cmd/internal/diff"
@@ -36,7 +37,12 @@ var forceRewrites = flag.String("force", "",
3637

3738
var allowed, force map[string]bool
3839

39-
var doDiff = flag.Bool("diff", false, "display diffs instead of rewriting files")
40+
var (
41+
doDiff = flag.Bool("diff", false, "display diffs instead of rewriting files")
42+
goVersionStr = flag.String("go", "", "go language version for files")
43+
44+
goVersion int // 115 for go1.15
45+
)
4046

4147
// enable for debugging fix failures
4248
const debug = false // display incorrectly reformatted source and exit
@@ -63,6 +69,26 @@ func main() {
6369
flag.Usage = usage
6470
flag.Parse()
6571

72+
if *goVersionStr != "" {
73+
if !strings.HasPrefix(*goVersionStr, "go") {
74+
report(fmt.Errorf("invalid -go=%s", *goVersionStr))
75+
os.Exit(exitCode)
76+
}
77+
majorStr := (*goVersionStr)[len("go"):]
78+
minorStr := "0"
79+
if i := strings.Index(majorStr, "."); i >= 0 {
80+
majorStr, minorStr = majorStr[:i], majorStr[i+len("."):]
81+
}
82+
major, err1 := strconv.Atoi(majorStr)
83+
minor, err2 := strconv.Atoi(minorStr)
84+
if err1 != nil || err2 != nil || major < 0 || major >= 100 || minor < 0 || minor >= 100 {
85+
report(fmt.Errorf("invalid -go=%s", *goVersionStr))
86+
os.Exit(exitCode)
87+
}
88+
89+
goVersion = major*100 + minor
90+
}
91+
6692
sort.Sort(byDate(fixes))
6793

6894
if *allowedRewrites != "" {

src/cmd/fix/main_test.go

+18-5
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ import (
1414
)
1515

1616
type testCase struct {
17-
Name string
18-
Fn func(*ast.File) bool
19-
In string
20-
Out string
17+
Name string
18+
Fn func(*ast.File) bool
19+
Version int
20+
In string
21+
Out string
2122
}
2223

2324
var testCases []testCase
@@ -78,7 +79,16 @@ func TestRewrite(t *testing.T) {
7879
for _, tt := range testCases {
7980
tt := tt
8081
t.Run(tt.Name, func(t *testing.T) {
81-
t.Parallel()
82+
if tt.Version == 0 {
83+
t.Parallel()
84+
} else {
85+
old := goVersion
86+
goVersion = tt.Version
87+
defer func() {
88+
goVersion = old
89+
}()
90+
}
91+
8292
// Apply fix: should get tt.Out.
8393
out, fixed, ok := parseFixPrint(t, tt.Fn, tt.Name, tt.In, true)
8494
if !ok {
@@ -91,6 +101,9 @@ func TestRewrite(t *testing.T) {
91101
return
92102
}
93103

104+
if tt.Out == "" {
105+
tt.Out = tt.In
106+
}
94107
if out != tt.Out {
95108
t.Errorf("incorrect output.\n")
96109
if !strings.HasPrefix(tt.Name, "testdata/") {

src/cmd/go/alldocs.go

+6-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/go/internal/fix/fix.go

+26-4
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,39 @@ import (
1111
"cmd/go/internal/load"
1212
"cmd/go/internal/modload"
1313
"cmd/go/internal/str"
14+
"cmd/go/internal/work"
1415
"context"
1516
"fmt"
17+
"go/build"
1618
"os"
1719
)
1820

1921
var CmdFix = &base.Command{
20-
Run: runFix,
21-
UsageLine: "go fix [packages]",
22+
UsageLine: "go fix [-fix list] [packages]",
2223
Short: "update packages to use new APIs",
2324
Long: `
2425
Fix runs the Go fix command on the packages named by the import paths.
2526
27+
The -fix flag sets a comma-separated list of fixes to run.
28+
The default is all known fixes.
29+
(Its value is passed to 'go tool fix -r'.)
30+
2631
For more about fix, see 'go doc cmd/fix'.
2732
For more about specifying packages, see 'go help packages'.
2833
29-
To run fix with specific options, run 'go tool fix'.
34+
To run fix with other options, run 'go tool fix'.
3035
3136
See also: go fmt, go vet.
3237
`,
3338
}
3439

40+
var fixes = CmdFix.Flag.String("fix", "", "comma-separated list of fixes to apply")
41+
42+
func init() {
43+
work.AddBuildFlags(CmdFix, work.DefaultBuildFlags)
44+
CmdFix.Run = runFix // fix cycle
45+
}
46+
3547
func runFix(ctx context.Context, cmd *base.Command, args []string) {
3648
pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{}, args)
3749
w := 0
@@ -58,6 +70,16 @@ func runFix(ctx context.Context, cmd *base.Command, args []string) {
5870
// the command only applies to this package,
5971
// not to packages in subdirectories.
6072
files := base.RelPaths(pkg.InternalAllGoFiles())
61-
base.Run(str.StringList(cfg.BuildToolexec, base.Tool("fix"), files))
73+
goVersion := ""
74+
if pkg.Module != nil {
75+
goVersion = "go" + pkg.Module.GoVersion
76+
} else if pkg.Standard {
77+
goVersion = build.Default.ReleaseTags[len(build.Default.ReleaseTags)-1]
78+
}
79+
var fixArg []string
80+
if *fixes != "" {
81+
fixArg = []string{"-r=" + *fixes}
82+
}
83+
base.Run(str.StringList(cfg.BuildToolexec, base.Tool("fix"), "-go="+goVersion, fixArg, files))
6284
}
6385
}

0 commit comments

Comments
 (0)