Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit 63cdf81bc91915426fbdfc941214a91fc201f8f0 @kisielk committed Feb 24, 2013
Showing with 198 additions and 0 deletions.
  1. +22 −0 LICENSE
  2. +21 −0 README.md
  3. BIN example/example
  4. +20 −0 example/main.go
  5. +135 −0 main.go
@@ -0,0 +1,22 @@
+Copyright (c) 2013 Kamil Kisiel
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,21 @@
+errcheck
+=========
+
+errcheck is a program for checking for unchecked errors in go programs.
+
+Install
+-------
+
+ go get github.com/kisielk/errcheck
+
+Note that errcheck depends on the go/types package which is currently only available in go tip.
+
+Use
+---
+
+For basic usage, just give the package path of interest as the first
+argument:
+
+ errcheck github.com/kisielk/errcheck/example
+
+The output format is incomplete.
Binary file not shown.
@@ -0,0 +1,20 @@
+package main
+
+import "fmt"
+
+func a() error {
+ fmt.Println("this function returns an error")
+ return nil
+}
+
+func b() (int, error) {
+ fmt.Println("this function returns an int and an error")
+ return 0, nil
+}
+
+func main() {
+ _ = a()
+ a()
+ _, _ = b()
+ b()
+}
@@ -0,0 +1,135 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/parser"
+ "go/token"
+ "go/types"
+ "os"
+ "path/filepath"
+)
+
+func main() {
+ flag.Parse()
+ pkgName := flag.Arg(0)
+ if pkgName == "" {
+ fmt.Fprintln(os.Stderr, "you must specify a package")
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ pkg, err := build.Import(pkgName, ".", 0)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, "could not import %s: %s", pkgName, err)
+ }
+
+ for _, fileName := range pkg.GoFiles {
+ filePath := filepath.Join(pkg.Dir, fileName)
+ if err := checkFile(filePath); err != nil {
+ fmt.Fprintln(os.Stderr, "could not check %s: %s", filePath, err)
+ }
+ }
+}
+
+func checkFile(fileName string) error {
+ fset := token.NewFileSet()
+
+ astFile, err := parser.ParseFile(fset, fileName, nil, parser.ParseComments)
+ if err != nil {
+ return fmt.Errorf("could not parse: %s", err)
+ }
+
+ callTypes := make(map[*ast.CallExpr]types.Type)
+
+ exprFn := func(x ast.Expr, typ types.Type, val interface{}) {
+ call, ok := x.(*ast.CallExpr)
+ if !ok {
+ return
+ }
+ callTypes[call] = typ
+ }
+ context := types.Context{
+ Expr: exprFn,
+ }
+ _, err = context.Check(fset, []*ast.File{astFile})
+ if err != nil {
+ return err
+ }
+
+ visitor := func(node ast.Node) {
+ n, ok := node.(*ast.ExprStmt)
+ if !ok {
+ return
+ }
+
+ // Check for a call expression
+ call, ok := n.X.(*ast.CallExpr)
+ if !ok {
+ return
+ }
+
+ var fun *ast.Ident
+ switch exp := call.Fun.(type) {
+ case (*ast.Ident):
+ fun = exp
+ case (*ast.SelectorExpr):
+ fun = exp.Sel
+ default:
+ fmt.Fprintf(os.Stderr, "unknown call: %T %+v\n", exp, exp)
+ return
+ }
+
+ // Get the types
+ callType := callTypes[call]
+
+ unchecked := false
+
+ switch t := callType.(type) {
+ case *types.NamedType:
+ // Single return
+ if isErrorType(t.Obj) {
+ unchecked = true
+ }
+ case *types.Result:
+ // Multiple returns
+ for _, v := range t.Values {
+ nt, ok := v.Type.(*types.NamedType)
+ if !ok {
+ continue
+ }
+ if isErrorType(nt.Obj) {
+ unchecked = true
+ break
+ }
+ }
+ }
+
+ if unchecked {
+ fmt.Fprintf(os.Stderr, "%s\n", fset.Position(fun.NamePos))
+ }
+ }
+
+ ast.Walk(visitorFunc(visitor), astFile)
+ ast.Fprint(os.Stderr, fset, astFile, nil)
+
+ return nil
+}
+
+type obj interface {
+ GetPkg() *types.Package
+ GetName() string
+}
+
+func isErrorType(v obj) bool {
+ return v.GetPkg() == nil && v.GetName() == "error"
+}
+
+type visitorFunc func(node ast.Node)
+
+func (v visitorFunc) Visit(node ast.Node) ast.Visitor {
+ v(node)
+ return v
+}

0 comments on commit 63cdf81

Please sign in to comment.