1
0
mirror of https://github.com/golang/go synced 2024-11-19 01:14:39 -07:00
go/gotype/gotype.go
Rob Pike 01f8cd246d go.tools: add go/types, ssa, and cmd/vet
They will be deleted from their current homes once this has landed.
Changes made to import paths to make the code compile, and to find
errchk in the right place in cmd/vet's Makefile.
TODO in a later CL: tidy up vet.

R=golang-dev, gri
CC=golang-dev
https://golang.org/cl/9495043
2013-05-17 13:20:39 -07:00

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)
}
}