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 var (
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 toolFlag string
41 diffFlag bool
42 jsonFlag bool
43 contextFlag = -1
44 )
45
46 func addFlags(cmd *base.Command) {
47
48
49 work.AddBuildFlags(cmd, work.OmitJSONFlag)
50
51 cmd.Flag.StringVar(&toolFlag, cmd.Name()+"tool", "", "")
52 cmd.Flag.BoolVar(&diffFlag, "diff", false, "print diff instead of applying it")
53 cmd.Flag.BoolVar(&jsonFlag, "json", false, "print diagnostics and fixes as JSON")
54 cmd.Flag.IntVar(&contextFlag, "c", -1, "display offending line with this many lines of context")
55 }
56
57
58 func parseToolFlag(cmd *base.Command, args []string) string {
59 toolFlagName := cmd.Name() + "tool"
60
61
62
63
64 for i, arg := range args {
65 if arg == "-"+toolFlagName || arg == "--"+toolFlagName {
66 if i+1 >= len(args) {
67 log.Fatalf("%s requires a filename", arg)
68 }
69 toolFlag = args[i+1]
70 break
71 } else if strings.HasPrefix(arg, "-"+toolFlagName+"=") ||
72 strings.HasPrefix(arg, "--"+toolFlagName+"=") {
73 toolFlag = arg[strings.IndexByte(arg, '=')+1:]
74 break
75 }
76 }
77
78 if toolFlag != "" {
79 tool, err := filepath.Abs(toolFlag)
80 if err != nil {
81 log.Fatal(err)
82 }
83 return tool
84 }
85
86 return base.Tool(cmd.Name())
87 }
88
89
90
91 func toolFlags(cmd *base.Command, args []string) (passToTool, packageNames []string) {
92 tool := parseToolFlag(cmd, args)
93 work.VetTool = tool
94
95
96 out := new(bytes.Buffer)
97 toolcmd := exec.Command(tool, "-flags")
98 toolcmd.Stdout = out
99 if err := toolcmd.Run(); err != nil {
100 fmt.Fprintf(os.Stderr, "go: %s -flags failed: %v\n", tool, err)
101 base.SetExitStatus(2)
102 base.Exit()
103 }
104 var analysisFlags []struct {
105 Name string
106 Bool bool
107 Usage string
108 }
109 if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil {
110 fmt.Fprintf(os.Stderr, "go: can't unmarshal JSON from %s -flags: %v", tool, err)
111 base.SetExitStatus(2)
112 base.Exit()
113 }
114
115
116
117
118
119
120 isToolFlag := make(map[string]bool, len(analysisFlags))
121 cf := cmd.Flag
122 for _, f := range analysisFlags {
123
124
125 if f.Name == "c" {
126 continue
127 }
128 isToolFlag[f.Name] = true
129 if cf.Lookup(f.Name) == nil {
130 if f.Bool {
131 cf.Bool(f.Name, false, "")
132 } else {
133 cf.String(f.Name, "", "")
134 }
135 }
136 }
137
138
139
140 base.SetFromGOFLAGS(&cmd.Flag)
141 addFromGOFLAGS := map[string]bool{}
142 cmd.Flag.Visit(func(f *flag.Flag) {
143 if isToolFlag[f.Name] {
144 addFromGOFLAGS[f.Name] = true
145 }
146 })
147
148 explicitFlags := make([]string, 0, len(args))
149 for len(args) > 0 {
150 f, remainingArgs, err := cmdflag.ParseOne(&cmd.Flag, args)
151
152 if errors.Is(err, flag.ErrHelp) {
153 exitWithUsage(cmd)
154 }
155
156 if errors.Is(err, cmdflag.ErrFlagTerminator) {
157
158
159 packageNames = remainingArgs
160 break
161 }
162
163 if _, ok := errors.AsType[cmdflag.NonFlagError](err); ok {
164
165
166 packageNames = args
167 break
168 }
169
170 if err != nil {
171 fmt.Fprintln(os.Stderr, err)
172 exitWithUsage(cmd)
173 }
174
175 if isToolFlag[f.Name] {
176
177
178 explicitFlags = append(explicitFlags, args[:len(args)-len(remainingArgs)]...)
179
180
181
182 delete(addFromGOFLAGS, f.Name)
183 }
184
185 args = remainingArgs
186 }
187
188
189 cmd.Flag.Visit(func(f *flag.Flag) {
190 if addFromGOFLAGS[f.Name] {
191 passToTool = append(passToTool, fmt.Sprintf("-%s=%s", f.Name, f.Value))
192 }
193 })
194 passToTool = append(passToTool, explicitFlags...)
195 return passToTool, packageNames
196 }
197
198 func exitWithUsage(cmd *base.Command) {
199 fmt.Fprintf(os.Stderr, "usage: %s\n", cmd.UsageLine)
200 fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", cmd.LongName())
201
202
203 tool := toolFlag
204 if tool == "" {
205 tool = "go tool " + cmd.Name()
206 }
207 fmt.Fprintf(os.Stderr, "Run '%s help' for a full list of flags and analyzers.\n", tool)
208 fmt.Fprintf(os.Stderr, "Run '%s -help' for an overview.\n", tool)
209
210 base.SetExitStatus(2)
211 base.Exit()
212 }
213
View as plain text