1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package vet implements the “go vet” command.
6package vet
7
8import (
9	"context"
10	"fmt"
11	"path/filepath"
12
13	"cmd/go/internal/base"
14	"cmd/go/internal/cfg"
15	"cmd/go/internal/load"
16	"cmd/go/internal/modload"
17	"cmd/go/internal/trace"
18	"cmd/go/internal/work"
19)
20
21// Break init loop.
22func init() {
23	CmdVet.Run = runVet
24}
25
26var CmdVet = &base.Command{
27	CustomFlags: true,
28	UsageLine:   "go vet [build flags] [-vettool prog] [vet flags] [packages]",
29	Short:       "report likely mistakes in packages",
30	Long: `
31Vet runs the Go vet command on the packages named by the import paths.
32
33For more about vet and its flags, see 'go doc cmd/vet'.
34For more about specifying packages, see 'go help packages'.
35For a list of checkers and their flags, see 'go tool vet help'.
36For details of a specific checker such as 'printf', see 'go tool vet help printf'.
37
38The -vettool=prog flag selects a different analysis tool with alternative
39or additional checks.
40For example, the 'shadow' analyzer can be built and run using these commands:
41
42  go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest
43  go vet -vettool=$(which shadow)
44
45The build flags supported by go vet are those that control package resolution
46and execution, such as -C, -n, -x, -v, -tags, and -toolexec.
47For more about these flags, see 'go help build'.
48
49See also: go fmt, go fix.
50	`,
51}
52
53func runVet(ctx context.Context, cmd *base.Command, args []string) {
54	vetFlags, pkgArgs := vetFlags(args)
55	modload.InitWorkfile() // The vet command does custom flag processing; initialize workspaces after that.
56
57	if cfg.DebugTrace != "" {
58		var close func() error
59		var err error
60		ctx, close, err = trace.Start(ctx, cfg.DebugTrace)
61		if err != nil {
62			base.Fatalf("failed to start trace: %v", err)
63		}
64		defer func() {
65			if err := close(); err != nil {
66				base.Fatalf("failed to stop trace: %v", err)
67			}
68		}()
69	}
70
71	ctx, span := trace.StartSpan(ctx, fmt.Sprint("Running ", cmd.Name(), " command"))
72	defer span.Done()
73
74	work.BuildInit()
75	work.VetFlags = vetFlags
76	if len(vetFlags) > 0 {
77		work.VetExplicit = true
78	}
79	if vetTool != "" {
80		var err error
81		work.VetTool, err = filepath.Abs(vetTool)
82		if err != nil {
83			base.Fatalf("%v", err)
84		}
85	}
86
87	pkgOpts := load.PackageOpts{ModResolveTests: true}
88	pkgs := load.PackagesAndErrors(ctx, pkgOpts, pkgArgs)
89	load.CheckPackageErrors(pkgs)
90	if len(pkgs) == 0 {
91		base.Fatalf("no packages to vet")
92	}
93
94	b := work.NewBuilder("")
95	defer func() {
96		if err := b.Close(); err != nil {
97			base.Fatal(err)
98		}
99	}()
100
101	root := &work.Action{Mode: "go vet"}
102	for _, p := range pkgs {
103		_, ptest, pxtest, err := load.TestPackagesFor(ctx, pkgOpts, p, nil)
104		if err != nil {
105			base.Errorf("%v", err)
106			continue
107		}
108		if len(ptest.GoFiles) == 0 && len(ptest.CgoFiles) == 0 && pxtest == nil {
109			base.Errorf("go: can't vet %s: no Go files in %s", p.ImportPath, p.Dir)
110			continue
111		}
112		if len(ptest.GoFiles) > 0 || len(ptest.CgoFiles) > 0 {
113			root.Deps = append(root.Deps, b.VetAction(work.ModeBuild, work.ModeBuild, ptest))
114		}
115		if pxtest != nil {
116			root.Deps = append(root.Deps, b.VetAction(work.ModeBuild, work.ModeBuild, pxtest))
117		}
118	}
119	b.Do(ctx, root)
120}
121