aboutsummaryrefslogtreecommitdiff
path: root/vendor/honnef.co/go/tools/lint/lintutil/util.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/honnef.co/go/tools/lint/lintutil/util.go')
-rw-r--r--vendor/honnef.co/go/tools/lint/lintutil/util.go392
1 files changed, 392 insertions, 0 deletions
diff --git a/vendor/honnef.co/go/tools/lint/lintutil/util.go b/vendor/honnef.co/go/tools/lint/lintutil/util.go
new file mode 100644
index 0000000..fe0279f
--- /dev/null
+++ b/vendor/honnef.co/go/tools/lint/lintutil/util.go
@@ -0,0 +1,392 @@
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.
8package lintutil // import "honnef.co/go/tools/lint/lintutil"
9
10import (
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/atomic"
27
28 "honnef.co/go/tools/config"
29 "honnef.co/go/tools/internal/cache"
30 "honnef.co/go/tools/lint"
31 "honnef.co/go/tools/lint/lintutil/format"
32 "honnef.co/go/tools/version"
33
34 "golang.org/x/tools/go/analysis"
35 "golang.org/x/tools/go/buildutil"
36 "golang.org/x/tools/go/packages"
37)
38
39func NewVersionFlag() flag.Getter {
40 tags := build.Default.ReleaseTags
41 v := tags[len(tags)-1][2:]
42 version := new(VersionFlag)
43 if err := version.Set(v); err != nil {
44 panic(fmt.Sprintf("internal error: %s", err))
45 }
46 return version
47}
48
49type VersionFlag int
50
51func (v *VersionFlag) String() string {
52 return fmt.Sprintf("1.%d", *v)
53
54}
55
56func (v *VersionFlag) Set(s string) error {
57 if len(s) < 3 {
58 return errors.New("invalid Go version")
59 }
60 if s[0] != '1' {
61 return errors.New("invalid Go version")
62 }
63 if s[1] != '.' {
64 return errors.New("invalid Go version")
65 }
66 i, err := strconv.Atoi(s[2:])
67 *v = VersionFlag(i)
68 return err
69}
70
71func (v *VersionFlag) Get() interface{} {
72 return int(*v)
73}
74
75func usage(name string, flags *flag.FlagSet) func() {
76 return func() {
77 fmt.Fprintf(os.Stderr, "Usage of %s:\n", name)
78 fmt.Fprintf(os.Stderr, "\t%s [flags] # runs on package in current directory\n", name)
79 fmt.Fprintf(os.Stderr, "\t%s [flags] packages\n", name)
80 fmt.Fprintf(os.Stderr, "\t%s [flags] directory\n", name)
81 fmt.Fprintf(os.Stderr, "\t%s [flags] files... # must be a single package\n", name)
82 fmt.Fprintf(os.Stderr, "Flags:\n")
83 flags.PrintDefaults()
84 }
85}
86
87type list []string
88
89func (list *list) String() string {
90 return `"` + strings.Join(*list, ",") + `"`
91}
92
93func (list *list) Set(s string) error {
94 if s == "" {
95 *list = nil
96 return nil
97 }
98
99 *list = strings.Split(s, ",")
100 return nil
101}
102
103func FlagSet(name string) *flag.FlagSet {
104 flags := flag.NewFlagSet("", flag.ExitOnError)
105 flags.Usage = usage(name, flags)
106 flags.String("tags", "", "List of `build tags`")
107 flags.Bool("tests", true, "Include tests")
108 flags.Bool("version", false, "Print version and exit")
109 flags.Bool("show-ignored", false, "Don't filter ignored problems")
110 flags.String("f", "text", "Output `format` (valid choices are 'stylish', 'text' and 'json')")
111 flags.String("explain", "", "Print description of `check`")
112
113 flags.String("debug.cpuprofile", "", "Write CPU profile to `file`")
114 flags.String("debug.memprofile", "", "Write memory profile to `file`")
115 flags.Bool("debug.version", false, "Print detailed version information about this program")
116 flags.Bool("debug.no-compile-errors", false, "Don't print compile errors")
117
118 checks := list{"inherit"}
119 fail := list{"all"}
120 flags.Var(&checks, "checks", "Comma-separated list of `checks` to enable.")
121 flags.Var(&fail, "fail", "Comma-separated list of `checks` that can cause a non-zero exit status.")
122
123 tags := build.Default.ReleaseTags
124 v := tags[len(tags)-1][2:]
125 version := new(VersionFlag)
126 if err := version.Set(v); err != nil {
127 panic(fmt.Sprintf("internal error: %s", err))
128 }
129
130 flags.Var(version, "go", "Target Go `version` in the format '1.x'")
131 return flags
132}
133
134func findCheck(cs []*analysis.Analyzer, check string) (*analysis.Analyzer, bool) {
135 for _, c := range cs {
136 if c.Name == check {
137 return c, true
138 }
139 }
140 return nil, false
141}
142
143func ProcessFlagSet(cs []*analysis.Analyzer, cums []lint.CumulativeChecker, fs *flag.FlagSet) {
144 tags := fs.Lookup("tags").Value.(flag.Getter).Get().(string)
145 tests := fs.Lookup("tests").Value.(flag.Getter).Get().(bool)
146 goVersion := fs.Lookup("go").Value.(flag.Getter).Get().(int)
147 formatter := fs.Lookup("f").Value.(flag.Getter).Get().(string)
148 printVersion := fs.Lookup("version").Value.(flag.Getter).Get().(bool)
149 showIgnored := fs.Lookup("show-ignored").Value.(flag.Getter).Get().(bool)
150 explain := fs.Lookup("explain").Value.(flag.Getter).Get().(string)
151
152 cpuProfile := fs.Lookup("debug.cpuprofile").Value.(flag.Getter).Get().(string)
153 memProfile := fs.Lookup("debug.memprofile").Value.(flag.Getter).Get().(string)
154 debugVersion := fs.Lookup("debug.version").Value.(flag.Getter).Get().(bool)
155 debugNoCompile := fs.Lookup("debug.no-compile-errors").Value.(flag.Getter).Get().(bool)
156
157 cfg := config.Config{}
158 cfg.Checks = *fs.Lookup("checks").Value.(*list)
159
160 exit := func(code int) {
161 if cpuProfile != "" {
162 pprof.StopCPUProfile()
163 }
164 if memProfile != "" {
165 f, err := os.Create(memProfile)
166 if err != nil {
167 panic(err)
168 }
169 runtime.GC()
170 pprof.WriteHeapProfile(f)
171 }
172 os.Exit(code)
173 }
174 if cpuProfile != "" {
175 f, err := os.Create(cpuProfile)
176 if err != nil {
177 log.Fatal(err)
178 }
179 pprof.StartCPUProfile(f)
180 }
181
182 if debugVersion {
183 version.Verbose()
184 exit(0)
185 }
186
187 if printVersion {
188 version.Print()
189 exit(0)
190 }
191
192 // Validate that the tags argument is well-formed. go/packages
193 // doesn't detect malformed build flags and returns unhelpful
194 // errors.
195 tf := buildutil.TagsFlag{}
196 if err := tf.Set(tags); err != nil {
197 fmt.Fprintln(os.Stderr, fmt.Errorf("invalid value %q for flag -tags: %s", tags, err))
198 exit(1)
199 }
200
201 if explain != "" {
202 var haystack []*analysis.Analyzer
203 haystack = append(haystack, cs...)
204 for _, cum := range cums {
205 haystack = append(haystack, cum.Analyzer())
206 }
207 check, ok := findCheck(haystack, explain)
208 if !ok {
209 fmt.Fprintln(os.Stderr, "Couldn't find check", explain)
210 exit(1)
211 }
212 if check.Doc == "" {
213 fmt.Fprintln(os.Stderr, explain, "has no documentation")
214 exit(1)
215 }
216 fmt.Println(check.Doc)
217 exit(0)
218 }
219
220 ps, err := Lint(cs, cums, fs.Args(), &Options{
221 Tags: tags,
222 LintTests: tests,
223 GoVersion: goVersion,
224 Config: cfg,
225 })
226 if err != nil {
227 fmt.Fprintln(os.Stderr, err)
228 exit(1)
229 }
230
231 var f format.Formatter
232 switch formatter {
233 case "text":
234 f = format.Text{W: os.Stdout}
235 case "stylish":
236 f = &format.Stylish{W: os.Stdout}
237 case "json":
238 f = format.JSON{W: os.Stdout}
239 default:
240 fmt.Fprintf(os.Stderr, "unsupported output format %q\n", formatter)
241 exit(2)
242 }
243
244 var (
245 total int
246 errors int
247 warnings int
248 )
249
250 fail := *fs.Lookup("fail").Value.(*list)
251 analyzers := make([]*analysis.Analyzer, len(cs), len(cs)+len(cums))
252 copy(analyzers, cs)
253 for _, cum := range cums {
254 analyzers = append(analyzers, cum.Analyzer())
255 }
256 shouldExit := lint.FilterChecks(analyzers, fail)
257 shouldExit["compile"] = true
258
259 total = len(ps)
260 for _, p := range ps {
261 if p.Check == "compile" && debugNoCompile {
262 continue
263 }
264 if p.Severity == lint.Ignored && !showIgnored {
265 continue
266 }
267 if shouldExit[p.Check] {
268 errors++
269 } else {
270 p.Severity = lint.Warning
271 warnings++
272 }
273 f.Format(p)
274 }
275 if f, ok := f.(format.Statter); ok {
276 f.Stats(total, errors, warnings)
277 }
278 if errors > 0 {
279 exit(1)
280 }
281 exit(0)
282}
283
284type Options struct {
285 Config config.Config
286
287 Tags string
288 LintTests bool
289 GoVersion int
290}
291
292func computeSalt() ([]byte, error) {
293 if version.Version != "devel" {
294 return []byte(version.Version), nil
295 }
296 p, err := os.Executable()
297 if err != nil {
298 return nil, err
299 }
300 f, err := os.Open(p)
301 if err != nil {
302 return nil, err
303 }
304 defer f.Close()
305 h := sha256.New()
306 if _, err := io.Copy(h, f); err != nil {
307 return nil, err
308 }
309 return h.Sum(nil), nil
310}
311
312func Lint(cs []*analysis.Analyzer, cums []lint.CumulativeChecker, paths []string, opt *Options) ([]lint.Problem, error) {
313 salt, err := computeSalt()
314 if err != nil {
315 return nil, fmt.Errorf("could not compute salt for cache: %s", err)
316 }
317 cache.SetSalt(salt)
318
319 if opt == nil {
320 opt = &Options{}
321 }
322
323 l := &lint.Linter{
324 Checkers: cs,
325 CumulativeCheckers: cums,
326 GoVersion: opt.GoVersion,
327 Config: opt.Config,
328 }
329 cfg := &packages.Config{}
330 if opt.LintTests {
331 cfg.Tests = true
332 }
333 if opt.Tags != "" {
334 cfg.BuildFlags = append(cfg.BuildFlags, "-tags", opt.Tags)
335 }
336
337 printStats := func() {
338 // Individual stats are read atomically, but overall there
339 // is no synchronisation. For printing rough progress
340 // information, this doesn't matter.
341 switch atomic.LoadUint32(&l.Stats.State) {
342 case lint.StateInitializing:
343 fmt.Fprintln(os.Stderr, "Status: initializing")
344 case lint.StateGraph:
345 fmt.Fprintln(os.Stderr, "Status: loading package graph")
346 case lint.StateProcessing:
347 fmt.Fprintf(os.Stderr, "Packages: %d/%d initial, %d/%d total; Workers: %d/%d; Problems: %d\n",
348 atomic.LoadUint32(&l.Stats.ProcessedInitialPackages),
349 atomic.LoadUint32(&l.Stats.InitialPackages),
350 atomic.LoadUint32(&l.Stats.ProcessedPackages),
351 atomic.LoadUint32(&l.Stats.TotalPackages),
352 atomic.LoadUint32(&l.Stats.ActiveWorkers),
353 atomic.LoadUint32(&l.Stats.TotalWorkers),
354 atomic.LoadUint32(&l.Stats.Problems),
355 )
356 case lint.StateCumulative:
357 fmt.Fprintln(os.Stderr, "Status: processing cumulative checkers")
358 }
359 }
360 if len(infoSignals) > 0 {
361 ch := make(chan os.Signal, 1)
362 signal.Notify(ch, infoSignals...)
363 defer signal.Stop(ch)
364 go func() {
365 for range ch {
366 printStats()
367 }
368 }()
369 }
370
371 return l.Lint(cfg, paths)
372}
373
374var posRe = regexp.MustCompile(`^(.+?):(\d+)(?::(\d+)?)?$`)
375
376func parsePos(pos string) token.Position {
377 if pos == "-" || pos == "" {
378 return token.Position{}
379 }
380 parts := posRe.FindStringSubmatch(pos)
381 if parts == nil {
382 panic(fmt.Sprintf("internal error: malformed position %q", pos))
383 }
384 file := parts[1]
385 line, _ := strconv.Atoi(parts[2])
386 col, _ := strconv.Atoi(parts[3])
387 return token.Position{
388 Filename: file,
389 Line: line,
390 Column: col,
391 }
392}