go/types: move gotype command into this directory
This is a copy of x/tools/cmd/gotype/gotype.go with the corresponding
x/tools/cmd/gotype/doc.go prepended and including a build tag (ignore).
This way, go/types can be built unaffected. If we need the gotype command,
it is trivially built in the go/types directory with: go build gotype.go .
Fixes #12303.
Change-Id: I2d792fcb39719cc5cc300f657e4735901cd20faa
Reviewed-on: https://go-review.googlesource.com/15152
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/src/go/types/gotype.go b/src/go/types/gotype.go
new file mode 100644
index 0000000..0a36c08
--- /dev/null
+++ b/src/go/types/gotype.go
@@ -0,0 +1,322 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// Build this command explicitly: go build gotype.go
+
+/*
+The gotype command does syntactic and semantic analysis of Go files
+and packages like the front-end of a Go compiler. Errors are reported
+if the analysis fails; otherwise gotype is quiet (unless -v is set).
+
+Without a list of paths, gotype reads from standard input, which
+must provide a single Go source file defining a complete package.
+
+If a single path is specified that is a directory, gotype checks
+the Go files in that directory; they must all belong to the same
+package.
+
+Otherwise, each path must be the filename of Go file belonging to
+the same package.
+
+Usage:
+ gotype [flags] [path...]
+
+The flags are:
+ -a
+ use all (incl. _test.go) files when processing a directory
+ -e
+ report all errors (not just the first 10)
+ -v
+ verbose mode
+ -c
+ compiler used to compile packages (gc or gccgo); default: gc
+ (gotype based on Go1.5 and up only)
+ -gccgo
+ use gccimporter instead of gcimporter
+ (gotype based on Go1.4 and before only)
+
+Debugging flags:
+ -seq
+ parse sequentially, rather than in parallel
+ -ast
+ print AST (forces -seq)
+ -trace
+ print parse trace (forces -seq)
+ -comments
+ parse comments (ignored unless -ast or -trace is provided)
+
+Examples:
+
+To check the files a.go, b.go, and c.go:
+
+ gotype a.go b.go c.go
+
+To check an entire package in the directory dir and print the processed files:
+
+ gotype -v dir
+
+To check an entire package including tests in the local directory:
+
+ gotype -a .
+
+To verify the output of a pipe:
+
+ echo "package foo" | gotype
+
+*/
+package main
+
+import (
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/importer"
+ "go/parser"
+ "go/scanner"
+ "go/token"
+ "go/types"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "time"
+)
+
+var (
+ // main operation modes
+ allFiles = flag.Bool("a", false, "use all (incl. _test.go) files when processing a directory")
+ allErrors = flag.Bool("e", false, "report all errors (not just the first 10)")
+ verbose = flag.Bool("v", false, "verbose mode")
+ gccgo = flag.Bool("gccgo", false, "use gccgoimporter instead of gcimporter")
+
+ // debugging support
+ sequential = flag.Bool("seq", false, "parse sequentially, rather than in parallel")
+ printAST = flag.Bool("ast", false, "print AST (forces -seq)")
+ printTrace = flag.Bool("trace", false, "print parse trace (forces -seq)")
+ parseComments = flag.Bool("comments", false, "parse comments (ignored unless -ast or -trace is provided)")
+)
+
+var (
+ fset = token.NewFileSet()
+ errorCount = 0
+ parserMode parser.Mode
+ sizes types.Sizes
+)
+
+func initParserMode() {
+ if *allErrors {
+ parserMode |= parser.AllErrors
+ }
+ if *printTrace {
+ parserMode |= parser.Trace
+ }
+ if *parseComments && (*printAST || *printTrace) {
+ parserMode |= parser.ParseComments
+ }
+}
+
+func initSizes() {
+ wordSize := 8
+ maxAlign := 8
+ switch build.Default.GOARCH {
+ case "386", "arm":
+ wordSize = 4
+ maxAlign = 4
+ // add more cases as needed
+ }
+ sizes = &types.StdSizes{WordSize: int64(wordSize), MaxAlign: int64(maxAlign)}
+}
+
+func usage() {
+ fmt.Fprintln(os.Stderr, "usage: gotype [flags] [path ...]")
+ flag.PrintDefaults()
+ os.Exit(2)
+}
+
+func report(err error) {
+ scanner.PrintError(os.Stderr, err)
+ if list, ok := err.(scanner.ErrorList); ok {
+ errorCount += len(list)
+ return
+ }
+ errorCount++
+}
+
+// parse may be called concurrently
+func parse(filename string, src interface{}) (*ast.File, error) {
+ if *verbose {
+ fmt.Println(filename)
+ }
+ file, err := parser.ParseFile(fset, filename, src, parserMode) // ok to access fset concurrently
+ if *printAST {
+ ast.Print(fset, file)
+ }
+ return file, err
+}
+
+func parseStdin() (*ast.File, error) {
+ src, err := ioutil.ReadAll(os.Stdin)
+ if err != nil {
+ return nil, err
+ }
+ return parse("<standard input>", src)
+}
+
+func parseFiles(filenames []string) ([]*ast.File, error) {
+ files := make([]*ast.File, len(filenames))
+
+ if *sequential {
+ for i, filename := range filenames {
+ var err error
+ files[i], err = parse(filename, nil)
+ if err != nil {
+ return nil, err // leave unfinished goroutines hanging
+ }
+ }
+ } else {
+ type parseResult struct {
+ file *ast.File
+ err error
+ }
+
+ out := make(chan parseResult)
+ for _, filename := range filenames {
+ go func(filename string) {
+ file, err := parse(filename, nil)
+ out <- parseResult{file, err}
+ }(filename)
+ }
+
+ for i := range filenames {
+ res := <-out
+ if res.err != nil {
+ return nil, res.err // leave unfinished goroutines hanging
+ }
+ files[i] = res.file
+ }
+ }
+
+ return files, nil
+}
+
+func parseDir(dirname string) ([]*ast.File, error) {
+ ctxt := build.Default
+ pkginfo, err := ctxt.ImportDir(dirname, 0)
+ if _, nogo := err.(*build.NoGoError); err != nil && !nogo {
+ return nil, err
+ }
+ filenames := append(pkginfo.GoFiles, pkginfo.CgoFiles...)
+ if *allFiles {
+ filenames = append(filenames, pkginfo.TestGoFiles...)
+ }
+
+ // complete file names
+ for i, filename := range filenames {
+ filenames[i] = filepath.Join(dirname, filename)
+ }
+
+ return parseFiles(filenames)
+}
+
+func getPkgFiles(args []string) ([]*ast.File, error) {
+ if len(args) == 0 {
+ // stdin
+ file, err := parseStdin()
+ if err != nil {
+ return nil, err
+ }
+ return []*ast.File{file}, nil
+ }
+
+ if len(args) == 1 {
+ // possibly a directory
+ path := args[0]
+ info, err := os.Stat(path)
+ if err != nil {
+ return nil, err
+ }
+ if info.IsDir() {
+ return parseDir(path)
+ }
+ }
+
+ // list of files
+ return parseFiles(args)
+}
+
+func checkPkgFiles(files []*ast.File) {
+ compiler := "gc"
+ if *gccgo {
+ compiler = "gccgo"
+ }
+ type bailout struct{}
+ conf := types.Config{
+ FakeImportC: true,
+ Error: func(err error) {
+ if !*allErrors && errorCount >= 10 {
+ panic(bailout{})
+ }
+ report(err)
+ },
+ Importer: importer.For(compiler, nil),
+ Sizes: sizes,
+ }
+
+ defer func() {
+ switch p := recover().(type) {
+ case nil, bailout:
+ // normal return or early exit
+ default:
+ // re-panic
+ panic(p)
+ }
+ }()
+
+ const path = "pkg" // any non-empty string will do for now
+ conf.Check(path, fset, files, nil)
+}
+
+func printStats(d time.Duration) {
+ fileCount := 0
+ lineCount := 0
+ fset.Iterate(func(f *token.File) bool {
+ fileCount++
+ lineCount += f.LineCount()
+ return true
+ })
+
+ fmt.Printf(
+ "%s (%d files, %d lines, %d lines/s)\n",
+ d, fileCount, lineCount, int64(float64(lineCount)/d.Seconds()),
+ )
+}
+
+func main() {
+ flag.Usage = usage
+ flag.Parse()
+ if *printAST || *printTrace {
+ *sequential = true
+ }
+ initParserMode()
+ initSizes()
+
+ start := time.Now()
+
+ files, err := getPkgFiles(flag.Args())
+ if err != nil {
+ report(err)
+ os.Exit(2)
+ }
+
+ checkPkgFiles(files)
+ if errorCount > 0 {
+ os.Exit(2)
+ }
+
+ if *verbose {
+ printStats(time.Since(start))
+ }
+}