Skip to content

Commit 926e99a

Browse files
committed
copy go/analysis internal/checker into the project as is
use commit f0bfdbff1f9c986484a9f02fc198b1efcfe76ebe
1 parent d278457 commit 926e99a

File tree

2 files changed

+1052
-0
lines changed

2 files changed

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

0 commit comments

Comments
 (0)