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
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,24 @@ if err != nil {
}
```

**`WithStack2[T any](v T, err error) (T, error)`** (Go 1.18+)

Adds a stack frame at the current location, while passing through a return value. This is useful for adding stack traces to errors from functions that return multiple values (value, error) in a single line.

If err is nil, returns (v, nil) unchanged. If err is not nil, returns (v, err_with_stack).

```go
// Instead of this:
result, err := externalLib.DoSomething()
if err != nil {
return result, errors.WithStack(err)
}
return result, nil

// You can write this:
return errors.WithStack2(externalLib.DoSomething())
```

**`WithFrame(err error, depth int) error`**

Adds a stack frame at a specific depth in the call stack.
Expand Down
39 changes: 36 additions & 3 deletions example_stacktrace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,54 @@ import (
func ExampleWithStack() {
// Simulate an error from an external library
externalErr := fmt.Errorf("external library error")

// Add stack trace at current location
err := errors.WithStack(externalErr)

fmt.Println(err)
// Output: external library error
}

// Result represents a simple computation result
type Result struct {
Value int
}

// mockCompute simulates an external library function
func mockCompute(input int) (Result, error) {
if input < 0 {
return Result{}, fmt.Errorf("negative input not allowed")
}
return Result{Value: input * 2}, nil

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [golangci] reported by reviewdog 🐶
return with no blank line before (nlreturn)

}

func ExampleWithStack2() {
// Demonstrates using WithStack2 to add stack traces inline while returning values
compute := func(input int) (Result, error) {
// WithStack2 adds stack trace in a single line
return errors.WithStack2(mockCompute(input))
}

// Success case - error is nil
result, err := compute(5)
fmt.Printf("Result: %+v, Error: %v\n", result, err)

// Error case - error gets stack trace added
result, err = compute(-1)
fmt.Printf("Result: %+v, Error: %v\n", result, err)

// Output:
// Result: {Value:10}, Error: <nil>
// Result: {Value:0}, Error: negative input not allowed
}

func ExampleWithFrame() {
// This helper function wraps errors with the caller's location
wrapError := func(err error) error {
// Skip 1 frame to capture the caller's location instead of this function
return errors.WithFrame(err, 1)
}

err := wrapError(fmt.Errorf("original error"))
fmt.Println(err)
// Output: original error
Expand Down
22 changes: 22 additions & 0 deletions stacktrace.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,28 @@ func WithStack(err error) error {
return WithFrame(err, 1)
}

// WithStack2 wraps an error with a stack frame captured at the call site, while
// passing through a return value. This is useful for adding stack traces to errors
// from functions that return multiple values (value, error).
//
// If err is nil, returns (v, nil) unchanged. If err is not nil, returns (v, err_with_stack)
// where err_with_stack includes the stack frame.
//
// Example:
//
// return errors.WithStack2(externalLib.DoSomething())
//
// This is equivalent to but more concise than:
//
// result, err := externalLib.DoSomething()
// if err != nil {
// return result, errors.WithStack(err)
// }
// return result, nil
func WithStack2[T any](v T, err error) (T, error) {
return v, WithFrame(err, 1)
}

// WithFrame wraps an error with a stack frame at the specified depth in the call stack.
// The depth parameter indicates how many stack frames to skip (0 = current frame).
func WithFrame(err error, d int) error {
Expand Down
Loading