Skip to content

Commit

Permalink
Add support for concrete error types. Fixes kisielk#80
Browse files Browse the repository at this point in the history
  • Loading branch information
kisielk committed Sep 3, 2015
1 parent 9162db7 commit 3861183
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 8 deletions.
12 changes: 9 additions & 3 deletions internal/errcheck/errcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ import (
"golang.org/x/tools/go/types"
)

var errorType *types.Interface

func init() {
errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)

}

var (
// ErrNoGoFiles is returned when CheckPackage is run on a package with no Go source files
ErrNoGoFiles = errors.New("package contains no go source files")
Expand Down Expand Up @@ -372,10 +379,9 @@ func (v *visitor) Visit(node ast.Node) ast.Visitor {
}

type obj interface {
Pkg() *types.Package
Name() string
Type() types.Type
}

func isErrorType(v obj) bool {
return v.Pkg() == nil && v.Name() == "error"
return types.Implements(v.Type(), errorType)
}
38 changes: 34 additions & 4 deletions internal/errcheck/errcheck_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package errcheck

import (
"fmt"
"go/build"
"go/parser"
"go/token"
Expand All @@ -19,6 +20,14 @@ type marker struct {
line int
}

func newMarker(e UncheckedError) marker {
return marker{e.Pos.Filename, e.Pos.Line}
}

func (m marker) String() string {
return fmt.Sprintf("%s:%d", m.file, m.line)
}

func init() {
unchecked = make(map[marker]bool)
blank = make(map[marker]bool)
Expand Down Expand Up @@ -61,8 +70,14 @@ func TestUnchecked(t *testing.T) {
numErrors := len(unchecked)
if len(uerr.Errors) != numErrors {
t.Errorf("got %d errors, want %d", len(uerr.Errors), numErrors)
for i, err := range uerr.Errors {
t.Errorf("%d: %v", i, err)
unchecked_loop:
for k := range unchecked {
for _, e := range uerr.Errors {
if newMarker(e) == k {
continue unchecked_loop
}
}
t.Errorf("Expected unchecked at %s", k)
}
return
}
Expand Down Expand Up @@ -90,8 +105,23 @@ func TestBlank(t *testing.T) {
numErrors := len(unchecked) + len(blank)
if len(uerr.Errors) != numErrors {
t.Errorf("got %d errors, want %d", len(uerr.Errors), numErrors)
for i, err := range uerr.Errors {
t.Errorf("%d: %v", i, err)
unchecked_loop:
for k := range unchecked {
for _, e := range uerr.Errors {
if newMarker(e) == k {
continue unchecked_loop
}
}
t.Errorf("Expected unchecked at %s", k)
}
blank_loop:
for k := range blank {
for _, e := range uerr.Errors {
if newMarker(e) == k {
continue blank_loop
}
}
t.Errorf("Expected blank at %s", k)
}
return
}
Expand Down
2 changes: 1 addition & 1 deletion main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func TestMain(t *testing.T) {
t.Errorf("Exit code is %d, expected %d", exitCode, exitUncheckedError)
}

expectUnchecked := 9
expectUnchecked := 10
if got := strings.Count(out, "UNCHECKED"); got != expectUnchecked {
t.Errorf("Got %d UNCHECKED errors, expected %d in:\n%s", got, expectUnchecked, out)
}
Expand Down
22 changes: 22 additions & 0 deletions testdata/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ func rec() {
defer recover() // UNCHECKED
}

type MyError string

func (e MyError) Error() string {
return string(e)
}

func customError() error {
return MyError("an error occurred")
}

func customConcreteError() MyError {
return MyError("an error occurred")
}

func main() {
// Single error return
_ = a() // BLANK
Expand All @@ -34,6 +48,14 @@ func main() {
_, _ = b() // BLANK
b() // UNCHECKED

// Return a custom error type
_ = customError() // BLANK
customError() // UNCHECKED

// Return a custom concrete error type
_ = customConcreteError() // BLANK
customConcreteError() // UNCHECKED

// Method with a single error return
x := t{}
_ = x.a() // BLANK
Expand Down

0 comments on commit 3861183

Please sign in to comment.