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