package vet
import (
+ "bytes"
+ "encoding/json"
"flag"
"fmt"
+ "log"
"os"
+ "os/exec"
+ "path/filepath"
"strings"
"cmd/go/internal/base"
"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")