Skip to content

Commit c6c86ce

Browse files
authored
Merge pull request #2444 from fadiroot/fix/exit-error-unwrap
fix: restore Unwrap on the error returned by Exit
2 parents 58ec406 + 804a4c0 commit c6c86ce

2 files changed

Lines changed: 53 additions & 0 deletions

File tree

‎errors.go‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@ func (ee *exitError) ExitCode() int {
152152
return ee.exitCode
153153
}
154154

155+
// Unwrap returns the error given to [Exit], so that [errors.Is] and
156+
// [errors.As] can inspect it through the ExitCoder wrapper.
157+
func (ee *exitError) Unwrap() error {
158+
return ee.err
159+
}
160+
155161
// HandleExitCoder handles errors implementing ExitCoder by printing their
156162
// message and calling OsExiter with the given exit code.
157163
//

‎errors_test.go‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"errors"
66
"fmt"
7+
"io/fs"
78
"testing"
89

910
"github.com/stretchr/testify/assert"
@@ -270,3 +271,49 @@ func TestHandleExitCoder_ExitCoderEmptyMessage(t *testing.T) {
270271
assert.True(t, called)
271272
assert.Empty(t, errBuf.String(), "expected no output to stderr for empty exit message")
272273
}
274+
275+
func TestExit_Unwrap(t *testing.T) {
276+
sentinel := errors.New("not found")
277+
278+
// The error given to Exit must stay reachable through the standard
279+
// wrapping helpers (#1090).
280+
tests := []struct {
281+
name string
282+
message any
283+
wantMsg string // message of errors.Unwrap(err)
284+
wantIs error // target for errors.Is, if any
285+
wantAs any // target for errors.As, if any
286+
}{
287+
{
288+
name: "error",
289+
message: sentinel,
290+
wantMsg: "not found",
291+
wantIs: sentinel,
292+
},
293+
{
294+
name: "error chain",
295+
message: &fs.PathError{Op: "open", Path: "x", Err: fs.ErrNotExist},
296+
wantMsg: "open x: file does not exist",
297+
wantIs: fs.ErrNotExist,
298+
wantAs: new(*fs.PathError),
299+
},
300+
{
301+
name: "non-error message",
302+
message: "boom",
303+
wantMsg: "boom",
304+
},
305+
}
306+
for _, tt := range tests {
307+
t.Run(tt.name, func(t *testing.T) {
308+
err := Exit(tt.message, 2)
309+
310+
assert.EqualError(t, errors.Unwrap(err), tt.wantMsg)
311+
if tt.wantIs != nil {
312+
assert.ErrorIs(t, err, tt.wantIs)
313+
}
314+
if tt.wantAs != nil {
315+
assert.ErrorAs(t, err, tt.wantAs)
316+
}
317+
})
318+
}
319+
}

0 commit comments

Comments
 (0)