Skip to content

Commit d8c7eb1

Browse files
benhoytbarrettj12
andauthored
Catch panics when calling GoString like fmt %#v does (#87)
This handles a few cases (similar to how fmt %#v does): - A GoString method on a value receiver, called with a nil pointer - A GoString method on a pointer receiver that doesn't check for nil - A GoString method that panics in some other way Because Go 1.17 added a method Time.GoString with value receiver, this broke structs that had *time.Time fields with nil values (which is common!). Also added a bunch of tests for these cases. Fixes #77 Co-authored-by: Jordan Barrett <[email protected]>
1 parent d928460 commit d8c7eb1

File tree

3 files changed

+57
-1
lines changed

3 files changed

+57
-1
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
_go*
33
_test*
44
_obj
5+
/.idea

formatter.go

+19
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,24 @@ type visit struct {
9292
typ reflect.Type
9393
}
9494

95+
func (p *printer) catchPanic(v reflect.Value, method string) {
96+
if r := recover(); r != nil {
97+
if v.Kind() == reflect.Ptr && v.IsNil() {
98+
writeByte(p, '(')
99+
io.WriteString(p, v.Type().String())
100+
io.WriteString(p, ")(nil)")
101+
return
102+
}
103+
writeByte(p, '(')
104+
io.WriteString(p, v.Type().String())
105+
io.WriteString(p, ")(PANIC=calling method ")
106+
io.WriteString(p, strconv.Quote(method))
107+
io.WriteString(p, ": ")
108+
fmt.Fprint(p, r)
109+
writeByte(p, ')')
110+
}
111+
}
112+
95113
func (p *printer) printValue(v reflect.Value, showType, quote bool) {
96114
if p.depth > 10 {
97115
io.WriteString(p, "!%v(DEPTH EXCEEDED)")
@@ -101,6 +119,7 @@ func (p *printer) printValue(v reflect.Value, showType, quote bool) {
101119
if v.IsValid() && v.CanInterface() {
102120
i := v.Interface()
103121
if goStringer, ok := i.(fmt.GoStringer); ok {
122+
defer p.catchPanic(v, "GoString")
104123
io.WriteString(p, goStringer.GoString())
105124
return
106125
}

formatter_test.go

+37-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"io"
66
"strings"
77
"testing"
8+
"time"
89
"unsafe"
910
)
1011

@@ -97,7 +98,7 @@ var gosyntax = []test{
9798
`[]string{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"}`,
9899
},
99100
{F(5), "pretty.F(5)"},
100-
{ NewStructWithPrivateFields("foo"), `NewStructWithPrivateFields("foo")`},
101+
{NewStructWithPrivateFields("foo"), `NewStructWithPrivateFields("foo")`},
101102
{
102103
SA{&T{1, 2}, T{3, 4}},
103104
`pretty.SA{
@@ -176,6 +177,41 @@ var gosyntax = []test{
176177
},
177178
}`,
178179
},
180+
{(*time.Time)(nil), "(*time.Time)(nil)"},
181+
{&ValueGoString{"vgs"}, `VGS vgs`},
182+
{(*ValueGoString)(nil), `(*pretty.ValueGoString)(nil)`},
183+
{(*VGSWrapper)(nil), `(*pretty.VGSWrapper)(nil)`},
184+
{&PointerGoString{"pgs"}, `PGS pgs`},
185+
{(*PointerGoString)(nil), "(*pretty.PointerGoString)(nil)"},
186+
{&PanicGoString{"oops!"}, `(*pretty.PanicGoString)(PANIC=calling method "GoString": oops!)`},
187+
}
188+
189+
type ValueGoString struct {
190+
s string
191+
}
192+
193+
func (g ValueGoString) GoString() string {
194+
return "VGS " + g.s
195+
}
196+
197+
type VGSWrapper struct {
198+
ValueGoString
199+
}
200+
201+
type PointerGoString struct {
202+
s string
203+
}
204+
205+
func (g *PointerGoString) GoString() string {
206+
return "PGS " + g.s
207+
}
208+
209+
type PanicGoString struct {
210+
s string
211+
}
212+
213+
func (g *PanicGoString) GoString() string {
214+
panic(g.s)
179215
}
180216

181217
func TestGoSyntax(t *testing.T) {

0 commit comments

Comments
 (0)