diff --git a/error.go b/error.go index b8aad09..e98ce3b 100644 --- a/error.go +++ b/error.go @@ -93,8 +93,25 @@ func New(e interface{}) *Error { // explicitly wrap an *Error with a new stacktrace use Errorf. The skip // parameter indicates how far up the stack to start the stacktrace. 0 is from // the current call, 1 from its caller, etc. -func Wrap(e interface{}, skip int) *Error { + +// isNil reports whether e is a nil interface value or a typed nil +// (nil pointer/slice/map/func/chan/interface). Zero-value structs that +// implement error are not considered nil. +func isNil(e interface{}) bool { if e == nil { + return true + } + v := reflect.ValueOf(e) + switch v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer: + return v.IsNil() + default: + return false + } +} + +func Wrap(e interface{}, skip int) *Error { + if isNil(e) { return nil } @@ -127,7 +144,7 @@ func Wrap(e interface{}, skip int) *Error { // the stack to start the stacktrace. 0 is from the current call, 1 from its // caller, etc. func WrapPrefix(e interface{}, prefix string, skip int) *Error { - if e == nil { + if isNil(e) { return nil } diff --git a/error_test.go b/error_test.go index 5f740e5..12eacc1 100644 --- a/error_test.go +++ b/error_test.go @@ -345,3 +345,24 @@ type errorString string func (e errorString) Error() string { return string(e) } + +type typedNilError struct{ message string } + +func (e *typedNilError) Error() string { return e.message } + +func TestWrapTypedNil(t *testing.T) { + var ptr *typedNilError + if Wrap(ptr, 0) != nil { + t.Errorf("Wrap typed nil pointer should return nil") + } + if WrapPrefix(ptr, "prefix", 0) != nil { + t.Errorf("WrapPrefix typed nil pointer should return nil") + } + if Wrap(nil, 0) != nil || WrapPrefix(nil, "p", 0) != nil { + t.Errorf("Wrap/WrapPrefix nil should return nil") + } + nonNil := &typedNilError{message: "x"} + if Wrap(nonNil, 0) == nil { + t.Errorf("Wrap non-nil pointer should wrap") + } +}