error.go raw

   1  // Copyright (c) 2017-2023 Uber Technologies, Inc.
   2  //
   3  // Permission is hereby granted, free of charge, to any person obtaining a copy
   4  // of this software and associated documentation files (the "Software"), to deal
   5  // in the Software without restriction, including without limitation the rights
   6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   7  // copies of the Software, and to permit persons to whom the Software is
   8  // furnished to do so, subject to the following conditions:
   9  //
  10  // The above copyright notice and this permission notice shall be included in
  11  // all copies or substantial portions of the Software.
  12  //
  13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19  // THE SOFTWARE.
  20  
  21  // Package multierr allows combining one or more errors together.
  22  //
  23  // # Overview
  24  //
  25  // Errors can be combined with the use of the Combine function.
  26  //
  27  //	multierr.Combine(
  28  //		reader.Close(),
  29  //		writer.Close(),
  30  //		conn.Close(),
  31  //	)
  32  //
  33  // If only two errors are being combined, the Append function may be used
  34  // instead.
  35  //
  36  //	err = multierr.Append(reader.Close(), writer.Close())
  37  //
  38  // The underlying list of errors for a returned error object may be retrieved
  39  // with the Errors function.
  40  //
  41  //	errors := multierr.Errors(err)
  42  //	if len(errors) > 0 {
  43  //		fmt.Println("The following errors occurred:", errors)
  44  //	}
  45  //
  46  // # Appending from a loop
  47  //
  48  // You sometimes need to append into an error from a loop.
  49  //
  50  //	var err error
  51  //	for _, item := range items {
  52  //		err = multierr.Append(err, process(item))
  53  //	}
  54  //
  55  // Cases like this may require knowledge of whether an individual instance
  56  // failed. This usually requires introduction of a new variable.
  57  //
  58  //	var err error
  59  //	for _, item := range items {
  60  //		if perr := process(item); perr != nil {
  61  //			log.Warn("skipping item", item)
  62  //			err = multierr.Append(err, perr)
  63  //		}
  64  //	}
  65  //
  66  // multierr includes AppendInto to simplify cases like this.
  67  //
  68  //	var err error
  69  //	for _, item := range items {
  70  //		if multierr.AppendInto(&err, process(item)) {
  71  //			log.Warn("skipping item", item)
  72  //		}
  73  //	}
  74  //
  75  // This will append the error into the err variable, and return true if that
  76  // individual error was non-nil.
  77  //
  78  // See [AppendInto] for more information.
  79  //
  80  // # Deferred Functions
  81  //
  82  // Go makes it possible to modify the return value of a function in a defer
  83  // block if the function was using named returns. This makes it possible to
  84  // record resource cleanup failures from deferred blocks.
  85  //
  86  //	func sendRequest(req Request) (err error) {
  87  //		conn, err := openConnection()
  88  //		if err != nil {
  89  //			return err
  90  //		}
  91  //		defer func() {
  92  //			err = multierr.Append(err, conn.Close())
  93  //		}()
  94  //		// ...
  95  //	}
  96  //
  97  // multierr provides the Invoker type and AppendInvoke function to make cases
  98  // like the above simpler and obviate the need for a closure. The following is
  99  // roughly equivalent to the example above.
 100  //
 101  //	func sendRequest(req Request) (err error) {
 102  //		conn, err := openConnection()
 103  //		if err != nil {
 104  //			return err
 105  //		}
 106  //		defer multierr.AppendInvoke(&err, multierr.Close(conn))
 107  //		// ...
 108  //	}
 109  //
 110  // See [AppendInvoke] and [Invoker] for more information.
 111  //
 112  // NOTE: If you're modifying an error from inside a defer, you MUST use a named
 113  // return value for that function.
 114  //
 115  // # Advanced Usage
 116  //
 117  // Errors returned by Combine and Append MAY implement the following
 118  // interface.
 119  //
 120  //	type errorGroup interface {
 121  //		// Returns a slice containing the underlying list of errors.
 122  //		//
 123  //		// This slice MUST NOT be modified by the caller.
 124  //		Errors() []error
 125  //	}
 126  //
 127  // Note that if you need access to list of errors behind a multierr error, you
 128  // should prefer using the Errors function. That said, if you need cheap
 129  // read-only access to the underlying errors slice, you can attempt to cast
 130  // the error to this interface. You MUST handle the failure case gracefully
 131  // because errors returned by Combine and Append are not guaranteed to
 132  // implement this interface.
 133  //
 134  //	var errors []error
 135  //	group, ok := err.(errorGroup)
 136  //	if ok {
 137  //		errors = group.Errors()
 138  //	} else {
 139  //		errors = []error{err}
 140  //	}
 141  package multierr // import "go.uber.org/multierr"
 142  
 143  import (
 144  	"bytes"
 145  	"errors"
 146  	"fmt"
 147  	"io"
 148  	"strings"
 149  	"sync"
 150  	"sync/atomic"
 151  )
 152  
 153  var (
 154  	// Separator for single-line error messages.
 155  	_singlelineSeparator = []byte("; ")
 156  
 157  	// Prefix for multi-line messages
 158  	_multilinePrefix = []byte("the following errors occurred:")
 159  
 160  	// Prefix for the first and following lines of an item in a list of
 161  	// multi-line error messages.
 162  	//
 163  	// For example, if a single item is:
 164  	//
 165  	// 	foo
 166  	// 	bar
 167  	//
 168  	// It will become,
 169  	//
 170  	// 	 -  foo
 171  	// 	    bar
 172  	_multilineSeparator = []byte("\n -  ")
 173  	_multilineIndent    = []byte("    ")
 174  )
 175  
 176  // _bufferPool is a pool of bytes.Buffers.
 177  var _bufferPool = sync.Pool{
 178  	New: func() interface{} {
 179  		return &bytes.Buffer{}
 180  	},
 181  }
 182  
 183  type errorGroup interface {
 184  	Errors() []error
 185  }
 186  
 187  // Errors returns a slice containing zero or more errors that the supplied
 188  // error is composed of. If the error is nil, a nil slice is returned.
 189  //
 190  //	err := multierr.Append(r.Close(), w.Close())
 191  //	errors := multierr.Errors(err)
 192  //
 193  // If the error is not composed of other errors, the returned slice contains
 194  // just the error that was passed in.
 195  //
 196  // Callers of this function are free to modify the returned slice.
 197  func Errors(err error) []error {
 198  	return extractErrors(err)
 199  }
 200  
 201  // multiError is an error that holds one or more errors.
 202  //
 203  // An instance of this is guaranteed to be non-empty and flattened. That is,
 204  // none of the errors inside multiError are other multiErrors.
 205  //
 206  // multiError formats to a semi-colon delimited list of error messages with
 207  // %v and with a more readable multi-line format with %+v.
 208  type multiError struct {
 209  	copyNeeded atomic.Bool
 210  	errors     []error
 211  }
 212  
 213  // Errors returns the list of underlying errors.
 214  //
 215  // This slice MUST NOT be modified.
 216  func (merr *multiError) Errors() []error {
 217  	if merr == nil {
 218  		return nil
 219  	}
 220  	return merr.errors
 221  }
 222  
 223  func (merr *multiError) Error() string {
 224  	if merr == nil {
 225  		return ""
 226  	}
 227  
 228  	buff := _bufferPool.Get().(*bytes.Buffer)
 229  	buff.Reset()
 230  
 231  	merr.writeSingleline(buff)
 232  
 233  	result := buff.String()
 234  	_bufferPool.Put(buff)
 235  	return result
 236  }
 237  
 238  // Every compares every error in the given err against the given target error
 239  // using [errors.Is], and returns true only if every comparison returned true.
 240  func Every(err error, target error) bool {
 241  	for _, e := range extractErrors(err) {
 242  		if !errors.Is(e, target) {
 243  			return false
 244  		}
 245  	}
 246  	return true
 247  }
 248  
 249  func (merr *multiError) Format(f fmt.State, c rune) {
 250  	if c == 'v' && f.Flag('+') {
 251  		merr.writeMultiline(f)
 252  	} else {
 253  		merr.writeSingleline(f)
 254  	}
 255  }
 256  
 257  func (merr *multiError) writeSingleline(w io.Writer) {
 258  	first := true
 259  	for _, item := range merr.errors {
 260  		if first {
 261  			first = false
 262  		} else {
 263  			w.Write(_singlelineSeparator)
 264  		}
 265  		io.WriteString(w, item.Error())
 266  	}
 267  }
 268  
 269  func (merr *multiError) writeMultiline(w io.Writer) {
 270  	w.Write(_multilinePrefix)
 271  	for _, item := range merr.errors {
 272  		w.Write(_multilineSeparator)
 273  		writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item))
 274  	}
 275  }
 276  
 277  // Writes s to the writer with the given prefix added before each line after
 278  // the first.
 279  func writePrefixLine(w io.Writer, prefix []byte, s string) {
 280  	first := true
 281  	for len(s) > 0 {
 282  		if first {
 283  			first = false
 284  		} else {
 285  			w.Write(prefix)
 286  		}
 287  
 288  		idx := strings.IndexByte(s, '\n')
 289  		if idx < 0 {
 290  			idx = len(s) - 1
 291  		}
 292  
 293  		io.WriteString(w, s[:idx+1])
 294  		s = s[idx+1:]
 295  	}
 296  }
 297  
 298  type inspectResult struct {
 299  	// Number of top-level non-nil errors
 300  	Count int
 301  
 302  	// Total number of errors including multiErrors
 303  	Capacity int
 304  
 305  	// Index of the first non-nil error in the list. Value is meaningless if
 306  	// Count is zero.
 307  	FirstErrorIdx int
 308  
 309  	// Whether the list contains at least one multiError
 310  	ContainsMultiError bool
 311  }
 312  
 313  // Inspects the given slice of errors so that we can efficiently allocate
 314  // space for it.
 315  func inspect(errors []error) (res inspectResult) {
 316  	first := true
 317  	for i, err := range errors {
 318  		if err == nil {
 319  			continue
 320  		}
 321  
 322  		res.Count++
 323  		if first {
 324  			first = false
 325  			res.FirstErrorIdx = i
 326  		}
 327  
 328  		if merr, ok := err.(*multiError); ok {
 329  			res.Capacity += len(merr.errors)
 330  			res.ContainsMultiError = true
 331  		} else {
 332  			res.Capacity++
 333  		}
 334  	}
 335  	return
 336  }
 337  
 338  // fromSlice converts the given list of errors into a single error.
 339  func fromSlice(errors []error) error {
 340  	// Don't pay to inspect small slices.
 341  	switch len(errors) {
 342  	case 0:
 343  		return nil
 344  	case 1:
 345  		return errors[0]
 346  	}
 347  
 348  	res := inspect(errors)
 349  	switch res.Count {
 350  	case 0:
 351  		return nil
 352  	case 1:
 353  		// only one non-nil entry
 354  		return errors[res.FirstErrorIdx]
 355  	case len(errors):
 356  		if !res.ContainsMultiError {
 357  			// Error list is flat. Make a copy of it
 358  			// Otherwise "errors" escapes to the heap
 359  			// unconditionally for all other cases.
 360  			// This lets us optimize for the "no errors" case.
 361  			out := append(([]error)(nil), errors...)
 362  			return &multiError{errors: out}
 363  		}
 364  	}
 365  
 366  	nonNilErrs := make([]error, 0, res.Capacity)
 367  	for _, err := range errors[res.FirstErrorIdx:] {
 368  		if err == nil {
 369  			continue
 370  		}
 371  
 372  		if nested, ok := err.(*multiError); ok {
 373  			nonNilErrs = append(nonNilErrs, nested.errors...)
 374  		} else {
 375  			nonNilErrs = append(nonNilErrs, err)
 376  		}
 377  	}
 378  
 379  	return &multiError{errors: nonNilErrs}
 380  }
 381  
 382  // Combine combines the passed errors into a single error.
 383  //
 384  // If zero arguments were passed or if all items are nil, a nil error is
 385  // returned.
 386  //
 387  //	Combine(nil, nil)  // == nil
 388  //
 389  // If only a single error was passed, it is returned as-is.
 390  //
 391  //	Combine(err)  // == err
 392  //
 393  // Combine skips over nil arguments so this function may be used to combine
 394  // together errors from operations that fail independently of each other.
 395  //
 396  //	multierr.Combine(
 397  //		reader.Close(),
 398  //		writer.Close(),
 399  //		pipe.Close(),
 400  //	)
 401  //
 402  // If any of the passed errors is a multierr error, it will be flattened along
 403  // with the other errors.
 404  //
 405  //	multierr.Combine(multierr.Combine(err1, err2), err3)
 406  //	// is the same as
 407  //	multierr.Combine(err1, err2, err3)
 408  //
 409  // The returned error formats into a readable multi-line error message if
 410  // formatted with %+v.
 411  //
 412  //	fmt.Sprintf("%+v", multierr.Combine(err1, err2))
 413  func Combine(errors ...error) error {
 414  	return fromSlice(errors)
 415  }
 416  
 417  // Append appends the given errors together. Either value may be nil.
 418  //
 419  // This function is a specialization of Combine for the common case where
 420  // there are only two errors.
 421  //
 422  //	err = multierr.Append(reader.Close(), writer.Close())
 423  //
 424  // The following pattern may also be used to record failure of deferred
 425  // operations without losing information about the original error.
 426  //
 427  //	func doSomething(..) (err error) {
 428  //		f := acquireResource()
 429  //		defer func() {
 430  //			err = multierr.Append(err, f.Close())
 431  //		}()
 432  //
 433  // Note that the variable MUST be a named return to append an error to it from
 434  // the defer statement. See also [AppendInvoke].
 435  func Append(left error, right error) error {
 436  	switch {
 437  	case left == nil:
 438  		return right
 439  	case right == nil:
 440  		return left
 441  	}
 442  
 443  	if _, ok := right.(*multiError); !ok {
 444  		if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) {
 445  			// Common case where the error on the left is constantly being
 446  			// appended to.
 447  			errs := append(l.errors, right)
 448  			return &multiError{errors: errs}
 449  		} else if !ok {
 450  			// Both errors are single errors.
 451  			return &multiError{errors: []error{left, right}}
 452  		}
 453  	}
 454  
 455  	// Either right or both, left and right, are multiErrors. Rely on usual
 456  	// expensive logic.
 457  	errors := [2]error{left, right}
 458  	return fromSlice(errors[0:])
 459  }
 460  
 461  // AppendInto appends an error into the destination of an error pointer and
 462  // returns whether the error being appended was non-nil.
 463  //
 464  //	var err error
 465  //	multierr.AppendInto(&err, r.Close())
 466  //	multierr.AppendInto(&err, w.Close())
 467  //
 468  // The above is equivalent to,
 469  //
 470  //	err := multierr.Append(r.Close(), w.Close())
 471  //
 472  // As AppendInto reports whether the provided error was non-nil, it may be
 473  // used to build a multierr error in a loop more ergonomically. For example:
 474  //
 475  //	var err error
 476  //	for line := range lines {
 477  //		var item Item
 478  //		if multierr.AppendInto(&err, parse(line, &item)) {
 479  //			continue
 480  //		}
 481  //		items = append(items, item)
 482  //	}
 483  //
 484  // Compare this with a version that relies solely on Append:
 485  //
 486  //	var err error
 487  //	for line := range lines {
 488  //		var item Item
 489  //		if parseErr := parse(line, &item); parseErr != nil {
 490  //			err = multierr.Append(err, parseErr)
 491  //			continue
 492  //		}
 493  //		items = append(items, item)
 494  //	}
 495  func AppendInto(into *error, err error) (errored bool) {
 496  	if into == nil {
 497  		// We panic if 'into' is nil. This is not documented above
 498  		// because suggesting that the pointer must be non-nil may
 499  		// confuse users into thinking that the error that it points
 500  		// to must be non-nil.
 501  		panic("misuse of multierr.AppendInto: into pointer must not be nil")
 502  	}
 503  
 504  	if err == nil {
 505  		return false
 506  	}
 507  	*into = Append(*into, err)
 508  	return true
 509  }
 510  
 511  // Invoker is an operation that may fail with an error. Use it with
 512  // AppendInvoke to append the result of calling the function into an error.
 513  // This allows you to conveniently defer capture of failing operations.
 514  //
 515  // See also, [Close] and [Invoke].
 516  type Invoker interface {
 517  	Invoke() error
 518  }
 519  
 520  // Invoke wraps a function which may fail with an error to match the Invoker
 521  // interface. Use it to supply functions matching this signature to
 522  // AppendInvoke.
 523  //
 524  // For example,
 525  //
 526  //	func processReader(r io.Reader) (err error) {
 527  //		scanner := bufio.NewScanner(r)
 528  //		defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
 529  //		for scanner.Scan() {
 530  //			// ...
 531  //		}
 532  //		// ...
 533  //	}
 534  //
 535  // In this example, the following line will construct the Invoker right away,
 536  // but defer the invocation of scanner.Err() until the function returns.
 537  //
 538  //	defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
 539  //
 540  // Note that the error you're appending to from the defer statement MUST be a
 541  // named return.
 542  type Invoke func() error
 543  
 544  // Invoke calls the supplied function and returns its result.
 545  func (i Invoke) Invoke() error { return i() }
 546  
 547  // Close builds an Invoker that closes the provided io.Closer. Use it with
 548  // AppendInvoke to close io.Closers and append their results into an error.
 549  //
 550  // For example,
 551  //
 552  //	func processFile(path string) (err error) {
 553  //		f, err := os.Open(path)
 554  //		if err != nil {
 555  //			return err
 556  //		}
 557  //		defer multierr.AppendInvoke(&err, multierr.Close(f))
 558  //		return processReader(f)
 559  //	}
 560  //
 561  // In this example, multierr.Close will construct the Invoker right away, but
 562  // defer the invocation of f.Close until the function returns.
 563  //
 564  //	defer multierr.AppendInvoke(&err, multierr.Close(f))
 565  //
 566  // Note that the error you're appending to from the defer statement MUST be a
 567  // named return.
 568  func Close(closer io.Closer) Invoker {
 569  	return Invoke(closer.Close)
 570  }
 571  
 572  // AppendInvoke appends the result of calling the given Invoker into the
 573  // provided error pointer. Use it with named returns to safely defer
 574  // invocation of fallible operations until a function returns, and capture the
 575  // resulting errors.
 576  //
 577  //	func doSomething(...) (err error) {
 578  //		// ...
 579  //		f, err := openFile(..)
 580  //		if err != nil {
 581  //			return err
 582  //		}
 583  //
 584  //		// multierr will call f.Close() when this function returns and
 585  //		// if the operation fails, its append its error into the
 586  //		// returned error.
 587  //		defer multierr.AppendInvoke(&err, multierr.Close(f))
 588  //
 589  //		scanner := bufio.NewScanner(f)
 590  //		// Similarly, this scheduled scanner.Err to be called and
 591  //		// inspected when the function returns and append its error
 592  //		// into the returned error.
 593  //		defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
 594  //
 595  //		// ...
 596  //	}
 597  //
 598  // NOTE: If used with a defer, the error variable MUST be a named return.
 599  //
 600  // Without defer, AppendInvoke behaves exactly like AppendInto.
 601  //
 602  //	err := // ...
 603  //	multierr.AppendInvoke(&err, mutltierr.Invoke(foo))
 604  //
 605  //	// ...is roughly equivalent to...
 606  //
 607  //	err := // ...
 608  //	multierr.AppendInto(&err, foo())
 609  //
 610  // The advantage of the indirection introduced by Invoker is to make it easy
 611  // to defer the invocation of a function. Without this indirection, the
 612  // invoked function will be evaluated at the time of the defer block rather
 613  // than when the function returns.
 614  //
 615  //	// BAD: This is likely not what the caller intended. This will evaluate
 616  //	// foo() right away and append its result into the error when the
 617  //	// function returns.
 618  //	defer multierr.AppendInto(&err, foo())
 619  //
 620  //	// GOOD: This will defer invocation of foo unutil the function returns.
 621  //	defer multierr.AppendInvoke(&err, multierr.Invoke(foo))
 622  //
 623  // multierr provides a few Invoker implementations out of the box for
 624  // convenience. See [Invoker] for more information.
 625  func AppendInvoke(into *error, invoker Invoker) {
 626  	AppendInto(into, invoker.Invoke())
 627  }
 628  
 629  // AppendFunc is a shorthand for [AppendInvoke].
 630  // It allows using function or method value directly
 631  // without having to wrap it into an [Invoker] interface.
 632  //
 633  //	func doSomething(...) (err error) {
 634  //		w, err := startWorker(...)
 635  //		if err != nil {
 636  //			return err
 637  //		}
 638  //
 639  //		// multierr will call w.Stop() when this function returns and
 640  //		// if the operation fails, it appends its error into the
 641  //		// returned error.
 642  //		defer multierr.AppendFunc(&err, w.Stop)
 643  //	}
 644  func AppendFunc(into *error, fn func() error) {
 645  	AppendInvoke(into, Invoke(fn))
 646  }
 647