From ad202eb9b6d04d47fcd7859253959321d08b337f Mon Sep 17 00:00:00 2001 From: Masahiro Nagano Date: Thu, 20 Aug 2026 00:21:05 +0900 Subject: [PATCH 1/2] fix: handle nil message return in internalGo function and add tests for error message type --- flagrun.go | 5 +++++ flagrun_go_test.go | 28 ++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/flagrun.go b/flagrun.go index 3b9d314..4cf637e 100644 --- a/flagrun.go +++ b/flagrun.go @@ -193,6 +193,11 @@ func internalGo[T any]( return "", *c } msg, code := opt.Run(args) + // If the message is nil, return an empty string and the code + typeOfMsg := reflect.TypeOf(msg) + if typeOfMsg == nil || (typeOfMsg.Kind() == reflect.Pointer && reflect.ValueOf(msg).IsNil()) { + return "", code + } return fmt.Sprintf("%v", msg), code } diff --git a/flagrun_go_test.go b/flagrun_go_test.go index 27ca61f..f82c76a 100644 --- a/flagrun_go_test.go +++ b/flagrun_go_test.go @@ -199,7 +199,20 @@ func (r *anyMessageRunner) Run(_ []string) (any, int) { return "Switch is OFF", OK } -func TestInternalGoWithAnyMessageType(t *testing.T) { +type errorMessageRunner struct { + Switch bool `short:"s" long:"switch" description:"A boolean switch"` +} + +//nolint:staticcheck // intentional: Runner[error] requires the (error, int) signature. +func (r *errorMessageRunner) Run(_ []string) (error, int) { + if r.Switch { + return fmt.Errorf("Switch is %v", r.Switch), CRITICAL + } + return nil, OK +} + +func runInternalGoMessageTypeTests[T any](t *testing.T, runner Runner[T], wantMsgOff string) { + t.Helper() tests := []struct { name string args []string @@ -209,7 +222,7 @@ func TestInternalGoWithAnyMessageType(t *testing.T) { { name: "Switch is OFF", args: []string{}, - wantMsg: "Switch is OFF", + wantMsg: wantMsgOff, wantCode: OK, }, { @@ -222,13 +235,20 @@ func TestInternalGoWithAnyMessageType(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - o := &anyMessageRunner{} var stdout bytes.Buffer var stderr bytes.Buffer f := buildFlagrun() - msg, code := internalGo(f, tt.args, &stdout, &stderr, o) // T is inferred + msg, code := internalGo(f, tt.args, &stdout, &stderr, runner) assert.Equal(t, tt.wantMsg, msg, "%s msg", tt.name) assert.Equal(t, code, tt.wantCode, "%s code", tt.name) }) } } + +func TestInternalGoWithAnyMessageType(t *testing.T) { + runInternalGoMessageTypeTests(t, &anyMessageRunner{}, "Switch is OFF") +} + +func TestInternalGoWithErrorMessageType(t *testing.T) { + runInternalGoMessageTypeTests(t, &errorMessageRunner{}, "") +} From 391aa80ef8cceff6ed2f5a08be435bebc79e58bb Mon Sep 17 00:00:00 2001 From: Masahiro Nagano Date: Thu, 20 Aug 2026 00:22:51 +0900 Subject: [PATCH 2/2] Update flagrun.go Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- flagrun.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/flagrun.go b/flagrun.go index 4cf637e..8dc8b66 100644 --- a/flagrun.go +++ b/flagrun.go @@ -194,10 +194,17 @@ func internalGo[T any]( } msg, code := opt.Run(args) // If the message is nil, return an empty string and the code - typeOfMsg := reflect.TypeOf(msg) - if typeOfMsg == nil || (typeOfMsg.Kind() == reflect.Pointer && reflect.ValueOf(msg).IsNil()) { + valueOfMsg := reflect.ValueOf(msg) + if !valueOfMsg.IsValid() { return "", code } + + switch valueOfMsg.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice: + if valueOfMsg.IsNil() { + return "", code + } + } return fmt.Sprintf("%v", msg), code }