]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/fix: add buildtag fix
authorRuss Cox <rsc@golang.org>
Mon, 29 Jun 2020 18:29:09 +0000 (14:29 -0400)
committerRuss Cox <rsc@golang.org>
Thu, 28 Oct 2021 17:52:59 +0000 (17:52 +0000)
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>
src/cmd/fix/buildtag.go [new file with mode: 0644]
src/cmd/fix/buildtag_test.go [new file with mode: 0644]
src/cmd/fix/fix.go
src/cmd/fix/main.go
src/cmd/fix/main_test.go
src/cmd/go/alldocs.go
src/cmd/go/internal/fix/fix.go

diff --git a/src/cmd/fix/buildtag.go b/src/cmd/fix/buildtag.go
new file mode 100644 (file)
index 0000000..5f4fbfe
--- /dev/null
@@ -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
+}
diff --git a/src/cmd/fix/buildtag_test.go b/src/cmd/fix/buildtag_test.go
new file mode 100644 (file)
index 0000000..1c6efbe
--- /dev/null
@@ -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
+`,
+       },
+}
index b49db375710c3c511ca26a3fa55fa406b19d19ff..b9980c17b9ae4594f7a78af46ba83b8ffd66ac2d 100644 (file)
@@ -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:
index d055929aac0767bcd6b0be7ff6550b239c55af34..b5f7b901d66cc52cd130418dc32399b20f575e3f 100644 (file)
@@ -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 != "" {
index af16bcaa31ed94ca6e1f611c847cbae3e0f41786..1baa95c5456bcc85d7158902f9c2afa382363fdf 100644 (file)
@@ -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/") {
index 685ccac82677375761719e91cb126f7419ccd7be..537f800944c8da444de2692cc8765a3187a37238 100644 (file)
 //
 // 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.
 //
index 988d45e71ccfe2ccf571e409962bdd3ade4e3430..d8ba353de6595091f10b693bcb3aed1fde9d2e0d 100644 (file)
@@ -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))
        }
 }