]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go: make go vet query cmd/vet for its flags
authorAlan Donovan <adonovan@google.com>
Tue, 16 Oct 2018 18:35:01 +0000 (14:35 -0400)
committerAlan Donovan <adonovan@google.com>
Wed, 17 Oct 2018 21:39:52 +0000 (21:39 +0000)
Add -flags flag to cmd/vet that causes it to describe its flags as JSON.

go vet's "-vettool" flag has been replaced with an environment
variable, GOVETTOOL, for two reasons:

  1) we need its value before flag processing,
     because we must run vet to discover its flags.

  2) users may change the env var to opt in/out of the new vet tool
     during the upcoming transition to vet based on the analysis API.

Change-Id: I5d8f90817623022f4170b88fab3c92c9b2fbdc37
Reviewed-on: https://go-review.googlesource.com/c/142617
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
src/cmd/go/internal/vet/vetflag.go
src/cmd/go/internal/work/buildid.go
src/cmd/vet/main.go
src/cmd/vet/vet_test.go

index 50eac425ec3693331e1d93cf933f7448296e7c4e..cfa4352cb99e13ea373358e9c7dbcdcdd7135889 100644 (file)
@@ -5,9 +5,14 @@
 package vet
 
 import (
+       "bytes"
+       "encoding/json"
        "flag"
        "fmt"
+       "log"
        "os"
+       "os/exec"
+       "path/filepath"
        "strings"
 
        "cmd/go/internal/base"
@@ -16,72 +21,94 @@ import (
        "cmd/go/internal/work"
 )
 
-const cmd = "vet"
+// go vet flag processing
+//
+// We query the flags of the tool specified by GOVETTOOL (default:
+// cmd/vet) and accept any of those flags plus any flag valid for 'go
+// build'. The tool must support -flags, which prints a description of
+// its flags in JSON to stdout.
 
-// vetFlagDefn is the set of flags we process.
-var vetFlagDefn = []*cmdflag.Defn{
-       // Note: Some flags, in particular -tags and -v, are known to
-       // vet but also defined as build flags. This works fine, so we
-       // don't define them here but use AddBuildFlags to init them.
-       // However some, like -x, are known to the build but not
-       // to vet. We handle them in vetFlags.
+// GOVETTOOL specifies the vet command to run.
+// This must be an environment variable because
+// we need it before flag processing, as we execute
+// $GOVETTOOL to discover the set of flags it supports.
+//
+// Using an environment variable also makes it easy for users to opt in
+// to (and later, opt out of) the new cmd/vet analysis driver during the
+// transition. It is also used by tests.
+var vetTool = os.Getenv("GOVETTOOL")
 
-       // local.
-       {Name: "all", BoolVar: new(bool), PassToTest: true},
-       {Name: "asmdecl", BoolVar: new(bool), PassToTest: true},
-       {Name: "assign", BoolVar: new(bool), PassToTest: true},
-       {Name: "atomic", BoolVar: new(bool), PassToTest: true},
-       {Name: "bool", BoolVar: new(bool), PassToTest: true},
-       {Name: "buildtags", BoolVar: new(bool), PassToTest: true},
-       {Name: "cgocall", BoolVar: new(bool), PassToTest: true},
-       {Name: "composites", BoolVar: new(bool), PassToTest: true},
-       {Name: "copylocks", BoolVar: new(bool), PassToTest: true},
-       {Name: "httpresponse", BoolVar: new(bool), PassToTest: true},
-       {Name: "lostcancel", BoolVar: new(bool), PassToTest: true},
-       {Name: "methods", BoolVar: new(bool), PassToTest: true},
-       {Name: "nilfunc", BoolVar: new(bool), PassToTest: true},
-       {Name: "printf", BoolVar: new(bool), PassToTest: true},
-       {Name: "printfuncs", PassToTest: true},
-       {Name: "rangeloops", BoolVar: new(bool), PassToTest: true},
-       {Name: "shadow", BoolVar: new(bool), PassToTest: true},
-       {Name: "shadowstrict", BoolVar: new(bool), PassToTest: true},
-       {Name: "shift", BoolVar: new(bool), PassToTest: true},
-       {Name: "source", BoolVar: new(bool), PassToTest: true},
-       {Name: "structtags", BoolVar: new(bool), PassToTest: true},
-       {Name: "tests", BoolVar: new(bool), PassToTest: true},
-       {Name: "unreachable", BoolVar: new(bool), PassToTest: true},
-       {Name: "unsafeptr", BoolVar: new(bool), PassToTest: true},
-       {Name: "unusedfuncs", PassToTest: true},
-       {Name: "unusedresult", BoolVar: new(bool), PassToTest: true},
-       {Name: "unusedstringmethods", PassToTest: true},
-}
+// vetFlags processes the command line, splitting it at the first non-flag
+// into the list of flags and list of packages.
+func vetFlags(args []string) (passToVet, packageNames []string) {
+       // Query the vet command for its flags.
+       tool := vetTool
+       if tool != "" {
+               var err error
+               tool, err = filepath.Abs(tool)
+               if err != nil {
+                       log.Fatal(err)
+               }
+       } else {
+               tool = base.Tool("vet")
+       }
+       out := new(bytes.Buffer)
+       vetcmd := exec.Command(tool, "-flags")
+       vetcmd.Stdout = out
+       if err := vetcmd.Run(); err != nil {
+               fmt.Fprintf(os.Stderr, "go vet: can't execute %s -flags: %v\n", tool, err)
+               os.Exit(2)
+       }
+       var analysisFlags []struct {
+               Name  string
+               Bool  bool
+               Usage string
+       }
+       if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil {
+               fmt.Fprintf(os.Stderr, "go vet: can't unmarshal JSON from %s -flags: %v", tool, err)
+               os.Exit(2)
+       }
 
-var vetTool string
+       // Add vet's flags to vetflagDefn.
+       //
+       // Some flags, in particular -tags and -v, are known to vet but
+       // also defined as build flags. This works fine, so we don't
+       // define them here but use AddBuildFlags to init them.
+       // However some, like -x, are known to the build but not to vet.
+       var vetFlagDefn []*cmdflag.Defn
+       for _, f := range analysisFlags {
+               switch f.Name {
+               case "tags", "v":
+                       continue
+               }
+               defn := &cmdflag.Defn{
+                       Name:       f.Name,
+                       PassToTest: true,
+               }
+               if f.Bool {
+                       defn.BoolVar = new(bool)
+               }
+               vetFlagDefn = append(vetFlagDefn, defn)
+       }
 
-// add build flags to vetFlagDefn.
-func init() {
-       cmdflag.AddKnownFlags("vet", vetFlagDefn)
+       // Add build flags to vetFlagDefn.
        var cmd base.Command
        work.AddBuildFlags(&cmd)
-       cmd.Flag.StringVar(&vetTool, "vettool", "", "path to vet tool binary") // for cmd/vet tests; undocumented for now
        cmd.Flag.VisitAll(func(f *flag.Flag) {
                vetFlagDefn = append(vetFlagDefn, &cmdflag.Defn{
                        Name:  f.Name,
                        Value: f.Value,
                })
        })
-}
 
-// vetFlags processes the command line, splitting it at the first non-flag
-// into the list of flags and list of packages.
-func vetFlags(args []string) (passToVet, packageNames []string) {
+       // Process args.
        args = str.StringList(cmdflag.FindGOFLAGS(vetFlagDefn), args)
        for i := 0; i < len(args); i++ {
                if !strings.HasPrefix(args[i], "-") {
                        return args[:i], args[i:]
                }
 
-               f, value, extraWord := cmdflag.Parse(cmd, vetFlagDefn, args, i)
+               f, value, extraWord := cmdflag.Parse("vet", vetFlagDefn, args, i)
                if f == nil {
                        fmt.Fprintf(os.Stderr, "vet: flag %q not defined\n", args[i])
                        fmt.Fprintf(os.Stderr, "Run \"go help vet\" for more information\n")
index 8b97e8b75b88535b2d51fe8e77e6b0cd01016307..af3183ae9a061fc656c9225248309404e61badc5 100644 (file)
@@ -178,7 +178,7 @@ func (b *Builder) toolID(name string) string {
        path := base.Tool(name)
        desc := "go tool " + name
 
-       // Special case: undocumented -vettool overrides usual vet, for testing vet.
+       // Special case: undocumented $GOVETTOOL overrides usual vet, for testing vet.
        if name == "vet" && VetTool != "" {
                path = VetTool
                desc = VetTool
index 6e885121c8cd581f1850c852725f557d230d193a..cf91e4d59679fe5b4eb20f4d3b15470be4f57dc0 100644 (file)
@@ -22,6 +22,7 @@ import (
        "go/types"
        "io"
        "io/ioutil"
+       "log"
        "os"
        "path/filepath"
        "sort"
@@ -31,10 +32,9 @@ import (
        "cmd/internal/objabi"
 )
 
-// Important! If you add flags here, make sure to update cmd/go/internal/vet/vetflag.go.
-
 var (
        verbose = flag.Bool("v", false, "verbose")
+       flags   = flag.Bool("flags", false, "print flags in JSON")
        source  = flag.Bool("source", false, "import from source instead of compiled object files")
        tags    = flag.String("tags", "", "space-separated list of build tags to apply when parsing")
        tagList = []string{} // exploded version of tags flag; set in main
@@ -259,6 +259,32 @@ func main() {
        flag.Usage = Usage
        flag.Parse()
 
+       // -flags: print flags as JSON. Used by go vet.
+       if *flags {
+               type jsonFlag struct {
+                       Name  string
+                       Bool  bool
+                       Usage string
+               }
+               var jsonFlags []jsonFlag
+               flag.VisitAll(func(f *flag.Flag) {
+                       isBool := false
+                       switch v := f.Value.(type) {
+                       case interface{ BoolFlag() bool }:
+                               isBool = v.BoolFlag()
+                       case *triState:
+                               isBool = true // go vet should treat it as boolean
+                       }
+                       jsonFlags = append(jsonFlags, jsonFlag{f.Name, isBool, f.Usage})
+               })
+               data, err := json.MarshalIndent(jsonFlags, "", "\t")
+               if err != nil {
+                       log.Fatal(err)
+               }
+               os.Stdout.Write(data)
+               os.Exit(0)
+       }
+
        // If any flag is set, we run only those checks requested.
        // If all flag is set true or if no flags are set true, set all the non-experimental ones
        // not explicitly set (in effect, set the "-all" flag).
index 6b2125924d7a8ae34074f0379fa9d88bf4e03adf..da5a6ed87c7034440a0c81359941f0578c228271 100644 (file)
@@ -118,11 +118,12 @@ func TestVetPrint(t *testing.T) {
        Build(t)
        file := filepath.Join("testdata", "print.go")
        cmd := exec.Command(
-               "go", "vet", "-vettool="+binary,
+               "go", "vet",
                "-printf",
                "-printfuncs=Warn:1,Warnf:1",
                file,
        )
+       cmd.Env = append(os.Environ(), "GOVETTOOL="+binary)
        errchk(cmd, []string{file}, t)
 }