diff --git a/src/cmd/go/internal/vet/vetflag.go b/src/cmd/go/internal/vet/vetflag.go
index 50eac425ec3693331e1d93cf933f7448296e7c4e..cfa4352cb99e13ea373358e9c7dbcdcdd7135889 100644
--- a/src/cmd/go/internal/vet/vetflag.go
+++ b/src/cmd/go/internal/vet/vetflag.go
@@ -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")
diff --git a/src/cmd/go/internal/work/buildid.go b/src/cmd/go/internal/work/buildid.go
index 8b97e8b75b88535b2d51fe8e77e6b0cd01016307..af3183ae9a061fc656c9225248309404e61badc5 100644
--- a/src/cmd/go/internal/work/buildid.go
+++ b/src/cmd/go/internal/work/buildid.go
@@ -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
diff --git a/src/cmd/vet/main.go b/src/cmd/vet/main.go
index 6e885121c8cd581f1850c852725f557d230d193a..cf91e4d59679fe5b4eb20f4d3b15470be4f57dc0 100644
--- a/src/cmd/vet/main.go
+++ b/src/cmd/vet/main.go
@@ -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).
diff --git a/src/cmd/vet/vet_test.go b/src/cmd/vet/vet_test.go
index 6b2125924d7a8ae34074f0379fa9d88bf4e03adf..da5a6ed87c7034440a0c81359941f0578c228271 100644
--- a/src/cmd/vet/vet_test.go
+++ b/src/cmd/vet/vet_test.go
@@ -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)
 }