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 / cmd / gotype / gotype.go
1 // Copyright 2011 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 // gotype.go is a copy of the original source maintained
6 // in $GOROOT/src/go/types/gotype.go, but with the call
7 // to types.SizesFor factored out so we can provide a local
8 // implementation when compiling against Go 1.8 and earlier.
9 //
10 // This code is here for the sole purpose of satisfying historic
11 // references to this location, and for making gotype accessible
12 // via 'go get'.
13 //
14 // Do NOT make changes to this version as they will not be maintained
15 // (and possibly overwritten). Any changes should be made to the original
16 // and then ported to here.
17
18 /*
19 The gotype command, like the front-end of a Go compiler, parses and
20 type-checks a single Go package. Errors are reported if the analysis
21 fails; otherwise gotype is quiet (unless -v is set).
22
23 Without a list of paths, gotype reads from standard input, which
24 must provide a single Go source file defining a complete package.
25
26 With a single directory argument, gotype checks the Go files in
27 that directory, comprising a single package. Use -t to include the
28 (in-package) _test.go files. Use -x to type check only external
29 test files.
30
31 Otherwise, each path must be the filename of a Go file belonging
32 to the same package.
33
34 Imports are processed by importing directly from the source of
35 imported packages (default), or by importing from compiled and
36 installed packages (by setting -c to the respective compiler).
37
38 The -c flag must be set to a compiler ("gc", "gccgo") when type-
39 checking packages containing imports with relative import paths
40 (import "./mypkg") because the source importer cannot know which
41 files to include for such packages.
42
43 Usage:
44         gotype [flags] [path...]
45
46 The flags are:
47         -t
48                 include local test files in a directory (ignored if -x is provided)
49         -x
50                 consider only external test files in a directory
51         -e
52                 report all errors (not just the first 10)
53         -v
54                 verbose mode
55         -c
56                 compiler used for installed packages (gc, gccgo, or source); default: source
57
58 Flags controlling additional output:
59         -ast
60                 print AST (forces -seq)
61         -trace
62                 print parse trace (forces -seq)
63         -comments
64                 parse comments (ignored unless -ast or -trace is provided)
65
66 Examples:
67
68 To check the files a.go, b.go, and c.go:
69
70         gotype a.go b.go c.go
71
72 To check an entire package including (in-package) tests in the directory dir and print the processed files:
73
74         gotype -t -v dir
75
76 To check the external test package (if any) in the current directory, based on installed packages compiled with
77 cmd/compile:
78
79         gotype -c=gc -x .
80
81 To verify the output of a pipe:
82
83         echo "package foo" | gotype
84
85 */
86 package main
87
88 import (
89         "flag"
90         "fmt"
91         "go/ast"
92         "go/build"
93         "go/importer"
94         "go/parser"
95         "go/scanner"
96         "go/token"
97         "go/types"
98         "io/ioutil"
99         "os"
100         "path/filepath"
101         "sync"
102         "time"
103 )
104
105 var (
106         // main operation modes
107         testFiles  = flag.Bool("t", false, "include in-package test files in a directory")
108         xtestFiles = flag.Bool("x", false, "consider only external test files in a directory")
109         allErrors  = flag.Bool("e", false, "report all errors, not just the first 10")
110         verbose    = flag.Bool("v", false, "verbose mode")
111         compiler   = flag.String("c", defaultCompiler, "compiler used for installed packages (gc, gccgo, or source)")
112
113         // additional output control
114         printAST      = flag.Bool("ast", false, "print AST (forces -seq)")
115         printTrace    = flag.Bool("trace", false, "print parse trace (forces -seq)")
116         parseComments = flag.Bool("comments", false, "parse comments (ignored unless -ast or -trace is provided)")
117 )
118
119 var (
120         fset       = token.NewFileSet()
121         errorCount = 0
122         sequential = false
123         parserMode parser.Mode
124 )
125
126 func initParserMode() {
127         if *allErrors {
128                 parserMode |= parser.AllErrors
129         }
130         if *printAST {
131                 sequential = true
132         }
133         if *printTrace {
134                 parserMode |= parser.Trace
135                 sequential = true
136         }
137         if *parseComments && (*printAST || *printTrace) {
138                 parserMode |= parser.ParseComments
139         }
140 }
141
142 const usageString = `usage: gotype [flags] [path ...]
143
144 The gotype command, like the front-end of a Go compiler, parses and
145 type-checks a single Go package. Errors are reported if the analysis
146 fails; otherwise gotype is quiet (unless -v is set).
147
148 Without a list of paths, gotype reads from standard input, which
149 must provide a single Go source file defining a complete package.
150
151 With a single directory argument, gotype checks the Go files in
152 that directory, comprising a single package. Use -t to include the
153 (in-package) _test.go files. Use -x to type check only external
154 test files.
155
156 Otherwise, each path must be the filename of a Go file belonging
157 to the same package.
158
159 Imports are processed by importing directly from the source of
160 imported packages (default), or by importing from compiled and
161 installed packages (by setting -c to the respective compiler).
162
163 The -c flag must be set to a compiler ("gc", "gccgo") when type-
164 checking packages containing imports with relative import paths
165 (import "./mypkg") because the source importer cannot know which
166 files to include for such packages.
167 `
168
169 func usage() {
170         fmt.Fprintln(os.Stderr, usageString)
171         flag.PrintDefaults()
172         os.Exit(2)
173 }
174
175 func report(err error) {
176         scanner.PrintError(os.Stderr, err)
177         if list, ok := err.(scanner.ErrorList); ok {
178                 errorCount += len(list)
179                 return
180         }
181         errorCount++
182 }
183
184 // parse may be called concurrently
185 func parse(filename string, src interface{}) (*ast.File, error) {
186         if *verbose {
187                 fmt.Println(filename)
188         }
189         file, err := parser.ParseFile(fset, filename, src, parserMode) // ok to access fset concurrently
190         if *printAST {
191                 ast.Print(fset, file)
192         }
193         return file, err
194 }
195
196 func parseStdin() (*ast.File, error) {
197         src, err := ioutil.ReadAll(os.Stdin)
198         if err != nil {
199                 return nil, err
200         }
201         return parse("<standard input>", src)
202 }
203
204 func parseFiles(dir string, filenames []string) ([]*ast.File, error) {
205         files := make([]*ast.File, len(filenames))
206         errors := make([]error, len(filenames))
207
208         var wg sync.WaitGroup
209         for i, filename := range filenames {
210                 wg.Add(1)
211                 go func(i int, filepath string) {
212                         defer wg.Done()
213                         files[i], errors[i] = parse(filepath, nil)
214                 }(i, filepath.Join(dir, filename))
215                 if sequential {
216                         wg.Wait()
217                 }
218         }
219         wg.Wait()
220
221         // if there are errors, return the first one for deterministic results
222         for _, err := range errors {
223                 if err != nil {
224                         return nil, err
225                 }
226         }
227
228         return files, nil
229 }
230
231 func parseDir(dir string) ([]*ast.File, error) {
232         ctxt := build.Default
233         pkginfo, err := ctxt.ImportDir(dir, 0)
234         if _, nogo := err.(*build.NoGoError); err != nil && !nogo {
235                 return nil, err
236         }
237
238         if *xtestFiles {
239                 return parseFiles(dir, pkginfo.XTestGoFiles)
240         }
241
242         filenames := append(pkginfo.GoFiles, pkginfo.CgoFiles...)
243         if *testFiles {
244                 filenames = append(filenames, pkginfo.TestGoFiles...)
245         }
246         return parseFiles(dir, filenames)
247 }
248
249 func getPkgFiles(args []string) ([]*ast.File, error) {
250         if len(args) == 0 {
251                 // stdin
252                 file, err := parseStdin()
253                 if err != nil {
254                         return nil, err
255                 }
256                 return []*ast.File{file}, nil
257         }
258
259         if len(args) == 1 {
260                 // possibly a directory
261                 path := args[0]
262                 info, err := os.Stat(path)
263                 if err != nil {
264                         return nil, err
265                 }
266                 if info.IsDir() {
267                         return parseDir(path)
268                 }
269         }
270
271         // list of files
272         return parseFiles("", args)
273 }
274
275 func checkPkgFiles(files []*ast.File) {
276         type bailout struct{}
277
278         // if checkPkgFiles is called multiple times, set up conf only once
279         conf := types.Config{
280                 FakeImportC: true,
281                 Error: func(err error) {
282                         if !*allErrors && errorCount >= 10 {
283                                 panic(bailout{})
284                         }
285                         report(err)
286                 },
287                 Importer: importer.ForCompiler(fset, *compiler, nil),
288                 Sizes:    SizesFor(build.Default.Compiler, build.Default.GOARCH),
289         }
290
291         defer func() {
292                 switch p := recover().(type) {
293                 case nil, bailout:
294                         // normal return or early exit
295                 default:
296                         // re-panic
297                         panic(p)
298                 }
299         }()
300
301         const path = "pkg" // any non-empty string will do for now
302         conf.Check(path, fset, files, nil)
303 }
304
305 func printStats(d time.Duration) {
306         fileCount := 0
307         lineCount := 0
308         fset.Iterate(func(f *token.File) bool {
309                 fileCount++
310                 lineCount += f.LineCount()
311                 return true
312         })
313
314         fmt.Printf(
315                 "%s (%d files, %d lines, %d lines/s)\n",
316                 d, fileCount, lineCount, int64(float64(lineCount)/d.Seconds()),
317         )
318 }
319
320 func main() {
321         flag.Usage = usage
322         flag.Parse()
323         initParserMode()
324
325         start := time.Now()
326
327         files, err := getPkgFiles(flag.Args())
328         if err != nil {
329                 report(err)
330                 os.Exit(2)
331         }
332
333         checkPkgFiles(files)
334         if errorCount > 0 {
335                 os.Exit(2)
336         }
337
338         if *verbose {
339                 printStats(time.Since(start))
340         }
341 }