diff --git a/map.go b/map.go index 453b1b9..5ae951e 100644 --- a/map.go +++ b/map.go @@ -14,10 +14,10 @@ import ( // // TODO(jmalloc): sort numerically-keyed maps numerically func (vis *visitor) visitMap(w io.Writer, v Value) { - if vis.enter(w, v) { + if v.Value.IsNil() { + vis.renderNil(w, v) return } - defer vis.leave(v) if v.IsAmbiguousType() { must.WriteString(w, v.TypeName()) diff --git a/ptr.go b/ptr.go index b79e8cb..86e6559 100644 --- a/ptr.go +++ b/ptr.go @@ -8,10 +8,10 @@ import ( // visitPtr formats values with a kind of reflect.Ptr. func (vis *visitor) visitPtr(w io.Writer, v Value) { - if vis.enter(w, v) { + if v.Value.IsNil() { + vis.renderNil(w, v) return } - defer vis.leave(v) if v.IsAmbiguousType() { must.WriteByte(w, '*') diff --git a/slice.go b/slice.go index 5ff2613..ef7e9d1 100644 --- a/slice.go +++ b/slice.go @@ -6,10 +6,10 @@ import ( // visitSlice formats values with a kind of reflect.Slice. func (vis *visitor) visitSlice(w io.Writer, v Value) { - if vis.enter(w, v) { + if v.Value.IsNil() { + vis.renderNil(w, v) return } - defer vis.leave(v) vis.visitArray(w, v) } diff --git a/value.go b/value.go index 5a6d527..060f9a5 100644 --- a/value.go +++ b/value.go @@ -57,3 +57,15 @@ func (v *Value) IsAnonymousType() bool { func (v *Value) IsAmbiguousType() bool { return v.IsAmbiguousDynamicType || v.IsAmbiguousStaticType } + +// canPointer reports if v.Value.Pointer() method can be called without +// panicking. +func (v *Value) canPointer() bool { + switch v.DynamicType.Kind() { + case reflect.Chan, reflect.Func, reflect.Map, + reflect.Ptr, reflect.Slice, reflect.UnsafePointer: + return true + default: + return false + } +} diff --git a/visitor.go b/visitor.go index ecd523b..0db3565 100644 --- a/visitor.go +++ b/visitor.go @@ -1,6 +1,7 @@ package dapper import ( + "fmt" "io" "reflect" @@ -30,6 +31,11 @@ func (vis *visitor) visit(w io.Writer, v Value) { return } + if vis.enter(w, v) { + return + } + defer vis.leave(v) + for _, f := range vis.filters { if n := must.Must(f(w, v)); n > 0 { return @@ -76,44 +82,49 @@ func (vis *visitor) visit(w io.Writer, v Value) { // enter indicates that a potentially recursive value is about to be formatted. // -// It returns true if the value is nil, or recursion has occurred, indicating -// that the value should not be rendered. +// It returns true if the recursion has occurred, indicating that the value +// should not be rendered. func (vis *visitor) enter(w io.Writer, v Value) bool { - marker := "nil" - - if !v.Value.IsNil() { + if v.canPointer() { ptr := v.Value.Pointer() - if _, ok := vis.recursionSet[ptr]; !ok { - if vis.recursionSet == nil { - vis.recursionSet = map[uintptr]struct{}{} + if _, ok := vis.recursionSet[ptr]; ok { + if v.IsAmbiguousType() { + must.WriteString(w, v.TypeName()) + must.WriteByte(w, '(') + must.WriteString(w, vis.recursionMarker) + must.WriteByte(w, ')') + } else { + must.WriteString(w, vis.recursionMarker) } - vis.recursionSet[ptr] = struct{}{} - - return false + return true } - marker = vis.recursionMarker - } + if vis.recursionSet == nil { + vis.recursionSet = map[uintptr]struct{}{} + } - if v.IsAmbiguousType() { - must.WriteString(w, v.TypeName()) - must.WriteByte(w, '(') - must.WriteString(w, marker) - must.WriteByte(w, ')') - } else { - must.WriteString(w, marker) + vis.recursionSet[ptr] = struct{}{} } - return true + return false } // leave indicates that a potentially recursive value has finished rendering. // // It must be called after enter(v) returns true. func (vis *visitor) leave(v Value) { - if !v.Value.IsNil() { + if v.canPointer() { delete(vis.recursionSet, v.Value.Pointer()) } } + +// renderNil renders a nil value of any type. +func (vis *visitor) renderNil(w io.Writer, v Value) { + if v.IsAmbiguousType() { + must.WriteString(w, fmt.Sprintf("%s(nil)", v.TypeName())) + } else { + must.WriteString(w, "nil") + } +}