diff --git a/.gitignore b/.gitignore index 1f0a99f..b2d4781 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ _go* _test* _obj +/.idea diff --git a/formatter.go b/formatter.go index 249f089..8e6969c 100644 --- a/formatter.go +++ b/formatter.go @@ -92,6 +92,24 @@ type visit struct { typ reflect.Type } +func (p *printer) catchPanic(v reflect.Value, method string) { + if r := recover(); r != nil { + if v.Kind() == reflect.Ptr && v.IsNil() { + writeByte(p, '(') + io.WriteString(p, v.Type().String()) + io.WriteString(p, ")(nil)") + return + } + writeByte(p, '(') + io.WriteString(p, v.Type().String()) + io.WriteString(p, ")(PANIC=calling method ") + io.WriteString(p, strconv.Quote(method)) + io.WriteString(p, ": ") + fmt.Fprint(p, r) + writeByte(p, ')') + } +} + func (p *printer) printValue(v reflect.Value, showType, quote bool) { if p.depth > 10 { io.WriteString(p, "!%v(DEPTH EXCEEDED)") @@ -101,6 +119,7 @@ func (p *printer) printValue(v reflect.Value, showType, quote bool) { if v.IsValid() && v.CanInterface() { i := v.Interface() if goStringer, ok := i.(fmt.GoStringer); ok { + defer p.catchPanic(v, "GoString") io.WriteString(p, goStringer.GoString()) return } diff --git a/formatter_test.go b/formatter_test.go index f23c700..6e27b27 100644 --- a/formatter_test.go +++ b/formatter_test.go @@ -5,6 +5,7 @@ import ( "io" "strings" "testing" + "time" "unsafe" ) @@ -97,7 +98,7 @@ var gosyntax = []test{ `[]string{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"}`, }, {F(5), "pretty.F(5)"}, - { NewStructWithPrivateFields("foo"), `NewStructWithPrivateFields("foo")`}, + {NewStructWithPrivateFields("foo"), `NewStructWithPrivateFields("foo")`}, { SA{&T{1, 2}, T{3, 4}}, `pretty.SA{ @@ -176,6 +177,41 @@ var gosyntax = []test{ }, }`, }, + {(*time.Time)(nil), "(*time.Time)(nil)"}, + {&ValueGoString{"vgs"}, `VGS vgs`}, + {(*ValueGoString)(nil), `(*pretty.ValueGoString)(nil)`}, + {(*VGSWrapper)(nil), `(*pretty.VGSWrapper)(nil)`}, + {&PointerGoString{"pgs"}, `PGS pgs`}, + {(*PointerGoString)(nil), "(*pretty.PointerGoString)(nil)"}, + {&PanicGoString{"oops!"}, `(*pretty.PanicGoString)(PANIC=calling method "GoString": oops!)`}, +} + +type ValueGoString struct { + s string +} + +func (g ValueGoString) GoString() string { + return "VGS " + g.s +} + +type VGSWrapper struct { + ValueGoString +} + +type PointerGoString struct { + s string +} + +func (g *PointerGoString) GoString() string { + return "PGS " + g.s +} + +type PanicGoString struct { + s string +} + +func (g *PanicGoString) GoString() string { + panic(g.s) } func TestGoSyntax(t *testing.T) {