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 / unitchecker / unitchecker.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 // The unitchecker package defines the main function for an analysis
6 // driver that analyzes a single compilation unit during a build.
7 // It is invoked by a build system such as "go vet":
8 //
9 //   $ go vet -vettool=$(which vet)
10 //
11 // It supports the following command-line protocol:
12 //
13 //      -V=full         describe executable               (to the build tool)
14 //      -flags          describe flags                    (to the build tool)
15 //      foo.cfg         description of compilation unit (from the build tool)
16 //
17 // This package does not depend on go/packages.
18 // If you need a standalone tool, use multichecker,
19 // which supports this mode but can also load packages
20 // from source using go/packages.
21 package unitchecker
22
23 // TODO(adonovan):
24 // - with gccgo, go build does not build standard library,
25 //   so we will not get to analyze it. Yet we must in order
26 //   to create base facts for, say, the fmt package for the
27 //   printf checker.
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         "reflect"
46         "sort"
47         "strings"
48         "sync"
49         "time"
50
51         "golang.org/x/tools/go/analysis"
52         "golang.org/x/tools/go/analysis/internal/analysisflags"
53         "golang.org/x/tools/go/analysis/internal/facts"
54 )
55
56 // A Config describes a compilation unit to be analyzed.
57 // It is provided to the tool in a JSON-encoded file
58 // whose name ends with ".cfg".
59 type Config struct {
60         ID                        string // e.g. "fmt [fmt.test]"
61         Compiler                  string
62         Dir                       string
63         ImportPath                string
64         GoFiles                   []string
65         NonGoFiles                []string
66         IgnoredFiles              []string
67         ImportMap                 map[string]string
68         PackageFile               map[string]string
69         Standard                  map[string]bool
70         PackageVetx               map[string]string
71         VetxOnly                  bool
72         VetxOutput                string
73         SucceedOnTypecheckFailure bool
74 }
75
76 // Main is the main function of a vet-like analysis tool that must be
77 // invoked by a build system to analyze a single package.
78 //
79 // The protocol required by 'go vet -vettool=...' is that the tool must support:
80 //
81 //      -flags          describe flags in JSON
82 //      -V=full         describe executable for build caching
83 //      foo.cfg         perform separate modular analyze on the single
84 //                      unit described by a JSON config file foo.cfg.
85 //
86 func Main(analyzers ...*analysis.Analyzer) {
87         progname := filepath.Base(os.Args[0])
88         log.SetFlags(0)
89         log.SetPrefix(progname + ": ")
90
91         if err := analysis.Validate(analyzers); err != nil {
92                 log.Fatal(err)
93         }
94
95         flag.Usage = func() {
96                 fmt.Fprintf(os.Stderr, `%[1]s is a tool for static analysis of Go programs.
97
98 Usage of %[1]s:
99         %.16[1]s unit.cfg       # execute analysis specified by config file
100         %.16[1]s help           # general help
101         %.16[1]s help name      # help on specific analyzer and its flags
102 `, progname)
103                 os.Exit(1)
104         }
105
106         analyzers = analysisflags.Parse(analyzers, true)
107
108         args := flag.Args()
109         if len(args) == 0 {
110                 flag.Usage()
111         }
112         if args[0] == "help" {
113                 analysisflags.Help(progname, analyzers, args[1:])
114                 os.Exit(0)
115         }
116         if len(args) != 1 || !strings.HasSuffix(args[0], ".cfg") {
117                 log.Fatalf(`invoking "go tool vet" directly is unsupported; use "go vet"`)
118         }
119         Run(args[0], analyzers)
120 }
121
122 // Run reads the *.cfg file, runs the analysis,
123 // and calls os.Exit with an appropriate error code.
124 // It assumes flags have already been set.
125 func Run(configFile string, analyzers []*analysis.Analyzer) {
126         cfg, err := readConfig(configFile)
127         if err != nil {
128                 log.Fatal(err)
129         }
130
131         fset := token.NewFileSet()
132         results, err := run(fset, cfg, analyzers)
133         if err != nil {
134                 log.Fatal(err)
135         }
136
137         // In VetxOnly mode, the analysis is run only for facts.
138         if !cfg.VetxOnly {
139                 if analysisflags.JSON {
140                         // JSON output
141                         tree := make(analysisflags.JSONTree)
142                         for _, res := range results {
143                                 tree.Add(fset, cfg.ID, res.a.Name, res.diagnostics, res.err)
144                         }
145                         tree.Print()
146                 } else {
147                         // plain text
148                         exit := 0
149                         for _, res := range results {
150                                 if res.err != nil {
151                                         log.Println(res.err)
152                                         exit = 1
153                                 }
154                         }
155                         for _, res := range results {
156                                 for _, diag := range res.diagnostics {
157                                         analysisflags.PrintPlain(fset, diag)
158                                         exit = 1
159                                 }
160                         }
161                         os.Exit(exit)
162                 }
163         }
164
165         os.Exit(0)
166 }
167
168 func readConfig(filename string) (*Config, error) {
169         data, err := ioutil.ReadFile(filename)
170         if err != nil {
171                 return nil, err
172         }
173         cfg := new(Config)
174         if err := json.Unmarshal(data, cfg); err != nil {
175                 return nil, fmt.Errorf("cannot decode JSON config file %s: %v", filename, err)
176         }
177         if len(cfg.GoFiles) == 0 {
178                 // The go command disallows packages with no files.
179                 // The only exception is unsafe, but the go command
180                 // doesn't call vet on it.
181                 return nil, fmt.Errorf("package has no files: %s", cfg.ImportPath)
182         }
183         return cfg, nil
184 }
185
186 var importerForCompiler = func(_ *token.FileSet, compiler string, lookup importer.Lookup) types.Importer {
187         // broken legacy implementation (https://golang.org/issue/28995)
188         return importer.For(compiler, lookup)
189 }
190
191 func run(fset *token.FileSet, cfg *Config, analyzers []*analysis.Analyzer) ([]result, error) {
192         // Load, parse, typecheck.
193         var files []*ast.File
194         for _, name := range cfg.GoFiles {
195                 f, err := parser.ParseFile(fset, name, nil, parser.ParseComments)
196                 if err != nil {
197                         if cfg.SucceedOnTypecheckFailure {
198                                 // Silently succeed; let the compiler
199                                 // report parse errors.
200                                 err = nil
201                         }
202                         return nil, err
203                 }
204                 files = append(files, f)
205         }
206         compilerImporter := importerForCompiler(fset, cfg.Compiler, func(path string) (io.ReadCloser, error) {
207                 // path is a resolved package path, not an import path.
208                 file, ok := cfg.PackageFile[path]
209                 if !ok {
210                         if cfg.Compiler == "gccgo" && cfg.Standard[path] {
211                                 return nil, nil // fall back to default gccgo lookup
212                         }
213                         return nil, fmt.Errorf("no package file for %q", path)
214                 }
215                 return os.Open(file)
216         })
217         importer := importerFunc(func(importPath string) (*types.Package, error) {
218                 path, ok := cfg.ImportMap[importPath] // resolve vendoring, etc
219                 if !ok {
220                         return nil, fmt.Errorf("can't resolve import %q", path)
221                 }
222                 return compilerImporter.Import(path)
223         })
224         tc := &types.Config{
225                 Importer: importer,
226                 Sizes:    types.SizesFor("gc", build.Default.GOARCH), // assume gccgo â‰¡ gc?
227         }
228         info := &types.Info{
229                 Types:      make(map[ast.Expr]types.TypeAndValue),
230                 Defs:       make(map[*ast.Ident]types.Object),
231                 Uses:       make(map[*ast.Ident]types.Object),
232                 Implicits:  make(map[ast.Node]types.Object),
233                 Scopes:     make(map[ast.Node]*types.Scope),
234                 Selections: make(map[*ast.SelectorExpr]*types.Selection),
235         }
236         pkg, err := tc.Check(cfg.ImportPath, fset, files, info)
237         if err != nil {
238                 if cfg.SucceedOnTypecheckFailure {
239                         // Silently succeed; let the compiler
240                         // report type errors.
241                         err = nil
242                 }
243                 return nil, err
244         }
245
246         // Register fact types with gob.
247         // In VetxOnly mode, analyzers are only for their facts,
248         // so we can skip any analysis that neither produces facts
249         // nor depends on any analysis that produces facts.
250         // Also build a map to hold working state and result.
251         type action struct {
252                 once        sync.Once
253                 result      interface{}
254                 err         error
255                 usesFacts   bool // (transitively uses)
256                 diagnostics []analysis.Diagnostic
257         }
258         actions := make(map[*analysis.Analyzer]*action)
259         var registerFacts func(a *analysis.Analyzer) bool
260         registerFacts = func(a *analysis.Analyzer) bool {
261                 act, ok := actions[a]
262                 if !ok {
263                         act = new(action)
264                         var usesFacts bool
265                         for _, f := range a.FactTypes {
266                                 usesFacts = true
267                                 gob.Register(f)
268                         }
269                         for _, req := range a.Requires {
270                                 if registerFacts(req) {
271                                         usesFacts = true
272                                 }
273                         }
274                         act.usesFacts = usesFacts
275                         actions[a] = act
276                 }
277                 return act.usesFacts
278         }
279         var filtered []*analysis.Analyzer
280         for _, a := range analyzers {
281                 if registerFacts(a) || !cfg.VetxOnly {
282                         filtered = append(filtered, a)
283                 }
284         }
285         analyzers = filtered
286
287         // Read facts from imported packages.
288         read := func(path string) ([]byte, error) {
289                 if vetx, ok := cfg.PackageVetx[path]; ok {
290                         return ioutil.ReadFile(vetx)
291                 }
292                 return nil, nil // no .vetx file, no facts
293         }
294         facts, err := facts.Decode(pkg, read)
295         if err != nil {
296                 return nil, err
297         }
298
299         // In parallel, execute the DAG of analyzers.
300         var exec func(a *analysis.Analyzer) *action
301         var execAll func(analyzers []*analysis.Analyzer)
302         exec = func(a *analysis.Analyzer) *action {
303                 act := actions[a]
304                 act.once.Do(func() {
305                         execAll(a.Requires) // prefetch dependencies in parallel
306
307                         // The inputs to this analysis are the
308                         // results of its prerequisites.
309                         inputs := make(map[*analysis.Analyzer]interface{})
310                         var failed []string
311                         for _, req := range a.Requires {
312                                 reqact := exec(req)
313                                 if reqact.err != nil {
314                                         failed = append(failed, req.String())
315                                         continue
316                                 }
317                                 inputs[req] = reqact.result
318                         }
319
320                         // Report an error if any dependency failed.
321                         if failed != nil {
322                                 sort.Strings(failed)
323                                 act.err = fmt.Errorf("failed prerequisites: %s", strings.Join(failed, ", "))
324                                 return
325                         }
326
327                         factFilter := make(map[reflect.Type]bool)
328                         for _, f := range a.FactTypes {
329                                 factFilter[reflect.TypeOf(f)] = true
330                         }
331
332                         pass := &analysis.Pass{
333                                 Analyzer:          a,
334                                 Fset:              fset,
335                                 Files:             files,
336                                 OtherFiles:        cfg.NonGoFiles,
337                                 IgnoredFiles:      cfg.IgnoredFiles,
338                                 Pkg:               pkg,
339                                 TypesInfo:         info,
340                                 TypesSizes:        tc.Sizes,
341                                 ResultOf:          inputs,
342                                 Report:            func(d analysis.Diagnostic) { act.diagnostics = append(act.diagnostics, d) },
343                                 ImportObjectFact:  facts.ImportObjectFact,
344                                 ExportObjectFact:  facts.ExportObjectFact,
345                                 AllObjectFacts:    func() []analysis.ObjectFact { return facts.AllObjectFacts(factFilter) },
346                                 ImportPackageFact: facts.ImportPackageFact,
347                                 ExportPackageFact: facts.ExportPackageFact,
348                                 AllPackageFacts:   func() []analysis.PackageFact { return facts.AllPackageFacts(factFilter) },
349                         }
350
351                         t0 := time.Now()
352                         act.result, act.err = a.Run(pass)
353                         if false {
354                                 log.Printf("analysis %s = %s", pass, time.Since(t0))
355                         }
356                 })
357                 return act
358         }
359         execAll = func(analyzers []*analysis.Analyzer) {
360                 var wg sync.WaitGroup
361                 for _, a := range analyzers {
362                         wg.Add(1)
363                         go func(a *analysis.Analyzer) {
364                                 _ = exec(a)
365                                 wg.Done()
366                         }(a)
367                 }
368                 wg.Wait()
369         }
370
371         execAll(analyzers)
372
373         // Return diagnostics and errors from root analyzers.
374         results := make([]result, len(analyzers))
375         for i, a := range analyzers {
376                 act := actions[a]
377                 results[i].a = a
378                 results[i].err = act.err
379                 results[i].diagnostics = act.diagnostics
380         }
381
382         data := facts.Encode()
383         if err := ioutil.WriteFile(cfg.VetxOutput, data, 0666); err != nil {
384                 return nil, fmt.Errorf("failed to write analysis facts: %v", err)
385         }
386
387         return results, nil
388 }
389
390 type result struct {
391         a           *analysis.Analyzer
392         diagnostics []analysis.Diagnostic
393         err         error
394 }
395
396 type importerFunc func(path string) (*types.Package, error)
397
398 func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }