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.
7 // Build this command explicitly: go build gotype.go
10 The gotype command, like the front-end of a Go compiler, parses and
11 type-checks a single Go package. Errors are reported if the analysis
12 fails; otherwise gotype is quiet (unless -v is set).
14 Without a list of paths, gotype reads from standard input, which
15 must provide a single Go source file defining a complete package.
17 With a single directory argument, gotype checks the Go files in
18 that directory, comprising a single package. Use -t to include the
19 (in-package) _test.go files. Use -x to type check only external
22 Otherwise, each path must be the filename of a Go file belonging
25 Imports are processed by importing directly from the source of
26 imported packages (default), or by importing from compiled and
27 installed packages (by setting -c to the respective compiler).
29 The -c flag must be set to a compiler ("gc", "gccgo") when type-
30 checking packages containing imports with relative import paths
31 (import "./mypkg") because the source importer cannot know which
32 files to include for such packages.
35 gotype [flags] [path...]
39 include local test files in a directory (ignored if -x is provided)
41 consider only external test files in a directory
43 report all errors (not just the first 10)
47 compiler used for installed packages (gc, gccgo, or source); default: source
49 Flags controlling additional output:
51 print AST (forces -seq)
53 print parse trace (forces -seq)
55 parse comments (ignored unless -ast or -trace is provided)
59 To check the files a.go, b.go, and c.go:
63 To check an entire package including (in-package) tests in the directory dir and print the processed files:
67 To check the external test package (if any) in the current directory, based on installed packages compiled with
72 To verify the output of a pipe:
74 echo "package foo" | gotype
97 // main operation modes
98 testFiles
= flag
.Bool("t", false, "include in-package test files in a directory")
99 xtestFiles
= flag
.Bool("x", false, "consider only external test files in a directory")
100 allErrors
= flag
.Bool("e", false, "report all errors, not just the first 10")
101 verbose
= flag
.Bool("v", false, "verbose mode")
102 compiler
= flag
.String("c", "source", "compiler used for installed packages (gc, gccgo, or source)")
104 // additional output control
105 printAST
= flag
.Bool("ast", false, "print AST (forces -seq)")
106 printTrace
= flag
.Bool("trace", false, "print parse trace (forces -seq)")
107 parseComments
= flag
.Bool("comments", false, "parse comments (ignored unless -ast or -trace is provided)")
111 fset
= token
.NewFileSet()
114 parserMode parser
.Mode
117 func initParserMode() {
119 parserMode |
= parser
.AllErrors
125 parserMode |
= parser
.Trace
128 if *parseComments
&& (*printAST ||
*printTrace
) {
129 parserMode |
= parser
.ParseComments
133 const usageString
= `usage: gotype [flags] [path ...]
135 The gotype command, like the front-end of a Go compiler, parses and
136 type-checks a single Go package. Errors are reported if the analysis
137 fails; otherwise gotype is quiet (unless -v is set).
139 Without a list of paths, gotype reads from standard input, which
140 must provide a single Go source file defining a complete package.
142 With a single directory argument, gotype checks the Go files in
143 that directory, comprising a single package. Use -t to include the
144 (in-package) _test.go files. Use -x to type check only external
147 Otherwise, each path must be the filename of a Go file belonging
150 Imports are processed by importing directly from the source of
151 imported packages (default), or by importing from compiled and
152 installed packages (by setting -c to the respective compiler).
154 The -c flag must be set to a compiler ("gc", "gccgo") when type-
155 checking packages containing imports with relative import paths
156 (import "./mypkg") because the source importer cannot know which
157 files to include for such packages.
161 fmt
.Fprintln(os
.Stderr
, usageString
)
166 func report(err error
) {
167 scanner
.PrintError(os
.Stderr
, err
)
168 if list
, ok
:= err
.(scanner
.ErrorList
); ok
{
169 errorCount
+= len(list
)
175 // parse may be called concurrently
176 func parse(filename
string, src
interface{}) (*ast
.File
, error
) {
178 fmt
.Println(filename
)
180 file
, err
:= parser
.ParseFile(fset
, filename
, src
, parserMode
) // ok to access fset concurrently
182 ast
.Print(fset
, file
)
187 func parseStdin() (*ast
.File
, error
) {
188 src
, err
:= ioutil
.ReadAll(os
.Stdin
)
192 return parse("<standard input>", src
)
195 func parseFiles(dir
string, filenames
[]string) ([]*ast
.File
, error
) {
196 files
:= make([]*ast
.File
, len(filenames
))
197 errors
:= make([]error
, len(filenames
))
199 var wg sync
.WaitGroup
200 for i
, filename
:= range filenames
{
202 go func(i
int, filepath
string) {
204 files
[i
], errors
[i
] = parse(filepath
, nil)
205 }(i
, filepath
.Join(dir
, filename
))
212 // if there are errors, return the first one for deterministic results
213 for _
, err
:= range errors
{
222 func parseDir(dir
string) ([]*ast
.File
, error
) {
223 ctxt
:= build
.Default
224 pkginfo
, err
:= ctxt
.ImportDir(dir
, 0)
225 if _
, nogo
:= err
.(*build
.NoGoError
); err
!= nil && !nogo
{
230 return parseFiles(dir
, pkginfo
.XTestGoFiles
)
233 filenames
:= append(pkginfo
.GoFiles
, pkginfo
.CgoFiles
...)
235 filenames
= append(filenames
, pkginfo
.TestGoFiles
...)
237 return parseFiles(dir
, filenames
)
240 func getPkgFiles(args
[]string) ([]*ast
.File
, error
) {
243 file
, err
:= parseStdin()
247 return []*ast
.File
{file
}, nil
251 // possibly a directory
253 info
, err
:= os
.Stat(path
)
258 return parseDir(path
)
263 return parseFiles("", args
)
266 func checkPkgFiles(files
[]*ast
.File
) {
267 type bailout
struct{}
269 // if checkPkgFiles is called multiple times, set up conf only once
270 conf
:= types
.Config
{
272 Error
: func(err error
) {
273 if !*allErrors
&& errorCount
>= 10 {
278 Importer
: importer
.For(*compiler
, nil),
279 Sizes
: types
.SizesFor(build
.Default
.Compiler
, build
.Default
.GOARCH
),
283 switch p
:= recover().(type) {
285 // normal return or early exit
292 const path
= "pkg" // any non-empty string will do for now
293 conf
.Check(path
, fset
, files
, nil)
296 func printStats(d time
.Duration
) {
299 fset
.Iterate(func(f
*token
.File
) bool {
301 lineCount
+= f
.LineCount()
306 "%s (%d files, %d lines, %d lines/s)\n",
307 d
, fileCount
, lineCount
, int64(float64(lineCount
)/d
.Seconds()),
318 files
, err
:= getPkgFiles(flag
.Args())
330 printStats(time
.Since(start
))