From 05b53d9f653e674061bbbbdcd9c3f2346a2a37bd Mon Sep 17 00:00:00 2001 From: Yanhu007 Date: Wed, 15 Apr 2026 17:44:33 +0800 Subject: [PATCH] fix: false positive cycle detection for shared struct references The visited map was never cleaned up after printing a struct, so when two sibling fields pointed to the same struct, the second reference was falsely flagged as a cyclic reference. Fix by deleting the visit entry after the struct is fully printed. This way only ancestors on the current path are tracked, not all previously seen nodes. Real cycles are still correctly detected because the entry exists while recursing into the struct's fields. Fixes #96 --- formatter.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/formatter.go b/formatter.go index 8e6969c..6232d9d 100644 --- a/formatter.go +++ b/formatter.go @@ -175,14 +175,17 @@ func (p *printer) printValue(v reflect.Value, showType, quote bool) { writeByte(p, '}') case reflect.Struct: t := v.Type() + var vis visit + var tracked bool if v.CanAddr() { addr := v.UnsafeAddr() - vis := visit{addr, t} + vis = visit{addr, t} if vd, ok := p.visited[vis]; ok && vd < p.depth { p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false) break // don't print v again } p.visited[vis] = p.depth + tracked = true } if showType { @@ -218,6 +221,11 @@ func (p *printer) printValue(v reflect.Value, showType, quote bool) { } } writeByte(p, '}') + // Remove visit tracking after printing so that shared (non-cyclic) + // references from sibling fields are not falsely detected as cycles. + if tracked { + delete(p.visited, vis) + } case reflect.Interface: switch e := v.Elem(); { case e.Kind() == reflect.Invalid: