libgo: update to go1.9
[official-gcc.git] / libgo / go / go / types / gotype.go
blob196fc9bbd604162de61eb3dff70149a0ae40797b
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.
5 // +build ignore
7 // Build this command explicitly: go build gotype.go
9 /*
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
20 test files.
22 Otherwise, each path must be the filename of a Go file belonging
23 to the same package.
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.
34 Usage:
35 gotype [flags] [path...]
37 The flags are:
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)
45 verbose mode
47 compiler used for installed packages (gc, gccgo, or source); default: source
49 Flags controlling additional output:
50 -ast
51 print AST (forces -seq)
52 -trace
53 print parse trace (forces -seq)
54 -comments
55 parse comments (ignored unless -ast or -trace is provided)
57 Examples:
59 To check the files a.go, b.go, and c.go:
61 gotype a.go b.go c.go
63 To check an entire package including (in-package) tests in the directory dir and print the processed files:
65 gotype -t -v dir
67 To check the external test package (if any) in the current directory, based on installed packages compiled with
68 cmd/compile:
70 gotype -c=gc -x .
72 To verify the output of a pipe:
74 echo "package foo" | gotype
77 package main
79 import (
80 "flag"
81 "fmt"
82 "go/ast"
83 "go/build"
84 "go/importer"
85 "go/parser"
86 "go/scanner"
87 "go/token"
88 "go/types"
89 "io/ioutil"
90 "os"
91 "path/filepath"
92 "sync"
93 "time"
96 var (
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)")
110 var (
111 fset = token.NewFileSet()
112 errorCount = 0
113 sequential = false
114 parserMode parser.Mode
117 func initParserMode() {
118 if *allErrors {
119 parserMode |= parser.AllErrors
121 if *printAST {
122 sequential = true
124 if *printTrace {
125 parserMode |= parser.Trace
126 sequential = true
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
145 test files.
147 Otherwise, each path must be the filename of a Go file belonging
148 to the same package.
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.
160 func usage() {
161 fmt.Fprintln(os.Stderr, usageString)
162 flag.PrintDefaults()
163 os.Exit(2)
166 func report(err error) {
167 scanner.PrintError(os.Stderr, err)
168 if list, ok := err.(scanner.ErrorList); ok {
169 errorCount += len(list)
170 return
172 errorCount++
175 // parse may be called concurrently
176 func parse(filename string, src interface{}) (*ast.File, error) {
177 if *verbose {
178 fmt.Println(filename)
180 file, err := parser.ParseFile(fset, filename, src, parserMode) // ok to access fset concurrently
181 if *printAST {
182 ast.Print(fset, file)
184 return file, err
187 func parseStdin() (*ast.File, error) {
188 src, err := ioutil.ReadAll(os.Stdin)
189 if err != nil {
190 return nil, err
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 {
201 wg.Add(1)
202 go func(i int, filepath string) {
203 defer wg.Done()
204 files[i], errors[i] = parse(filepath, nil)
205 }(i, filepath.Join(dir, filename))
206 if sequential {
207 wg.Wait()
210 wg.Wait()
212 // if there are errors, return the first one for deterministic results
213 for _, err := range errors {
214 if err != nil {
215 return nil, err
219 return files, nil
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 {
226 return nil, err
229 if *xtestFiles {
230 return parseFiles(dir, pkginfo.XTestGoFiles)
233 filenames := append(pkginfo.GoFiles, pkginfo.CgoFiles...)
234 if *testFiles {
235 filenames = append(filenames, pkginfo.TestGoFiles...)
237 return parseFiles(dir, filenames)
240 func getPkgFiles(args []string) ([]*ast.File, error) {
241 if len(args) == 0 {
242 // stdin
243 file, err := parseStdin()
244 if err != nil {
245 return nil, err
247 return []*ast.File{file}, nil
250 if len(args) == 1 {
251 // possibly a directory
252 path := args[0]
253 info, err := os.Stat(path)
254 if err != nil {
255 return nil, err
257 if info.IsDir() {
258 return parseDir(path)
262 // list of files
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{
271 FakeImportC: true,
272 Error: func(err error) {
273 if !*allErrors && errorCount >= 10 {
274 panic(bailout{})
276 report(err)
278 Importer: importer.For(*compiler, nil),
279 Sizes: types.SizesFor(build.Default.Compiler, build.Default.GOARCH),
282 defer func() {
283 switch p := recover().(type) {
284 case nil, bailout:
285 // normal return or early exit
286 default:
287 // re-panic
288 panic(p)
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) {
297 fileCount := 0
298 lineCount := 0
299 fset.Iterate(func(f *token.File) bool {
300 fileCount++
301 lineCount += f.LineCount()
302 return true
305 fmt.Printf(
306 "%s (%d files, %d lines, %d lines/s)\n",
307 d, fileCount, lineCount, int64(float64(lineCount)/d.Seconds()),
311 func main() {
312 flag.Usage = usage
313 flag.Parse()
314 initParserMode()
316 start := time.Now()
318 files, err := getPkgFiles(flag.Args())
319 if err != nil {
320 report(err)
321 os.Exit(2)
324 checkPkgFiles(files)
325 if errorCount > 0 {
326 os.Exit(2)
329 if *verbose {
330 printStats(time.Since(start))