Commit dbff0ada authored by Robert Griesemer's avatar Robert Griesemer

gotype: commandline tool to typecheck go programs

First version. Handles scope analysis only at the
moment.

R=rsc, r, eds
CC=golang-dev
https://golang.org/cl/4259065
parent 9554e671
...@@ -43,6 +43,7 @@ CLEANDIRS=\ ...@@ -43,6 +43,7 @@ CLEANDIRS=\
godoc\ godoc\
gofmt\ gofmt\
goinstall\ goinstall\
gotype\
goyacc\ goyacc\
hgpatch\ hgpatch\
......
# 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.
include ../../Make.inc
TARG=gotype
GOFILES=\
gotype.go\
include ../../Make.cmd
// 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.
/*
The gotype command does syntactic and semantic analysis of Go files
and packages similar to the analysis performed by 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 processes the standard input, which must
be the source of a single package file.
Given a list of file names, each file must be a source file belonging to
the same package unless the package name is explicitly specified with the
-p flag.
Given a directory name, gotype collects all .go files in the directory
and processes them as if they were provided as an explicit list of file
names. Each directory is processed independently. Files starting with .
or not ending in .go are ignored.
Usage:
gotype [flags] [path ...]
The flags are:
-p pkgName
process only those files in package pkgName.
-r
recursively process subdirectories.
-v
verbose mode.
Debugging flags:
-trace
print parse trace (disables concurrent parsing).
-ast
print AST (disables concurrent parsing).
Examples
To check the files file.go, old.saved, and .ignored:
gotype file.go old.saved .ignored
To check all .go files belonging to package main in the current directory
and recursively in all subdirectories:
gotype -p main -r .
To verify the output of a pipe:
echo "package foo" | gotype
*/
package documentation
// 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"
)
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")
// debugging support
printTrace = flag.Bool("trace", false, "print parse trace")
printAST = flag.Bool("ast", false, "print AST")
)
var (
fset = token.NewFileSet()
exitCode = 0
parserMode = parser.DeclarationErrors
)
func usage() {
fmt.Fprintf(os.Stderr, "usage: gotype [flags] [path ...]\n")
flag.PrintDefaults()
os.Exit(2)
}
func processFlags() {
flag.Usage = usage
flag.Parse()
if *printTrace {
parserMode |= parser.Trace
}
}
func report(err os.Error) {
scanner.PrintError(os.Stderr, err)
exitCode = 2
}
// parseFile returns the AST for the given file.
// The result
func parseFile(filename string) *ast.File {
if *verbose {
fmt.Println(filename)
}
// get source
src, err := ioutil.ReadFile(filename)
if err != nil {
report(err)
return nil
}
// 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
file, err := parser.ParseFile(fset, filename, src, parserMode)
if err != nil {
report(err)
return nil
}
if *printAST {
ast.Print(fset, file)
}
return file
}
// BUG(gri): At the moment, only single-file scope analysis is performed.
func processPackage(filenames []string) {
var files []*ast.File
pkgName := ""
for _, filename := range filenames {
file := parseFile(filename)
if file == nil {
continue // ignore file
}
// package names must match
// TODO(gri): this check should be moved into a
// function making the package below
if pkgName == "" {
// first package file
pkgName = file.Name.Name
} else {
if file.Name.Name != pkgName {
report(os.NewError(fmt.Sprintf("file %q is in package %q not %q", filename, file.Name.Name, pkgName)))
continue
}
}
files = append(files, file)
}
// TODO(gri): make a ast.Package and analyze it
_ = files
}
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, os.O_RDONLY, 0)
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.IsRegular():
if allFiles || isGoFilename(info.Name) {
filenames[i] = filename
i++
}
case info.IsDirectory():
if allFiles || *recursive {
processDirectory(filename)
}
}
}
processPackage(filenames[0:i])
}
func main() {
processFlags()
if flag.NArg() == 0 {
processPackage([]string{os.Stdin.Name()})
} else {
processFiles(flag.Args(), true)
}
os.Exit(exitCode)
}
...@@ -156,6 +156,7 @@ DIRS=\ ...@@ -156,6 +156,7 @@ DIRS=\
../cmd/ebnflint\ ../cmd/ebnflint\
../cmd/godoc\ ../cmd/godoc\
../cmd/gofmt\ ../cmd/gofmt\
../cmd/gotype\
../cmd/goinstall\ ../cmd/goinstall\
../cmd/govet\ ../cmd/govet\
../cmd/goyacc\ ../cmd/goyacc\
...@@ -191,6 +192,7 @@ NOTEST=\ ...@@ -191,6 +192,7 @@ NOTEST=\
../cmd/ebnflint\ ../cmd/ebnflint\
../cmd/godoc\ ../cmd/godoc\
../cmd/gofmt\ ../cmd/gofmt\
../cmd/gotype\
../cmd/govet\ ../cmd/govet\
../cmd/goyacc\ ../cmd/goyacc\
../cmd/hgpatch\ ../cmd/hgpatch\
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment