Skip to content

Commit

Permalink
xerrors: make As match on assignability
Browse files Browse the repository at this point in the history
Change As to consider assignability rather than type equivalence, allowing
it to convert an error to an interface type. e.g.,

	var to interface{ Timeout() bool }
	if errors.As(err, &to) && to.Timeout() { ... }j

Change-Id: Ia3ecaefb6c27f878f81f1d467b07b69fee9db976
Reviewed-on: https://go-review.googlesource.com/c/161017
Reviewed-by: Marcel van Lohuizen <[email protected]>
  • Loading branch information
neild committed Feb 7, 2019
1 parent d6e390b commit 4e09f5a
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 9 deletions.
9 changes: 5 additions & 4 deletions wrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,10 @@ func Is(err, target error) bool {
}

// As finds the first error in err's chain that matches the type to which target
// points, and if so, sets the target to its value and and returns true. An error
// matches a type if it is of the same type, or if it has a method As(interface{}) bool
// such that As(target) returns true. As will panic if target is nil or not a pointer.
// points, and if so, sets the target to its value and returns true. An error
// matches a type if it is assignable to the target type, or if it has a method
// As(interface{}) bool such that As(target) returns true. As will panic if target
// is nil or not a pointer.
//
// The As method should set the target to its value and return true if err
// matches the type to which target points.
Expand All @@ -84,7 +85,7 @@ func As(err error, target interface{}) bool {
}
targetType := typ.Elem()
for {
if reflect.TypeOf(err) == targetType {
if reflect.TypeOf(err).AssignableTo(targetType) {
reflect.ValueOf(target).Elem().Set(reflect.ValueOf(err))
return true
}
Expand Down
19 changes: 14 additions & 5 deletions wrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ func (p *poser) As(err interface{}) bool {
func TestAs(t *testing.T) {
var errT errorT
var errP *os.PathError
var timeout interface{ Timeout() bool }
var p *poser
_, errF := os.Open("non-existing")

Expand Down Expand Up @@ -120,16 +121,24 @@ func TestAs(t *testing.T) {
&p,
true,
}, {
&poser{"oo", nil},
&errF,
xerrors.New("err"),
&timeout,
false,
}, {
errF,
&timeout,
true,
}, {
xerrors.Errorf("path error: %w", errF),
&timeout,
true,
}}
for _, tc := range testCases {
name := fmt.Sprintf("As(Errorf(..., %v), %v)", tc.err, tc.target)
for i, tc := range testCases {
name := fmt.Sprintf("%d:As(Errorf(..., %v), %v)", i, tc.err, tc.target)
t.Run(name, func(t *testing.T) {
match := xerrors.As(tc.err, tc.target)
if match != tc.match {
t.Fatalf("match: got %v; want %v", match, tc.match)
t.Fatalf("xerrors.As(%T, %T): got %v; want %v", tc.err, tc.target, match, tc.match)
}
if !match {
return
Expand Down

0 comments on commit 4e09f5a

Please sign in to comment.