summaryrefslogtreecommitdiff
path: root/src/cmd/vet/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/vet/main.go')
-rw-r--r--src/cmd/vet/main.go118
1 files changed, 70 insertions, 48 deletions
diff --git a/src/cmd/vet/main.go b/src/cmd/vet/main.go
index 20f6cca1a..2fefa0b47 100644
--- a/src/cmd/vet/main.go
+++ b/src/cmd/vet/main.go
@@ -15,7 +15,6 @@ import (
"go/parser"
"go/printer"
"go/token"
- "go/types"
"io/ioutil"
"os"
"path/filepath"
@@ -29,14 +28,17 @@ var exitCode = 0
// Flags to control which checks to perform. "all" is set to true here, and disabled later if
// a flag is set explicitly.
var report = map[string]*bool{
- "all": flag.Bool("all", true, "check everything; disabled if any explicit check is requested"),
- "atomic": flag.Bool("atomic", false, "check for common mistaken usages of the sync/atomic package"),
- "buildtags": flag.Bool("buildtags", false, "check that +build tags are valid"),
- "composites": flag.Bool("composites", false, "check that composite literals used type-tagged elements"),
- "methods": flag.Bool("methods", false, "check that canonically named methods are canonically defined"),
- "printf": flag.Bool("printf", false, "check printf-like invocations"),
- "structtags": flag.Bool("structtags", false, "check that struct field tags have canonical format"),
- "rangeloops": flag.Bool("rangeloops", false, "check that range loop variables are used correctly"),
+ "all": flag.Bool("all", true, "check everything; disabled if any explicit check is requested"),
+ "asmdecl": flag.Bool("asmdecl", false, "check assembly against Go declarations"),
+ "assign": flag.Bool("assign", false, "check for useless assignments"),
+ "atomic": flag.Bool("atomic", false, "check for common mistaken usages of the sync/atomic package"),
+ "buildtags": flag.Bool("buildtags", false, "check that +build tags are valid"),
+ "composites": flag.Bool("composites", false, "check that composite literals used type-tagged elements"),
+ "methods": flag.Bool("methods", false, "check that canonically named methods are canonically defined"),
+ "printf": flag.Bool("printf", false, "check printf-like invocations"),
+ "rangeloops": flag.Bool("rangeloops", false, "check that range loop variables are used correctly"),
+ "structtags": flag.Bool("structtags", false, "check that struct field tags have canonical format"),
+ "unreachable": flag.Bool("unreachable", false, "check for unreachable code"),
}
// vet tells whether to report errors for the named check, a flag name.
@@ -64,11 +66,12 @@ func Usage() {
// File is a wrapper for the state of a file used in the parser.
// The parse tree walkers are all methods of this type.
type File struct {
- pkg *Package
- fset *token.FileSet
- name string
- file *ast.File
- b bytes.Buffer // for use by methods
+ pkg *Package
+ fset *token.FileSet
+ name string
+ content []byte
+ file *ast.File
+ b bytes.Buffer // for use by methods
}
func main() {
@@ -163,6 +166,7 @@ func doPackageDir(directory string) {
names = append(names, pkg.GoFiles...)
names = append(names, pkg.CgoFiles...)
names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package.
+ names = append(names, pkg.SFiles...)
prefixDirectory(directory, names)
doPackage(names)
// Is there also a "foo_test" package? If so, do that one as well.
@@ -174,8 +178,9 @@ func doPackageDir(directory string) {
}
type Package struct {
- types map[ast.Expr]types.Type
+ types map[ast.Expr]Type
values map[ast.Expr]interface{}
+ files []*File
}
// doPackage analyzes the single package constructed from the named files.
@@ -186,50 +191,48 @@ func doPackage(names []string) {
for _, name := range names {
f, err := os.Open(name)
if err != nil {
- errorf("%s: %s", name, err)
+ // Warn but continue to next package.
+ warnf("%s: %s", name, err)
+ return
}
defer f.Close()
data, err := ioutil.ReadAll(f)
if err != nil {
- errorf("%s: %s", name, err)
+ warnf("%s: %s", name, err)
+ return
}
checkBuildTag(name, data)
- parsedFile, err := parser.ParseFile(fs, name, bytes.NewReader(data), 0)
- if err != nil {
- errorf("%s: %s", name, err)
+ var parsedFile *ast.File
+ if strings.HasSuffix(name, ".go") {
+ parsedFile, err = parser.ParseFile(fs, name, bytes.NewReader(data), 0)
+ if err != nil {
+ warnf("%s: %s", name, err)
+ return
+ }
+ astFiles = append(astFiles, parsedFile)
}
- files = append(files, &File{fset: fs, name: name, file: parsedFile})
- astFiles = append(astFiles, parsedFile)
+ files = append(files, &File{fset: fs, content: data, name: name, file: parsedFile})
}
pkg := new(Package)
- pkg.types = make(map[ast.Expr]types.Type)
- pkg.values = make(map[ast.Expr]interface{})
- exprFn := func(x ast.Expr, typ types.Type, val interface{}) {
- pkg.types[x] = typ
- if val != nil {
- pkg.values[x] = val
- }
- }
- // By providing the Context with our own error function, it will continue
- // past the first error. There is no need for that function to do anything.
- context := types.Context{
- Expr: exprFn,
- Error: func(error) {},
- }
+ pkg.files = files
// Type check the package.
- _, err := context.Check(fs, astFiles)
+ err := pkg.check(fs, astFiles)
if err != nil && *verbose {
warnf("%s", err)
}
for _, file := range files {
file.pkg = pkg
- file.walkFile(file.name, file.file)
+ if file.file != nil {
+ file.walkFile(file.name, file.file)
+ }
}
+ asmCheck(pkg)
}
func visit(path string, f os.FileInfo, err error) error {
if err != nil {
- errorf("walk error: %s", err)
+ warnf("walk error: %s", err)
+ return err
}
// One package per directory. Ignore the files themselves.
if !f.IsDir() {
@@ -239,7 +242,16 @@ func visit(path string, f os.FileInfo, err error) error {
return nil
}
-// walkDir recursively walks the tree looking for .go files.
+func (pkg *Package) hasFileWithSuffix(suffix string) bool {
+ for _, f := range pkg.files {
+ if strings.HasSuffix(f.name, suffix) {
+ return true
+ }
+ }
+ return false
+}
+
+// walkDir recursively walks the tree looking for Go packages.
func walkDir(root string) {
filepath.Walk(root, visit)
}
@@ -287,6 +299,9 @@ func (f *File) Badf(pos token.Pos, format string, args ...interface{}) {
}
func (f *File) loc(pos token.Pos) string {
+ if pos == token.NoPos {
+ return ""
+ }
// Do not print columns. Because the pos often points to the start of an
// expression instead of the inner part with the actual error, the
// precision can mislead.
@@ -322,7 +337,9 @@ func (f *File) Visit(node ast.Node) ast.Visitor {
case *ast.Field:
f.walkFieldTag(n)
case *ast.FuncDecl:
- f.walkMethodDecl(n)
+ f.walkFuncDecl(n)
+ case *ast.FuncLit:
+ f.walkFuncLit(n)
case *ast.InterfaceType:
f.walkInterfaceType(n)
case *ast.RangeStmt:
@@ -333,6 +350,7 @@ func (f *File) Visit(node ast.Node) ast.Visitor {
// walkAssignStmt walks an assignment statement
func (f *File) walkAssignStmt(stmt *ast.AssignStmt) {
+ f.checkAssignStmt(stmt)
f.checkAtomicAssignment(stmt)
}
@@ -364,18 +382,22 @@ func (f *File) walkFieldTag(field *ast.Field) {
f.checkCanonicalFieldTag(field)
}
-// walkMethodDecl walks the method's signature.
+// walkMethod walks the method's signature.
func (f *File) walkMethod(id *ast.Ident, t *ast.FuncType) {
f.checkCanonicalMethod(id, t)
}
-// walkMethodDecl walks the method signature in the declaration.
-func (f *File) walkMethodDecl(d *ast.FuncDecl) {
- if d.Recv == nil {
- // not a method
- return
+// walkFuncDecl walks a function declaration.
+func (f *File) walkFuncDecl(d *ast.FuncDecl) {
+ f.checkUnreachable(d.Body)
+ if d.Recv != nil {
+ f.walkMethod(d.Name, d.Type)
}
- f.walkMethod(d.Name, d.Type)
+}
+
+// walkFuncLit walks a function literal.
+func (f *File) walkFuncLit(x *ast.FuncLit) {
+ f.checkUnreachable(x.Body)
}
// walkInterfaceType walks the method signatures of an interface.