Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / go / analysis / internal / analysisflags / flags.go
1 // Copyright 2018 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 analysisflags defines helpers for processing flags of
6 // analysis driver tools.
7 package analysisflags
8
9 import (
10         "crypto/sha256"
11         "encoding/gob"
12         "encoding/json"
13         "flag"
14         "fmt"
15         "go/token"
16         "io"
17         "io/ioutil"
18         "log"
19         "os"
20         "strconv"
21         "strings"
22
23         "golang.org/x/tools/go/analysis"
24 )
25
26 // flags common to all {single,multi,unit}checkers.
27 var (
28         JSON    = false // -json
29         Context = -1    // -c=N: if N>0, display offending line plus N lines of context
30 )
31
32 // Parse creates a flag for each of the analyzer's flags,
33 // including (in multi mode) a flag named after the analyzer,
34 // parses the flags, then filters and returns the list of
35 // analyzers enabled by flags.
36 //
37 // The result is intended to be passed to unitchecker.Run or checker.Run.
38 // Use in unitchecker.Run will gob.Register all fact types for the returned
39 // graph of analyzers but of course not the ones only reachable from
40 // dropped analyzers. To avoid inconsistency about which gob types are
41 // registered from run to run, Parse itself gob.Registers all the facts
42 // only reachable from dropped analyzers.
43 // This is not a particularly elegant API, but this is an internal package.
44 func Parse(analyzers []*analysis.Analyzer, multi bool) []*analysis.Analyzer {
45         // Connect each analysis flag to the command line as -analysis.flag.
46         enabled := make(map[*analysis.Analyzer]*triState)
47         for _, a := range analyzers {
48                 var prefix string
49
50                 // Add -NAME flag to enable it.
51                 if multi {
52                         prefix = a.Name + "."
53
54                         enable := new(triState)
55                         enableUsage := "enable " + a.Name + " analysis"
56                         flag.Var(enable, a.Name, enableUsage)
57                         enabled[a] = enable
58                 }
59
60                 a.Flags.VisitAll(func(f *flag.Flag) {
61                         if !multi && flag.Lookup(f.Name) != nil {
62                                 log.Printf("%s flag -%s would conflict with driver; skipping", a.Name, f.Name)
63                                 return
64                         }
65
66                         name := prefix + f.Name
67                         flag.Var(f.Value, name, f.Usage)
68                 })
69         }
70
71         // standard flags: -flags, -V.
72         printflags := flag.Bool("flags", false, "print analyzer flags in JSON")
73         addVersionFlag()
74
75         // flags common to all checkers
76         flag.BoolVar(&JSON, "json", JSON, "emit JSON output")
77         flag.IntVar(&Context, "c", Context, `display offending line with this many lines of context`)
78
79         // Add shims for legacy vet flags to enable existing
80         // scripts that run vet to continue to work.
81         _ = flag.Bool("source", false, "no effect (deprecated)")
82         _ = flag.Bool("v", false, "no effect (deprecated)")
83         _ = flag.Bool("all", false, "no effect (deprecated)")
84         _ = flag.String("tags", "", "no effect (deprecated)")
85         for old, new := range vetLegacyFlags {
86                 newFlag := flag.Lookup(new)
87                 if newFlag != nil && flag.Lookup(old) == nil {
88                         flag.Var(newFlag.Value, old, "deprecated alias for -"+new)
89                 }
90         }
91
92         flag.Parse() // (ExitOnError)
93
94         // -flags: print flags so that go vet knows which ones are legitimate.
95         if *printflags {
96                 printFlags()
97                 os.Exit(0)
98         }
99
100         everything := expand(analyzers)
101
102         // If any -NAME flag is true,  run only those analyzers. Otherwise,
103         // if any -NAME flag is false, run all but those analyzers.
104         if multi {
105                 var hasTrue, hasFalse bool
106                 for _, ts := range enabled {
107                         switch *ts {
108                         case setTrue:
109                                 hasTrue = true
110                         case setFalse:
111                                 hasFalse = true
112                         }
113                 }
114
115                 var keep []*analysis.Analyzer
116                 if hasTrue {
117                         for _, a := range analyzers {
118                                 if *enabled[a] == setTrue {
119                                         keep = append(keep, a)
120                                 }
121                         }
122                         analyzers = keep
123                 } else if hasFalse {
124                         for _, a := range analyzers {
125                                 if *enabled[a] != setFalse {
126                                         keep = append(keep, a)
127                                 }
128                         }
129                         analyzers = keep
130                 }
131         }
132
133         // Register fact types of skipped analyzers
134         // in case we encounter them in imported files.
135         kept := expand(analyzers)
136         for a := range everything {
137                 if !kept[a] {
138                         for _, f := range a.FactTypes {
139                                 gob.Register(f)
140                         }
141                 }
142         }
143
144         return analyzers
145 }
146
147 func expand(analyzers []*analysis.Analyzer) map[*analysis.Analyzer]bool {
148         seen := make(map[*analysis.Analyzer]bool)
149         var visitAll func([]*analysis.Analyzer)
150         visitAll = func(analyzers []*analysis.Analyzer) {
151                 for _, a := range analyzers {
152                         if !seen[a] {
153                                 seen[a] = true
154                                 visitAll(a.Requires)
155                         }
156                 }
157         }
158         visitAll(analyzers)
159         return seen
160 }
161
162 func printFlags() {
163         type jsonFlag struct {
164                 Name  string
165                 Bool  bool
166                 Usage string
167         }
168         var flags []jsonFlag = nil
169         flag.VisitAll(func(f *flag.Flag) {
170                 // Don't report {single,multi}checker debugging
171                 // flags or fix as these have no effect on unitchecker
172                 // (as invoked by 'go vet').
173                 switch f.Name {
174                 case "debug", "cpuprofile", "memprofile", "trace", "fix":
175                         return
176                 }
177
178                 b, ok := f.Value.(interface{ IsBoolFlag() bool })
179                 isBool := ok && b.IsBoolFlag()
180                 flags = append(flags, jsonFlag{f.Name, isBool, f.Usage})
181         })
182         data, err := json.MarshalIndent(flags, "", "\t")
183         if err != nil {
184                 log.Fatal(err)
185         }
186         os.Stdout.Write(data)
187 }
188
189 // addVersionFlag registers a -V flag that, if set,
190 // prints the executable version and exits 0.
191 //
192 // If the -V flag already exists â€” for example, because it was already
193 // registered by a call to cmd/internal/objabi.AddVersionFlag â€” then
194 // addVersionFlag does nothing.
195 func addVersionFlag() {
196         if flag.Lookup("V") == nil {
197                 flag.Var(versionFlag{}, "V", "print version and exit")
198         }
199 }
200
201 // versionFlag minimally complies with the -V protocol required by "go vet".
202 type versionFlag struct{}
203
204 func (versionFlag) IsBoolFlag() bool { return true }
205 func (versionFlag) Get() interface{} { return nil }
206 func (versionFlag) String() string   { return "" }
207 func (versionFlag) Set(s string) error {
208         if s != "full" {
209                 log.Fatalf("unsupported flag value: -V=%s", s)
210         }
211
212         // This replicates the minimal subset of
213         // cmd/internal/objabi.AddVersionFlag, which is private to the
214         // go tool yet forms part of our command-line interface.
215         // TODO(adonovan): clarify the contract.
216
217         // Print the tool version so the build system can track changes.
218         // Formats:
219         //   $progname version devel ... buildID=...
220         //   $progname version go1.9.1
221         progname := os.Args[0]
222         f, err := os.Open(progname)
223         if err != nil {
224                 log.Fatal(err)
225         }
226         h := sha256.New()
227         if _, err := io.Copy(h, f); err != nil {
228                 log.Fatal(err)
229         }
230         f.Close()
231         fmt.Printf("%s version devel comments-go-here buildID=%02x\n",
232                 progname, string(h.Sum(nil)))
233         os.Exit(0)
234         return nil
235 }
236
237 // A triState is a boolean that knows whether
238 // it has been set to either true or false.
239 // It is used to identify whether a flag appears;
240 // the standard boolean flag cannot
241 // distinguish missing from unset.
242 // It also satisfies flag.Value.
243 type triState int
244
245 const (
246         unset triState = iota
247         setTrue
248         setFalse
249 )
250
251 func triStateFlag(name string, value triState, usage string) *triState {
252         flag.Var(&value, name, usage)
253         return &value
254 }
255
256 // triState implements flag.Value, flag.Getter, and flag.boolFlag.
257 // They work like boolean flags: we can say vet -printf as well as vet -printf=true
258 func (ts *triState) Get() interface{} {
259         return *ts == setTrue
260 }
261
262 func (ts triState) isTrue() bool {
263         return ts == setTrue
264 }
265
266 func (ts *triState) Set(value string) error {
267         b, err := strconv.ParseBool(value)
268         if err != nil {
269                 // This error message looks poor but package "flag" adds
270                 // "invalid boolean value %q for -NAME: %s"
271                 return fmt.Errorf("want true or false")
272         }
273         if b {
274                 *ts = setTrue
275         } else {
276                 *ts = setFalse
277         }
278         return nil
279 }
280
281 func (ts *triState) String() string {
282         switch *ts {
283         case unset:
284                 return "true"
285         case setTrue:
286                 return "true"
287         case setFalse:
288                 return "false"
289         }
290         panic("not reached")
291 }
292
293 func (ts triState) IsBoolFlag() bool {
294         return true
295 }
296
297 // Legacy flag support
298
299 // vetLegacyFlags maps flags used by legacy vet to their corresponding
300 // new names. The old names will continue to work.
301 var vetLegacyFlags = map[string]string{
302         // Analyzer name changes
303         "bool":       "bools",
304         "buildtags":  "buildtag",
305         "methods":    "stdmethods",
306         "rangeloops": "loopclosure",
307
308         // Analyzer flags
309         "compositewhitelist":  "composites.whitelist",
310         "printfuncs":          "printf.funcs",
311         "shadowstrict":        "shadow.strict",
312         "unusedfuncs":         "unusedresult.funcs",
313         "unusedstringmethods": "unusedresult.stringmethods",
314 }
315
316 // ---- output helpers common to all drivers ----
317
318 // PrintPlain prints a diagnostic in plain text form,
319 // with context specified by the -c flag.
320 func PrintPlain(fset *token.FileSet, diag analysis.Diagnostic) {
321         posn := fset.Position(diag.Pos)
322         fmt.Fprintf(os.Stderr, "%s: %s\n", posn, diag.Message)
323
324         // -c=N: show offending line plus N lines of context.
325         if Context >= 0 {
326                 posn := fset.Position(diag.Pos)
327                 end := fset.Position(diag.End)
328                 if !end.IsValid() {
329                         end = posn
330                 }
331                 data, _ := ioutil.ReadFile(posn.Filename)
332                 lines := strings.Split(string(data), "\n")
333                 for i := posn.Line - Context; i <= end.Line+Context; i++ {
334                         if 1 <= i && i <= len(lines) {
335                                 fmt.Fprintf(os.Stderr, "%d\t%s\n", i, lines[i-1])
336                         }
337                 }
338         }
339 }
340
341 // A JSONTree is a mapping from package ID to analysis name to result.
342 // Each result is either a jsonError or a list of jsonDiagnostic.
343 type JSONTree map[string]map[string]interface{}
344
345 // Add adds the result of analysis 'name' on package 'id'.
346 // The result is either a list of diagnostics or an error.
347 func (tree JSONTree) Add(fset *token.FileSet, id, name string, diags []analysis.Diagnostic, err error) {
348         var v interface{}
349         if err != nil {
350                 type jsonError struct {
351                         Err string `json:"error"`
352                 }
353                 v = jsonError{err.Error()}
354         } else if len(diags) > 0 {
355                 type jsonDiagnostic struct {
356                         Category string `json:"category,omitempty"`
357                         Posn     string `json:"posn"`
358                         Message  string `json:"message"`
359                 }
360                 var diagnostics []jsonDiagnostic
361                 // TODO(matloob): Should the JSON diagnostics contain ranges?
362                 // If so, how should they be formatted?
363                 for _, f := range diags {
364                         diagnostics = append(diagnostics, jsonDiagnostic{
365                                 Category: f.Category,
366                                 Posn:     fset.Position(f.Pos).String(),
367                                 Message:  f.Message,
368                         })
369                 }
370                 v = diagnostics
371         }
372         if v != nil {
373                 m, ok := tree[id]
374                 if !ok {
375                         m = make(map[string]interface{})
376                         tree[id] = m
377                 }
378                 m[name] = v
379         }
380 }
381
382 func (tree JSONTree) Print() {
383         data, err := json.MarshalIndent(tree, "", "\t")
384         if err != nil {
385                 log.Panicf("internal error: JSON marshaling failed: %v", err)
386         }
387         fmt.Printf("%s\n", data)
388 }