2013-05-17 14:20:39 -06:00
|
|
|
// 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.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
|
|
|
"go/parser"
|
|
|
|
"go/scanner"
|
|
|
|
"go/token"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"code.google.com/p/go.tools/go/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// main operation modes
|
|
|
|
pkgName = flag.String("p", "", "process only those files in package pkgName")
|
|
|
|
recursive = flag.Bool("r", false, "recursively process subdirectories")
|
|
|
|
verbose = flag.Bool("v", false, "verbose mode")
|
|
|
|
allErrors = flag.Bool("e", false, "report all errors (not just the first 10 on different lines)")
|
|
|
|
|
|
|
|
// debugging support
|
|
|
|
parseComments = flag.Bool("comments", false, "parse comments (ignored if -ast not set)")
|
|
|
|
printTrace = flag.Bool("trace", false, "print parse trace")
|
|
|
|
printAST = flag.Bool("ast", false, "print AST")
|
|
|
|
)
|
|
|
|
|
|
|
|
var errorCount int
|
|
|
|
|
|
|
|
func usage() {
|
|
|
|
fmt.Fprintf(os.Stderr, "usage: gotype [flags] [path ...]\n")
|
|
|
|
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 returns the AST for the Go source src.
|
|
|
|
// The filename is for error reporting only.
|
|
|
|
// The result is nil if there were errors or if
|
|
|
|
// the file does not belong to the -p package.
|
|
|
|
func parse(fset *token.FileSet, filename string, src []byte) *ast.File {
|
|
|
|
if *verbose {
|
|
|
|
fmt.Println(filename)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ignore files with different package name
|
|
|
|
if *pkgName != "" {
|
|
|
|
file, err := parser.ParseFile(fset, filename, src, parser.PackageClauseOnly)
|
|
|
|
if err != nil {
|
|
|
|
report(err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if file.Name.Name != *pkgName {
|
|
|
|
if *verbose {
|
|
|
|
fmt.Printf("\tignored (package %s)\n", file.Name.Name)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse entire file
|
|
|
|
mode := parser.DeclarationErrors
|
|
|
|
if *allErrors {
|
|
|
|
mode |= parser.AllErrors
|
|
|
|
}
|
|
|
|
if *parseComments && *printAST {
|
|
|
|
mode |= parser.ParseComments
|
|
|
|
}
|
|
|
|
if *printTrace {
|
|
|
|
mode |= parser.Trace
|
|
|
|
}
|
|
|
|
file, err := parser.ParseFile(fset, filename, src, mode)
|
|
|
|
if err != nil {
|
|
|
|
report(err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if *printAST {
|
|
|
|
ast.Print(fset, file)
|
|
|
|
}
|
|
|
|
|
|
|
|
return file
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseStdin(fset *token.FileSet) (files []*ast.File) {
|
|
|
|
src, err := ioutil.ReadAll(os.Stdin)
|
|
|
|
if err != nil {
|
|
|
|
report(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
const filename = "<standard input>"
|
|
|
|
if file := parse(fset, filename, src); file != nil {
|
|
|
|
files = []*ast.File{file}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseFiles(fset *token.FileSet, filenames []string) (files []*ast.File) {
|
|
|
|
for _, filename := range filenames {
|
|
|
|
src, err := ioutil.ReadFile(filename)
|
|
|
|
if err != nil {
|
|
|
|
report(err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if file := parse(fset, filename, src); file != nil {
|
|
|
|
files = append(files, file)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func isGoFilename(filename string) bool {
|
|
|
|
// ignore non-Go files
|
|
|
|
return !strings.HasPrefix(filename, ".") && strings.HasSuffix(filename, ".go")
|
|
|
|
}
|
|
|
|
|
|
|
|
func processDirectory(dirname string) {
|
|
|
|
f, err := os.Open(dirname)
|
|
|
|
if err != nil {
|
|
|
|
report(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
filenames, err := f.Readdirnames(-1)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
report(err)
|
|
|
|
// continue since filenames may not be empty
|
|
|
|
}
|
|
|
|
for i, filename := range filenames {
|
|
|
|
filenames[i] = filepath.Join(dirname, filename)
|
|
|
|
}
|
2013-05-20 13:56:17 -06:00
|
|
|
processFiles(dirname, filenames, false)
|
2013-05-17 14:20:39 -06:00
|
|
|
}
|
|
|
|
|
2013-05-20 13:56:17 -06:00
|
|
|
func processFiles(path string, filenames []string, allFiles bool) {
|
2013-05-17 14:20:39 -06:00
|
|
|
i := 0
|
|
|
|
for _, filename := range filenames {
|
|
|
|
switch info, err := os.Stat(filename); {
|
|
|
|
case err != nil:
|
|
|
|
report(err)
|
|
|
|
case info.IsDir():
|
|
|
|
if allFiles || *recursive {
|
|
|
|
processDirectory(filename)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
if allFiles || isGoFilename(info.Name()) {
|
|
|
|
filenames[i] = filename
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fset := token.NewFileSet()
|
2013-05-20 13:56:17 -06:00
|
|
|
processPackage(path, fset, parseFiles(fset, filenames[0:i]))
|
2013-05-17 14:20:39 -06:00
|
|
|
}
|
|
|
|
|
2013-05-20 13:56:17 -06:00
|
|
|
func processPackage(path string, fset *token.FileSet, files []*ast.File) {
|
2013-05-17 14:20:39 -06:00
|
|
|
type bailout struct{}
|
|
|
|
ctxt := types.Context{
|
|
|
|
Error: func(err error) {
|
|
|
|
if !*allErrors && errorCount >= 10 {
|
|
|
|
panic(bailout{})
|
|
|
|
}
|
|
|
|
report(err)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
switch err := recover().(type) {
|
|
|
|
case nil, bailout:
|
|
|
|
default:
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2013-05-20 13:56:17 -06:00
|
|
|
ctxt.Check(path, fset, files...)
|
2013-05-17 14:20:39 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
flag.Usage = usage
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
if flag.NArg() == 0 {
|
|
|
|
fset := token.NewFileSet()
|
2013-05-20 13:56:17 -06:00
|
|
|
processPackage("<stdin>", fset, parseStdin(fset))
|
2013-05-17 14:20:39 -06:00
|
|
|
} else {
|
2013-05-20 13:56:17 -06:00
|
|
|
processFiles("<files>", flag.Args(), true)
|
2013-05-17 14:20:39 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if errorCount > 0 {
|
|
|
|
os.Exit(2)
|
|
|
|
}
|
|
|
|
}
|