mirror of
https://github.com/golang/go
synced 2024-11-19 07:44:49 -07:00
03e3f0cf81
This CL makes gotype usable again. Removed -r (recursive) mode; use go/build to determine the correct set of Go files when processing a directory. The -v (verbose) mode now prints some basic stats (duration, number of files, lines, and lines/s). Thoroughly restructured the code. Applying gotype -v -a . to the go/types directory: 128.94141ms (40 files, 12008 lines, 93127 lines/s) On a 2.8 GHz Quad-Core Intel Xeon, 800 MHz DDR2 FB-DIMM, with go/types built with the (interal) debug flag set to false. There's still quite a bit of room for performance improvement in all parts of the code since no tuning has been done. R=golang-dev, adonovan CC=golang-dev https://golang.org/cl/19930043
220 lines
4.2 KiB
Go
220 lines
4.2 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/build"
|
|
"go/parser"
|
|
"go/scanner"
|
|
"go/token"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"code.google.com/p/go.tools/go/types"
|
|
)
|
|
|
|
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")
|
|
|
|
// debugging support
|
|
printAST = flag.Bool("ast", false, "print AST")
|
|
printTrace = flag.Bool("trace", false, "print parse trace")
|
|
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++
|
|
}
|
|
|
|
func parse(filename string, src interface{}) (*ast.File, error) {
|
|
if *verbose {
|
|
fmt.Println(filename)
|
|
}
|
|
file, err := parser.ParseFile(fset, filename, src, parserMode)
|
|
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) {
|
|
var files []*ast.File
|
|
for _, filename := range filenames {
|
|
file, err := parse(filename, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
files = append(files, 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
|
|
var file *ast.File
|
|
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) {
|
|
type bailout struct{}
|
|
conf := types.Config{
|
|
FakeImportC: true,
|
|
Error: func(err error) {
|
|
if !*allErrors && errorCount >= 10 {
|
|
panic(bailout{})
|
|
}
|
|
report(err)
|
|
},
|
|
Sizes: sizes,
|
|
}
|
|
|
|
defer func() {
|
|
switch err := recover().(type) {
|
|
case nil, bailout:
|
|
default:
|
|
panic(err)
|
|
}
|
|
}()
|
|
|
|
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()
|
|
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))
|
|
}
|
|
}
|