Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.0.1-2020.1.5 / lint / lintutil / util.go
1 // Copyright (c) 2013 The Go Authors. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd.
6
7 // Package lintutil provides helpers for writing linter command lines.
8 package lintutil // import "honnef.co/go/tools/lint/lintutil"
9
10 import (
11         "crypto/sha256"
12         "errors"
13         "flag"
14         "fmt"
15         "go/build"
16         "go/token"
17         "io"
18         "log"
19         "os"
20         "os/signal"
21         "regexp"
22         "runtime"
23         "runtime/pprof"
24         "strconv"
25         "strings"
26         "sync"
27         "sync/atomic"
28         "time"
29
30         "honnef.co/go/tools/config"
31         "honnef.co/go/tools/internal/cache"
32         "honnef.co/go/tools/lint"
33         "honnef.co/go/tools/lint/lintutil/format"
34         "honnef.co/go/tools/version"
35
36         "golang.org/x/tools/go/analysis"
37         "golang.org/x/tools/go/buildutil"
38         "golang.org/x/tools/go/packages"
39 )
40
41 func NewVersionFlag() flag.Getter {
42         tags := build.Default.ReleaseTags
43         v := tags[len(tags)-1][2:]
44         version := new(VersionFlag)
45         if err := version.Set(v); err != nil {
46                 panic(fmt.Sprintf("internal error: %s", err))
47         }
48         return version
49 }
50
51 type VersionFlag int
52
53 func (v *VersionFlag) String() string {
54         return fmt.Sprintf("1.%d", *v)
55
56 }
57
58 func (v *VersionFlag) Set(s string) error {
59         if len(s) < 3 {
60                 return errors.New("invalid Go version")
61         }
62         if s[0] != '1' {
63                 return errors.New("invalid Go version")
64         }
65         if s[1] != '.' {
66                 return errors.New("invalid Go version")
67         }
68         i, err := strconv.Atoi(s[2:])
69         *v = VersionFlag(i)
70         return err
71 }
72
73 func (v *VersionFlag) Get() interface{} {
74         return int(*v)
75 }
76
77 func usage(name string, flags *flag.FlagSet) func() {
78         return func() {
79                 fmt.Fprintf(os.Stderr, "Usage of %s:\n", name)
80                 fmt.Fprintf(os.Stderr, "\t%s [flags] # runs on package in current directory\n", name)
81                 fmt.Fprintf(os.Stderr, "\t%s [flags] packages\n", name)
82                 fmt.Fprintf(os.Stderr, "\t%s [flags] directory\n", name)
83                 fmt.Fprintf(os.Stderr, "\t%s [flags] files... # must be a single package\n", name)
84                 fmt.Fprintf(os.Stderr, "Flags:\n")
85                 flags.PrintDefaults()
86         }
87 }
88
89 type list []string
90
91 func (list *list) String() string {
92         return `"` + strings.Join(*list, ",") + `"`
93 }
94
95 func (list *list) Set(s string) error {
96         if s == "" {
97                 *list = nil
98                 return nil
99         }
100
101         *list = strings.Split(s, ",")
102         return nil
103 }
104
105 func FlagSet(name string) *flag.FlagSet {
106         flags := flag.NewFlagSet("", flag.ExitOnError)
107         flags.Usage = usage(name, flags)
108         flags.String("tags", "", "List of `build tags`")
109         flags.Bool("tests", true, "Include tests")
110         flags.Bool("version", false, "Print version and exit")
111         flags.Bool("show-ignored", false, "Don't filter ignored problems")
112         flags.String("f", "text", "Output `format` (valid choices are 'stylish', 'text' and 'json')")
113         flags.String("explain", "", "Print description of `check`")
114
115         flags.String("debug.cpuprofile", "", "Write CPU profile to `file`")
116         flags.String("debug.memprofile", "", "Write memory profile to `file`")
117         flags.Bool("debug.version", false, "Print detailed version information about this program")
118         flags.Bool("debug.no-compile-errors", false, "Don't print compile errors")
119         flags.String("debug.measure-analyzers", "", "Write analysis measurements to `file`. `file` will be opened for appending if it already exists.")
120         flags.Uint("debug.repeat-analyzers", 0, "Run analyzers `num` times")
121
122         checks := list{"inherit"}
123         fail := list{"all"}
124         flags.Var(&checks, "checks", "Comma-separated list of `checks` to enable.")
125         flags.Var(&fail, "fail", "Comma-separated list of `checks` that can cause a non-zero exit status.")
126
127         tags := build.Default.ReleaseTags
128         v := tags[len(tags)-1][2:]
129         version := new(VersionFlag)
130         if err := version.Set(v); err != nil {
131                 panic(fmt.Sprintf("internal error: %s", err))
132         }
133
134         flags.Var(version, "go", "Target Go `version` in the format '1.x'")
135         return flags
136 }
137
138 func findCheck(cs []*analysis.Analyzer, check string) (*analysis.Analyzer, bool) {
139         for _, c := range cs {
140                 if c.Name == check {
141                         return c, true
142                 }
143         }
144         return nil, false
145 }
146
147 func ProcessFlagSet(cs []*analysis.Analyzer, cums []lint.CumulativeChecker, fs *flag.FlagSet) {
148         tags := fs.Lookup("tags").Value.(flag.Getter).Get().(string)
149         tests := fs.Lookup("tests").Value.(flag.Getter).Get().(bool)
150         goVersion := fs.Lookup("go").Value.(flag.Getter).Get().(int)
151         formatter := fs.Lookup("f").Value.(flag.Getter).Get().(string)
152         printVersion := fs.Lookup("version").Value.(flag.Getter).Get().(bool)
153         showIgnored := fs.Lookup("show-ignored").Value.(flag.Getter).Get().(bool)
154         explain := fs.Lookup("explain").Value.(flag.Getter).Get().(string)
155
156         cpuProfile := fs.Lookup("debug.cpuprofile").Value.(flag.Getter).Get().(string)
157         memProfile := fs.Lookup("debug.memprofile").Value.(flag.Getter).Get().(string)
158         debugVersion := fs.Lookup("debug.version").Value.(flag.Getter).Get().(bool)
159         debugNoCompile := fs.Lookup("debug.no-compile-errors").Value.(flag.Getter).Get().(bool)
160         debugRepeat := fs.Lookup("debug.repeat-analyzers").Value.(flag.Getter).Get().(uint)
161
162         var measureAnalyzers func(analysis *analysis.Analyzer, pkg *lint.Package, d time.Duration)
163         if path := fs.Lookup("debug.measure-analyzers").Value.(flag.Getter).Get().(string); path != "" {
164                 f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
165                 if err != nil {
166                         log.Fatal(err)
167                 }
168
169                 mu := &sync.Mutex{}
170                 measureAnalyzers = func(analysis *analysis.Analyzer, pkg *lint.Package, d time.Duration) {
171                         mu.Lock()
172                         defer mu.Unlock()
173                         if _, err := fmt.Fprintf(f, "%s\t%s\t%d\n", analysis.Name, pkg.ID, d.Nanoseconds()); err != nil {
174                                 log.Println("error writing analysis measurements:", err)
175                         }
176                 }
177         }
178
179         cfg := config.Config{}
180         cfg.Checks = *fs.Lookup("checks").Value.(*list)
181
182         exit := func(code int) {
183                 if cpuProfile != "" {
184                         pprof.StopCPUProfile()
185                 }
186                 if memProfile != "" {
187                         f, err := os.Create(memProfile)
188                         if err != nil {
189                                 panic(err)
190                         }
191                         runtime.GC()
192                         pprof.WriteHeapProfile(f)
193                 }
194                 os.Exit(code)
195         }
196         if cpuProfile != "" {
197                 f, err := os.Create(cpuProfile)
198                 if err != nil {
199                         log.Fatal(err)
200                 }
201                 pprof.StartCPUProfile(f)
202         }
203
204         if debugVersion {
205                 version.Verbose()
206                 exit(0)
207         }
208
209         if printVersion {
210                 version.Print()
211                 exit(0)
212         }
213
214         // Validate that the tags argument is well-formed. go/packages
215         // doesn't detect malformed build flags and returns unhelpful
216         // errors.
217         tf := buildutil.TagsFlag{}
218         if err := tf.Set(tags); err != nil {
219                 fmt.Fprintln(os.Stderr, fmt.Errorf("invalid value %q for flag -tags: %s", tags, err))
220                 exit(1)
221         }
222
223         if explain != "" {
224                 var haystack []*analysis.Analyzer
225                 haystack = append(haystack, cs...)
226                 for _, cum := range cums {
227                         haystack = append(haystack, cum.Analyzer())
228                 }
229                 check, ok := findCheck(haystack, explain)
230                 if !ok {
231                         fmt.Fprintln(os.Stderr, "Couldn't find check", explain)
232                         exit(1)
233                 }
234                 if check.Doc == "" {
235                         fmt.Fprintln(os.Stderr, explain, "has no documentation")
236                         exit(1)
237                 }
238                 fmt.Println(check.Doc)
239                 exit(0)
240         }
241
242         ps, err := Lint(cs, cums, fs.Args(), &Options{
243                 Tags:                     tags,
244                 LintTests:                tests,
245                 GoVersion:                goVersion,
246                 Config:                   cfg,
247                 PrintAnalyzerMeasurement: measureAnalyzers,
248                 RepeatAnalyzers:          debugRepeat,
249         })
250         if err != nil {
251                 fmt.Fprintln(os.Stderr, err)
252                 exit(1)
253         }
254
255         var f format.Formatter
256         switch formatter {
257         case "text":
258                 f = format.Text{W: os.Stdout}
259         case "stylish":
260                 f = &format.Stylish{W: os.Stdout}
261         case "json":
262                 f = format.JSON{W: os.Stdout}
263         default:
264                 fmt.Fprintf(os.Stderr, "unsupported output format %q\n", formatter)
265                 exit(2)
266         }
267
268         var (
269                 total    int
270                 errors   int
271                 warnings int
272                 ignored  int
273         )
274
275         fail := *fs.Lookup("fail").Value.(*list)
276         analyzers := make([]*analysis.Analyzer, len(cs), len(cs)+len(cums))
277         copy(analyzers, cs)
278         for _, cum := range cums {
279                 analyzers = append(analyzers, cum.Analyzer())
280         }
281         shouldExit := lint.FilterChecks(analyzers, fail)
282         shouldExit["compile"] = true
283
284         total = len(ps)
285         for _, p := range ps {
286                 if p.Check == "compile" && debugNoCompile {
287                         continue
288                 }
289                 if p.Severity == lint.Ignored && !showIgnored {
290                         ignored++
291                         continue
292                 }
293                 if shouldExit[p.Check] {
294                         errors++
295                 } else {
296                         p.Severity = lint.Warning
297                         warnings++
298                 }
299                 f.Format(p)
300         }
301         if f, ok := f.(format.Statter); ok {
302                 f.Stats(total, errors, warnings, ignored)
303         }
304         if errors > 0 {
305                 exit(1)
306         }
307         exit(0)
308 }
309
310 type Options struct {
311         Config config.Config
312
313         Tags                     string
314         LintTests                bool
315         GoVersion                int
316         PrintAnalyzerMeasurement func(analysis *analysis.Analyzer, pkg *lint.Package, d time.Duration)
317         RepeatAnalyzers          uint
318 }
319
320 func computeSalt() ([]byte, error) {
321         if version.Version != "devel" {
322                 return []byte(version.Version), nil
323         }
324         p, err := os.Executable()
325         if err != nil {
326                 return nil, err
327         }
328         f, err := os.Open(p)
329         if err != nil {
330                 return nil, err
331         }
332         defer f.Close()
333         h := sha256.New()
334         if _, err := io.Copy(h, f); err != nil {
335                 return nil, err
336         }
337         return h.Sum(nil), nil
338 }
339
340 func Lint(cs []*analysis.Analyzer, cums []lint.CumulativeChecker, paths []string, opt *Options) ([]lint.Problem, error) {
341         salt, err := computeSalt()
342         if err != nil {
343                 return nil, fmt.Errorf("could not compute salt for cache: %s", err)
344         }
345         cache.SetSalt(salt)
346
347         if opt == nil {
348                 opt = &Options{}
349         }
350
351         l := &lint.Linter{
352                 Checkers:           cs,
353                 CumulativeCheckers: cums,
354                 GoVersion:          opt.GoVersion,
355                 Config:             opt.Config,
356                 RepeatAnalyzers:    opt.RepeatAnalyzers,
357         }
358         l.Stats.PrintAnalyzerMeasurement = opt.PrintAnalyzerMeasurement
359         cfg := &packages.Config{}
360         if opt.LintTests {
361                 cfg.Tests = true
362         }
363         if opt.Tags != "" {
364                 cfg.BuildFlags = append(cfg.BuildFlags, "-tags", opt.Tags)
365         }
366
367         printStats := func() {
368                 // Individual stats are read atomically, but overall there
369                 // is no synchronisation. For printing rough progress
370                 // information, this doesn't matter.
371                 switch atomic.LoadUint32(&l.Stats.State) {
372                 case lint.StateInitializing:
373                         fmt.Fprintln(os.Stderr, "Status: initializing")
374                 case lint.StateGraph:
375                         fmt.Fprintln(os.Stderr, "Status: loading package graph")
376                 case lint.StateProcessing:
377                         fmt.Fprintf(os.Stderr, "Packages: %d/%d initial, %d/%d total; Workers: %d/%d; Problems: %d\n",
378                                 atomic.LoadUint32(&l.Stats.ProcessedInitialPackages),
379                                 atomic.LoadUint32(&l.Stats.InitialPackages),
380                                 atomic.LoadUint32(&l.Stats.ProcessedPackages),
381                                 atomic.LoadUint32(&l.Stats.TotalPackages),
382                                 atomic.LoadUint32(&l.Stats.ActiveWorkers),
383                                 atomic.LoadUint32(&l.Stats.TotalWorkers),
384                                 atomic.LoadUint32(&l.Stats.Problems),
385                         )
386                 case lint.StateCumulative:
387                         fmt.Fprintln(os.Stderr, "Status: processing cumulative checkers")
388                 }
389         }
390         if len(infoSignals) > 0 {
391                 ch := make(chan os.Signal, 1)
392                 signal.Notify(ch, infoSignals...)
393                 defer signal.Stop(ch)
394                 go func() {
395                         for range ch {
396                                 printStats()
397                         }
398                 }()
399         }
400
401         ps, err := l.Lint(cfg, paths)
402         return ps, err
403 }
404
405 var posRe = regexp.MustCompile(`^(.+?):(\d+)(?::(\d+)?)?$`)
406
407 func parsePos(pos string) token.Position {
408         if pos == "-" || pos == "" {
409                 return token.Position{}
410         }
411         parts := posRe.FindStringSubmatch(pos)
412         if parts == nil {
413                 panic(fmt.Sprintf("internal error: malformed position %q", pos))
414         }
415         file := parts[1]
416         line, _ := strconv.Atoi(parts[2])
417         col, _ := strconv.Atoi(parts[3])
418         return token.Position{
419                 Filename: file,
420                 Line:     line,
421                 Column:   col,
422         }
423 }
424
425 func InitializeAnalyzers(docs map[string]*lint.Documentation, analyzers map[string]*analysis.Analyzer) map[string]*analysis.Analyzer {
426         out := make(map[string]*analysis.Analyzer, len(analyzers))
427         for k, v := range analyzers {
428                 vc := *v
429                 out[k] = &vc
430
431                 vc.Name = k
432                 doc, ok := docs[k]
433                 if !ok {
434                         panic(fmt.Sprintf("missing documentation for check %s", k))
435                 }
436                 vc.Doc = doc.String()
437                 if vc.Flags.Usage == nil {
438                         fs := flag.NewFlagSet("", flag.PanicOnError)
439                         fs.Var(NewVersionFlag(), "go", "Target Go version")
440                         vc.Flags = *fs
441                 }
442         }
443         return out
444 }