mirror of
https://github.com/golang/go
synced 2024-11-19 03:14:42 -07:00
207 lines
4.3 KiB
Go
207 lines
4.3 KiB
Go
|
// 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)
|
||
|
}
|
||
|
processFiles(filenames, false)
|
||
|
}
|
||
|
|
||
|
func processFiles(filenames []string, allFiles bool) {
|
||
|
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()
|
||
|
processPackage(fset, parseFiles(fset, filenames[0:i]))
|
||
|
}
|
||
|
|
||
|
func processPackage(fset *token.FileSet, files []*ast.File) {
|
||
|
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)
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
ctxt.Check(fset, files)
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
flag.Usage = usage
|
||
|
flag.Parse()
|
||
|
|
||
|
if flag.NArg() == 0 {
|
||
|
fset := token.NewFileSet()
|
||
|
processPackage(fset, parseStdin(fset))
|
||
|
} else {
|
||
|
processFiles(flag.Args(), true)
|
||
|
}
|
||
|
|
||
|
if errorCount > 0 {
|
||
|
os.Exit(2)
|
||
|
}
|
||
|
}
|