From 867b83059383a52488a5f662a861ded4263a4822 Mon Sep 17 00:00:00 2001 From: Abhinav Gupta Date: Fri, 10 Feb 2023 05:33:30 -0800 Subject: [PATCH] Support Go 1.20 Unwrap() []error 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 --- .github/workflows/go.yml | 4 +-- CHANGELOG.md | 5 ++++ error.go | 30 +------------------- error_post_go120.go | 29 ++++++++++++++++++++ error_pre_go120.go | 59 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 31 deletions(-) create mode 100644 error_post_go120.go create mode 100644 error_pre_go120.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 6ac89ec..23c78ab 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -13,9 +13,9 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go: ["1.18.x", "1.19.x"] + go: ["1.19.x", "1.20.x"] include: - - go: 1.19.x + - go: 1.20.x latest: true steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index d2c8aad..e00484c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Releases ======== +v1.10.0 (unreleased) +==================== + +- Comply with Go 1.20's multiple-error interface. + v1.9.0 (2022-12-12) =================== diff --git a/error.go b/error.go index cdd91ae..0166a2f 100644 --- a/error.go +++ b/error.go @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2021 Uber Technologies, Inc. +// Copyright (c) 2017-2023 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 @@ -142,7 +142,6 @@ package multierr // import "go.uber.org/multierr" import ( "bytes" - "errors" "fmt" "io" "strings" @@ -239,33 +238,6 @@ 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 "" diff --git a/error_post_go120.go b/error_post_go120.go new file mode 100644 index 0000000..0b00bec --- /dev/null +++ b/error_post_go120.go @@ -0,0 +1,29 @@ +// Copyright (c) 2017-2023 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. + +//go:build go1.20 +// +build go1.20 + +package multierr + +// Unwrap returns a list of errors wrapped by this multierr. +func (merr *multiError) Unwrap() []error { + return merr.Errors() +} diff --git a/error_pre_go120.go b/error_pre_go120.go new file mode 100644 index 0000000..8da10f1 --- /dev/null +++ b/error_pre_go120.go @@ -0,0 +1,59 @@ +// Copyright (c) 2017-2023 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. + +//go:build !go1.20 +// +build !go1.20 + +package multierr + +import "errors" + +// Versions of Go before 1.20 did not support the Unwrap() []error method. +// This provides a similar behavior by implementing the Is(..) and As(..) +// methods. +// See the errors.Join proposal for details: +// https://github.com/golang/go/issues/53435 + +// 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 +}