Skip to content

Commit 4c16368

Browse files
authored
Merge pull request #59 from knz/20201212-errors
markers: delegate to an Is() method if present
2 parents a9df884 + fef5f1a commit 4c16368

File tree

3 files changed

+68
-1
lines changed

3 files changed

+68
-1
lines changed

markers/markers.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,22 @@ import (
2828
// Is determines whether one of the causes of the given error or any
2929
// of its causes is equivalent to some reference error.
3030
//
31+
// As in the Go standard library, an error is considered to match a
32+
// reference error if it is equal to that target or if it implements a
33+
// method Is(error) bool such that Is(reference) returns true.
34+
//
35+
// Note: the inverse is not true - making an Is(reference) method
36+
// return false does not imply that errors.Is() also returns
37+
// false. Errors can be equal because their network equality marker is
38+
// the same. To force errors to appear different to Is(), use
39+
// errors.Mark().
40+
//
3141
// Note: if any of the error types has been migrated from a previous
3242
// package location or a different type, ensure that
3343
// RegisterTypeMigration() was called prior to Is().
3444
func Is(err, reference error) bool {
3545
if reference == nil {
36-
return err == reference
46+
return err == nil
3747
}
3848

3949
// Direct reference comparison is the fastest, and most
@@ -42,6 +52,11 @@ func Is(err, reference error) bool {
4252
if c == reference {
4353
return true
4454
}
55+
// Compatibility with std go errors: if the error object itself
56+
// implements Is(), try to use that.
57+
if tryDelegateToIsMethod(c, reference) {
58+
return true
59+
}
4560
}
4661

4762
if err == nil {
@@ -67,6 +82,13 @@ func Is(err, reference error) bool {
6782
return false
6883
}
6984

85+
func tryDelegateToIsMethod(err, reference error) bool {
86+
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(reference) {
87+
return true
88+
}
89+
return false
90+
}
91+
7092
// HasType returns true iff err contains an error whose concrete type
7193
// matches that of referenceType.
7294
func HasType(err error, referenceType error) bool {
@@ -125,6 +147,11 @@ func IsAny(err error, references ...error) bool {
125147
if c == refErr {
126148
return true
127149
}
150+
// Compatibility with std go errors: if the error object itself
151+
// implements Is(), try to use that.
152+
if tryDelegateToIsMethod(c, refErr) {
153+
return true
154+
}
128155
}
129156
if c == nil {
130157
// This special case is to support a comparison to a nil

markers/markers_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,3 +569,33 @@ type invalidError struct {
569569

570570
func (e *invalidError) Error() string { return e.emptyRef.Error() }
571571
func (e *invalidError) Cause() error { return e.emptyRef }
572+
573+
func TestDelegateToIsMethod(t *testing.T) {
574+
tt := testutils.T{T: t}
575+
576+
efoo := &errWithIs{msg: "foo", seecret: "foo"}
577+
efoo2 := &errWithIs{msg: "foo", seecret: "bar"}
578+
ebar := &errWithIs{msg: "bar", seecret: "foo"}
579+
580+
tt.Check(markers.Is(efoo, efoo2)) // equality based on message
581+
tt.Check(markers.Is(efoo, ebar)) // equality based on method
582+
tt.Check(!markers.Is(efoo2, ebar)) // neither msg nor method
583+
584+
tt.Check(markers.IsAny(efoo, efoo2, ebar))
585+
tt.Check(markers.IsAny(efoo2, ebar, efoo))
586+
tt.Check(!markers.IsAny(efoo2, ebar, errors.New("other")))
587+
}
588+
589+
type errWithIs struct {
590+
msg string
591+
seecret string
592+
}
593+
594+
func (e *errWithIs) Error() string { return e.msg }
595+
596+
func (e *errWithIs) Is(o error) bool {
597+
if ex, ok := o.(*errWithIs); ok {
598+
return e.seecret == ex.seecret
599+
}
600+
return false
601+
}

markers_api.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ import "github.com/cockroachdb/errors/markers"
1919
// Is determines whether one of the causes of the given error or any
2020
// of its causes is equivalent to some reference error.
2121
//
22+
// As in the Go standard library, an error is considered to match a
23+
// reference error if it is equal to that target or if it implements a
24+
// method Is(error) bool such that Is(reference) returns true.
25+
//
26+
// Note: the inverse is not true - making an Is(reference) method
27+
// return false does not imply that errors.Is() also returns
28+
// false. Errors can be equal because their network equality marker is
29+
// the same. To force errors to appear different to Is(), use
30+
// errors.Mark().
31+
//
2232
// Note: if any of the error types has been migrated from a previous
2333
// package location or a different type, ensure that
2434
// RegisterTypeMigration() was called prior to Is().

0 commit comments

Comments
 (0)