This PR upgrades CI actions to their latest versions.
At the very least, the codecov action is necessary because otherwise PRs
stall on codecov upload step (example:
https://github.com/uber-go/multierr/pull/81)
Drops support for versions of Go older than 1.20.
With 1.20 being the minimum supported Go version,
we can remove the pre_go120 code, and merge the post_go120 code.
With Go 1.20's multi-error interface, our errorGroup interface
is not necessary, but we need it for backwards compatibility.
Also updates the documentation to suggest Go 1.20's interface
instead of ours.
The `Errors() []error` function is now an implementation detail.
This prepares release for v1.11.0 which contains:
* support for `Errors` on any error that implements the multiple-error
interface specified by standard library starting from Go 1.20.
* `Every`, which checks whether all errors in the error chain satisfies
the `errors.Is` comparison against the target error.
This PR introduces a new exported function in the multierr package
called `Every`, which checks every error within a given error against a
given target using `errors.Is`, and only returns true if all checks
return true.
```go
err := multierr.Combine(ErrIgnorable, errors.New("great sadness"))
fmt.Println(errors.Is(err, ErrIgnorable)) // output = "true"
fmt.Println(multierr.Every(err, ErrIgnorable)) // output = "false"
err := multierr.Combine(ErrIgnorable, ErrIgnorable)
fmt.Println(errors.Is(err, ErrIgnorable)) // output = "true"
fmt.Println(multierr.Every(err, ErrIgnorable)) // output = "true"
```
This also works when the error passed in as the first argument is not a
`multiErr`, including when it is a go1.20+ error that implements
`Unwrap() []error`.
This solves #66.
Starting Go 1.20, any multi-error should conform to the standard unwrap
method: Unwrap() []error.
This changes multierr.Errors() method to support any error that complies
to that interface.
Fix#70 / GO-1883
Now that this package only supports Go 1.19 and 1.20,
it can make use of the atomic.Bool [1] added in Go 1.19.
[1] https://pkg.go.dev/sync/atomic#Bool
This has the effect of eliminating any external dependencies
from this package -- minus testify, which is only used for testing.
Go 1.20 includes native support for wrapping multiple errors.
Errors which wrap multiple other errors must implement,
Unwrap() []error
If an error implements this method, `errors.Is` and `errors.As`
will descend into the list and continue matching.
Versions of Go prior to 1.20, however, still need the old
`Is` and `As` method implementations on the error object
to get a similar behavior.
This change adds the `Unwrap() []error` method
gated by a build constraint requiring Go 1.20 or higher.
It similarly moves the existing `Is` and `As` methods
to a file that is ignored on Go 1.20 or higher.
Once Go 1.21 is released and 1.19 is no longer supported,
the pre-go1.20 file may be deleted and the build constraints removed.
For details, see also the section,
"How should existing multierror types adopt the new interface?"
of the [multiple errors proposal][1].
[1]: https://github.com/golang/go/issues/53435
Refs #64
I really like the idea of catching returned errors from deferred functions. Though having to use `multierr` package name twice in the same line makes it a bit verbose in many occasions.
This PR introduces a shorthand for AppendInvoke which allows passing function or method value directly without wrapping it into an Invoker.
So this:
```go
defer multierr.AppendInvoke(&err, multierr.Invoke(my.StopFunc))
```
could become this:
```go
defer multierr.AppendFunc(&err, my.StopFunc)
```
Co-authored-by: Sung Yoon Whang <sungyoonwhang@gmail.com>
As discussed in #61, if you attempt to use defer multierr.AppendInvoke
with an error that is not a named return, the system will lose the
error.
func fails() error { return errors.New("great sadness") }
func foo() error {
var err error
defer multierr.AppendInvoke(&err, multierr.Invoke(fails))
return err
}
func main() {
fmt.Println(foo()) // nil
}
https://go.dev/play/p/qK4NR-VYLvo
This isn't something the library can address because of how defers work.
This change adds a warning about the error variable being a named return
in all places where we suggest use of multierr with defer.
While we're at it, this makes use of the new [Foo] godoc syntax to
generate links to other functions in the package in "See Foo"
statements in the documentation.
Add Go 1.19 to the test matrix, and switch linting to Go 1.19 since we
prefer to lint with the latest stable release.
With Go 1.19, we need to:
Update tools dependencies
Fix any use of ioutil
gofmt all files to match new godoc format
FOSSA analysis currently blocks CI on pull requests because they are
denied access to secrets.
Run FOSSA as a separate job only when we push to a branch of the
project.
Use GitHub Workflows for CI instead of Travis. In the process, this resulted in
some cleanup of the Makefile (in particular, dropping the update-license tool)
and updating the licenses manually.
Follow up to #47 with the following changes.
- The tests for AppendInvoke with Close duplicate the tests for
AppendInvoke. These can be merged, and the unit tests for Close only
need to verify whether the Invoker returned by multierr.Close calls
the Close function in the provided io.Closer.
- Replace AppendInvoke example with something more realistic.
- Update changelog.
Add the following APIs:
type Invoker
type Invoke // implements Invoker
func Close(io.Closer) Invoker
func AppendInvoke(*error, Invoker)
Together, these APIs provide support for appending to an error from a
`defer` block.
func foo() (err error) {
file, err := os.Open(..)
if err != nil {
return err
}
defer multierr.AppendInvoke(&err, multierr.Close(file))
// ...
Resolves#46
Co-authored-by: Abhinav Gupta <abg@uber.com>
Support for `errors.Is` and `errors.As` was special-cased to Go 1.13 or
higher. This is no longer necessary since, as of right now, only Go 1.14
and 1.15 need to be supported.
Renaming tools.go to tools_test.go isn't enough because the transitive
dependency is still carried over for commands like `go mod download`,
which may be used for pre-downloading dependencies for systems like
Docker.
Use the most recent two minor versions of Go for testing.
Use `oldstable` and `stable` introduced in gimme v1.5.4.
Co-authored-by: Abhinav Gupta <mail@abhinavg.net>
This drops library-level dependencies on tools used during development.
See also uber-go/atomic#65.
Note that we'll also tag a release when this lands.
Resolves#35
Previously the docs incorrectly stated that `multierr.Errors(err)`
would return an empty slice rather than a nil slice, which would imply
that `multierr.Errors(nil) == []error{}`, which isn't true.
This updates the docs to reflect reality: `multierr.Errors(nil) == nil`.
This adds an AppendInto function that behaves similarly to Append
except, it operates on a `*error` on the left side and it reports
whether the right side error was non-nil.
func AppendInto(*error, error) (errored bool)
Making the left side a pointer aligns with the fast path of `Append`.
Returning whether the right error was non-nil aligns with the standard
`if err := ...; err != nil` pattern.
```diff
-if err := thing(); err != nil {
+if multierr.AppendInto(&err, thing()) {
continue
}
```
Resolves#21
Go 1.13 includes the `errors.As` and `errors.Is` APIs.
The base functionality provided by these functions is to cast errors in
an error chain to a specific type or check for equality.
The only definition of error chain currently supported is via errors
which return the underlying cause with a `Unwrap() error` method, but
the design does not make any assumptions otherwise.
Specifically, both, `errors.As` and `errors.Is` support customizing
their behavior by implementing,
interface { As(interface{}) bool }
interface { Is(error) bool }
This change does exactly that, making it possible to extract or match
against individual errors in a multierr error. This will work for both,
top-level errors in a multierr error, as well as for errors wrapped by
any of those errors.