mirror of
https://github.com/golang/go
synced 2024-11-21 21:34:40 -07:00
fmt: only use Stringer or Error for strings
This is a slight change to fmt's semantics, but means that if you use %d to print an integer with a Stringable value, it will print as an integer. This came up because Time.Month() couldn't cleanly print as an integer rather than a name. Using %d on Stringables is silly anyway, so there should be no effect outside the fmt tests. As a mild bonus, certain recursive failures of String methods will also be avoided this way. R=golang-dev, adg CC=golang-dev https://golang.org/cl/5453053
This commit is contained in:
parent
af84892643
commit
2ed57a8cd8
@ -89,18 +89,22 @@
|
|||||||
If an operand implements interface Formatter, that interface
|
If an operand implements interface Formatter, that interface
|
||||||
can be used for fine control of formatting.
|
can be used for fine control of formatting.
|
||||||
|
|
||||||
Next, if an operand implements the error interface, the Error method
|
If the format (which is implicitly %v for Println etc.) is valid
|
||||||
|
for a string (%s %q %v %x %X), the following two rules also apply:
|
||||||
|
|
||||||
|
1. If an operand implements the error interface, the Error method
|
||||||
will be used to convert the object to a string, which will then
|
will be used to convert the object to a string, which will then
|
||||||
be formatted as required by the verb (if any).
|
be formatted as required by the verb (if any).
|
||||||
|
|
||||||
Finally, if an operand implements method String() string that method
|
2. If an operand implements method String() string, that method
|
||||||
will be used to convert the object to a string, which will then
|
will be used to convert the object to a string, which will then
|
||||||
be formatted as required by the verb (if any).
|
be formatted as required by the verb (if any).
|
||||||
|
|
||||||
To avoid recursion in cases such as
|
To avoid recursion in cases such as
|
||||||
type X int
|
type X string
|
||||||
func (x X) String() string { return Sprintf("%d", x) }
|
func (x X) String() string { return Sprintf("<%s>", x) }
|
||||||
cast the value before recurring:
|
cast the value before recurring:
|
||||||
func (x X) String() string { return Sprintf("%d", int(x)) }
|
func (x X) String() string { return Sprintf("<%s>", string(x)) }
|
||||||
|
|
||||||
Format errors:
|
Format errors:
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"runtime" // for the malloc count test only
|
"runtime" // for the malloc count test only
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@ -352,7 +353,7 @@ var fmttests = []struct {
|
|||||||
{"%s", I(23), `<23>`},
|
{"%s", I(23), `<23>`},
|
||||||
{"%q", I(23), `"<23>"`},
|
{"%q", I(23), `"<23>"`},
|
||||||
{"%x", I(23), `3c32333e`},
|
{"%x", I(23), `3c32333e`},
|
||||||
{"%d", I(23), `%!d(string=<23>)`},
|
{"%d", I(23), `23`}, // Stringer applies only to string formats.
|
||||||
|
|
||||||
// go syntax
|
// go syntax
|
||||||
{"%#v", A{1, 2, "a", []int{1, 2}}, `fmt_test.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}`},
|
{"%#v", A{1, 2, "a", []int{1, 2}}, `fmt_test.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}`},
|
||||||
@ -430,6 +431,10 @@ var fmttests = []struct {
|
|||||||
{"%p", make([]int, 1), "0xPTR"},
|
{"%p", make([]int, 1), "0xPTR"},
|
||||||
{"%p", 27, "%!p(int=27)"}, // not a pointer at all
|
{"%p", 27, "%!p(int=27)"}, // not a pointer at all
|
||||||
|
|
||||||
|
// %d on Stringer should give integer if possible
|
||||||
|
{"%s", time.Time{}.Month(), "January"},
|
||||||
|
{"%d", time.Time{}.Month(), "1"},
|
||||||
|
|
||||||
// erroneous things
|
// erroneous things
|
||||||
{"%s %", "hello", "hello %!(NOVERB)"},
|
{"%s %", "hello", "hello %!(NOVERB)"},
|
||||||
{"%s %.2", "hello", "hello %!(NOVERB)"},
|
{"%s %.2", "hello", "hello %!(NOVERB)"},
|
||||||
@ -772,9 +777,9 @@ var panictests = []struct {
|
|||||||
out string
|
out string
|
||||||
}{
|
}{
|
||||||
// String
|
// String
|
||||||
{"%d", (*Panic)(nil), "<nil>"}, // nil pointer special case
|
{"%s", (*Panic)(nil), "<nil>"}, // nil pointer special case
|
||||||
{"%d", Panic{io.ErrUnexpectedEOF}, "%d(PANIC=unexpected EOF)"},
|
{"%s", Panic{io.ErrUnexpectedEOF}, "%s(PANIC=unexpected EOF)"},
|
||||||
{"%d", Panic{3}, "%d(PANIC=3)"},
|
{"%s", Panic{3}, "%s(PANIC=3)"},
|
||||||
// GoString
|
// GoString
|
||||||
{"%#v", (*Panic)(nil), "<nil>"}, // nil pointer special case
|
{"%#v", (*Panic)(nil), "<nil>"}, // nil pointer special case
|
||||||
{"%#v", Panic{io.ErrUnexpectedEOF}, "%v(PANIC=unexpected EOF)"},
|
{"%#v", Panic{io.ErrUnexpectedEOF}, "%v(PANIC=unexpected EOF)"},
|
||||||
|
@ -631,24 +631,30 @@ func (p *pp) handleMethods(verb rune, plus, goSyntax bool, depth int) (wasString
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Is it an error or Stringer?
|
// If a string is acceptable according to the format, see if
|
||||||
// The duplication in the bodies is necessary:
|
// the value satisfies one of the string-valued interfaces.
|
||||||
// setting wasString and handled and deferring catchPanic
|
// Println etc. set verb to %v, which is "stringable".
|
||||||
// must happen before calling the method.
|
switch verb {
|
||||||
switch v := p.field.(type) {
|
case 'v', 's', 'x', 'X', 'q':
|
||||||
case error:
|
// Is it an error or Stringer?
|
||||||
wasString = false
|
// The duplication in the bodies is necessary:
|
||||||
handled = true
|
// setting wasString and handled, and deferring catchPanic,
|
||||||
defer p.catchPanic(p.field, verb)
|
// must happen before calling the method.
|
||||||
p.printField(v.Error(), verb, plus, false, depth)
|
switch v := p.field.(type) {
|
||||||
return
|
case error:
|
||||||
|
wasString = false
|
||||||
|
handled = true
|
||||||
|
defer p.catchPanic(p.field, verb)
|
||||||
|
p.printField(v.Error(), verb, plus, false, depth)
|
||||||
|
return
|
||||||
|
|
||||||
case Stringer:
|
case Stringer:
|
||||||
wasString = false
|
wasString = false
|
||||||
handled = true
|
handled = true
|
||||||
defer p.catchPanic(p.field, verb)
|
defer p.catchPanic(p.field, verb)
|
||||||
p.printField(v.String(), verb, plus, false, depth)
|
p.printField(v.String(), verb, plus, false, depth)
|
||||||
return
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handled = false
|
handled = false
|
||||||
|
Loading…
Reference in New Issue
Block a user