Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ errors.Wrap(err, "message")
* The [cockroachdb/errors](https://github.com/cockroachdb/errors) is too big
* The `errors` has no caller stack trace

## Wrap on nil

`Wrap(nil, "msg")` returns a **non-nil** error, matching `fmt.Errorf("msg: %w", err)`
with a nil `err` (and unlike `pkg/errors`, which returns nil). Wrap only after
checking `err != nil`:

```go
if err := do(); err != nil {
return errors.Wrap(err, "do")
}
return nil
```

## Don't need traces?
Call `errors.DisableTrace` or use build tag `noerrtrace`.

Expand Down
12 changes: 11 additions & 1 deletion format.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,19 @@ type Printer interface {
}

// Errorf creates new error with format.
//
// If format contains the %w directive, Errorf falls back to fmt.Errorf
// and does not capture a caller frame.
func Errorf(format string, a ...interface{}) error {
if !Trace() || strings.Contains(format, "%w") {
return fmt.Errorf(format, a...)
}
return &errorString{fmt.Sprintf(format, a...), Caller(1)}
return &errorString{sprintf(format, a), Caller(1)}
}

// sprintf is deliberately non-variadic: forwarding (format, a...) straight
// to fmt.Sprintf from Errorf makes vet's printf analyzer classify Errorf as
// a plain printf wrapper, rejecting %w in callers' format strings.
func sprintf(format string, a []interface{}) string {
return fmt.Sprintf(format, a...)
}
6 changes: 6 additions & 0 deletions wrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ func (e *wrapError) Unwrap() error {
}

// Wrap error with message and caller.
//
// Wrap always returns a non-nil error, even if err is nil, matching the
// behavior of fmt.Errorf with a nil %w operand. Call Wrap only after
// checking that err is non-nil.
func Wrap(err error, message string) error {
frame := Frame{}
if Trace() {
Expand All @@ -87,6 +91,8 @@ func Wrap(err error, message string) error {
}

// Wrapf wraps error with formatted message and caller.
//
// Like Wrap, Wrapf always returns a non-nil error, even if err is nil.
func Wrapf(err error, format string, a ...interface{}) error {
frame := Frame{}
if Trace() {
Expand Down
13 changes: 13 additions & 0 deletions wrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,19 @@ func TestUnwrap(t *testing.T) {
}
}

func TestWrapNil(t *testing.T) {
// Wrap and Wrapf return a non-nil error even for a nil input, matching
// fmt.Errorf with a nil %w operand. See the Wrap doc comment.
if err := errors.Wrap(nil, "foo"); err == nil {
t.Error("Wrap(nil) = nil, want non-nil")
} else if got := errors.Unwrap(err); got != nil {
t.Errorf("Unwrap(Wrap(nil)) = %v, want nil", got)
}
if err := errors.Wrapf(nil, "foo %d", 1); err == nil {
t.Error("Wrapf(nil) = nil, want non-nil")
}
}

func TestOpaque(t *testing.T) {
got := fmt.Sprintf("%v", errors.Wrap(errors.Opaque(errorT{}), "foo"))
want := "foo: errorT"
Expand Down