diff options
Diffstat (limited to 'src/cmd/vet/main.go')
| -rw-r--r-- | src/cmd/vet/main.go | 118 |
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. |
