Initial commit: Go 1.23 release state
This commit is contained in:
87
src/errors/errors.go
Normal file
87
src/errors/errors.go
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package errors implements functions to manipulate errors.
|
||||
//
|
||||
// The [New] function creates errors whose only content is a text message.
|
||||
//
|
||||
// An error e wraps another error if e's type has one of the methods
|
||||
//
|
||||
// Unwrap() error
|
||||
// Unwrap() []error
|
||||
//
|
||||
// If e.Unwrap() returns a non-nil error w or a slice containing w,
|
||||
// then we say that e wraps w. A nil error returned from e.Unwrap()
|
||||
// indicates that e does not wrap any error. It is invalid for an
|
||||
// Unwrap method to return an []error containing a nil error value.
|
||||
//
|
||||
// An easy way to create wrapped errors is to call [fmt.Errorf] and apply
|
||||
// the %w verb to the error argument:
|
||||
//
|
||||
// wrapsErr := fmt.Errorf("... %w ...", ..., err, ...)
|
||||
//
|
||||
// Successive unwrapping of an error creates a tree. The [Is] and [As]
|
||||
// functions inspect an error's tree by examining first the error
|
||||
// itself followed by the tree of each of its children in turn
|
||||
// (pre-order, depth-first traversal).
|
||||
//
|
||||
// [Is] examines the tree of its first argument looking for an error that
|
||||
// matches the second. It reports whether it finds a match. It should be
|
||||
// used in preference to simple equality checks:
|
||||
//
|
||||
// if errors.Is(err, fs.ErrExist)
|
||||
//
|
||||
// is preferable to
|
||||
//
|
||||
// if err == fs.ErrExist
|
||||
//
|
||||
// because the former will succeed if err wraps [io/fs.ErrExist].
|
||||
//
|
||||
// [As] examines the tree of its first argument looking for an error that can be
|
||||
// assigned to its second argument, which must be a pointer. If it succeeds, it
|
||||
// performs the assignment and returns true. Otherwise, it returns false. The form
|
||||
//
|
||||
// var perr *fs.PathError
|
||||
// if errors.As(err, &perr) {
|
||||
// fmt.Println(perr.Path)
|
||||
// }
|
||||
//
|
||||
// is preferable to
|
||||
//
|
||||
// if perr, ok := err.(*fs.PathError); ok {
|
||||
// fmt.Println(perr.Path)
|
||||
// }
|
||||
//
|
||||
// because the former will succeed if err wraps an [*io/fs.PathError].
|
||||
package errors
|
||||
|
||||
// New returns an error that formats as the given text.
|
||||
// Each call to New returns a distinct error value even if the text is identical.
|
||||
func New(text string) error {
|
||||
return &errorString{text}
|
||||
}
|
||||
|
||||
// errorString is a trivial implementation of error.
|
||||
type errorString struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func (e *errorString) Error() string {
|
||||
return e.s
|
||||
}
|
||||
|
||||
// ErrUnsupported indicates that a requested operation cannot be performed,
|
||||
// because it is unsupported. For example, a call to [os.Link] when using a
|
||||
// file system that does not support hard links.
|
||||
//
|
||||
// Functions and methods should not return this error but should instead
|
||||
// return an error including appropriate context that satisfies
|
||||
//
|
||||
// errors.Is(err, errors.ErrUnsupported)
|
||||
//
|
||||
// either by directly wrapping ErrUnsupported or by implementing an [Is] method.
|
||||
//
|
||||
// Functions and methods should document the cases in which an error
|
||||
// wrapping this will be returned.
|
||||
var ErrUnsupported = New("unsupported operation")
|
||||
33
src/errors/errors_test.go
Normal file
33
src/errors/errors_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package errors_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewEqual(t *testing.T) {
|
||||
// Different allocations should not be equal.
|
||||
if errors.New("abc") == errors.New("abc") {
|
||||
t.Errorf(`New("abc") == New("abc")`)
|
||||
}
|
||||
if errors.New("abc") == errors.New("xyz") {
|
||||
t.Errorf(`New("abc") == New("xyz")`)
|
||||
}
|
||||
|
||||
// Same allocation should be equal to itself (not crash).
|
||||
err := errors.New("jkl")
|
||||
if err != err {
|
||||
t.Errorf(`err != err`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorMethod(t *testing.T) {
|
||||
err := errors.New("abc")
|
||||
if err.Error() != "abc" {
|
||||
t.Errorf(`New("abc").Error() = %q, want %q`, err.Error(), "abc")
|
||||
}
|
||||
}
|
||||
111
src/errors/example_test.go
Normal file
111
src/errors/example_test.go
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package errors_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MyError is an error implementation that includes a time and message.
|
||||
type MyError struct {
|
||||
When time.Time
|
||||
What string
|
||||
}
|
||||
|
||||
func (e MyError) Error() string {
|
||||
return fmt.Sprintf("%v: %v", e.When, e.What)
|
||||
}
|
||||
|
||||
func oops() error {
|
||||
return MyError{
|
||||
time.Date(1989, 3, 15, 22, 30, 0, 0, time.UTC),
|
||||
"the file system has gone away",
|
||||
}
|
||||
}
|
||||
|
||||
func Example() {
|
||||
if err := oops(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// Output: 1989-03-15 22:30:00 +0000 UTC: the file system has gone away
|
||||
}
|
||||
|
||||
func ExampleNew() {
|
||||
err := errors.New("emit macho dwarf: elf header corrupted")
|
||||
if err != nil {
|
||||
fmt.Print(err)
|
||||
}
|
||||
// Output: emit macho dwarf: elf header corrupted
|
||||
}
|
||||
|
||||
// The fmt package's Errorf function lets us use the package's formatting
|
||||
// features to create descriptive error messages.
|
||||
func ExampleNew_errorf() {
|
||||
const name, id = "bimmler", 17
|
||||
err := fmt.Errorf("user %q (id %d) not found", name, id)
|
||||
if err != nil {
|
||||
fmt.Print(err)
|
||||
}
|
||||
// Output: user "bimmler" (id 17) not found
|
||||
}
|
||||
|
||||
func ExampleJoin() {
|
||||
err1 := errors.New("err1")
|
||||
err2 := errors.New("err2")
|
||||
err := errors.Join(err1, err2)
|
||||
fmt.Println(err)
|
||||
if errors.Is(err, err1) {
|
||||
fmt.Println("err is err1")
|
||||
}
|
||||
if errors.Is(err, err2) {
|
||||
fmt.Println("err is err2")
|
||||
}
|
||||
// Output:
|
||||
// err1
|
||||
// err2
|
||||
// err is err1
|
||||
// err is err2
|
||||
}
|
||||
|
||||
func ExampleIs() {
|
||||
if _, err := os.Open("non-existing"); err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
fmt.Println("file does not exist")
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// file does not exist
|
||||
}
|
||||
|
||||
func ExampleAs() {
|
||||
if _, err := os.Open("non-existing"); err != nil {
|
||||
var pathError *fs.PathError
|
||||
if errors.As(err, &pathError) {
|
||||
fmt.Println("Failed at path:", pathError.Path)
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Failed at path: non-existing
|
||||
}
|
||||
|
||||
func ExampleUnwrap() {
|
||||
err1 := errors.New("error1")
|
||||
err2 := fmt.Errorf("error2: [%w]", err1)
|
||||
fmt.Println(err2)
|
||||
fmt.Println(errors.Unwrap(err2))
|
||||
// Output:
|
||||
// error2: [error1]
|
||||
// error1
|
||||
}
|
||||
62
src/errors/join.go
Normal file
62
src/errors/join.go
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Join returns an error that wraps the given errors.
|
||||
// Any nil error values are discarded.
|
||||
// Join returns nil if every value in errs is nil.
|
||||
// The error formats as the concatenation of the strings obtained
|
||||
// by calling the Error method of each element of errs, with a newline
|
||||
// between each string.
|
||||
//
|
||||
// A non-nil error returned by Join implements the Unwrap() []error method.
|
||||
func Join(errs ...error) error {
|
||||
n := 0
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
n++
|
||||
}
|
||||
}
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
e := &joinError{
|
||||
errs: make([]error, 0, n),
|
||||
}
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
e.errs = append(e.errs, err)
|
||||
}
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
type joinError struct {
|
||||
errs []error
|
||||
}
|
||||
|
||||
func (e *joinError) Error() string {
|
||||
// Since Join returns nil if every value in errs is nil,
|
||||
// e.errs cannot be empty.
|
||||
if len(e.errs) == 1 {
|
||||
return e.errs[0].Error()
|
||||
}
|
||||
|
||||
b := []byte(e.errs[0].Error())
|
||||
for _, err := range e.errs[1:] {
|
||||
b = append(b, '\n')
|
||||
b = append(b, err.Error()...)
|
||||
}
|
||||
// At this point, b has at least one byte '\n'.
|
||||
return unsafe.String(&b[0], len(b))
|
||||
}
|
||||
|
||||
func (e *joinError) Unwrap() []error {
|
||||
return e.errs
|
||||
}
|
||||
72
src/errors/join_test.go
Normal file
72
src/errors/join_test.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package errors_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestJoinReturnsNil(t *testing.T) {
|
||||
if err := errors.Join(); err != nil {
|
||||
t.Errorf("errors.Join() = %v, want nil", err)
|
||||
}
|
||||
if err := errors.Join(nil); err != nil {
|
||||
t.Errorf("errors.Join(nil) = %v, want nil", err)
|
||||
}
|
||||
if err := errors.Join(nil, nil); err != nil {
|
||||
t.Errorf("errors.Join(nil, nil) = %v, want nil", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoin(t *testing.T) {
|
||||
err1 := errors.New("err1")
|
||||
err2 := errors.New("err2")
|
||||
for _, test := range []struct {
|
||||
errs []error
|
||||
want []error
|
||||
}{{
|
||||
errs: []error{err1},
|
||||
want: []error{err1},
|
||||
}, {
|
||||
errs: []error{err1, err2},
|
||||
want: []error{err1, err2},
|
||||
}, {
|
||||
errs: []error{err1, nil, err2},
|
||||
want: []error{err1, err2},
|
||||
}} {
|
||||
got := errors.Join(test.errs...).(interface{ Unwrap() []error }).Unwrap()
|
||||
if !reflect.DeepEqual(got, test.want) {
|
||||
t.Errorf("Join(%v) = %v; want %v", test.errs, got, test.want)
|
||||
}
|
||||
if len(got) != cap(got) {
|
||||
t.Errorf("Join(%v) returns errors with len=%v, cap=%v; want len==cap", test.errs, len(got), cap(got))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinErrorMethod(t *testing.T) {
|
||||
err1 := errors.New("err1")
|
||||
err2 := errors.New("err2")
|
||||
for _, test := range []struct {
|
||||
errs []error
|
||||
want string
|
||||
}{{
|
||||
errs: []error{err1},
|
||||
want: "err1",
|
||||
}, {
|
||||
errs: []error{err1, err2},
|
||||
want: "err1\nerr2",
|
||||
}, {
|
||||
errs: []error{err1, nil, err2},
|
||||
want: "err1\nerr2",
|
||||
}} {
|
||||
got := errors.Join(test.errs...).Error()
|
||||
if got != test.want {
|
||||
t.Errorf("Join(%v).Error() = %q; want %q", test.errs, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
147
src/errors/wrap.go
Normal file
147
src/errors/wrap.go
Normal file
@@ -0,0 +1,147 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
"internal/reflectlite"
|
||||
)
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err's
|
||||
// type contains an Unwrap method returning error.
|
||||
// Otherwise, Unwrap returns nil.
|
||||
//
|
||||
// Unwrap only calls a method of the form "Unwrap() error".
|
||||
// In particular Unwrap does not unwrap errors returned by [Join].
|
||||
func Unwrap(err error) error {
|
||||
u, ok := err.(interface {
|
||||
Unwrap() error
|
||||
})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return u.Unwrap()
|
||||
}
|
||||
|
||||
// Is reports whether any error in err's tree matches target.
|
||||
//
|
||||
// The tree consists of err itself, followed by the errors obtained by repeatedly
|
||||
// calling its Unwrap() error or Unwrap() []error method. When err wraps multiple
|
||||
// errors, Is examines err followed by a depth-first traversal of its children.
|
||||
//
|
||||
// An error is considered to match a target if it is equal to that target or if
|
||||
// it implements a method Is(error) bool such that Is(target) returns true.
|
||||
//
|
||||
// An error type might provide an Is method so it can be treated as equivalent
|
||||
// to an existing error. For example, if MyError defines
|
||||
//
|
||||
// func (m MyError) Is(target error) bool { return target == fs.ErrExist }
|
||||
//
|
||||
// then Is(MyError{}, fs.ErrExist) returns true. See [syscall.Errno.Is] for
|
||||
// an example in the standard library. An Is method should only shallowly
|
||||
// compare err and the target and not call [Unwrap] on either.
|
||||
func Is(err, target error) bool {
|
||||
if err == nil || target == nil {
|
||||
return err == target
|
||||
}
|
||||
|
||||
isComparable := reflectlite.TypeOf(target).Comparable()
|
||||
return is(err, target, isComparable)
|
||||
}
|
||||
|
||||
func is(err, target error, targetComparable bool) bool {
|
||||
for {
|
||||
if targetComparable && err == target {
|
||||
return true
|
||||
}
|
||||
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
|
||||
return true
|
||||
}
|
||||
switch x := err.(type) {
|
||||
case interface{ Unwrap() error }:
|
||||
err = x.Unwrap()
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
case interface{ Unwrap() []error }:
|
||||
for _, err := range x.Unwrap() {
|
||||
if is(err, target, targetComparable) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// As finds the first error in err's tree that matches target, and if one is found, sets
|
||||
// target to that error value and returns true. Otherwise, it returns false.
|
||||
//
|
||||
// The tree consists of err itself, followed by the errors obtained by repeatedly
|
||||
// calling its Unwrap() error or Unwrap() []error method. When err wraps multiple
|
||||
// errors, As examines err followed by a depth-first traversal of its children.
|
||||
//
|
||||
// An error matches target if the error's concrete value is assignable to the value
|
||||
// pointed to by target, or if the error has a method As(any) bool such that
|
||||
// As(target) returns true. In the latter case, the As method is responsible for
|
||||
// setting target.
|
||||
//
|
||||
// An error type might provide an As method so it can be treated as if it were a
|
||||
// different error type.
|
||||
//
|
||||
// As panics if target is not a non-nil pointer to either a type that implements
|
||||
// error, or to any interface type.
|
||||
func As(err error, target any) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
if target == nil {
|
||||
panic("errors: target cannot be nil")
|
||||
}
|
||||
val := reflectlite.ValueOf(target)
|
||||
typ := val.Type()
|
||||
if typ.Kind() != reflectlite.Ptr || val.IsNil() {
|
||||
panic("errors: target must be a non-nil pointer")
|
||||
}
|
||||
targetType := typ.Elem()
|
||||
if targetType.Kind() != reflectlite.Interface && !targetType.Implements(errorType) {
|
||||
panic("errors: *target must be interface or implement error")
|
||||
}
|
||||
return as(err, target, val, targetType)
|
||||
}
|
||||
|
||||
func as(err error, target any, targetVal reflectlite.Value, targetType reflectlite.Type) bool {
|
||||
for {
|
||||
if reflectlite.TypeOf(err).AssignableTo(targetType) {
|
||||
targetVal.Elem().Set(reflectlite.ValueOf(err))
|
||||
return true
|
||||
}
|
||||
if x, ok := err.(interface{ As(any) bool }); ok && x.As(target) {
|
||||
return true
|
||||
}
|
||||
switch x := err.(type) {
|
||||
case interface{ Unwrap() error }:
|
||||
err = x.Unwrap()
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
case interface{ Unwrap() []error }:
|
||||
for _, err := range x.Unwrap() {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
if as(err, target, targetVal, targetType) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var errorType = reflectlite.TypeOf((*error)(nil)).Elem()
|
||||
312
src/errors/wrap_test.go
Normal file
312
src/errors/wrap_test.go
Normal file
@@ -0,0 +1,312 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package errors_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIs(t *testing.T) {
|
||||
err1 := errors.New("1")
|
||||
erra := wrapped{"wrap 2", err1}
|
||||
errb := wrapped{"wrap 3", erra}
|
||||
|
||||
err3 := errors.New("3")
|
||||
|
||||
poser := &poser{"either 1 or 3", func(err error) bool {
|
||||
return err == err1 || err == err3
|
||||
}}
|
||||
|
||||
testCases := []struct {
|
||||
err error
|
||||
target error
|
||||
match bool
|
||||
}{
|
||||
{nil, nil, true},
|
||||
{nil, err1, false},
|
||||
{err1, nil, false},
|
||||
{err1, err1, true},
|
||||
{erra, err1, true},
|
||||
{errb, err1, true},
|
||||
{err1, err3, false},
|
||||
{erra, err3, false},
|
||||
{errb, err3, false},
|
||||
{poser, err1, true},
|
||||
{poser, err3, true},
|
||||
{poser, erra, false},
|
||||
{poser, errb, false},
|
||||
{errorUncomparable{}, errorUncomparable{}, true},
|
||||
{errorUncomparable{}, &errorUncomparable{}, false},
|
||||
{&errorUncomparable{}, errorUncomparable{}, true},
|
||||
{&errorUncomparable{}, &errorUncomparable{}, false},
|
||||
{errorUncomparable{}, err1, false},
|
||||
{&errorUncomparable{}, err1, false},
|
||||
{multiErr{}, err1, false},
|
||||
{multiErr{err1, err3}, err1, true},
|
||||
{multiErr{err3, err1}, err1, true},
|
||||
{multiErr{err1, err3}, errors.New("x"), false},
|
||||
{multiErr{err3, errb}, errb, true},
|
||||
{multiErr{err3, errb}, erra, true},
|
||||
{multiErr{err3, errb}, err1, true},
|
||||
{multiErr{errb, err3}, err1, true},
|
||||
{multiErr{poser}, err1, true},
|
||||
{multiErr{poser}, err3, true},
|
||||
{multiErr{nil}, nil, false},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
if got := errors.Is(tc.err, tc.target); got != tc.match {
|
||||
t.Errorf("Is(%v, %v) = %v, want %v", tc.err, tc.target, got, tc.match)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type poser struct {
|
||||
msg string
|
||||
f func(error) bool
|
||||
}
|
||||
|
||||
var poserPathErr = &fs.PathError{Op: "poser"}
|
||||
|
||||
func (p *poser) Error() string { return p.msg }
|
||||
func (p *poser) Is(err error) bool { return p.f(err) }
|
||||
func (p *poser) As(err any) bool {
|
||||
switch x := err.(type) {
|
||||
case **poser:
|
||||
*x = p
|
||||
case *errorT:
|
||||
*x = errorT{"poser"}
|
||||
case **fs.PathError:
|
||||
*x = poserPathErr
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestAs(t *testing.T) {
|
||||
var errT errorT
|
||||
var errP *fs.PathError
|
||||
var timeout interface{ Timeout() bool }
|
||||
var p *poser
|
||||
_, errF := os.Open("non-existing")
|
||||
poserErr := &poser{"oh no", nil}
|
||||
|
||||
testCases := []struct {
|
||||
err error
|
||||
target any
|
||||
match bool
|
||||
want any // value of target on match
|
||||
}{{
|
||||
nil,
|
||||
&errP,
|
||||
false,
|
||||
nil,
|
||||
}, {
|
||||
wrapped{"pitied the fool", errorT{"T"}},
|
||||
&errT,
|
||||
true,
|
||||
errorT{"T"},
|
||||
}, {
|
||||
errF,
|
||||
&errP,
|
||||
true,
|
||||
errF,
|
||||
}, {
|
||||
errorT{},
|
||||
&errP,
|
||||
false,
|
||||
nil,
|
||||
}, {
|
||||
wrapped{"wrapped", nil},
|
||||
&errT,
|
||||
false,
|
||||
nil,
|
||||
}, {
|
||||
&poser{"error", nil},
|
||||
&errT,
|
||||
true,
|
||||
errorT{"poser"},
|
||||
}, {
|
||||
&poser{"path", nil},
|
||||
&errP,
|
||||
true,
|
||||
poserPathErr,
|
||||
}, {
|
||||
poserErr,
|
||||
&p,
|
||||
true,
|
||||
poserErr,
|
||||
}, {
|
||||
errors.New("err"),
|
||||
&timeout,
|
||||
false,
|
||||
nil,
|
||||
}, {
|
||||
errF,
|
||||
&timeout,
|
||||
true,
|
||||
errF,
|
||||
}, {
|
||||
wrapped{"path error", errF},
|
||||
&timeout,
|
||||
true,
|
||||
errF,
|
||||
}, {
|
||||
multiErr{},
|
||||
&errT,
|
||||
false,
|
||||
nil,
|
||||
}, {
|
||||
multiErr{errors.New("a"), errorT{"T"}},
|
||||
&errT,
|
||||
true,
|
||||
errorT{"T"},
|
||||
}, {
|
||||
multiErr{errorT{"T"}, errors.New("a")},
|
||||
&errT,
|
||||
true,
|
||||
errorT{"T"},
|
||||
}, {
|
||||
multiErr{errorT{"a"}, errorT{"b"}},
|
||||
&errT,
|
||||
true,
|
||||
errorT{"a"},
|
||||
}, {
|
||||
multiErr{multiErr{errors.New("a"), errorT{"a"}}, errorT{"b"}},
|
||||
&errT,
|
||||
true,
|
||||
errorT{"a"},
|
||||
}, {
|
||||
multiErr{wrapped{"path error", errF}},
|
||||
&timeout,
|
||||
true,
|
||||
errF,
|
||||
}, {
|
||||
multiErr{nil},
|
||||
&errT,
|
||||
false,
|
||||
nil,
|
||||
}}
|
||||
for i, tc := range testCases {
|
||||
name := fmt.Sprintf("%d:As(Errorf(..., %v), %v)", i, tc.err, tc.target)
|
||||
// Clear the target pointer, in case it was set in a previous test.
|
||||
rtarget := reflect.ValueOf(tc.target)
|
||||
rtarget.Elem().Set(reflect.Zero(reflect.TypeOf(tc.target).Elem()))
|
||||
t.Run(name, func(t *testing.T) {
|
||||
match := errors.As(tc.err, tc.target)
|
||||
if match != tc.match {
|
||||
t.Fatalf("match: got %v; want %v", match, tc.match)
|
||||
}
|
||||
if !match {
|
||||
return
|
||||
}
|
||||
if got := rtarget.Elem().Interface(); got != tc.want {
|
||||
t.Fatalf("got %#v, want %#v", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsValidation(t *testing.T) {
|
||||
var s string
|
||||
testCases := []any{
|
||||
nil,
|
||||
(*int)(nil),
|
||||
"error",
|
||||
&s,
|
||||
}
|
||||
err := errors.New("error")
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("%T(%v)", tc, tc), func(t *testing.T) {
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
if errors.As(err, tc) {
|
||||
t.Errorf("As(err, %T(%v)) = true, want false", tc, tc)
|
||||
return
|
||||
}
|
||||
t.Errorf("As(err, %T(%v)) did not panic", tc, tc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIs(b *testing.B) {
|
||||
err1 := errors.New("1")
|
||||
err2 := multiErr{multiErr{multiErr{err1, errorT{"a"}}, errorT{"b"}}}
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
if !errors.Is(err2, err1) {
|
||||
b.Fatal("Is failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAs(b *testing.B) {
|
||||
err := multiErr{multiErr{multiErr{errors.New("a"), errorT{"a"}}, errorT{"b"}}}
|
||||
for i := 0; i < b.N; i++ {
|
||||
var target errorT
|
||||
if !errors.As(err, &target) {
|
||||
b.Fatal("As failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnwrap(t *testing.T) {
|
||||
err1 := errors.New("1")
|
||||
erra := wrapped{"wrap 2", err1}
|
||||
|
||||
testCases := []struct {
|
||||
err error
|
||||
want error
|
||||
}{
|
||||
{nil, nil},
|
||||
{wrapped{"wrapped", nil}, nil},
|
||||
{err1, nil},
|
||||
{erra, err1},
|
||||
{wrapped{"wrap 3", erra}, erra},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
if got := errors.Unwrap(tc.err); got != tc.want {
|
||||
t.Errorf("Unwrap(%v) = %v, want %v", tc.err, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type errorT struct{ s string }
|
||||
|
||||
func (e errorT) Error() string { return fmt.Sprintf("errorT(%s)", e.s) }
|
||||
|
||||
type wrapped struct {
|
||||
msg string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e wrapped) Error() string { return e.msg }
|
||||
func (e wrapped) Unwrap() error { return e.err }
|
||||
|
||||
type multiErr []error
|
||||
|
||||
func (m multiErr) Error() string { return "multiError" }
|
||||
func (m multiErr) Unwrap() []error { return []error(m) }
|
||||
|
||||
type errorUncomparable struct {
|
||||
f []string
|
||||
}
|
||||
|
||||
func (errorUncomparable) Error() string {
|
||||
return "uncomparable error"
|
||||
}
|
||||
|
||||
func (errorUncomparable) Is(target error) bool {
|
||||
_, ok := target.(errorUncomparable)
|
||||
return ok
|
||||
}
|
||||
Reference in New Issue
Block a user