Source file src/errors/wrap.go

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package errors
     6  
     7  import (
     8  	"internal/reflectlite"
     9  )
    10  
    11  // Unwrap returns the result of calling the Unwrap method on err, if err's
    12  // type contains an Unwrap method returning error.
    13  // Otherwise, Unwrap returns nil.
    14  //
    15  // Unwrap only calls a method of the form "Unwrap() error".
    16  // In particular Unwrap does not unwrap errors returned by [Join].
    17  func Unwrap(err error) error {
    18  	u, ok := err.(interface {
    19  		Unwrap() error
    20  	})
    21  	if !ok {
    22  		return nil
    23  	}
    24  	return u.Unwrap()
    25  }
    26  
    27  // Is reports whether any error in err's tree matches target.
    28  // The target must be comparable.
    29  //
    30  // The tree consists of err itself, followed by the errors obtained by repeatedly
    31  // calling its Unwrap() error or Unwrap() []error method. When err wraps multiple
    32  // errors, Is examines err followed by a depth-first traversal of its children.
    33  //
    34  // An error is considered to match a target if it is equal to that target or if
    35  // it implements a method Is(error) bool such that Is(target) returns true.
    36  //
    37  // An error type might provide an Is method so it can be treated as equivalent
    38  // to an existing error. For example, if MyError defines
    39  //
    40  //	func (m MyError) Is(target error) bool { return target == fs.ErrExist }
    41  //
    42  // then Is(MyError{}, fs.ErrExist) returns true. See [syscall.Errno.Is] for
    43  // an example in the standard library. An Is method should only shallowly
    44  // compare err and the target and not call [Unwrap] on either.
    45  func Is(err, target error) bool {
    46  	if err == nil || target == nil {
    47  		return err == target
    48  	}
    49  
    50  	isComparable := reflectlite.TypeOf(target).Comparable()
    51  	return is(err, target, isComparable)
    52  }
    53  
    54  func is(err, target error, targetComparable bool) bool {
    55  	for {
    56  		if targetComparable && err == target {
    57  			return true
    58  		}
    59  		if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
    60  			return true
    61  		}
    62  		switch x := err.(type) {
    63  		case interface{ Unwrap() error }:
    64  			err = x.Unwrap()
    65  			if err == nil {
    66  				return false
    67  			}
    68  		case interface{ Unwrap() []error }:
    69  			for _, err := range x.Unwrap() {
    70  				if is(err, target, targetComparable) {
    71  					return true
    72  				}
    73  			}
    74  			return false
    75  		default:
    76  			return false
    77  		}
    78  	}
    79  }
    80  
    81  // As finds the first error in err's tree that matches target, and if one is found, sets
    82  // target to that error value and returns true. Otherwise, it returns false.
    83  //
    84  // For most uses, prefer [AsType]. As is equivalent to [AsType] but sets its target
    85  // argument rather than returning the matching error and doesn't require its target
    86  // argument to implement error.
    87  //
    88  // The tree consists of err itself, followed by the errors obtained by repeatedly
    89  // calling its Unwrap() error or Unwrap() []error method. When err wraps multiple
    90  // errors, As examines err followed by a depth-first traversal of its children.
    91  //
    92  // An error matches target if the error's concrete value is assignable to the value
    93  // pointed to by target, or if the error has a method As(any) bool such that
    94  // As(target) returns true. In the latter case, the As method is responsible for
    95  // setting target.
    96  //
    97  // An error type might provide an As method so it can be treated as if it were a
    98  // different error type.
    99  //
   100  // As panics if target is not a non-nil pointer to either a type that implements
   101  // error, or to any interface type.
   102  func As(err error, target any) bool {
   103  	if err == nil {
   104  		return false
   105  	}
   106  	if target == nil {
   107  		panic("errors: target cannot be nil")
   108  	}
   109  	val := reflectlite.ValueOf(target)
   110  	typ := val.Type()
   111  	if typ.Kind() != reflectlite.Ptr || val.IsNil() {
   112  		panic("errors: target must be a non-nil pointer")
   113  	}
   114  	targetType := typ.Elem()
   115  	if targetType.Kind() != reflectlite.Interface && !targetType.Implements(errorType) {
   116  		panic("errors: *target must be interface or implement error")
   117  	}
   118  	return as(err, target, val, targetType)
   119  }
   120  
   121  func as(err error, target any, targetVal reflectlite.Value, targetType reflectlite.Type) bool {
   122  	for {
   123  		if reflectlite.TypeOf(err).AssignableTo(targetType) {
   124  			targetVal.Elem().Set(reflectlite.ValueOf(err))
   125  			return true
   126  		}
   127  		if x, ok := err.(interface{ As(any) bool }); ok && x.As(target) {
   128  			return true
   129  		}
   130  		switch x := err.(type) {
   131  		case interface{ Unwrap() error }:
   132  			err = x.Unwrap()
   133  			if err == nil {
   134  				return false
   135  			}
   136  		case interface{ Unwrap() []error }:
   137  			for _, err := range x.Unwrap() {
   138  				if err == nil {
   139  					continue
   140  				}
   141  				if as(err, target, targetVal, targetType) {
   142  					return true
   143  				}
   144  			}
   145  			return false
   146  		default:
   147  			return false
   148  		}
   149  	}
   150  }
   151  
   152  var errorType = reflectlite.TypeOf((*error)(nil)).Elem()
   153  
   154  // AsType finds the first error in err's tree that matches the type E, and
   155  // if one is found, returns that error value and true. Otherwise, it
   156  // returns the zero value of E and false.
   157  //
   158  // The tree consists of err itself, followed by the errors obtained by
   159  // repeatedly calling its Unwrap() error or Unwrap() []error method. When
   160  // err wraps multiple errors, AsType examines err followed by a
   161  // depth-first traversal of its children.
   162  //
   163  // An error err matches the type E if the type assertion err.(E) holds,
   164  // or if the error has a method As(any) bool such that err.As(target)
   165  // returns true when target is a non-nil *E. In the latter case, the As
   166  // method is responsible for setting target.
   167  func AsType[E error](err error) (E, bool) {
   168  	if err == nil {
   169  		var zero E
   170  		return zero, false
   171  	}
   172  	var pe *E // lazily initialized
   173  	return asType(err, &pe)
   174  }
   175  
   176  func asType[E error](err error, ppe **E) (_ E, _ bool) {
   177  	for {
   178  		if e, ok := err.(E); ok {
   179  			return e, true
   180  		}
   181  		if x, ok := err.(interface{ As(any) bool }); ok {
   182  			if *ppe == nil {
   183  				*ppe = new(E)
   184  			}
   185  			if x.As(*ppe) {
   186  				return **ppe, true
   187  			}
   188  		}
   189  		switch x := err.(type) {
   190  		case interface{ Unwrap() error }:
   191  			err = x.Unwrap()
   192  			if err == nil {
   193  				return
   194  			}
   195  		case interface{ Unwrap() []error }:
   196  			for _, err := range x.Unwrap() {
   197  				if err == nil {
   198  					continue
   199  				}
   200  				if x, ok := asType(err, ppe); ok {
   201  					return x, true
   202  				}
   203  			}
   204  			return
   205  		default:
   206  			return
   207  		}
   208  	}
   209  }
   210  

View as plain text