mirror of
https://github.com/golang/go
synced 2024-11-11 23:50:22 -07:00
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 <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com>
This commit is contained in:
parent
6bd0e7fa8a
commit
3124968848
51
src/cmd/fix/buildtag.go
Normal file
51
src/cmd/fix/buildtag.go
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register(buildtagFix)
|
||||
}
|
||||
|
||||
const buildtagGoVersionCutoff = 1_18
|
||||
|
||||
var buildtagFix = fix{
|
||||
name: "buildtag",
|
||||
date: "2021-08-25",
|
||||
f: buildtag,
|
||||
desc: `Remove +build comments from modules using Go 1.18 or later`,
|
||||
}
|
||||
|
||||
func buildtag(f *ast.File) bool {
|
||||
if goVersion < buildtagGoVersionCutoff {
|
||||
return false
|
||||
}
|
||||
|
||||
// File is already gofmt-ed, so we know that if there are +build lines,
|
||||
// they are in a comment group that starts with a //go:build line followed
|
||||
// by a blank line. While we cannot delete comments from an AST and
|
||||
// expect consistent output in general, this specific case - deleting only
|
||||
// some lines from a comment block - does format correctly.
|
||||
fixed := false
|
||||
for _, g := range f.Comments {
|
||||
sawGoBuild := false
|
||||
for i, c := range g.List {
|
||||
if strings.HasPrefix(c.Text, "//go:build ") {
|
||||
sawGoBuild = true
|
||||
}
|
||||
if sawGoBuild && strings.HasPrefix(c.Text, "// +build ") {
|
||||
g.List = g.List[:i]
|
||||
fixed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fixed
|
||||
}
|
34
src/cmd/fix/buildtag_test.go
Normal file
34
src/cmd/fix/buildtag_test.go
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
func init() {
|
||||
addTestCases(buildtagTests, buildtag)
|
||||
}
|
||||
|
||||
var buildtagTests = []testCase{
|
||||
{
|
||||
Name: "buildtag.oldGo",
|
||||
Version: 1_10,
|
||||
In: `//go:build yes
|
||||
// +build yes
|
||||
|
||||
package main
|
||||
`,
|
||||
},
|
||||
{
|
||||
Name: "buildtag.new",
|
||||
Version: 1_99,
|
||||
In: `//go:build yes
|
||||
// +build yes
|
||||
|
||||
package main
|
||||
`,
|
||||
Out: `//go:build yes
|
||||
|
||||
package main
|
||||
`,
|
||||
},
|
||||
}
|
@ -125,6 +125,9 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
|
||||
case *ast.IndexExpr:
|
||||
walkBeforeAfter(&n.X, before, after)
|
||||
walkBeforeAfter(&n.Index, before, after)
|
||||
case *ast.IndexListExpr:
|
||||
walkBeforeAfter(&n.X, before, after)
|
||||
walkBeforeAfter(&n.Indices, before, after)
|
||||
case *ast.SliceExpr:
|
||||
walkBeforeAfter(&n.X, before, after)
|
||||
if n.Low != nil {
|
||||
@ -156,6 +159,9 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
|
||||
case *ast.StructType:
|
||||
walkBeforeAfter(&n.Fields, before, after)
|
||||
case *ast.FuncType:
|
||||
if n.TypeParams != nil {
|
||||
walkBeforeAfter(&n.TypeParams, before, after)
|
||||
}
|
||||
walkBeforeAfter(&n.Params, before, after)
|
||||
if n.Results != nil {
|
||||
walkBeforeAfter(&n.Results, before, after)
|
||||
@ -231,6 +237,9 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
|
||||
walkBeforeAfter(&n.Values, before, after)
|
||||
walkBeforeAfter(&n.Names, before, after)
|
||||
case *ast.TypeSpec:
|
||||
if n.TypeParams != nil {
|
||||
walkBeforeAfter(&n.TypeParams, before, after)
|
||||
}
|
||||
walkBeforeAfter(&n.Type, before, after)
|
||||
|
||||
case *ast.BadDecl:
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"cmd/internal/diff"
|
||||
@ -36,7 +37,12 @@ var forceRewrites = flag.String("force", "",
|
||||
|
||||
var allowed, force map[string]bool
|
||||
|
||||
var doDiff = flag.Bool("diff", false, "display diffs instead of rewriting files")
|
||||
var (
|
||||
doDiff = flag.Bool("diff", false, "display diffs instead of rewriting files")
|
||||
goVersionStr = flag.String("go", "", "go language version for files")
|
||||
|
||||
goVersion int // 115 for go1.15
|
||||
)
|
||||
|
||||
// enable for debugging fix failures
|
||||
const debug = false // display incorrectly reformatted source and exit
|
||||
@ -63,6 +69,26 @@ func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
if *goVersionStr != "" {
|
||||
if !strings.HasPrefix(*goVersionStr, "go") {
|
||||
report(fmt.Errorf("invalid -go=%s", *goVersionStr))
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
majorStr := (*goVersionStr)[len("go"):]
|
||||
minorStr := "0"
|
||||
if i := strings.Index(majorStr, "."); i >= 0 {
|
||||
majorStr, minorStr = majorStr[:i], majorStr[i+len("."):]
|
||||
}
|
||||
major, err1 := strconv.Atoi(majorStr)
|
||||
minor, err2 := strconv.Atoi(minorStr)
|
||||
if err1 != nil || err2 != nil || major < 0 || major >= 100 || minor < 0 || minor >= 100 {
|
||||
report(fmt.Errorf("invalid -go=%s", *goVersionStr))
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
goVersion = major*100 + minor
|
||||
}
|
||||
|
||||
sort.Sort(byDate(fixes))
|
||||
|
||||
if *allowedRewrites != "" {
|
||||
|
@ -14,10 +14,11 @@ import (
|
||||
)
|
||||
|
||||
type testCase struct {
|
||||
Name string
|
||||
Fn func(*ast.File) bool
|
||||
In string
|
||||
Out string
|
||||
Name string
|
||||
Fn func(*ast.File) bool
|
||||
Version int
|
||||
In string
|
||||
Out string
|
||||
}
|
||||
|
||||
var testCases []testCase
|
||||
@ -78,7 +79,16 @@ func TestRewrite(t *testing.T) {
|
||||
for _, tt := range testCases {
|
||||
tt := tt
|
||||
t.Run(tt.Name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if tt.Version == 0 {
|
||||
t.Parallel()
|
||||
} else {
|
||||
old := goVersion
|
||||
goVersion = tt.Version
|
||||
defer func() {
|
||||
goVersion = old
|
||||
}()
|
||||
}
|
||||
|
||||
// Apply fix: should get tt.Out.
|
||||
out, fixed, ok := parseFixPrint(t, tt.Fn, tt.Name, tt.In, true)
|
||||
if !ok {
|
||||
@ -91,6 +101,9 @@ func TestRewrite(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
if tt.Out == "" {
|
||||
tt.Out = tt.In
|
||||
}
|
||||
if out != tt.Out {
|
||||
t.Errorf("incorrect output.\n")
|
||||
if !strings.HasPrefix(tt.Name, "testdata/") {
|
||||
|
@ -464,14 +464,18 @@
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// go fix [packages]
|
||||
// go fix [-fix list] [packages]
|
||||
//
|
||||
// Fix runs the Go fix command on the packages named by the import paths.
|
||||
//
|
||||
// The -fix flag sets a comma-separated list of fixes to run.
|
||||
// The default is all known fixes.
|
||||
// (Its value is passed to 'go tool fix -r'.)
|
||||
//
|
||||
// For more about fix, see 'go doc cmd/fix'.
|
||||
// For more about specifying packages, see 'go help packages'.
|
||||
//
|
||||
// To run fix with specific options, run 'go tool fix'.
|
||||
// To run fix with other options, run 'go tool fix'.
|
||||
//
|
||||
// See also: go fmt, go vet.
|
||||
//
|
||||
|
@ -11,27 +11,39 @@ import (
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/str"
|
||||
"cmd/go/internal/work"
|
||||
"context"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os"
|
||||
)
|
||||
|
||||
var CmdFix = &base.Command{
|
||||
Run: runFix,
|
||||
UsageLine: "go fix [packages]",
|
||||
UsageLine: "go fix [-fix list] [packages]",
|
||||
Short: "update packages to use new APIs",
|
||||
Long: `
|
||||
Fix runs the Go fix command on the packages named by the import paths.
|
||||
|
||||
The -fix flag sets a comma-separated list of fixes to run.
|
||||
The default is all known fixes.
|
||||
(Its value is passed to 'go tool fix -r'.)
|
||||
|
||||
For more about fix, see 'go doc cmd/fix'.
|
||||
For more about specifying packages, see 'go help packages'.
|
||||
|
||||
To run fix with specific options, run 'go tool fix'.
|
||||
To run fix with other options, run 'go tool fix'.
|
||||
|
||||
See also: go fmt, go vet.
|
||||
`,
|
||||
}
|
||||
|
||||
var fixes = CmdFix.Flag.String("fix", "", "comma-separated list of fixes to apply")
|
||||
|
||||
func init() {
|
||||
work.AddBuildFlags(CmdFix, work.DefaultBuildFlags)
|
||||
CmdFix.Run = runFix // fix cycle
|
||||
}
|
||||
|
||||
func runFix(ctx context.Context, cmd *base.Command, args []string) {
|
||||
pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{}, args)
|
||||
w := 0
|
||||
@ -58,6 +70,16 @@ func runFix(ctx context.Context, cmd *base.Command, args []string) {
|
||||
// the command only applies to this package,
|
||||
// not to packages in subdirectories.
|
||||
files := base.RelPaths(pkg.InternalAllGoFiles())
|
||||
base.Run(str.StringList(cfg.BuildToolexec, base.Tool("fix"), files))
|
||||
goVersion := ""
|
||||
if pkg.Module != nil {
|
||||
goVersion = "go" + pkg.Module.GoVersion
|
||||
} else if pkg.Standard {
|
||||
goVersion = build.Default.ReleaseTags[len(build.Default.ReleaseTags)-1]
|
||||
}
|
||||
var fixArg []string
|
||||
if *fixes != "" {
|
||||
fixArg = []string{"-r=" + *fixes}
|
||||
}
|
||||
base.Run(str.StringList(cfg.BuildToolexec, base.Tool("fix"), "-go="+goVersion, fixArg, files))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user