// Package errors implements functions to manipulate errors.
package errors
-import (
- "internal/errinternal"
- "runtime"
-)
-
// New returns an error that formats as the given text.
-//
-// The returned error contains a Frame set to the caller's location and
-// implements Formatter to show this information when printed with details.
func New(text string) error {
- // Inline call to errors.Callers to improve performance.
- var s Frame
- runtime.Callers(2, s.frames[:])
- return &errorString{text, nil, s}
-}
-
-func init() {
- errinternal.NewError = func(text string, err error) error {
- var s Frame
- runtime.Callers(3, s.frames[:])
- return &errorString{text, err, s}
- }
+ return &errorString{text}
}
// errorString is a trivial implementation of error.
type errorString struct {
- s string
- err error
- frame Frame
+ s string
}
func (e *errorString) Error() string {
- if e.err != nil {
- return e.s + ": " + e.err.Error()
- }
return e.s
}
-
-func (e *errorString) FormatError(p Printer) (next error) {
- p.Print(e.s)
- e.frame.Format(p)
- return e.err
-}
+++ /dev/null
-// 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
-
-// A Formatter formats error messages.
-type Formatter interface {
- error
-
- // FormatError prints the receiver's first error and returns the next error in
- // the error chain, if any.
- FormatError(p Printer) (next error)
-}
-
-// A Printer formats error messages.
-//
-// The most common implementation of Printer is the one provided by package fmt
-// during Printf. Localization packages such as golang.org/x/text/message
-// typically provide their own implementations.
-type Printer interface {
- // Print appends args to the message output.
- Print(args ...interface{})
-
- // Printf writes a formatted string.
- Printf(format string, args ...interface{})
-
- // Detail reports whether error detail is requested.
- // After the first call to Detail, all text written to the Printer
- // is formatted as additional detail, or ignored when
- // detail has not been requested.
- // If Detail returns false, the caller can avoid printing the detail at all.
- Detail() bool
-}
+++ /dev/null
-// 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 (
- "runtime"
-)
-
-// A Frame contains part of a call stack.
-type Frame struct {
- frames [1]uintptr
-}
-
-// Caller returns a Frame that describes a frame on the caller's stack.
-// The argument skip is the number of frames to skip over.
-// Caller(0) returns the frame for the caller of Caller.
-func Caller(skip int) Frame {
- var s Frame
- runtime.Callers(skip+2, s.frames[:])
- return s
-}
-
-// location reports the file, line, and function of a frame.
-//
-// The returned function may be "" even if file and line are not.
-func (f Frame) location() (function, file string, line int) {
- frames := runtime.CallersFrames(f.frames[:])
- fr, _ := frames.Next()
- return fr.Function, fr.File, fr.Line
-}
-
-// Format prints the stack as error detail.
-// It should be called from an error's Format implementation,
-// before printing any other error detail.
-func (f Frame) Format(p Printer) {
- if p.Detail() {
- function, file, line := f.location()
- if function != "" {
- p.Printf("%s\n ", function)
- }
- if file != "" {
- p.Printf("%s:%d\n", file, line)
- }
- }
-}
+++ /dev/null
-// 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 (
- "bytes"
- "errors"
- "fmt"
- "math/big"
- "regexp"
- "strings"
- "testing"
-)
-
-func TestFrame(t *testing.T) {
-
- // Extra line
- got := fmt.Sprintf("%+v", errors.New("Test"))
- got = got[strings.Index(got, "Test"):]
- const want = "^Test:" +
- "\n errors_test.TestFrame" +
- "\n .*/errors/frame_test.go:20$"
- ok, err := regexp.MatchString(want, got)
- if err != nil {
- t.Fatal(err)
- }
- if !ok {
- t.Errorf("\n got %v;\nwant %v", got, want)
- }
-}
-
-type myType struct{}
-
-func (myType) Format(s fmt.State, v rune) {
- s.Write(bytes.Repeat([]byte("Hi! "), 10))
-}
-
-func BenchmarkNew(b *testing.B) {
- for i := 0; i < b.N; i++ {
- _ = errors.New("new error")
- }
-}
-
-func BenchmarkErrorf(b *testing.B) {
- err := errors.New("foo")
- args := func(a ...interface{}) []interface{} { return a }
- benchCases := []struct {
- name string
- format string
- args []interface{}
- }{
- {"no_format", "msg: %v", args(err)},
- {"with_format", "failed %d times: %v", args(5, err)},
- {"method: mytype", "pi %s %v: %v", args("myfile.go", myType{}, err)},
- {"method: number", "pi %s %d: %v", args("myfile.go", big.NewInt(5), err)},
- }
- for _, bc := range benchCases {
- b.Run(bc.name, func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- _ = fmt.Errorf(bc.format, bc.args...)
- }
- })
- }
-}
"internal/reflectlite"
)
-// A Wrapper provides context around another error.
-type Wrapper interface {
- // Unwrap returns the next error in the error chain.
- // If there is no next error, Unwrap returns nil.
- Unwrap() error
-}
-
-// Opaque returns an error with the same error formatting as err
-// but that does not match err and cannot be unwrapped.
-func Opaque(err error) error {
- return noWrapper{err}
-}
-
-type noWrapper struct {
- error
-}
-
-func (e noWrapper) FormatError(p Printer) (next error) {
- if f, ok := e.error.(Formatter); ok {
- return f.FormatError(p)
- }
- p.Print(e.error)
- return nil
-}
-
// Unwrap returns the result of calling the Unwrap method on err, if err
// implements Wrapper. Otherwise, Unwrap returns nil.
func Unwrap(err error) error {
- u, ok := err.(Wrapper)
+ u, ok := err.(interface {
+ Unwrap() error
+ })
if !ok {
return nil
}
package errors_test
import (
- "bytes"
"errors"
"fmt"
"os"
err1 := errors.New("1")
erra := wrapped{"wrap 2", err1}
errb := wrapped{"wrap 3", erra}
- erro := errors.Opaque(err1)
- errco := wrapped{"opaque", erro}
err3 := errors.New("3")
{err1, err1, true},
{erra, err1, true},
{errb, err1, true},
- {errco, erro, true},
- {errco, err1, false},
- {erro, erro, true},
{err1, err3, false},
{erra, err3, false},
{errb, err3, false},
{poser, err3, true},
{poser, erra, false},
{poser, errb, false},
- {poser, erro, false},
- {poser, errco, false},
{errorUncomparable{}, errorUncomparable{}, true},
{errorUncomparable{}, &errorUncomparable{}, false},
{&errorUncomparable{}, errorUncomparable{}, true},
errF,
&errP,
true,
- }, {
- errors.Opaque(errT),
- &errT,
- false,
}, {
errorT{},
&errP,
func TestUnwrap(t *testing.T) {
err1 := errors.New("1")
erra := wrapped{"wrap 2", err1}
- erro := errors.Opaque(err1)
testCases := []struct {
err error
{err1, nil},
{erra, err1},
{wrapped{"wrap 3", erra}, erra},
-
- {erro, nil},
- {wrapped{"opaque", erro}, erro},
}
for _, tc := range testCases {
if got := errors.Unwrap(tc.err); got != tc.want {
}
}
-func TestOpaque(t *testing.T) {
- someError := errors.New("some error")
- testCases := []struct {
- err error
- next error
- }{
- {errorT{}, nil},
- {wrapped{"b", nil}, nil},
- {wrapped{"c", someError}, someError},
- }
- for _, tc := range testCases {
- t.Run("", func(t *testing.T) {
- opaque := errors.Opaque(tc.err)
-
- f, ok := opaque.(errors.Formatter)
- if !ok {
- t.Fatal("Opaque error does not implement Formatter")
- }
- var p printer
- next := f.FormatError(&p)
- if next != tc.next {
- t.Errorf("next was %v; want %v", next, tc.next)
- }
- if got, want := p.buf.String(), tc.err.Error(); got != want {
- t.Errorf("error was %q; want %q", got, want)
- }
- if got := errors.Unwrap(opaque); got != nil {
- t.Errorf("Unwrap returned non-nil error (%v)", got)
- }
- })
- }
-}
-
type errorT struct{}
func (errorT) Error() string { return "errorT" }
func (e wrapped) Unwrap() error { return e.err }
-func (e wrapped) FormatError(p errors.Printer) error {
- p.Print(e.msg)
- return e.err
-}
-
-type printer struct {
- errors.Printer
- buf bytes.Buffer
-}
-
-func (p *printer) Print(args ...interface{}) { fmt.Fprint(&p.buf, args...) }
-
type errorUncomparable struct {
f []string
}
1. If the operand is a reflect.Value, the operand is replaced by the
concrete value that it holds, and printing continues with the next rule.
- 2. If an operand implements the Formatter interface, and not
- errors.Formatter, it will be invoked. Formatter provides fine
- control of formatting.
+ 2. If an operand implements the Formatter interface, it will
+ be invoked. Formatter provides fine control of formatting.
3. If the %v verb is used with the # flag (%#v) and the operand
implements the GoStringer interface, that will be invoked.
If the format (which is implicitly %v for Println etc.) is valid
- for a string (%s %q %v %x %X), the following three rules apply:
+ for a string (%s %q %v %x %X), the following two rules apply:
- 4. If an operand implements errors.Formatter, the FormatError
- method will be invoked with an errors.Printer to print the error.
- If the %v flag is used with the + flag (%+v), the Detail method
- of the Printer will return true and the error will be formatted
- as a detailed error message. Otherwise the printed string will
- be formatted as required by the verb (if any).
-
- 5. If an operand implements the error interface, the Error method
+ 4. If an operand implements the error interface, the Error method
will be invoked to convert the object to a string, which will then
be formatted as required by the verb (if any).
- 6. If an operand implements method String() string, that method
+ 5. If an operand implements method String() string, that method
will be invoked to convert the object to a string, which will then
be formatted as required by the verb (if any).
+++ /dev/null
-// 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 fmt
-
-import (
- "errors"
- "internal/errinternal"
- "strings"
-)
-
-// Errorf formats according to a format specifier and returns the string as a
-// value that satisfies error.
-//
-// The returned error includes the file and line number of the caller when
-// formatted with additional detail enabled. If the last argument is an error
-// the returned error's Format method will return it if the format string ends
-// with ": %s", ": %v", or ": %w". If the last argument is an error and the
-// format string ends with ": %w", the returned error implements errors.Wrapper
-// with an Unwrap method returning it.
-func Errorf(format string, a ...interface{}) error {
- err, wrap := lastError(format, a)
- if err == nil {
- return errinternal.NewError(Sprintf(format, a...), nil)
- }
-
- // TODO: this is not entirely correct. The error value could be
- // printed elsewhere in format if it mixes numbered with unnumbered
- // substitutions. With relatively small changes to doPrintf we can
- // have it optionally ignore extra arguments and pass the argument
- // list in its entirety.
- msg := Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...)
- if wrap {
- return &wrapError{msg, err, errors.Caller(1)}
- }
- return errinternal.NewError(msg, err)
-}
-
-func lastError(format string, a []interface{}) (err error, wrap bool) {
- wrap = strings.HasSuffix(format, ": %w")
- if !wrap &&
- !strings.HasSuffix(format, ": %s") &&
- !strings.HasSuffix(format, ": %v") {
- return nil, false
- }
-
- if len(a) == 0 {
- return nil, false
- }
-
- err, ok := a[len(a)-1].(error)
- if !ok {
- return nil, false
- }
-
- return err, wrap
-}
-
-type noWrapError struct {
- msg string
- err error
- frame errors.Frame
-}
-
-func (e *noWrapError) Error() string {
- return Sprint(e)
-}
-
-func (e *noWrapError) FormatError(p errors.Printer) (next error) {
- p.Print(e.msg)
- e.frame.Format(p)
- return e.err
-}
-
-type wrapError struct {
- msg string
- err error
- frame errors.Frame
-}
-
-func (e *wrapError) Error() string {
- return Sprint(e)
-}
-
-func (e *wrapError) FormatError(p errors.Printer) (next error) {
- p.Print(e.msg)
- e.frame.Format(p)
- return e.err
-}
-
-func (e *wrapError) Unwrap() error {
- return e.err
-}
-
-func fmtError(p *pp, verb rune, err error) (handled bool) {
- var (
- sep = " " // separator before next error
- w = p // print buffer where error text is written
- )
- switch {
- // Note that this switch must match the preference order
- // for ordinary string printing (%#v before %+v, and so on).
-
- case p.fmt.sharpV:
- if stringer, ok := p.arg.(GoStringer); ok {
- // Print the result of GoString unadorned.
- p.fmt.fmtS(stringer.GoString())
- return true
- }
- return false
-
- case p.fmt.plusV:
- sep = "\n - "
- w.fmt.fmtFlags = fmtFlags{plusV: p.fmt.plusV} // only keep detail flag
-
- // The width or precision of a detailed view could be the number of
- // errors to print from a list.
-
- default:
- // Use an intermediate buffer in the rare cases that precision,
- // truncation, or one of the alternative verbs (q, x, and X) are
- // specified.
- switch verb {
- case 's', 'v':
- if (!w.fmt.widPresent || w.fmt.wid == 0) && !w.fmt.precPresent {
- break
- }
- fallthrough
- case 'q', 'x', 'X':
- w = newPrinter()
- defer w.free()
- default:
- return false
- }
- }
-
-loop:
- for {
- w.fmt.inDetail = false
- switch v := err.(type) {
- case errors.Formatter:
- err = v.FormatError((*errPP)(w))
- case Formatter:
- if w.fmt.plusV {
- v.Format((*errPPState)(w), 'v') // indent new lines
- } else {
- v.Format(w, 'v') // do not indent new lines
- }
- break loop
- default:
- w.fmtString(v.Error(), 's')
- break loop
- }
- if err == nil {
- break
- }
- if w.fmt.needColon || !p.fmt.plusV {
- w.buf.writeByte(':')
- w.fmt.needColon = false
- }
- w.buf.writeString(sep)
- w.fmt.inDetail = false
- w.fmt.needNewline = false
- }
-
- if w != p {
- p.fmtString(string(w.buf), verb)
- }
- return true
-}
-
-var detailSep = []byte("\n ")
-
-// errPPState wraps a pp to implement State with indentation. It is used
-// for errors implementing fmt.Formatter.
-type errPPState pp
-
-func (p *errPPState) Width() (wid int, ok bool) { return (*pp)(p).Width() }
-func (p *errPPState) Precision() (prec int, ok bool) { return (*pp)(p).Precision() }
-func (p *errPPState) Flag(c int) bool { return (*pp)(p).Flag(c) }
-
-func (p *errPPState) Write(b []byte) (n int, err error) {
- if p.fmt.plusV {
- if len(b) == 0 {
- return 0, nil
- }
- if p.fmt.inDetail && p.fmt.needColon {
- p.fmt.needNewline = true
- if b[0] == '\n' {
- b = b[1:]
- }
- }
- k := 0
- for i, c := range b {
- if p.fmt.needNewline {
- if p.fmt.inDetail && p.fmt.needColon {
- p.buf.writeByte(':')
- p.fmt.needColon = false
- }
- p.buf.write(detailSep)
- p.fmt.needNewline = false
- }
- if c == '\n' {
- p.buf.write(b[k:i])
- k = i + 1
- p.fmt.needNewline = true
- }
- }
- p.buf.write(b[k:])
- if !p.fmt.inDetail {
- p.fmt.needColon = true
- }
- } else if !p.fmt.inDetail {
- p.buf.write(b)
- }
- return len(b), nil
-
-}
-
-// errPP wraps a pp to implement an errors.Printer.
-type errPP pp
-
-func (p *errPP) Print(args ...interface{}) {
- if !p.fmt.inDetail || p.fmt.plusV {
- Fprint((*errPPState)(p), args...)
- }
-}
-
-func (p *errPP) Printf(format string, args ...interface{}) {
- if !p.fmt.inDetail || p.fmt.plusV {
- Fprintf((*errPPState)(p), format, args...)
- }
-}
-
-func (p *errPP) Detail() bool {
- p.fmt.inDetail = true
- return p.fmt.plusV
-}
+++ /dev/null
-// 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 fmt_test
-
-import (
- "errors"
- "fmt"
- "io"
- "os"
- "path"
- "reflect"
- "regexp"
- "strconv"
- "strings"
- "testing"
-)
-
-func TestErrorf(t *testing.T) {
- chained := &wrapped{"chained", nil}
- chain := func(s ...string) (a []string) {
- for _, s := range s {
- a = append(a, cleanPath(s))
- }
- return a
- }
- noArgsWrap := "no args: %w" // avoid vet check
- testCases := []struct {
- got error
- want []string
- }{{
- fmt.Errorf("no args"),
- chain("no args/path.TestErrorf/path.go:xxx"),
- }, {
- fmt.Errorf(noArgsWrap),
- chain("no args: %!w(MISSING)/path.TestErrorf/path.go:xxx"),
- }, {
- fmt.Errorf("nounwrap: %s", "simple"),
- chain(`nounwrap: simple/path.TestErrorf/path.go:xxx`),
- }, {
- fmt.Errorf("nounwrap: %v", "simple"),
- chain(`nounwrap: simple/path.TestErrorf/path.go:xxx`),
- }, {
- fmt.Errorf("%s failed: %v", "foo", chained),
- chain("foo failed/path.TestErrorf/path.go:xxx",
- "chained/somefile.go:xxx"),
- }, {
- fmt.Errorf("no wrap: %s", chained),
- chain("no wrap/path.TestErrorf/path.go:xxx",
- "chained/somefile.go:xxx"),
- }, {
- fmt.Errorf("%s failed: %w", "foo", chained),
- chain("wraps:foo failed/path.TestErrorf/path.go:xxx",
- "chained/somefile.go:xxx"),
- }, {
- fmt.Errorf("nowrapv: %v", chained),
- chain("nowrapv/path.TestErrorf/path.go:xxx",
- "chained/somefile.go:xxx"),
- }, {
- fmt.Errorf("wrapw: %w", chained),
- chain("wraps:wrapw/path.TestErrorf/path.go:xxx",
- "chained/somefile.go:xxx"),
- }, {
- fmt.Errorf("not wrapped: %+v", chained),
- chain("not wrapped: chained: somefile.go:123/path.TestErrorf/path.go:xxx"),
- }}
- for i, tc := range testCases {
- t.Run(strconv.Itoa(i)+"/"+path.Join(tc.want...), func(t *testing.T) {
- got := errToParts(tc.got)
- if !reflect.DeepEqual(got, tc.want) {
- t.Errorf("Format:\n got: %+q\nwant: %+q", got, tc.want)
- }
-
- gotStr := tc.got.Error()
- wantStr := fmt.Sprint(tc.got)
- if gotStr != wantStr {
- t.Errorf("Error:\n got: %+q\nwant: %+q", gotStr, wantStr)
- }
- })
- }
-}
-
-func TestErrorFormatter(t *testing.T) {
- testCases := []struct {
- err error
- fmt string
- want string
- regexp bool
- }{{
- err: errors.New("foo"),
- fmt: "%+v",
- want: "foo:" +
- "\n fmt_test.TestErrorFormatter" +
- "\n .+/fmt/errors_test.go:\\d\\d",
- regexp: true,
- }, {
- err: &wrapped{"simple", nil},
- fmt: "%s",
- want: "simple",
- }, {
- err: &wrapped{"can't adumbrate elephant", outOfPeanuts{}},
- fmt: "%s",
- want: "can't adumbrate elephant: out of peanuts",
- }, {
- err: &wrapped{"a", &wrapped{"b", &wrapped{"c", nil}}},
- fmt: "%s",
- want: "a: b: c",
- }, {
- err: &wrapped{"simple", nil},
- fmt: "%+v",
- want: "simple:" +
- "\n somefile.go:123",
- }, {
- err: &wrapped{"can't adumbrate elephant", outOfPeanuts{}},
- fmt: "%+v",
- want: "can't adumbrate elephant:" +
- "\n somefile.go:123" +
- "\n - out of peanuts:" +
- "\n the elephant is on strike" +
- "\n and the 12 monkeys" +
- "\n are laughing",
- }, {
- err: &wrapped{"simple", nil},
- fmt: "%#v",
- want: "&fmt_test.wrapped{msg:\"simple\", err:error(nil)}",
- }, {
- err: ¬AFormatterError{},
- fmt: "%+v",
- want: "not a formatter",
- }, {
- err: &wrapped{"wrap", ¬AFormatterError{}},
- fmt: "%+v",
- want: "wrap:" +
- "\n somefile.go:123" +
- "\n - not a formatter",
- }, {
- err: &withFrameAndMore{frame: errors.Caller(0)},
- fmt: "%+v",
- want: "something:" +
- "\n fmt_test.TestErrorFormatter" +
- "\n .+/fmt/errors_test.go:\\d\\d\\d" +
- "\n something more",
- regexp: true,
- }, {
- err: fmtTwice("Hello World!"),
- fmt: "%#v",
- want: "2 times Hello World!",
- }, {
- err: &wrapped{"fallback", os.ErrNotExist},
- fmt: "%s",
- want: "fallback: file does not exist",
- }, {
- err: &wrapped{"fallback", os.ErrNotExist},
- fmt: "%+v",
- // Note: no colon after the last error, as there are no details.
- want: "fallback:" +
- "\n somefile.go:123" +
- "\n - file does not exist:" +
- "\n .*" +
- "\n .+.go:\\d+",
- regexp: true,
- }, {
- err: &wrapped{"outer",
- errors.Opaque(&wrapped{"mid",
- &wrapped{"inner", nil}})},
- fmt: "%s",
- want: "outer: mid: inner",
- }, {
- err: &wrapped{"outer",
- errors.Opaque(&wrapped{"mid",
- &wrapped{"inner", nil}})},
- fmt: "%+v",
- want: "outer:" +
- "\n somefile.go:123" +
- "\n - mid:" +
- "\n somefile.go:123" +
- "\n - inner:" +
- "\n somefile.go:123",
- }, {
- err: &wrapped{"new style", formatError("old style")},
- fmt: "%v",
- want: "new style: old style",
- }, {
- err: &wrapped{"new style", formatError("old style")},
- fmt: "%q",
- want: `"new style: old style"`,
- }, {
- err: &wrapped{"new style", formatError("old style")},
- fmt: "%+v",
- // Note the extra indentation.
- // Colon for old style error is rendered by the fmt.Formatter
- // implementation of the old-style error.
- want: "new style:" +
- "\n somefile.go:123" +
- "\n - old style:" +
- "\n otherfile.go:456",
- }, {
- err: &wrapped{"simple", nil},
- fmt: "%-12s",
- want: "simple ",
- }, {
- // Don't use formatting flags for detailed view.
- err: &wrapped{"simple", nil},
- fmt: "%+12v",
- want: "simple:" +
- "\n somefile.go:123",
- }, {
- err: &wrapped{"can't adumbrate elephant", outOfPeanuts{}},
- fmt: "%+50s",
- want: " can't adumbrate elephant: out of peanuts",
- }, {
- err: &wrapped{"café", nil},
- fmt: "%q",
- want: `"café"`,
- }, {
- err: &wrapped{"café", nil},
- fmt: "%+q",
- want: `"caf\u00e9"`,
- }, {
- err: &wrapped{"simple", nil},
- fmt: "% x",
- want: "73 69 6d 70 6c 65",
- }, {
- err: &wrapped{"msg with\nnewline",
- &wrapped{"and another\none", nil}},
- fmt: "%s",
- want: "msg with" +
- "\nnewline: and another" +
- "\none",
- }, {
- err: &wrapped{"msg with\nnewline",
- &wrapped{"and another\none", nil}},
- fmt: "%+v",
- want: "msg with" +
- "\n newline:" +
- "\n somefile.go:123" +
- "\n - and another" +
- "\n one:" +
- "\n somefile.go:123",
- }, {
- err: wrapped{"", wrapped{"inner message", nil}},
- fmt: "%+v",
- want: "somefile.go:123" +
- "\n - inner message:" +
- "\n somefile.go:123",
- }, {
- err: detail{"empty detail", "", nil},
- fmt: "%s",
- want: "empty detail",
- }, {
- err: detail{"empty detail", "", nil},
- fmt: "%+v",
- want: "empty detail",
- }, {
- err: detail{"newline at start", "\nextra", nil},
- fmt: "%s",
- want: "newline at start",
- }, {
- err: detail{"newline at start", "\n extra", nil},
- fmt: "%+v",
- want: "newline at start:" +
- "\n extra",
- }, {
- err: detail{"newline at start", "\nextra",
- detail{"newline at start", "\nmore", nil}},
- fmt: "%+v",
- want: "newline at start:" +
- "\n extra" +
- "\n - newline at start:" +
- "\n more",
- }, {
- err: detail{"two newlines at start", "\n\nextra",
- detail{"two newlines at start", "\n\nmore", nil}},
- fmt: "%+v",
- want: "two newlines at start:" +
- "\n " + // note the explicit space
- "\n extra" +
- "\n - two newlines at start:" +
- "\n " +
- "\n more",
- }, {
- err: &detail{"single newline", "\n", nil},
- fmt: "%+v",
- want: "single newline",
- }, {
- err: &detail{"single newline", "\n",
- &detail{"single newline", "\n", nil}},
- fmt: "%+v",
- want: "single newline:" +
- "\n - single newline",
- }, {
- err: &detail{"newline at end", "detail\n", nil},
- fmt: "%+v",
- want: "newline at end:" +
- "\n detail",
- }, {
- err: &detail{"newline at end", "detail\n",
- &detail{"newline at end", "detail\n", nil}},
- fmt: "%+v",
- want: "newline at end:" +
- "\n detail" +
- "\n - newline at end:" +
- "\n detail",
- }, {
- err: &detail{"two newlines at end", "detail\n\n",
- &detail{"two newlines at end", "detail\n\n", nil}},
- fmt: "%+v",
- want: "two newlines at end:" +
- "\n detail" +
- "\n " +
- "\n - two newlines at end:" +
- "\n detail" +
- "\n ", // note the additional space
- }, {
- err: nil,
- fmt: "%+v",
- want: "<nil>",
- }, {
- err: (*wrapped)(nil),
- fmt: "%+v",
- want: "<nil>",
- }, {
- err: &wrapped{"simple", nil},
- fmt: "%T",
- want: "*fmt_test.wrapped",
- }, {
- err: &wrapped{"simple", nil},
- fmt: "%🤪",
- want: "&{%!🤪(string=simple) <nil>}",
- }, {
- err: formatError("use fmt.Formatter"),
- fmt: "%#v",
- want: "use fmt.Formatter",
- }, {
- err: wrapped{"using errors.Formatter",
- formatError("use fmt.Formatter")},
- fmt: "%#v",
- want: "fmt_test.wrapped{msg:\"using errors.Formatter\", err:\"use fmt.Formatter\"}",
- }, {
- err: fmtTwice("%s %s", "ok", panicValue{}),
- fmt: "%s",
- want: "ok %!s(PANIC=String method: panic)/ok %!s(PANIC=String method: panic)",
- }, {
- err: fmtTwice("%o %s", panicValue{}, "ok"),
- fmt: "%s",
- want: "{} ok/{} ok",
- }, {
- err: intError(4),
- fmt: "%v",
- want: "error 4",
- }, {
- err: intError(4),
- fmt: "%d",
- want: "4",
- }, {
- err: intError(4),
- fmt: "%🤪",
- want: "%!🤪(fmt_test.intError=4)",
- }}
- for i, tc := range testCases {
- t.Run(fmt.Sprintf("%d/%s", i, tc.fmt), func(t *testing.T) {
- got := fmt.Sprintf(tc.fmt, tc.err)
- var ok bool
- if tc.regexp {
- var err error
- ok, err = regexp.MatchString(tc.want+"$", got)
- if err != nil {
- t.Fatal(err)
- }
- } else {
- ok = got == tc.want
- }
- if !ok {
- t.Errorf("\n got: %q\nwant: %q", got, tc.want)
- }
- })
- }
-}
-
-func TestSameType(t *testing.T) {
- err0 := errors.New("inner")
- want := fmt.Sprintf("%T", err0)
-
- err := fmt.Errorf("foo: %v", err0)
- if got := fmt.Sprintf("%T", err); got != want {
- t.Errorf("got %v; want %v", got, want)
- }
-
- err = fmt.Errorf("foo %s", "bar")
- if got := fmt.Sprintf("%T", err); got != want {
- t.Errorf("got %v; want %v", got, want)
- }
-}
-
-var _ errors.Formatter = wrapped{}
-
-type wrapped struct {
- msg string
- err error
-}
-
-func (e wrapped) Error() string { return fmt.Sprint(e) }
-
-func (e wrapped) FormatError(p errors.Printer) (next error) {
- p.Print(e.msg)
- p.Detail()
- p.Print("somefile.go:123")
- return e.err
-}
-
-var _ errors.Formatter = outOfPeanuts{}
-
-type outOfPeanuts struct{}
-
-func (e outOfPeanuts) Error() string { return fmt.Sprint(e) }
-
-func (e outOfPeanuts) Format(fmt.State, rune) {
- panic("should never be called by one of the tests")
-}
-
-func (outOfPeanuts) FormatError(p errors.Printer) (next error) {
- p.Printf("out of %s", "peanuts")
- p.Detail()
- p.Print("the elephant is on strike\n")
- p.Printf("and the %d monkeys\nare laughing", 12)
- return nil
-}
-
-type withFrameAndMore struct {
- frame errors.Frame
-}
-
-func (e *withFrameAndMore) Error() string { return fmt.Sprint(e) }
-
-func (e *withFrameAndMore) FormatError(p errors.Printer) (next error) {
- p.Print("something")
- if p.Detail() {
- e.frame.Format(p)
- p.Print("something more")
- }
- return nil
-}
-
-type notAFormatterError struct{}
-
-func (e notAFormatterError) Error() string { return "not a formatter" }
-
-type detail struct {
- msg string
- detail string
- next error
-}
-
-func (e detail) Error() string { return fmt.Sprint(e) }
-
-func (e detail) FormatError(p errors.Printer) (next error) {
- p.Print(e.msg)
- p.Detail()
- p.Print(e.detail)
- return e.next
-}
-
-type intError int
-
-func (e intError) Error() string { return fmt.Sprint(e) }
-
-func (e wrapped) Format(w fmt.State, r rune) {
- // Test that the normal fallback handling after handleMethod for
- // non-string verbs is used. This path should not be reached.
- fmt.Fprintf(w, "Unreachable: %d", e)
-}
-
-func (e intError) FormatError(p errors.Printer) (next error) {
- p.Printf("error %d", e)
- return nil
-}
-
-// formatError is an error implementing Format instead of errors.Formatter.
-// The implementation mimics the implementation of github.com/pkg/errors.
-type formatError string
-
-func (e formatError) Error() string { return string(e) }
-
-func (e formatError) Format(s fmt.State, verb rune) {
- // Body based on pkg/errors/errors.go
- switch verb {
- case 'v':
- if s.Flag('+') {
- io.WriteString(s, string(e))
- fmt.Fprintf(s, ":\n%s", "otherfile.go:456")
- return
- }
- fallthrough
- case 's':
- io.WriteString(s, string(e))
- case 'q':
- fmt.Fprintf(s, "%q", string(e))
- }
-}
-
-func (e formatError) GoString() string {
- panic("should never be called")
-}
-
-type fmtTwiceErr struct {
- format string
- args []interface{}
-}
-
-func fmtTwice(format string, a ...interface{}) error {
- return fmtTwiceErr{format, a}
-}
-
-func (e fmtTwiceErr) Error() string { return fmt.Sprint(e) }
-
-func (e fmtTwiceErr) FormatError(p errors.Printer) (next error) {
- p.Printf(e.format, e.args...)
- p.Print("/")
- p.Printf(e.format, e.args...)
- return nil
-}
-
-func (e fmtTwiceErr) GoString() string {
- return "2 times " + fmt.Sprintf(e.format, e.args...)
-}
-
-type panicValue struct{}
-
-func (panicValue) String() string { panic("panic") }
-
-var rePath = regexp.MustCompile(`( [^ ]*)fmt.*test\.`)
-var reLine = regexp.MustCompile(":[0-9]*\n?$")
-
-func cleanPath(s string) string {
- s = rePath.ReplaceAllString(s, "/path.")
- s = reLine.ReplaceAllString(s, ":xxx")
- s = strings.Replace(s, "\n ", "", -1)
- s = strings.Replace(s, " /", "/", -1)
- return s
-}
-
-func errToParts(err error) (a []string) {
- for err != nil {
- var p testPrinter
- if errors.Unwrap(err) != nil {
- p.str += "wraps:"
- }
- f, ok := err.(errors.Formatter)
- if !ok {
- a = append(a, err.Error())
- break
- }
- err = f.FormatError(&p)
- a = append(a, cleanPath(p.str))
- }
- return a
-
-}
-
-type testPrinter struct {
- str string
-}
-
-func (p *testPrinter) Print(a ...interface{}) {
- p.str += fmt.Sprint(a...)
-}
-
-func (p *testPrinter) Printf(format string, a ...interface{}) {
- p.str += fmt.Sprintf(format, a...)
-}
-
-func (p *testPrinter) Detail() bool {
- p.str += " /"
- return true
-}
// different, flagless formats set at the top level.
plusV bool
sharpV bool
-
- // error-related flags.
- inDetail bool
- needNewline bool
- needColon bool
}
// A fmt is the raw formatter used by Printf etc.
+++ /dev/null
-// 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 fmt_test
-
-import (
- "errors"
- "fmt"
- "path/filepath"
- "regexp"
-)
-
-func baz() error { return errors.New("baz flopped") }
-func bar() error { return fmt.Errorf("bar(nameserver 139): %v", baz()) }
-func foo() error { return fmt.Errorf("foo: %s", bar()) }
-
-func Example_formatting() {
- err := foo()
- fmt.Println("Error:")
- fmt.Printf("%v\n", err)
- fmt.Println()
- fmt.Println("Detailed error:")
- fmt.Println(stripPath(fmt.Sprintf("%+v\n", err)))
- // Output:
- // Error:
- // foo: bar(nameserver 139): baz flopped
- //
- // Detailed error:
- // foo:
- // fmt_test.foo
- // fmt/format_example_test.go:16
- // - bar(nameserver 139):
- // fmt_test.bar
- // fmt/format_example_test.go:15
- // - baz flopped:
- // fmt_test.baz
- // fmt/format_example_test.go:14
-}
-
-func stripPath(s string) string {
- rePath := regexp.MustCompile(`( [^ ]*)fmt`)
- s = rePath.ReplaceAllString(s, " fmt")
- s = filepath.ToSlash(s)
- return s
-}
return s
}
+// Errorf formats according to a format specifier and returns the string
+// as a value that satisfies error.
+func Errorf(format string, a ...interface{}) error {
+ return errors.New(Sprintf(format, a...))
+}
+
// These routines do not take a format string
// Fprint formats using the default formats for its operands and writes to w.
if p.erroring {
return
}
- switch x := p.arg.(type) {
- case errors.Formatter:
- handled = true
- defer p.catchPanic(p.arg, verb, "FormatError")
- return fmtError(p, verb, x)
-
- case Formatter:
+ // Is it a Formatter?
+ if formatter, ok := p.arg.(Formatter); ok {
handled = true
defer p.catchPanic(p.arg, verb, "Format")
- x.Format(p, verb)
+ formatter.Format(p, verb)
return
-
- case error:
- handled = true
- defer p.catchPanic(p.arg, verb, "Error")
- return fmtError(p, verb, x)
}
// If we're doing Go syntax and the argument knows how to supply it, take care of it now.
// Println etc. set verb to %v, which is "stringable".
switch verb {
case 'v', 's', 'x', 'X', 'q':
- if v, ok := p.arg.(Stringer); ok {
+ // Is it an error or Stringer?
+ // The duplication in the bodies is necessary:
+ // setting handled and deferring catchPanic
+ // must happen before calling the method.
+ switch v := p.arg.(type) {
+ case error:
+ handled = true
+ defer p.catchPanic(p.arg, verb, "Error")
+ p.fmtString(v.Error(), verb)
+ return
+
+ case Stringer:
handled = true
defer p.catchPanic(p.arg, verb, "String")
p.fmtString(v.String(), verb)
//
var pkgDeps = map[string][]string{
// L0 is the lowest level, core, nearly unavoidable packages.
- "errors": {"runtime", "internal/errinternal", "internal/reflectlite"},
+ "errors": {"runtime", "internal/reflectlite"},
"io": {"errors", "sync", "sync/atomic"},
"runtime": {"unsafe", "runtime/internal/atomic", "runtime/internal/sys", "runtime/internal/math", "internal/cpu", "internal/bytealg"},
"runtime/internal/sys": {},
"unsafe": {},
"internal/cpu": {},
"internal/bytealg": {"unsafe", "internal/cpu"},
- "internal/errinternal": {},
"internal/reflectlite": {"runtime", "unsafe"},
"L0": {
},
// Formatted I/O: few dependencies (L1) but we must add reflect and internal/fmtsort.
- "fmt": {"L1", "bytes", "strings", "os", "reflect", "internal/errinternal", "internal/fmtsort"},
+ "fmt": {"L1", "os", "reflect", "internal/fmtsort"},
"log": {"L1", "os", "fmt", "time"},
// Packages used by testing must be low-level (L2+fmt).
+++ /dev/null
-// Copyright 2019 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 errinternal
-
-// NewError creates a new error as created by errors.New, but with one
-// additional stack frame depth.
-var NewError func(msg string, err error) error
{true, ttError{timeout: true}},
{true, isError{os.ErrTimeout}},
{true, os.ErrTimeout},
- {true, fmt.Errorf("wrap: %w", os.ErrTimeout)},
+ // TODO: restore when %w is reimplemented
+ //{true, fmt.Errorf("wrap: %w", os.ErrTimeout)},
{false, ttError{timeout: false}},
{false, errors.New("error")},
} {
{true, ttError{temporary: true}},
{true, isError{os.ErrTemporary}},
{true, os.ErrTemporary},
- {true, fmt.Errorf("wrap: %w", os.ErrTemporary)},
+ // TODO: restore when %w is reimplemented
+ //{true, fmt.Errorf("wrap: %w", os.ErrTemporary)},
{false, ttError{temporary: false}},
{false, errors.New("error")},
} {