Commit 62aeb777 authored by Rob Pike's avatar Rob Pike

cmd/go: allow full flag processing in go vet

This allows the go tool to run "go vet" with both the build flags
that make sense, such as -x and -tags, and vet with all its flags.

To do this, create a new package cmd/go/internal/cmdflag to
hold functionality common to flag handling for test and vet.

Fixes #19350

RELNOTES=yes

Change-Id: Ia1ae213bd3f6cab1c5e492501c8d43ce61a7ee89
Reviewed-on: https://go-review.googlesource.com/40112Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent 9ffd9339
...@@ -21,10 +21,11 @@ var builddeps = map[string][]string{ ...@@ -21,10 +21,11 @@ var builddeps = map[string][]string{
"cmd/go/internal/load": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/str", "compress/flate", "compress/zlib", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "math/bits", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, "cmd/go/internal/load": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/str", "compress/flate", "compress/zlib", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "math/bits", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"},
"cmd/go/internal/run": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "math/bits", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, "cmd/go/internal/run": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "math/bits", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"},
"cmd/go/internal/str": {"bytes", "errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, "cmd/go/internal/str": {"bytes", "errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
"cmd/go/internal/test": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "math/bits", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, "cmd/go/internal/test": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/cmdflag", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "math/bits", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"},
"cmd/go/internal/tool": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/cfg", "cmd/go/internal/str", "context", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, "cmd/go/internal/tool": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/cfg", "cmd/go/internal/str", "context", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"},
"cmd/go/internal/version": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/cfg", "cmd/go/internal/str", "context", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, "cmd/go/internal/version": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/cfg", "cmd/go/internal/str", "context", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"},
"cmd/go/internal/vet": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "math/bits", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, "cmd/go/internal/cmdflag": {"cmd/go/internal/base", "flag", "fmt", "os", "strconv", "strings"},
"cmd/go/internal/vet": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/cmdflag", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "math/bits", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"},
"cmd/go/internal/web": {"errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic"}, "cmd/go/internal/web": {"errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic"},
"cmd/go/internal/work": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "math/bits", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, "cmd/go/internal/work": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "math/bits", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"},
"compress/flate": {"bufio", "bytes", "errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "math/bits", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, "compress/flate": {"bufio", "bytes", "errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "math/bits", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
......
...@@ -798,15 +798,13 @@ ...@@ -798,15 +798,13 @@
// //
// Usage: // Usage:
// //
// go vet [-n] [-x] [build flags] [packages] // go vet [-n] [-x] [build flags] [vet flags] [packages]
// //
// Vet runs the Go vet command on the packages named by the import paths. // Vet runs the Go vet command on the packages named by the import paths.
// //
// For more about vet, see 'go doc cmd/vet'. // For more about vet and its flags, see 'go doc cmd/vet'.
// For more about specifying packages, see 'go help packages'. // For more about specifying packages, see 'go help packages'.
// //
// To run the vet tool with specific options, run 'go tool vet'.
//
// The -n flag prints commands that would be executed. // The -n flag prints commands that would be executed.
// The -x flag prints commands as they are executed. // The -x flag prints commands as they are executed.
// //
......
...@@ -2650,8 +2650,6 @@ func TestGoGetInternalWildcard(t *testing.T) { ...@@ -2650,8 +2650,6 @@ func TestGoGetInternalWildcard(t *testing.T) {
} }
func TestGoVetWithExternalTests(t *testing.T) { func TestGoVetWithExternalTests(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
tg.makeTempdir() tg.makeTempdir()
...@@ -2662,15 +2660,32 @@ func TestGoVetWithExternalTests(t *testing.T) { ...@@ -2662,15 +2660,32 @@ func TestGoVetWithExternalTests(t *testing.T) {
} }
func TestGoVetWithTags(t *testing.T) { func TestGoVetWithTags(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
tg.makeTempdir() tg.makeTempdir()
tg.run("install", "cmd/vet") tg.run("install", "cmd/vet")
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.runFail("vet", "-tags", "tagtest", "vetpkg") tg.runFail("vet", "-tags", "tagtest", "vetpkg")
tg.grepBoth(`c\.go.*wrong number of args for format`, "go get vetpkg did not run scan tagged file") tg.grepBoth(`c\.go.*wrong number of args for format`, "go vet vetpkg did not run scan tagged file")
}
func TestGoVetWithFlagsOn(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.makeTempdir()
tg.run("install", "cmd/vet")
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.runFail("vet", "-printf", "vetpkg")
tg.grepBoth("missing argument for Printf", "go vet -printf vetpkg did not find missing argument for Printf")
}
func TestGoVetWithFlagsOff(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.makeTempdir()
tg.run("install", "cmd/vet")
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.run("vet", "-printf=false", "vetpkg")
} }
// Issue 9767, 19769. // Issue 9767, 19769.
......
// Copyright 2017 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 cmdflag handles flag processing common to several go tools.
package cmdflag
import (
"flag"
"fmt"
"os"
"strconv"
"strings"
"cmd/go/internal/base"
)
// The flag handling part of go commands such as test is large and distracting.
// We can't use the standard flag package because some of the flags from
// our command line are for us, and some are for the binary we're running,
// and some are for both.
// Defn defines a flag we know about.
type Defn struct {
Name string // Name on command line.
BoolVar *bool // If it's a boolean flag, this points to it.
Value flag.Value // The flag.Value represented.
PassToTest bool // Pass to the test binary? Used only by go test.
Present bool // Flag has been seen.
}
// IsBool reports whether v is a bool flag.
func IsBool(v flag.Value) bool {
vv, ok := v.(interface {
IsBoolFlag() bool
})
if ok {
return vv.IsBoolFlag()
}
return false
}
// SetBool sets the addressed boolean to the value.
func SetBool(cmd string, flag *bool, value string) {
x, err := strconv.ParseBool(value)
if err != nil {
SyntaxError(cmd, "illegal bool flag value "+value)
}
*flag = x
}
// SetInt sets the addressed integer to the value.
func SetInt(cmd string, flag *int, value string) {
x, err := strconv.Atoi(value)
if err != nil {
SyntaxError(cmd, "illegal int flag value "+value)
}
*flag = x
}
// SyntaxError reports an argument syntax error and exits the program.
func SyntaxError(cmd, msg string) {
fmt.Fprintf(os.Stderr, "go %s: %s\n", cmd, msg)
if cmd == "test" {
fmt.Fprintf(os.Stderr, `run "go help %s" or "go help testflag" for more information`+"\n", cmd)
} else {
fmt.Fprintf(os.Stderr, `run "go help %s" for more information`+"\n", cmd)
}
os.Exit(2)
}
// Parse sees if argument i is present in the definitions and if so,
// returns its definition, value, and whether it consumed an extra word.
// If the flag begins (cmd+".") it is ignored for the purpose of this function.
func Parse(cmd string, defns []*Defn, args []string, i int) (f *Defn, value string, extra bool) {
arg := args[i]
if strings.HasPrefix(arg, "--") { // reduce two minuses to one
arg = arg[1:]
}
switch arg {
case "-?", "-h", "-help":
base.Usage()
}
if arg == "" || arg[0] != '-' {
return
}
name := arg[1:]
// If there's already a prefix such as "test.", drop it for now.
name = strings.TrimPrefix(name, cmd+".")
equals := strings.Index(name, "=")
if equals >= 0 {
value = name[equals+1:]
name = name[:equals]
}
for _, f = range defns {
if name == f.Name {
// Booleans are special because they have modes -x, -x=true, -x=false.
if f.BoolVar != nil || IsBool(f.Value) {
if equals < 0 { // Otherwise, it's been set and will be verified in SetBool.
value = "true"
} else {
// verify it parses
SetBool(cmd, new(bool), value)
}
} else { // Non-booleans must have a value.
extra = equals < 0
if extra {
if i+1 >= len(args) {
SyntaxError(cmd, "missing argument for flag "+f.Name)
}
value = args[i+1]
}
}
if f.Present {
SyntaxError(cmd, f.Name+" flag may be set only once")
}
f.Present = true
return
}
}
f = nil
return
}
...@@ -6,64 +6,55 @@ package test ...@@ -6,64 +6,55 @@ package test
import ( import (
"flag" "flag"
"fmt"
"os" "os"
"strconv"
"strings" "strings"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg" "cmd/go/internal/cfg"
"cmd/go/internal/cmdflag"
"cmd/go/internal/str" "cmd/go/internal/str"
"cmd/go/internal/work" "cmd/go/internal/work"
) )
const cmd = "test"
// The flag handling part of go test is large and distracting. // The flag handling part of go test is large and distracting.
// We can't use the flag package because some of the flags from // We can't use the flag package because some of the flags from
// our command line are for us, and some are for 6.out, and // our command line are for us, and some are for 6.out, and
// some are for both. // some are for both.
// testFlagSpec defines a flag we know about.
type testFlagSpec struct {
name string
boolVar *bool
flagValue flag.Value
passToTest bool // pass to Test
multiOK bool // OK to have multiple instances
present bool // flag has been seen
}
// testFlagDefn is the set of flags we process. // testFlagDefn is the set of flags we process.
var testFlagDefn = []*testFlagSpec{ var testFlagDefn = []*cmdflag.Defn{
// local. // local.
{name: "c", boolVar: &testC}, {Name: "c", BoolVar: &testC},
{name: "i", boolVar: &cfg.BuildI}, {Name: "i", BoolVar: &cfg.BuildI},
{name: "o"}, {Name: "o"},
{name: "cover", boolVar: &testCover}, {Name: "cover", BoolVar: &testCover},
{name: "covermode"}, {Name: "covermode"},
{name: "coverpkg"}, {Name: "coverpkg"},
{name: "exec"}, {Name: "exec"},
// passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v. // Passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
{name: "bench", passToTest: true}, {Name: "bench", PassToTest: true},
{name: "benchmem", boolVar: new(bool), passToTest: true}, {Name: "benchmem", BoolVar: new(bool), PassToTest: true},
{name: "benchtime", passToTest: true}, {Name: "benchtime", PassToTest: true},
{name: "count", passToTest: true}, {Name: "count", PassToTest: true},
{name: "coverprofile", passToTest: true}, {Name: "coverprofile", PassToTest: true},
{name: "cpu", passToTest: true}, {Name: "cpu", PassToTest: true},
{name: "cpuprofile", passToTest: true}, {Name: "cpuprofile", PassToTest: true},
{name: "memprofile", passToTest: true}, {Name: "memprofile", PassToTest: true},
{name: "memprofilerate", passToTest: true}, {Name: "memprofilerate", PassToTest: true},
{name: "blockprofile", passToTest: true}, {Name: "blockprofile", PassToTest: true},
{name: "blockprofilerate", passToTest: true}, {Name: "blockprofilerate", PassToTest: true},
{name: "mutexprofile", passToTest: true}, {Name: "mutexprofile", PassToTest: true},
{name: "mutexprofilefraction", passToTest: true}, {Name: "mutexprofilefraction", PassToTest: true},
{name: "outputdir", passToTest: true}, {Name: "outputdir", PassToTest: true},
{name: "parallel", passToTest: true}, {Name: "parallel", PassToTest: true},
{name: "run", passToTest: true}, {Name: "run", PassToTest: true},
{name: "short", boolVar: new(bool), passToTest: true}, {Name: "short", BoolVar: new(bool), PassToTest: true},
{name: "timeout", passToTest: true}, {Name: "timeout", PassToTest: true},
{name: "trace", passToTest: true}, {Name: "trace", PassToTest: true},
{name: "v", boolVar: &testV, passToTest: true}, {Name: "v", BoolVar: &testV, PassToTest: true},
} }
// add build flags to testFlagDefn // add build flags to testFlagDefn
...@@ -75,9 +66,9 @@ func init() { ...@@ -75,9 +66,9 @@ func init() {
// test overrides the build -v flag // test overrides the build -v flag
return return
} }
testFlagDefn = append(testFlagDefn, &testFlagSpec{ testFlagDefn = append(testFlagDefn, &cmdflag.Defn{
name: f.Name, Name: f.Name,
flagValue: f.Value, Value: f.Value,
}) })
}) })
} }
...@@ -112,7 +103,7 @@ func testFlags(args []string) (packageNames, passToTest []string) { ...@@ -112,7 +103,7 @@ func testFlags(args []string) (packageNames, passToTest []string) {
inPkg = false inPkg = false
} }
f, value, extraWord := testFlag(args, i) f, value, extraWord := cmdflag.Parse(cmd, testFlagDefn, args, i)
if f == nil { if f == nil {
// This is a flag we do not know; we must assume // This is a flag we do not know; we must assume
// that any args we see after this might be flag // that any args we see after this might be flag
...@@ -131,24 +122,24 @@ func testFlags(args []string) (packageNames, passToTest []string) { ...@@ -131,24 +122,24 @@ func testFlags(args []string) (packageNames, passToTest []string) {
passToTest = append(passToTest, args[i]) passToTest = append(passToTest, args[i])
continue continue
} }
if f.flagValue != nil { if f.Value != nil {
if err := f.flagValue.Set(value); err != nil { if err := f.Value.Set(value); err != nil {
base.Fatalf("invalid flag argument for -%s: %v", f.name, err) base.Fatalf("invalid flag argument for -%s: %v", f.Name, err)
} }
} else { } else {
// Test-only flags. // Test-only flags.
// Arguably should be handled by f.flagValue, but aren't. // Arguably should be handled by f.Value, but aren't.
switch f.name { switch f.Name {
// bool flags. // bool flags.
case "c", "i", "v", "cover": case "c", "i", "v", "cover":
setBoolFlag(f.boolVar, value) cmdflag.SetBool(cmd, f.BoolVar, value)
case "o": case "o":
testO = value testO = value
testNeedBinary = true testNeedBinary = true
case "exec": case "exec":
xcmd, err := str.SplitQuotedFields(value) xcmd, err := str.SplitQuotedFields(value)
if err != nil { if err != nil {
base.Fatalf("invalid flag argument for -%s: %v", f.name, err) base.Fatalf("invalid flag argument for -%s: %v", f.Name, err)
} }
work.ExecCmd = xcmd work.ExecCmd = xcmd
case "bench": case "bench":
...@@ -186,8 +177,8 @@ func testFlags(args []string) (packageNames, passToTest []string) { ...@@ -186,8 +177,8 @@ func testFlags(args []string) (packageNames, passToTest []string) {
if extraWord { if extraWord {
i++ i++
} }
if f.passToTest { if f.PassToTest {
passToTest = append(passToTest, "-test."+f.name+"="+value) passToTest = append(passToTest, "-test."+f.Name+"="+value)
} }
} }
...@@ -211,89 +202,3 @@ func testFlags(args []string) (packageNames, passToTest []string) { ...@@ -211,89 +202,3 @@ func testFlags(args []string) (packageNames, passToTest []string) {
passToTest = append(passToTest, explicitArgs...) passToTest = append(passToTest, explicitArgs...)
return return
} }
// testFlag sees if argument i is a known flag and returns its definition, value, and whether it consumed an extra word.
func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) {
arg := args[i]
if strings.HasPrefix(arg, "--") { // reduce two minuses to one
arg = arg[1:]
}
switch arg {
case "-?", "-h", "-help":
base.Usage()
}
if arg == "" || arg[0] != '-' {
return
}
name := arg[1:]
// If there's already "test.", drop it for now.
name = strings.TrimPrefix(name, "test.")
equals := strings.Index(name, "=")
if equals >= 0 {
value = name[equals+1:]
name = name[:equals]
}
for _, f = range testFlagDefn {
if name == f.name {
// Booleans are special because they have modes -x, -x=true, -x=false.
if f.boolVar != nil || isBoolFlag(f.flagValue) {
if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag
value = "true"
} else {
// verify it parses
setBoolFlag(new(bool), value)
}
} else { // Non-booleans must have a value.
extra = equals < 0
if extra {
if i+1 >= len(args) {
testSyntaxError("missing argument for flag " + f.name)
}
value = args[i+1]
}
}
if f.present && !f.multiOK {
testSyntaxError(f.name + " flag may be set only once")
}
f.present = true
return
}
}
f = nil
return
}
// isBoolFlag reports whether v is a bool flag.
func isBoolFlag(v flag.Value) bool {
vv, ok := v.(interface {
IsBoolFlag() bool
})
if ok {
return vv.IsBoolFlag()
}
return false
}
// setBoolFlag sets the addressed boolean to the value.
func setBoolFlag(flag *bool, value string) {
x, err := strconv.ParseBool(value)
if err != nil {
testSyntaxError("illegal bool flag value " + value)
}
*flag = x
}
// setIntFlag sets the addressed integer to the value.
func setIntFlag(flag *int, value string) {
x, err := strconv.Atoi(value)
if err != nil {
testSyntaxError("illegal int flag value " + value)
}
*flag = x
}
func testSyntaxError(msg string) {
fmt.Fprintf(os.Stderr, "go test: %s\n", msg)
fmt.Fprintf(os.Stderr, `run "go help test" or "go help testflag" for more information`+"\n")
os.Exit(2)
}
...@@ -12,25 +12,19 @@ import ( ...@@ -12,25 +12,19 @@ import (
"cmd/go/internal/cfg" "cmd/go/internal/cfg"
"cmd/go/internal/load" "cmd/go/internal/load"
"cmd/go/internal/str" "cmd/go/internal/str"
"cmd/go/internal/work"
) )
func init() {
work.AddBuildFlags(CmdVet)
}
var CmdVet = &base.Command{ var CmdVet = &base.Command{
Run: runVet, Run: runVet,
UsageLine: "vet [-n] [-x] [build flags] [packages]", CustomFlags: true,
Short: "run go tool vet on packages", UsageLine: "vet [-n] [-x] [build flags] [vet flags] [packages]",
Short: "run go tool vet on packages",
Long: ` Long: `
Vet runs the Go vet command on the packages named by the import paths. Vet runs the Go vet command on the packages named by the import paths.
For more about vet, see 'go doc cmd/vet'. For more about vet and its flags, see 'go doc cmd/vet'.
For more about specifying packages, see 'go help packages'. For more about specifying packages, see 'go help packages'.
To run the vet tool with specific options, run 'go tool vet'.
The -n flag prints commands that would be executed. The -n flag prints commands that would be executed.
The -x flag prints commands as they are executed. The -x flag prints commands as they are executed.
...@@ -41,21 +35,22 @@ See also: go fmt, go fix. ...@@ -41,21 +35,22 @@ See also: go fmt, go fix.
} }
func runVet(cmd *base.Command, args []string) { func runVet(cmd *base.Command, args []string) {
for _, p := range load.Packages(args) { vetFlags, packages := vetFlags(args)
for _, p := range load.Packages(packages) {
// Vet expects to be given a set of files all from the same package. // Vet expects to be given a set of files all from the same package.
// Run once for package p and once for package p_test. // Run once for package p and once for package p_test.
if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles) > 0 { if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles) > 0 {
runVetFiles(p, str.StringList(p.GoFiles, p.CgoFiles, p.TestGoFiles, p.SFiles)) runVetFiles(p, vetFlags, str.StringList(p.GoFiles, p.CgoFiles, p.TestGoFiles, p.SFiles))
} }
if len(p.XTestGoFiles) > 0 { if len(p.XTestGoFiles) > 0 {
runVetFiles(p, str.StringList(p.XTestGoFiles)) runVetFiles(p, vetFlags, str.StringList(p.XTestGoFiles))
} }
} }
} }
func runVetFiles(p *load.Package, files []string) { func runVetFiles(p *load.Package, flags, files []string) {
for i := range files { for i := range files {
files[i] = filepath.Join(p.Dir, files[i]) files[i] = filepath.Join(p.Dir, files[i])
} }
base.Run(cfg.BuildToolexec, base.Tool("vet"), base.RelPaths(files)) base.Run(cfg.BuildToolexec, base.Tool("vet"), flags, base.RelPaths(files))
} }
// Copyright 2017 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 vet
import (
"flag"
"fmt"
"os"
"strings"
"cmd/go/internal/base"
"cmd/go/internal/cmdflag"
"cmd/go/internal/work"
)
const cmd = "vet"
// 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.
// local.
{Name: "all", BoolVar: new(bool)},
{Name: "asmdecl", BoolVar: new(bool)},
{Name: "assign", BoolVar: new(bool)},
{Name: "atomic", BoolVar: new(bool)},
{Name: "bool", BoolVar: new(bool)},
{Name: "buildtags", BoolVar: new(bool)},
{Name: "cgocall", BoolVar: new(bool)},
{Name: "composites", BoolVar: new(bool)},
{Name: "copylocks", BoolVar: new(bool)},
{Name: "httpresponse", BoolVar: new(bool)},
{Name: "lostcancel", BoolVar: new(bool)},
{Name: "methods", BoolVar: new(bool)},
{Name: "nilfunc", BoolVar: new(bool)},
{Name: "printf", BoolVar: new(bool)},
{Name: "printfuncs"},
{Name: "rangeloops", BoolVar: new(bool)},
{Name: "shadow", BoolVar: new(bool)},
{Name: "shadowstrict", BoolVar: new(bool)},
{Name: "source", BoolVar: new(bool)},
{Name: "structtags", BoolVar: new(bool)},
{Name: "tests", BoolVar: new(bool)},
{Name: "unreachable", BoolVar: new(bool)},
{Name: "unsafeptr", BoolVar: new(bool)},
{Name: "unusedfuncs"},
{Name: "unusedresult", BoolVar: new(bool)},
{Name: "unusedstringmethods"},
}
// add build flags to vetFlagDefn.
func init() {
var cmd base.Command
work.AddBuildFlags(&cmd)
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) {
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)
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")
os.Exit(2)
}
if f.Value != nil {
if err := f.Value.Set(value); err != nil {
base.Fatalf("invalid flag argument for -%s: %v", f.Name, err)
}
switch f.Name {
// Flags known to the build but not to vet, so must be dropped.
case "x", "n":
args = append(args[:i], args[i+1:]...)
i--
}
}
if extraWord {
i++
}
}
return args, nil
}
...@@ -23,6 +23,8 @@ import ( ...@@ -23,6 +23,8 @@ import (
"strings" "strings"
) )
// Important! If you add flags here, make sure to update cmd/go/internal/vet/vetflag.go.
var ( var (
verbose = flag.Bool("v", false, "verbose") verbose = flag.Bool("v", false, "verbose")
source = flag.Bool("source", false, "import from source instead of compiled object files") source = flag.Bool("source", false, "import from source instead of compiled object files")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment