Source file src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package unitchecker
22
23
24
25
26
27
28
29 import (
30 "encoding/gob"
31 "encoding/json"
32 "flag"
33 "fmt"
34 "go/ast"
35 "go/build"
36 "go/importer"
37 "go/parser"
38 "go/token"
39 "go/types"
40 "io"
41 "io/ioutil"
42 "log"
43 "os"
44 "path/filepath"
45 "sort"
46 "strings"
47 "sync"
48 "time"
49
50 "golang.org/x/tools/go/analysis"
51 "golang.org/x/tools/go/analysis/internal/analysisflags"
52 "golang.org/x/tools/go/analysis/internal/facts"
53 )
54
55
56
57
58 type Config struct {
59 ID string
60 Compiler string
61 Dir string
62 ImportPath string
63 GoFiles []string
64 NonGoFiles []string
65 ImportMap map[string]string
66 PackageFile map[string]string
67 Standard map[string]bool
68 PackageVetx map[string]string
69 VetxOnly bool
70 VetxOutput string
71 SucceedOnTypecheckFailure bool
72 }
73
74
75
76
77
78
79
80
81
82
83
84 func Main(analyzers ...*analysis.Analyzer) {
85 progname := filepath.Base(os.Args[0])
86 log.SetFlags(0)
87 log.SetPrefix(progname + ": ")
88
89 if err := analysis.Validate(analyzers); err != nil {
90 log.Fatal(err)
91 }
92
93 flag.Usage = func() {
94 fmt.Fprintf(os.Stderr, `%[1]s is a tool for static analysis of Go programs.
95
96 Usage of %[1]s:
97 %.16[1]s unit.cfg # execute analysis specified by config file
98 %.16[1]s help # general help
99 %.16[1]s help name # help on specific analyzer and its flags
100 `, progname)
101 os.Exit(1)
102 }
103
104 analyzers = analysisflags.Parse(analyzers, true)
105
106 args := flag.Args()
107 if len(args) == 0 {
108 flag.Usage()
109 }
110 if args[0] == "help" {
111 analysisflags.Help(progname, analyzers, args[1:])
112 os.Exit(0)
113 }
114 if len(args) != 1 || !strings.HasSuffix(args[0], ".cfg") {
115 log.Fatalf(`invoking "go tool vet" directly is unsupported; use "go vet"`)
116 }
117 Run(args[0], analyzers)
118 }
119
120
121
122
123 func Run(configFile string, analyzers []*analysis.Analyzer) {
124 cfg, err := readConfig(configFile)
125 if err != nil {
126 log.Fatal(err)
127 }
128
129 fset := token.NewFileSet()
130 results, err := run(fset, cfg, analyzers)
131 if err != nil {
132 log.Fatal(err)
133 }
134
135
136 if !cfg.VetxOnly {
137 if analysisflags.JSON {
138
139 tree := make(analysisflags.JSONTree)
140 for _, res := range results {
141 tree.Add(fset, cfg.ID, res.a.Name, res.diagnostics, res.err)
142 }
143 tree.Print()
144 } else {
145
146 exit := 0
147 for _, res := range results {
148 if res.err != nil {
149 log.Println(res.err)
150 exit = 1
151 }
152 }
153 for _, res := range results {
154 for _, diag := range res.diagnostics {
155 analysisflags.PrintPlain(fset, diag)
156 exit = 1
157 }
158 }
159 os.Exit(exit)
160 }
161 }
162
163 os.Exit(0)
164 }
165
166 func readConfig(filename string) (*Config, error) {
167 data, err := ioutil.ReadFile(filename)
168 if err != nil {
169 return nil, err
170 }
171 cfg := new(Config)
172 if err := json.Unmarshal(data, cfg); err != nil {
173 return nil, fmt.Errorf("cannot decode JSON config file %s: %v", filename, err)
174 }
175 if len(cfg.GoFiles) == 0 {
176
177
178
179 return nil, fmt.Errorf("package has no files: %s", cfg.ImportPath)
180 }
181 return cfg, nil
182 }
183
184 var importerForCompiler = func(_ *token.FileSet, compiler string, lookup importer.Lookup) types.Importer {
185
186 return importer.For(compiler, lookup)
187 }
188
189 func run(fset *token.FileSet, cfg *Config, analyzers []*analysis.Analyzer) ([]result, error) {
190
191 var files []*ast.File
192 for _, name := range cfg.GoFiles {
193 f, err := parser.ParseFile(fset, name, nil, parser.ParseComments)
194 if err != nil {
195 if cfg.SucceedOnTypecheckFailure {
196
197
198 err = nil
199 }
200 return nil, err
201 }
202 files = append(files, f)
203 }
204 compilerImporter := importerForCompiler(fset, cfg.Compiler, func(path string) (io.ReadCloser, error) {
205
206 file, ok := cfg.PackageFile[path]
207 if !ok {
208 if cfg.Compiler == "gccgo" && cfg.Standard[path] {
209 return nil, nil
210 }
211 return nil, fmt.Errorf("no package file for %q", path)
212 }
213 return os.Open(file)
214 })
215 importer := importerFunc(func(importPath string) (*types.Package, error) {
216 path, ok := cfg.ImportMap[importPath]
217 if !ok {
218 return nil, fmt.Errorf("can't resolve import %q", path)
219 }
220 return compilerImporter.Import(path)
221 })
222 tc := &types.Config{
223 Importer: importer,
224 Sizes: types.SizesFor("gc", build.Default.GOARCH),
225 }
226 info := &types.Info{
227 Types: make(map[ast.Expr]types.TypeAndValue),
228 Defs: make(map[*ast.Ident]types.Object),
229 Uses: make(map[*ast.Ident]types.Object),
230 Implicits: make(map[ast.Node]types.Object),
231 Scopes: make(map[ast.Node]*types.Scope),
232 Selections: make(map[*ast.SelectorExpr]*types.Selection),
233 }
234 pkg, err := tc.Check(cfg.ImportPath, fset, files, info)
235 if err != nil {
236 if cfg.SucceedOnTypecheckFailure {
237
238
239 err = nil
240 }
241 return nil, err
242 }
243
244
245
246
247
248
249 type action struct {
250 once sync.Once
251 result interface{}
252 err error
253 usesFacts bool
254 diagnostics []analysis.Diagnostic
255 }
256 actions := make(map[*analysis.Analyzer]*action)
257 var registerFacts func(a *analysis.Analyzer) bool
258 registerFacts = func(a *analysis.Analyzer) bool {
259 act, ok := actions[a]
260 if !ok {
261 act = new(action)
262 var usesFacts bool
263 for _, f := range a.FactTypes {
264 usesFacts = true
265 gob.Register(f)
266 }
267 for _, req := range a.Requires {
268 if registerFacts(req) {
269 usesFacts = true
270 }
271 }
272 act.usesFacts = usesFacts
273 actions[a] = act
274 }
275 return act.usesFacts
276 }
277 var filtered []*analysis.Analyzer
278 for _, a := range analyzers {
279 if registerFacts(a) || !cfg.VetxOnly {
280 filtered = append(filtered, a)
281 }
282 }
283 analyzers = filtered
284
285
286 read := func(path string) ([]byte, error) {
287 if vetx, ok := cfg.PackageVetx[path]; ok {
288 return ioutil.ReadFile(vetx)
289 }
290 return nil, nil
291 }
292 facts, err := facts.Decode(pkg, read)
293 if err != nil {
294 return nil, err
295 }
296
297
298 var exec func(a *analysis.Analyzer) *action
299 var execAll func(analyzers []*analysis.Analyzer)
300 exec = func(a *analysis.Analyzer) *action {
301 act := actions[a]
302 act.once.Do(func() {
303 execAll(a.Requires)
304
305
306
307 inputs := make(map[*analysis.Analyzer]interface{})
308 var failed []string
309 for _, req := range a.Requires {
310 reqact := exec(req)
311 if reqact.err != nil {
312 failed = append(failed, req.String())
313 continue
314 }
315 inputs[req] = reqact.result
316 }
317
318
319 if failed != nil {
320 sort.Strings(failed)
321 act.err = fmt.Errorf("failed prerequisites: %s", strings.Join(failed, ", "))
322 return
323 }
324
325 pass := &analysis.Pass{
326 Analyzer: a,
327 Fset: fset,
328 Files: files,
329 OtherFiles: cfg.NonGoFiles,
330 Pkg: pkg,
331 TypesInfo: info,
332 TypesSizes: tc.Sizes,
333 ResultOf: inputs,
334 Report: func(d analysis.Diagnostic) { act.diagnostics = append(act.diagnostics, d) },
335 ImportObjectFact: facts.ImportObjectFact,
336 ExportObjectFact: facts.ExportObjectFact,
337 ImportPackageFact: facts.ImportPackageFact,
338 ExportPackageFact: facts.ExportPackageFact,
339 }
340
341 t0 := time.Now()
342 act.result, act.err = a.Run(pass)
343 if false {
344 log.Printf("analysis %s = %s", pass, time.Since(t0))
345 }
346 })
347 return act
348 }
349 execAll = func(analyzers []*analysis.Analyzer) {
350 var wg sync.WaitGroup
351 for _, a := range analyzers {
352 wg.Add(1)
353 go func(a *analysis.Analyzer) {
354 _ = exec(a)
355 wg.Done()
356 }(a)
357 }
358 wg.Wait()
359 }
360
361 execAll(analyzers)
362
363
364 results := make([]result, len(analyzers))
365 for i, a := range analyzers {
366 act := actions[a]
367 results[i].a = a
368 results[i].err = act.err
369 results[i].diagnostics = act.diagnostics
370 }
371
372 data := facts.Encode()
373 if err := ioutil.WriteFile(cfg.VetxOutput, data, 0666); err != nil {
374 return nil, fmt.Errorf("failed to write analysis facts: %v", err)
375 }
376
377 return results, nil
378 }
379
380 type result struct {
381 a *analysis.Analyzer
382 diagnostics []analysis.Diagnostic
383 err error
384 }
385
386 type importerFunc func(path string) (*types.Package, error)
387
388 func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
389
View as plain text