summaryrefslogtreecommitdiffstats
path: root/src/cmd/go/internal/vet
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
commitccd992355df7192993c666236047820244914598 (patch)
treef00fea65147227b7743083c6148396f74cd66935 /src/cmd/go/internal/vet
parentInitial commit. (diff)
downloadgolang-1.21-ccd992355df7192993c666236047820244914598.tar.xz
golang-1.21-ccd992355df7192993c666236047820244914598.zip
Adding upstream version 1.21.8.upstream/1.21.8
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/go/internal/vet')
-rw-r--r--src/cmd/go/internal/vet/vet.go120
-rw-r--r--src/cmd/go/internal/vet/vetflag.go191
2 files changed, 311 insertions, 0 deletions
diff --git a/src/cmd/go/internal/vet/vet.go b/src/cmd/go/internal/vet/vet.go
new file mode 100644
index 0000000..2d42097
--- /dev/null
+++ b/src/cmd/go/internal/vet/vet.go
@@ -0,0 +1,120 @@
+// Copyright 2011 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 implements the “go vet” command.
+package vet
+
+import (
+ "context"
+ "fmt"
+ "path/filepath"
+
+ "cmd/go/internal/base"
+ "cmd/go/internal/cfg"
+ "cmd/go/internal/load"
+ "cmd/go/internal/modload"
+ "cmd/go/internal/trace"
+ "cmd/go/internal/work"
+)
+
+// Break init loop.
+func init() {
+ CmdVet.Run = runVet
+}
+
+var CmdVet = &base.Command{
+ CustomFlags: true,
+ UsageLine: "go vet [build flags] [-vettool prog] [vet flags] [packages]",
+ Short: "report likely mistakes in packages",
+ Long: `
+Vet runs the Go vet command on the packages named by the import paths.
+
+For more about vet and its flags, see 'go doc cmd/vet'.
+For more about specifying packages, see 'go help packages'.
+For a list of checkers and their flags, see 'go tool vet help'.
+For details of a specific checker such as 'printf', see 'go tool vet help printf'.
+
+The -vettool=prog flag selects a different analysis tool with alternative
+or additional checks.
+For example, the 'shadow' analyzer can be built and run using these commands:
+
+ go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest
+ go vet -vettool=$(which shadow)
+
+The build flags supported by go vet are those that control package resolution
+and execution, such as -C, -n, -x, -v, -tags, and -toolexec.
+For more about these flags, see 'go help build'.
+
+See also: go fmt, go fix.
+ `,
+}
+
+func runVet(ctx context.Context, cmd *base.Command, args []string) {
+ vetFlags, pkgArgs := vetFlags(args)
+ modload.InitWorkfile() // The vet command does custom flag processing; initialize workspaces after that.
+
+ if cfg.DebugTrace != "" {
+ var close func() error
+ var err error
+ ctx, close, err = trace.Start(ctx, cfg.DebugTrace)
+ if err != nil {
+ base.Fatalf("failed to start trace: %v", err)
+ }
+ defer func() {
+ if err := close(); err != nil {
+ base.Fatalf("failed to stop trace: %v", err)
+ }
+ }()
+ }
+
+ ctx, span := trace.StartSpan(ctx, fmt.Sprint("Running ", cmd.Name(), " command"))
+ defer span.Done()
+
+ work.BuildInit()
+ work.VetFlags = vetFlags
+ if len(vetFlags) > 0 {
+ work.VetExplicit = true
+ }
+ if vetTool != "" {
+ var err error
+ work.VetTool, err = filepath.Abs(vetTool)
+ if err != nil {
+ base.Fatalf("%v", err)
+ }
+ }
+
+ pkgOpts := load.PackageOpts{ModResolveTests: true}
+ pkgs := load.PackagesAndErrors(ctx, pkgOpts, pkgArgs)
+ load.CheckPackageErrors(pkgs)
+ if len(pkgs) == 0 {
+ base.Fatalf("no packages to vet")
+ }
+
+ b := work.NewBuilder("")
+ defer func() {
+ if err := b.Close(); err != nil {
+ base.Fatal(err)
+ }
+ }()
+
+ root := &work.Action{Mode: "go vet"}
+ for _, p := range pkgs {
+ _, ptest, pxtest, err := load.TestPackagesFor(ctx, pkgOpts, p, nil)
+ if err != nil {
+ base.Errorf("%v", err)
+ continue
+ }
+ if len(ptest.GoFiles) == 0 && len(ptest.CgoFiles) == 0 && pxtest == nil {
+ base.Errorf("go: can't vet %s: no Go files in %s", p.ImportPath, p.Dir)
+ continue
+ }
+ if len(ptest.GoFiles) > 0 || len(ptest.CgoFiles) > 0 {
+ root.Deps = append(root.Deps, b.VetAction(work.ModeBuild, work.ModeBuild, ptest))
+ }
+ if pxtest != nil {
+ root.Deps = append(root.Deps, b.VetAction(work.ModeBuild, work.ModeBuild, pxtest))
+ }
+ }
+ b.Do(ctx, root)
+}
diff --git a/src/cmd/go/internal/vet/vetflag.go b/src/cmd/go/internal/vet/vetflag.go
new file mode 100644
index 0000000..eb7af65
--- /dev/null
+++ b/src/cmd/go/internal/vet/vetflag.go
@@ -0,0 +1,191 @@
+// 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 (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+
+ "cmd/go/internal/base"
+ "cmd/go/internal/cmdflag"
+ "cmd/go/internal/work"
+)
+
+// go vet flag processing
+//
+// We query the flags of the tool specified by -vettool 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.
+
+// vetTool specifies the vet command to run.
+// Any tool that supports the (still unpublished) vet
+// command-line protocol may be supplied; see
+// golang.org/x/tools/go/analysis/unitchecker for one
+// implementation. It is also used by tests.
+//
+// The default behavior (vetTool=="") runs 'go tool vet'.
+var vetTool string // -vettool
+
+func init() {
+ work.AddBuildFlags(CmdVet, work.DefaultBuildFlags)
+ CmdVet.Flag.StringVar(&vetTool, "vettool", "", "")
+}
+
+func parseVettoolFlag(args []string) {
+ // Extract -vettool by ad hoc flag processing:
+ // its value is needed even before we can declare
+ // the flags available during main flag processing.
+ for i, arg := range args {
+ if arg == "-vettool" || arg == "--vettool" {
+ if i+1 >= len(args) {
+ log.Fatalf("%s requires a filename", arg)
+ }
+ vetTool = args[i+1]
+ return
+ } else if strings.HasPrefix(arg, "-vettool=") ||
+ strings.HasPrefix(arg, "--vettool=") {
+ vetTool = arg[strings.IndexByte(arg, '=')+1:]
+ return
+ }
+ }
+}
+
+// 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) {
+ parseVettoolFlag(args)
+
+ // Query the vet command for its flags.
+ var tool string
+ if vetTool == "" {
+ tool = base.Tool("vet")
+ } else {
+ var err error
+ tool, err = filepath.Abs(vetTool)
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+ out := new(bytes.Buffer)
+ vetcmd := exec.Command(tool, "-flags")
+ vetcmd.Stdout = out
+ if err := vetcmd.Run(); err != nil {
+ fmt.Fprintf(os.Stderr, "go: can't execute %s -flags: %v\n", tool, err)
+ base.SetExitStatus(2)
+ base.Exit()
+ }
+ var analysisFlags []struct {
+ Name string
+ Bool bool
+ Usage string
+ }
+ if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil {
+ fmt.Fprintf(os.Stderr, "go: can't unmarshal JSON from %s -flags: %v", tool, err)
+ base.SetExitStatus(2)
+ base.Exit()
+ }
+
+ // Add vet's flags to CmdVet.Flag.
+ //
+ // Some flags, in particular -tags and -v, are known to vet but
+ // also defined as build flags. This works fine, so we omit duplicates here.
+ // However some, like -x, are known to the build but not to vet.
+ isVetFlag := make(map[string]bool, len(analysisFlags))
+ cf := CmdVet.Flag
+ for _, f := range analysisFlags {
+ isVetFlag[f.Name] = true
+ if cf.Lookup(f.Name) == nil {
+ if f.Bool {
+ cf.Bool(f.Name, false, "")
+ } else {
+ cf.String(f.Name, "", "")
+ }
+ }
+ }
+
+ // Record the set of vet tool flags set by GOFLAGS. We want to pass them to
+ // the vet tool, but only if they aren't overridden by an explicit argument.
+ base.SetFromGOFLAGS(&CmdVet.Flag)
+ addFromGOFLAGS := map[string]bool{}
+ CmdVet.Flag.Visit(func(f *flag.Flag) {
+ if isVetFlag[f.Name] {
+ addFromGOFLAGS[f.Name] = true
+ }
+ })
+
+ explicitFlags := make([]string, 0, len(args))
+ for len(args) > 0 {
+ f, remainingArgs, err := cmdflag.ParseOne(&CmdVet.Flag, args)
+
+ if errors.Is(err, flag.ErrHelp) {
+ exitWithUsage()
+ }
+
+ if errors.Is(err, cmdflag.ErrFlagTerminator) {
+ // All remaining args must be package names, but the flag terminator is
+ // not included.
+ packageNames = remainingArgs
+ break
+ }
+
+ if nf := (cmdflag.NonFlagError{}); errors.As(err, &nf) {
+ // Everything from here on out — including the argument we just consumed —
+ // must be a package name.
+ packageNames = args
+ break
+ }
+
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ exitWithUsage()
+ }
+
+ if isVetFlag[f.Name] {
+ // Forward the raw arguments rather than cleaned equivalents, just in
+ // case the vet tool parses them idiosyncratically.
+ explicitFlags = append(explicitFlags, args[:len(args)-len(remainingArgs)]...)
+
+ // This flag has been overridden explicitly, so don't forward its implicit
+ // value from GOFLAGS.
+ delete(addFromGOFLAGS, f.Name)
+ }
+
+ args = remainingArgs
+ }
+
+ // Prepend arguments from GOFLAGS before other arguments.
+ CmdVet.Flag.Visit(func(f *flag.Flag) {
+ if addFromGOFLAGS[f.Name] {
+ passToVet = append(passToVet, fmt.Sprintf("-%s=%s", f.Name, f.Value))
+ }
+ })
+ passToVet = append(passToVet, explicitFlags...)
+ return passToVet, packageNames
+}
+
+func exitWithUsage() {
+ fmt.Fprintf(os.Stderr, "usage: %s\n", CmdVet.UsageLine)
+ fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", CmdVet.LongName())
+
+ // This part is additional to what (*Command).Usage does:
+ cmd := "go tool vet"
+ if vetTool != "" {
+ cmd = vetTool
+ }
+ fmt.Fprintf(os.Stderr, "Run '%s help' for a full list of flags and analyzers.\n", cmd)
+ fmt.Fprintf(os.Stderr, "Run '%s -help' for an overview.\n", cmd)
+
+ base.SetExitStatus(2)
+ base.Exit()
+}