GoPLS Viewer

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