Skip to content

Commit dcbbb16

Browse files
authored
Fix panic when trying to print a nil Stringer (#284)
This fixes an issue where we're trying to print the value of a nil Stringer which causes a panic instead of failing the test, hurting the debuggability of the failure. Fixes #283.
1 parent 4f3370f commit dcbbb16

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

gomock/string.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,24 @@ func getString(x any) string {
1313
return fmt.Sprintf("%T", x)
1414
}
1515
if s, ok := x.(fmt.Stringer); ok {
16-
return s.String()
16+
// Use defer/recover to handle panics from calling String() on nil receivers
17+
// This matches the behavior of fmt.Sprintf("%v", x) which handles nil Stringers safely
18+
return safeString(s)
1719
}
1820
return fmt.Sprintf("%v", x)
1921
}
2022

23+
// safeString calls String() with panic recovery to handle nil receivers
24+
func safeString(s fmt.Stringer) (result string) {
25+
defer func() {
26+
if r := recover(); r != nil {
27+
// If String() panicked (e.g., nil receiver), use fmt.Sprintf instead
28+
result = fmt.Sprintf("%v", s)
29+
}
30+
}()
31+
return s.String()
32+
}
33+
2134
// isGeneratedMock checks if the given type has a "isgomock" field,
2235
// indicating it is a generated mock.
2336
func isGeneratedMock(x any) bool {

gomock/string_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package gomock
2+
3+
import (
4+
"testing"
5+
"time"
6+
)
7+
8+
func TestGetString(t *testing.T) {
9+
now := time.Now()
10+
var nilTime *time.Time
11+
12+
tests := []struct {
13+
name string
14+
input any
15+
want string
16+
}{
17+
{
18+
name: "nil stringer should not panic",
19+
input: nilTime,
20+
want: "<nil>",
21+
},
22+
{
23+
name: "non-nil stringer",
24+
input: &now,
25+
want: now.String(),
26+
},
27+
{
28+
name: "non-stringer value",
29+
input: 42,
30+
want: "42",
31+
},
32+
{
33+
name: "nil interface value",
34+
input: nil,
35+
want: "<nil>",
36+
},
37+
}
38+
39+
for _, tt := range tests {
40+
t.Run(tt.name, func(t *testing.T) {
41+
got := getString(tt.input)
42+
if got != tt.want {
43+
t.Errorf("getString() = %q, want %q", got, tt.want)
44+
}
45+
})
46+
}
47+
}

0 commit comments

Comments
 (0)