New Upstream Release - golang-go.uber-multierr
Ready changes
Summary
Merged new upstream version: 1.8.0 (was: 1.6.0).
Resulting package
Built on 2022-04-26T22:06 (took 4m25s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-releases golang-go.uber-multierr-dev
Lintian Result
Diff
diff --git a/.github/workflows/fossa.yaml b/.github/workflows/fossa.yaml
new file mode 100644
index 0000000..86e6db7
--- /dev/null
+++ b/.github/workflows/fossa.yaml
@@ -0,0 +1,17 @@
+name: FOSSA Analysis
+on: push
+
+jobs:
+
+ build:
+ runs-on: ubuntu-latest
+ if: github.repository_owner == 'uber-go'
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+
+ - name: FOSSA analysis
+ uses: fossas/fossa-action@v1
+ with:
+ api-key: ${{ secrets.FOSSA_API_KEY }}
+
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
new file mode 100644
index 0000000..7fb612b
--- /dev/null
+++ b/.github/workflows/go.yml
@@ -0,0 +1,49 @@
+name: Go
+
+on:
+ push:
+ branches: ['*']
+ tags: ['v*']
+ pull_request:
+ branches: ['*']
+
+jobs:
+
+ build:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ go: ["1.16.x", "1.17.x"]
+ include:
+ - go: 1.17.x
+ latest: true
+
+ steps:
+ - name: Setup Go
+ uses: actions/setup-go@v2
+ with:
+ go-version: ${{ matrix.go }}
+
+ - name: Checkout code
+ uses: actions/checkout@v2
+
+ - name: Load cached dependencies
+ uses: actions/cache@v1
+ with:
+ path: ~/go/pkg/mod
+ key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+ restore-keys: |
+ ${{ runner.os }}-go-
+
+ - name: Download Dependencies
+ run: go mod download
+
+ - name: Lint
+ if: matrix.latest
+ run: make lint
+
+ - name: Test
+ run: make cover
+
+ - name: Upload coverage to codecov.io
+ uses: codecov/codecov-action@v1
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 8636ab4..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-sudo: false
-language: go
-go_import_path: go.uber.org/multierr
-
-env:
- global:
- - GO111MODULE=on
-
-go:
- - oldstable
- - stable
-
-before_install:
-- go version
-
-script:
-- |
- set -e
- make lint
- make cover
-
-after_success:
-- bash <(curl -s https://codecov.io/bash)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6f1db9e..3ba0527 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,18 @@
Releases
========
+v1.8.0 (2022-02-28)
+===================
+
+- `Combine`: perform zero allocations when there are no errors.
+
+
+v1.7.0 (2021-05-06)
+===================
+
+- Add `AppendInvoke` to append into errors from `defer` blocks.
+
+
v1.6.0 (2020-09-14)
===================
diff --git a/LICENSE.txt b/LICENSE.txt
index 858e024..413e30f 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2017 Uber Technologies, Inc.
+Copyright (c) 2017-2021 Uber Technologies, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/Makefile b/Makefile
index 3160044..dcb6fe7 100644
--- a/Makefile
+++ b/Makefile
@@ -34,9 +34,5 @@ lint: gofmt golint staticcheck
.PHONY: cover
cover:
- go test -coverprofile=cover.out -coverpkg=./... -v ./...
+ go test -race -coverprofile=cover.out -coverpkg=./... -v ./...
go tool cover -html=cover.out -o cover.html
-
-update-license:
- @cd tools && go install go.uber.org/tools/update-license
- @$(GOBIN)/update-license $(GO_FILES)
diff --git a/README.md b/README.md
index 751bd65..70aacec 100644
--- a/README.md
+++ b/README.md
@@ -15,9 +15,9 @@ Stable: No breaking changes will be made before 2.0.
Released under the [MIT License].
[MIT License]: LICENSE.txt
-[doc-img]: https://godoc.org/go.uber.org/multierr?status.svg
-[doc]: https://godoc.org/go.uber.org/multierr
-[ci-img]: https://travis-ci.com/uber-go/multierr.svg?branch=master
+[doc-img]: https://pkg.go.dev/badge/go.uber.org/multierr
+[doc]: https://pkg.go.dev/go.uber.org/multierr
+[ci-img]: https://github.com/uber-go/multierr/actions/workflows/go.yml/badge.svg
[cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg
-[ci]: https://travis-ci.com/uber-go/multierr
+[ci]: https://github.com/uber-go/multierr/actions/workflows/go.yml
[cov]: https://codecov.io/gh/uber-go/multierr
diff --git a/appendinvoke_example_test.go b/appendinvoke_example_test.go
new file mode 100644
index 0000000..c2b48f3
--- /dev/null
+++ b/appendinvoke_example_test.go
@@ -0,0 +1,74 @@
+// Copyright (c) 2021 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package multierr_test
+
+import (
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+
+ "go.uber.org/multierr"
+)
+
+func ExampleAppendInvoke() {
+ if err := run(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func run() (err error) {
+ dir, err := ioutil.TempDir("", "multierr")
+ // We create a temporary directory and defer its deletion when this
+ // function returns.
+ //
+ // If we failed to delete the temporary directory, we append its
+ // failure into the returned value with multierr.AppendInvoke.
+ //
+ // This uses a custom invoker that we implement below.
+ defer multierr.AppendInvoke(&err, RemoveAll(dir))
+
+ path := filepath.Join(dir, "example.txt")
+ f, err := os.Create(path)
+ if err != nil {
+ return err
+ }
+ // Similarly, we defer closing the open file when the function returns,
+ // and appends its failure, if any, into the returned error.
+ //
+ // This uses the multierr.Close invoker included in multierr.
+ defer multierr.AppendInvoke(&err, multierr.Close(f))
+
+ if _, err := fmt.Fprintln(f, "hello"); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// RemoveAll is a multierr.Invoker that deletes the provided directory and all
+// of its contents.
+type RemoveAll string
+
+func (r RemoveAll) Invoke() error {
+ return os.RemoveAll(string(r))
+}
diff --git a/benchmarks_test.go b/benchmarks_test.go
index 797f5a0..562d3bc 100644
--- a/benchmarks_test.go
+++ b/benchmarks_test.go
@@ -60,3 +60,68 @@ func BenchmarkAppend(b *testing.B) {
}
}
}
+
+func BenchmarkCombine(b *testing.B) {
+ b.Run("inline 1", func(b *testing.B) {
+ var x error
+ for i := 0; i < b.N; i++ {
+ Combine(x)
+ }
+ })
+
+ b.Run("inline 2", func(b *testing.B) {
+ var x, y error
+ for i := 0; i < b.N; i++ {
+ Combine(x, y)
+ }
+ })
+
+ b.Run("inline 3 no error", func(b *testing.B) {
+ var x, y, z error
+ for i := 0; i < b.N; i++ {
+ Combine(x, y, z)
+ }
+ })
+
+ b.Run("inline 3 one error", func(b *testing.B) {
+ var x, y, z error
+ z = fmt.Errorf("failed")
+ for i := 0; i < b.N; i++ {
+ Combine(x, y, z)
+ }
+ })
+
+ b.Run("inline 3 multiple errors", func(b *testing.B) {
+ var x, y, z error
+ z = fmt.Errorf("failed3")
+ y = fmt.Errorf("failed2")
+ x = fmt.Errorf("failed")
+ for i := 0; i < b.N; i++ {
+ Combine(x, y, z)
+ }
+ })
+
+ b.Run("slice 100 no errors", func(b *testing.B) {
+ errs := make([]error, 100)
+ for i := 0; i < b.N; i++ {
+ Combine(errs...)
+ }
+ })
+
+ b.Run("slice 100 one error", func(b *testing.B) {
+ errs := make([]error, 100)
+ errs[len(errs)-1] = fmt.Errorf("failed")
+ for i := 0; i < b.N; i++ {
+ Combine(errs...)
+ }
+ })
+
+ b.Run("slice 100 multi error", func(b *testing.B) {
+ errs := make([]error, 100)
+ errs[0] = fmt.Errorf("failed1")
+ errs[len(errs)-1] = fmt.Errorf("failed2")
+ for i := 0; i < b.N; i++ {
+ Combine(errs...)
+ }
+ })
+}
diff --git a/debian/changelog b/debian/changelog
index 760c2ee..52ad36b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,12 @@
-golang-go.uber-multierr (1.6.0-1) UNRELEASED; urgency=medium
+golang-go.uber-multierr (1.8.0-1) UNRELEASED; urgency=medium
+ [ Nobuhiro Iwamatsu ]
+ * New upstream release.
+
+ [ Debian Janitor ]
* New upstream release.
- -- Nobuhiro Iwamatsu <iwamatsu@debian.org> Sat, 05 Feb 2022 18:01:10 +0900
+ -- Nobuhiro Iwamatsu <iwamatsu@debian.org> Tue, 26 Apr 2022 22:02:35 -0000
golang-go.uber-multierr (1.1.0-2) unstable; urgency=medium
diff --git a/error.go b/error.go
index 5c9b67d..f45af14 100644
--- a/error.go
+++ b/error.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2019 Uber Technologies, Inc.
+// Copyright (c) 2017-2021 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -35,8 +35,53 @@
//
// err = multierr.Append(reader.Close(), writer.Close())
//
-// This makes it possible to record resource cleanup failures from deferred
-// blocks with the help of named return values.
+// The underlying list of errors for a returned error object may be retrieved
+// with the Errors function.
+//
+// errors := multierr.Errors(err)
+// if len(errors) > 0 {
+// fmt.Println("The following errors occurred:", errors)
+// }
+//
+// Appending from a loop
+//
+// You sometimes need to append into an error from a loop.
+//
+// var err error
+// for _, item := range items {
+// err = multierr.Append(err, process(item))
+// }
+//
+// Cases like this may require knowledge of whether an individual instance
+// failed. This usually requires introduction of a new variable.
+//
+// var err error
+// for _, item := range items {
+// if perr := process(item); perr != nil {
+// log.Warn("skipping item", item)
+// err = multierr.Append(err, perr)
+// }
+// }
+//
+// multierr includes AppendInto to simplify cases like this.
+//
+// var err error
+// for _, item := range items {
+// if multierr.AppendInto(&err, process(item)) {
+// log.Warn("skipping item", item)
+// }
+// }
+//
+// This will append the error into the err variable, and return true if that
+// individual error was non-nil.
+//
+// See AppendInto for more information.
+//
+// Deferred Functions
+//
+// Go makes it possible to modify the return value of a function in a defer
+// block if the function was using named returns. This makes it possible to
+// record resource cleanup failures from deferred blocks.
//
// func sendRequest(req Request) (err error) {
// conn, err := openConnection()
@@ -49,14 +94,21 @@
// // ...
// }
//
-// The underlying list of errors for a returned error object may be retrieved
-// with the Errors function.
+// multierr provides the Invoker type and AppendInvoke function to make cases
+// like the above simpler and obviate the need for a closure. The following is
+// roughly equivalent to the example above.
//
-// errors := multierr.Errors(err)
-// if len(errors) > 0 {
-// fmt.Println("The following errors occurred:", errors)
+// func sendRequest(req Request) (err error) {
+// conn, err := openConnection()
+// if err != nil {
+// return err
+// }
+// defer multierr.AppendInvoke(&err, multierr.Close(conn))
+// // ...
// }
//
+// See AppendInvoke and Invoker for more information.
+//
// Advanced Usage
//
// Errors returned by Combine and Append MAY implement the following
@@ -87,6 +139,7 @@ package multierr // import "go.uber.org/multierr"
import (
"bytes"
+ "errors"
"fmt"
"io"
"strings"
@@ -186,6 +239,33 @@ func (merr *multiError) Errors() []error {
return merr.errors
}
+// As attempts to find the first error in the error list that matches the type
+// of the value that target points to.
+//
+// This function allows errors.As to traverse the values stored on the
+// multierr error.
+func (merr *multiError) As(target interface{}) bool {
+ for _, err := range merr.Errors() {
+ if errors.As(err, target) {
+ return true
+ }
+ }
+ return false
+}
+
+// Is attempts to match the provided error against errors in the error list.
+//
+// This function allows errors.Is to traverse the values stored on the
+// multierr error.
+func (merr *multiError) Is(target error) bool {
+ for _, err := range merr.Errors() {
+ if errors.Is(err, target) {
+ return true
+ }
+ }
+ return false
+}
+
func (merr *multiError) Error() string {
if merr == nil {
return ""
@@ -292,6 +372,14 @@ func inspect(errors []error) (res inspectResult) {
// fromSlice converts the given list of errors into a single error.
func fromSlice(errors []error) error {
+ // Don't pay to inspect small slices.
+ switch len(errors) {
+ case 0:
+ return nil
+ case 1:
+ return errors[0]
+ }
+
res := inspect(errors)
switch res.Count {
case 0:
@@ -301,8 +389,13 @@ func fromSlice(errors []error) error {
return errors[res.FirstErrorIdx]
case len(errors):
if !res.ContainsMultiError {
- // already flat
- return &multiError{errors: errors}
+ // Error list is flat. Make a copy of it
+ // Otherwise "errors" escapes to the heap
+ // unconditionally for all other cases.
+ // This lets us optimize for the "no errors" case.
+ out := make([]error, len(errors))
+ copy(out, errors)
+ return &multiError{errors: out}
}
}
@@ -421,7 +514,7 @@ func Append(left error, right error) error {
// items = append(items, item)
// }
//
-// Compare this with a verison that relies solely on Append:
+// Compare this with a version that relies solely on Append:
//
// var err error
// for line := range lines {
@@ -447,3 +540,113 @@ func AppendInto(into *error, err error) (errored bool) {
*into = Append(*into, err)
return true
}
+
+// Invoker is an operation that may fail with an error. Use it with
+// AppendInvoke to append the result of calling the function into an error.
+// This allows you to conveniently defer capture of failing operations.
+//
+// See also, Close and Invoke.
+type Invoker interface {
+ Invoke() error
+}
+
+// Invoke wraps a function which may fail with an error to match the Invoker
+// interface. Use it to supply functions matching this signature to
+// AppendInvoke.
+//
+// For example,
+//
+// func processReader(r io.Reader) (err error) {
+// scanner := bufio.NewScanner(r)
+// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
+// for scanner.Scan() {
+// // ...
+// }
+// // ...
+// }
+//
+// In this example, the following line will construct the Invoker right away,
+// but defer the invocation of scanner.Err() until the function returns.
+//
+// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
+type Invoke func() error
+
+// Invoke calls the supplied function and returns its result.
+func (i Invoke) Invoke() error { return i() }
+
+// Close builds an Invoker that closes the provided io.Closer. Use it with
+// AppendInvoke to close io.Closers and append their results into an error.
+//
+// For example,
+//
+// func processFile(path string) (err error) {
+// f, err := os.Open(path)
+// if err != nil {
+// return err
+// }
+// defer multierr.AppendInvoke(&err, multierr.Close(f))
+// return processReader(f)
+// }
+//
+// In this example, multierr.Close will construct the Invoker right away, but
+// defer the invocation of f.Close until the function returns.
+//
+// defer multierr.AppendInvoke(&err, multierr.Close(f))
+func Close(closer io.Closer) Invoker {
+ return Invoke(closer.Close)
+}
+
+// AppendInvoke appends the result of calling the given Invoker into the
+// provided error pointer. Use it with named returns to safely defer
+// invocation of fallible operations until a function returns, and capture the
+// resulting errors.
+//
+// func doSomething(...) (err error) {
+// // ...
+// f, err := openFile(..)
+// if err != nil {
+// return err
+// }
+//
+// // multierr will call f.Close() when this function returns and
+// // if the operation fails, its append its error into the
+// // returned error.
+// defer multierr.AppendInvoke(&err, multierr.Close(f))
+//
+// scanner := bufio.NewScanner(f)
+// // Similarly, this scheduled scanner.Err to be called and
+// // inspected when the function returns and append its error
+// // into the returned error.
+// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
+//
+// // ...
+// }
+//
+// Without defer, AppendInvoke behaves exactly like AppendInto.
+//
+// err := // ...
+// multierr.AppendInvoke(&err, mutltierr.Invoke(foo))
+//
+// // ...is roughly equivalent to...
+//
+// err := // ...
+// multierr.AppendInto(&err, foo())
+//
+// The advantage of the indirection introduced by Invoker is to make it easy
+// to defer the invocation of a function. Without this indirection, the
+// invoked function will be evaluated at the time of the defer block rather
+// than when the function returns.
+//
+// // BAD: This is likely not what the caller intended. This will evaluate
+// // foo() right away and append its result into the error when the
+// // function returns.
+// defer multierr.AppendInto(&err, foo())
+//
+// // GOOD: This will defer invocation of foo unutil the function returns.
+// defer multierr.AppendInvoke(&err, multierr.Invoke(foo))
+//
+// multierr provides a few Invoker implementations out of the box for
+// convenience. See Invoker for more information.
+func AppendInvoke(into *error, invoker Invoker) {
+ AppendInto(into, invoker.Invoke())
+}
diff --git a/go113_test.go b/error_ext_test.go
similarity index 98%
rename from go113_test.go
rename to error_ext_test.go
index 4fbd0bb..9936e36 100644
--- a/go113_test.go
+++ b/error_ext_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2019 Uber Technologies, Inc.
+// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -18,8 +18,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-// +build go1.13
-
package multierr_test
import (
diff --git a/error_test.go b/error_test.go
index 96c869e..c053167 100644
--- a/error_test.go
+++ b/error_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2019 Uber Technologies, Inc.
+// Copyright (c) 2017-2021 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -97,6 +97,12 @@ func TestCombine(t *testing.T) {
" - bar",
wantSingleline: "foo; bar",
},
+ {
+ giveErrors: []error{nil, nil, errors.New("great sadness"), nil},
+ wantError: errors.New("great sadness"),
+ wantMultiline: "great sadness",
+ wantSingleline: "great sadness",
+ },
{
giveErrors: []error{
errors.New("foo"),
@@ -273,6 +279,14 @@ func TestCombineDoesNotModifySlice(t *testing.T) {
assert.Nil(t, errors[1], 3)
}
+func TestCombineGoodCaseNoAlloc(t *testing.T) {
+ errs := make([]error, 10)
+ allocs := testing.AllocsPerRun(100, func() {
+ Combine(errs...)
+ })
+ assert.Equal(t, 0.0, allocs)
+}
+
func TestAppend(t *testing.T) {
tests := []struct {
left error
@@ -555,6 +569,90 @@ func TestAppendInto(t *testing.T) {
}
}
+func TestAppendInvoke(t *testing.T) {
+ tests := []struct {
+ desc string
+ into *error
+ give Invoker
+ want error
+ }{
+ {
+ desc: "append into empty",
+ into: new(error),
+ give: Invoke(func() error {
+ return errors.New("foo")
+ }),
+ want: errors.New("foo"),
+ },
+ {
+ desc: "append into non-empty, non-multierr",
+ into: errorPtr(errors.New("foo")),
+ give: Invoke(func() error {
+ return errors.New("bar")
+ }),
+ want: Combine(
+ errors.New("foo"),
+ errors.New("bar"),
+ ),
+ },
+ {
+ desc: "append into non-empty multierr",
+ into: errorPtr(Combine(
+ errors.New("foo"),
+ errors.New("bar"),
+ )),
+ give: Invoke(func() error {
+ return errors.New("baz")
+ }),
+ want: Combine(
+ errors.New("foo"),
+ errors.New("bar"),
+ errors.New("baz"),
+ ),
+ },
+ {
+ desc: "close/empty",
+ into: new(error),
+ give: Close(newCloserMock(t, errors.New("foo"))),
+ want: errors.New("foo"),
+ },
+ {
+ desc: "close/no fail",
+ into: new(error),
+ give: Close(newCloserMock(t, nil)),
+ want: nil,
+ },
+ {
+ desc: "close/non-empty",
+ into: errorPtr(errors.New("foo")),
+ give: Close(newCloserMock(t, errors.New("bar"))),
+ want: Combine(
+ errors.New("foo"),
+ errors.New("bar"),
+ ),
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ AppendInvoke(tt.into, tt.give)
+ assert.Equal(t, tt.want, *tt.into)
+ })
+ }
+}
+
+func TestClose(t *testing.T) {
+ t.Run("fail", func(t *testing.T) {
+ give := errors.New("great sadness")
+ got := Close(newCloserMock(t, give)).Invoke()
+ assert.Same(t, give, got)
+ })
+
+ t.Run("success", func(t *testing.T) {
+ got := Close(newCloserMock(t, nil)).Invoke()
+ assert.Nil(t, got)
+ })
+}
+
func TestAppendIntoNil(t *testing.T) {
t.Run("nil pointer panics", func(t *testing.T) {
assert.Panics(t, func() {
@@ -580,3 +678,22 @@ func TestAppendIntoNil(t *testing.T) {
func errorPtr(err error) *error {
return &err
}
+
+type closerMock func() error
+
+func (c closerMock) Close() error {
+ return c()
+}
+
+func newCloserMock(tb testing.TB, err error) io.Closer {
+ var closed bool
+ tb.Cleanup(func() {
+ if !closed {
+ tb.Error("closerMock wasn't closed before test end")
+ }
+ })
+ return closerMock(func() error {
+ closed = true
+ return err
+ })
+}
diff --git a/example_test.go b/example_test.go
index ad7b802..e46a633 100644
--- a/example_test.go
+++ b/example_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Uber Technologies, Inc.
+// Copyright (c) 2017-2021 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -23,6 +23,7 @@ package multierr_test
import (
"errors"
"fmt"
+ "io"
"go.uber.org/multierr"
)
@@ -92,3 +93,32 @@ func ExampleAppendInto() {
// call 3 failed
// foo; baz
}
+
+type fakeCloser func() error
+
+func (f fakeCloser) Close() error {
+ return f()
+}
+
+func FakeCloser(err error) io.Closer {
+ return fakeCloser(func() error {
+ return err
+ })
+}
+
+func ExampleClose() {
+ var err error
+
+ closer := FakeCloser(errors.New("foo"))
+
+ defer func() {
+ fmt.Println(err)
+ }()
+ defer multierr.AppendInvoke(&err, multierr.Close(closer))
+
+ fmt.Println("Hello, World")
+
+ // Output:
+ // Hello, World
+ // foo
+}
diff --git a/go.mod b/go.mod
index ff8bdf9..398d6c9 100644
--- a/go.mod
+++ b/go.mod
@@ -1,8 +1,9 @@
module go.uber.org/multierr
-go 1.12
+go 1.14
require (
- github.com/stretchr/testify v1.3.0
+ github.com/stretchr/testify v1.7.0
go.uber.org/atomic v1.7.0
+ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
diff --git a/go.sum b/go.sum
index ecfc286..75edd73 100644
--- a/go.sum
+++ b/go.sum
@@ -1,11 +1,16 @@
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/go113.go b/go113.go
deleted file mode 100644
index 264b0ea..0000000
--- a/go113.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2019 Uber Technologies, Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-// +build go1.13
-
-package multierr
-
-import "errors"
-
-// As attempts to find the first error in the error list that matches the type
-// of the value that target points to.
-//
-// This function allows errors.As to traverse the values stored on the
-// multierr error.
-func (merr *multiError) As(target interface{}) bool {
- for _, err := range merr.Errors() {
- if errors.As(err, target) {
- return true
- }
- }
- return false
-}
-
-// Is attempts to match the provided error against errors in the error list.
-//
-// This function allows errors.Is to traverse the values stored on the
-// multierr error.
-func (merr *multiError) Is(target error) bool {
- for _, err := range merr.Errors() {
- if errors.Is(err, target) {
- return true
- }
- }
- return false
-}
diff --git a/tools/go.mod b/tools/go.mod
index 147f3cf..ebf0180 100644
--- a/tools/go.mod
+++ b/tools/go.mod
@@ -3,8 +3,6 @@ module go.uber.org/multierr/tools
go 1.12
require (
- go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee
- golang.org/x/lint v0.0.0-20190930215403-16217165b5de
- golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 // indirect
- honnef.co/go/tools v0.0.1-2019.2.3
+ golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5
+ honnef.co/go/tools v0.1.4
)
diff --git a/tools/go.sum b/tools/go.sum
index 11e3ae3..fc0f016 100644
--- a/tools/go.sum
+++ b/tools/go.sum
@@ -1,32 +1,35 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
-go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
-golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI=
+golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
-golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
+golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
-honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+honnef.co/go/tools v0.1.4 h1:SadWOkti5uVN1FAMgxn165+Mw00fuQKyk4Gyn/inxNQ=
+honnef.co/go/tools v0.1.4/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
diff --git a/tools/tools.go b/tools/tools.go
index df93f07..fa5465c 100644
--- a/tools/tools.go
+++ b/tools/tools.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2019 Uber Technologies, Inc.
+// Copyright (c) 2019-2021 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -18,13 +18,13 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+//go:build tools
// +build tools
package multierr
import (
// Tools we use during development.
- _ "go.uber.org/tools/update-license"
_ "golang.org/x/lint/golint"
_ "honnef.co/go/tools/cmd/staticcheck"
)
Debdiff
[The following lists of changes regard files as different if they have different names, permissions or owners.]
Files in second set of .debs but not in first
-rw-r--r-- root/root /usr/share/gocode/src/go.uber.org/multierr/appendinvoke_example_test.go -rw-r--r-- root/root /usr/share/gocode/src/go.uber.org/multierr/error_ext_test.go
Files in first set of .debs but not in second
-rw-r--r-- root/root /usr/share/gocode/src/go.uber.org/multierr/go113.go -rw-r--r-- root/root /usr/share/gocode/src/go.uber.org/multierr/go113_test.go
No differences were encountered in the control files