1
2
3
4
5 package vet
6
7 import (
8 "bytes"
9 "encoding/json"
10 "errors"
11 "flag"
12 "fmt"
13 "log"
14 "os"
15 "os/exec"
16 "path/filepath"
17 "strings"
18
19 "cmd/go/internal/base"
20 "cmd/go/internal/cmdflag"
21 "cmd/go/internal/work"
22 )
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 var vetTool string
39
40 func init() {
41
42
43
44 work.AddBuildFlags(CmdVet, work.OmitJSONFlag)
45 CmdVet.Flag.StringVar(&vetTool, "vettool", "", "")
46 }
47
48 func parseVettoolFlag(args []string) {
49
50
51
52 for i, arg := range args {
53 if arg == "-vettool" || arg == "--vettool" {
54 if i+1 >= len(args) {
55 log.Fatalf("%s requires a filename", arg)
56 }
57 vetTool = args[i+1]
58 return
59 } else if strings.HasPrefix(arg, "-vettool=") ||
60 strings.HasPrefix(arg, "--vettool=") {
61 vetTool = arg[strings.IndexByte(arg, '=')+1:]
62 return
63 }
64 }
65 }
66
67
68
69 func vetFlags(args []string) (passToVet, packageNames []string) {
70 parseVettoolFlag(args)
71
72
73 var tool string
74 if vetTool == "" {
75 tool = base.Tool("vet")
76 } else {
77 var err error
78 tool, err = filepath.Abs(vetTool)
79 if err != nil {
80 log.Fatal(err)
81 }
82 }
83 out := new(bytes.Buffer)
84 vetcmd := exec.Command(tool, "-flags")
85 vetcmd.Stdout = out
86 if err := vetcmd.Run(); err != nil {
87 fmt.Fprintf(os.Stderr, "go: can't execute %s -flags: %v\n", tool, err)
88 base.SetExitStatus(2)
89 base.Exit()
90 }
91 var analysisFlags []struct {
92 Name string
93 Bool bool
94 Usage string
95 }
96 if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil {
97 fmt.Fprintf(os.Stderr, "go: can't unmarshal JSON from %s -flags: %v", tool, err)
98 base.SetExitStatus(2)
99 base.Exit()
100 }
101
102
103
104
105
106
107 isVetFlag := make(map[string]bool, len(analysisFlags))
108 cf := CmdVet.Flag
109 for _, f := range analysisFlags {
110 isVetFlag[f.Name] = true
111 if cf.Lookup(f.Name) == nil {
112 if f.Bool {
113 cf.Bool(f.Name, false, "")
114 } else {
115 cf.String(f.Name, "", "")
116 }
117 }
118 }
119
120
121
122 base.SetFromGOFLAGS(&CmdVet.Flag)
123 addFromGOFLAGS := map[string]bool{}
124 CmdVet.Flag.Visit(func(f *flag.Flag) {
125 if isVetFlag[f.Name] {
126 addFromGOFLAGS[f.Name] = true
127 }
128 })
129
130 explicitFlags := make([]string, 0, len(args))
131 for len(args) > 0 {
132 f, remainingArgs, err := cmdflag.ParseOne(&CmdVet.Flag, args)
133
134 if errors.Is(err, flag.ErrHelp) {
135 exitWithUsage()
136 }
137
138 if errors.Is(err, cmdflag.ErrFlagTerminator) {
139
140
141 packageNames = remainingArgs
142 break
143 }
144
145 if nf := (cmdflag.NonFlagError{}); errors.As(err, &nf) {
146
147
148 packageNames = args
149 break
150 }
151
152 if err != nil {
153 fmt.Fprintln(os.Stderr, err)
154 exitWithUsage()
155 }
156
157 if isVetFlag[f.Name] {
158
159
160 explicitFlags = append(explicitFlags, args[:len(args)-len(remainingArgs)]...)
161
162
163
164 delete(addFromGOFLAGS, f.Name)
165 }
166
167 args = remainingArgs
168 }
169
170
171 CmdVet.Flag.Visit(func(f *flag.Flag) {
172 if addFromGOFLAGS[f.Name] {
173 passToVet = append(passToVet, fmt.Sprintf("-%s=%s", f.Name, f.Value))
174 }
175 })
176 passToVet = append(passToVet, explicitFlags...)
177 return passToVet, packageNames
178 }
179
180 func exitWithUsage() {
181 fmt.Fprintf(os.Stderr, "usage: %s\n", CmdVet.UsageLine)
182 fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", CmdVet.LongName())
183
184
185 cmd := "go tool vet"
186 if vetTool != "" {
187 cmd = vetTool
188 }
189 fmt.Fprintf(os.Stderr, "Run '%s help' for a full list of flags and analyzers.\n", cmd)
190 fmt.Fprintf(os.Stderr, "Run '%s -help' for an overview.\n", cmd)
191
192 base.SetExitStatus(2)
193 base.Exit()
194 }
195
View as plain text