error.go

  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//	}
141package multierr // import "go.uber.org/multierr"
142
143import (
144	"bytes"
145	"errors"
146	"fmt"
147	"io"
148	"strings"
149	"sync"
150	"sync/atomic"
151)
152
153var (
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.
177var _bufferPool = sync.Pool{
178	New: func() interface{} {
179		return &bytes.Buffer{}
180	},
181}
182
183type 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.
197func 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.
208type 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.
216func (merr *multiError) Errors() []error {
217	if merr == nil {
218		return nil
219	}
220	return merr.errors
221}
222
223func (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.
240func 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
249func (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
257func (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
269func (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.
279func 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
298type 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.
315func 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.
339func 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))
413func 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].
435func 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//	}
495func 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].
516type 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.
542type Invoke func() error
543
544// Invoke calls the supplied function and returns its result.
545func (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.
568func 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.
625func 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//	}
644func AppendFunc(into *error, fn func() error) {
645	AppendInvoke(into, Invoke(fn))
646}