src/pkg/fmt/fmt_test.go | 34 ++++++++++++++++++++++++++++++++++ src/pkg/fmt/print.go | 7 +++++++ diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go index 63c33380a25a307a60ef93752bfa402a1510259c..d7fe296f09575ef384b826c82ce1cfacade2f377 100644 --- a/src/pkg/fmt/fmt_test.go +++ b/src/pkg/fmt/fmt_test.go @@ -813,3 +813,37 @@ t.Errorf("%q: got %q expected %q", tt.fmt, s, tt.out) } } } + +// Test that erroneous String routine doesn't cause fatal recursion. +var recurCount = 0 + +type Recur struct { + i int + failed *bool +} + +func (r Recur) String() string { + if recurCount++; recurCount > 10 { + *r.failed = true + return "FAIL" + } + // This will call badVerb. Before the fix, that would cause us to recur into + // this routine to print %!p(value). Now we don't call the user's method + // during an error. + return Sprintf("recur@%p value: %d", r, r.i) +} + +func TestBadVerbRecursion(t *testing.T) { + failed := false + r := Recur{3, &failed} + Sprintf("recur@%p value: %d\n", &r, r.i) + if failed { + t.Error("fail with pointer") + } + failed = false + r = Recur{4, &failed} + Sprintf("recur@%p, value: %d\n", r, r.i) + if failed { + t.Error("fail with value") + } +} diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go index 8b15a82e773b69b3d9e97351bac9990ba712569d..9f157daaee01d043890900818df3056f4d9d1534 100644 --- a/src/pkg/fmt/print.go +++ b/src/pkg/fmt/print.go @@ -74,6 +74,7 @@ type pp struct { n int panicking bool + erroring bool // printing an error condition buf bytes.Buffer // field holds the current item, as an interface{}. field interface{} @@ -124,6 +125,7 @@ // Allocate a new pp struct or grab a cached one. func newPrinter() *pp { p := ppFree.get().(*pp) p.panicking = false + p.erroring = false p.fmt.init(&p.buf) return p } @@ -299,6 +301,7 @@ p.buf.WriteByte('?') } func (p *pp) badVerb(verb rune) { + p.erroring = true p.add('%') p.add('!') p.add(verb) @@ -316,6 +319,7 @@ default: p.buf.Write(nilAngleBytes) } p.add(')') + p.erroring = false } func (p *pp) fmtBool(v bool, verb rune) { @@ -606,6 +610,9 @@ } } func (p *pp) handleMethods(verb rune, plus, goSyntax bool, depth int) (wasString, handled bool) { + if p.erroring { + return + } // Is it a Formatter? if formatter, ok := p.field.(Formatter); ok { handled = true