mirror of
https://github.com/golang/go
synced 2024-11-21 21:04:41 -07:00
fmt: don't recur if String method (etc.) misbehaves
Fixes #2555. R=golang-dev, dsymonds, r CC=golang-dev https://golang.org/cl/5486076
This commit is contained in:
parent
85fdd68bd9
commit
24e9683ae6
@ -813,3 +813,37 @@ func TestPanics(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -74,6 +74,7 @@ type GoStringer interface {
|
|||||||
type pp struct {
|
type pp struct {
|
||||||
n int
|
n int
|
||||||
panicking bool
|
panicking bool
|
||||||
|
erroring bool // printing an error condition
|
||||||
buf bytes.Buffer
|
buf bytes.Buffer
|
||||||
// field holds the current item, as an interface{}.
|
// field holds the current item, as an interface{}.
|
||||||
field interface{}
|
field interface{}
|
||||||
@ -124,6 +125,7 @@ var ppFree = newCache(func() interface{} { return new(pp) })
|
|||||||
func newPrinter() *pp {
|
func newPrinter() *pp {
|
||||||
p := ppFree.get().(*pp)
|
p := ppFree.get().(*pp)
|
||||||
p.panicking = false
|
p.panicking = false
|
||||||
|
p.erroring = false
|
||||||
p.fmt.init(&p.buf)
|
p.fmt.init(&p.buf)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
@ -299,6 +301,7 @@ func (p *pp) unknownType(v interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *pp) badVerb(verb rune) {
|
func (p *pp) badVerb(verb rune) {
|
||||||
|
p.erroring = true
|
||||||
p.add('%')
|
p.add('%')
|
||||||
p.add('!')
|
p.add('!')
|
||||||
p.add(verb)
|
p.add(verb)
|
||||||
@ -316,6 +319,7 @@ func (p *pp) badVerb(verb rune) {
|
|||||||
p.buf.Write(nilAngleBytes)
|
p.buf.Write(nilAngleBytes)
|
||||||
}
|
}
|
||||||
p.add(')')
|
p.add(')')
|
||||||
|
p.erroring = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pp) fmtBool(v bool, verb rune) {
|
func (p *pp) fmtBool(v bool, verb rune) {
|
||||||
@ -606,6 +610,9 @@ func (p *pp) catchPanic(field interface{}, verb rune) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *pp) handleMethods(verb rune, plus, goSyntax bool, depth int) (wasString, handled bool) {
|
func (p *pp) handleMethods(verb rune, plus, goSyntax bool, depth int) (wasString, handled bool) {
|
||||||
|
if p.erroring {
|
||||||
|
return
|
||||||
|
}
|
||||||
// Is it a Formatter?
|
// Is it a Formatter?
|
||||||
if formatter, ok := p.field.(Formatter); ok {
|
if formatter, ok := p.field.(Formatter); ok {
|
||||||
handled = true
|
handled = true
|
||||||
|
Loading…
Reference in New Issue
Block a user