1
0
mirror of https://github.com/golang/go synced 2024-11-22 07:34:40 -07:00

fmt/Printf: document and tweak error messages produced for bad formats

R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/2198044
This commit is contained in:
Rob Pike 2010-09-24 11:53:26 +10:00
parent d181625b9c
commit 344600f689
3 changed files with 42 additions and 21 deletions

View File

@ -69,8 +69,8 @@
Regardless of the verb, if an operand is an interface value, Regardless of the verb, if an operand is an interface value,
the internal concrete value is used, not the interface itself. the internal concrete value is used, not the interface itself.
Thus: Thus:
var i interface{} = 23; var i interface{} = 23
fmt.Printf("%v\n", i); fmt.Printf("%v\n", i)
will print 23. will print 23.
If an operand implements interface Formatter, that interface If an operand implements interface Formatter, that interface
@ -85,6 +85,26 @@
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("%d", int(x)) }
Format errors:
If an invalid argument is given for a verb, such as providing
a string to %d, the generated string will contain a
description of the problem, as in these examples:
Wrong type or unknown verb: %!verb(type=value)
Printf("%d", hi): %!d(string=hi)
Too many arguments: %!(EXTRA type=value)
Printf("hi", "guys"): hi%!(EXTRA string=guys)
Too few arguments: %!verb(MISSING)
Printf("hi%d"): hi %!d(MISSING)
Non-int for width or precision: %!(BADWIDTH) or %!(BADPREC)
Printf("%*s", 4.5, "hi"): %!(BADWIDTH)hi
Printf("%.*s", 4.5, "hi"): %!(BADPREC)hi
All errors begin with the string "%!" followed sometimes
by a single character (the verb) and end with a parenthesized
description.
Scanning: Scanning:
An analogous set of functions scans formatted text to yield An analogous set of functions scans formatted text to yield
@ -97,7 +117,7 @@
routines treat newlines as spaces. routines treat newlines as spaces.
Scanf, Fscanf, and Sscanf parse the arguments according to a Scanf, Fscanf, and Sscanf parse the arguments according to a
format string, analogous to that of Printf. For example, "%x" format string, analogous to that of Printf. For example, %x
will scan an integer as a hexadecimal number, and %v will scan will scan an integer as a hexadecimal number, and %v will scan
the default representation format for the value. the default representation format for the value.

View File

@ -193,8 +193,8 @@ var fmttests = []fmtTest{
fmtTest{"%+.3g", complex128(1 + 2i), "(+1+2i)"}, fmtTest{"%+.3g", complex128(1 + 2i), "(+1+2i)"},
// erroneous formats // erroneous formats
fmtTest{"", 2, "?(extra int=2)"}, fmtTest{"", 2, "%!(EXTRA int=2)"},
fmtTest{"%d", "hello", "%d(string=hello)"}, fmtTest{"%d", "hello", "%!d(string=hello)"},
// old test/fmt_test.go // old test/fmt_test.go
fmtTest{"%d", 1234, "1234"}, fmtTest{"%d", 1234, "1234"},
@ -301,7 +301,7 @@ var fmttests = []fmtTest{
fmtTest{"%s", I(23), `<23>`}, fmtTest{"%s", I(23), `<23>`},
fmtTest{"%q", I(23), `"<23>"`}, fmtTest{"%q", I(23), `"<23>"`},
fmtTest{"%x", I(23), `3c32333e`}, fmtTest{"%x", I(23), `3c32333e`},
fmtTest{"%d", I(23), `%d(string=<23>)`}, fmtTest{"%d", I(23), `%!d(string=<23>)`},
// go syntax // go syntax
fmtTest{"%#v", A{1, 2, "a", []int{1, 2}}, `fmt_test.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}`}, fmtTest{"%#v", A{1, 2, "a", []int{1, 2}}, `fmt_test.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}`},
@ -321,7 +321,7 @@ var fmttests = []fmtTest{
// renamings // renamings
fmtTest{"%v", renamedBool(true), "true"}, fmtTest{"%v", renamedBool(true), "true"},
fmtTest{"%d", renamedBool(true), "%d(fmt_test.renamedBool=true)"}, fmtTest{"%d", renamedBool(true), "%!d(fmt_test.renamedBool=true)"},
fmtTest{"%o", renamedInt(8), "10"}, fmtTest{"%o", renamedInt(8), "10"},
fmtTest{"%d", renamedInt8(-9), "-9"}, fmtTest{"%d", renamedInt8(-9), "-9"},
fmtTest{"%v", renamedInt16(10), "10"}, fmtTest{"%v", renamedInt16(10), "10"},
@ -366,14 +366,14 @@ var fmttests = []fmtTest{
fmtTest{"%p", make(chan int), "PTR"}, fmtTest{"%p", make(chan int), "PTR"},
fmtTest{"%p", make(map[int]int), "PTR"}, fmtTest{"%p", make(map[int]int), "PTR"},
fmtTest{"%p", make([]int, 1), "PTR"}, fmtTest{"%p", make([]int, 1), "PTR"},
fmtTest{"%p", 27, "%p(int=27)"}, // not a pointer at all fmtTest{"%p", 27, "%!p(int=27)"}, // not a pointer at all
// erroneous things // erroneous things
fmtTest{"%d", "hello", "%d(string=hello)"}, fmtTest{"%d", "hello", "%!d(string=hello)"},
fmtTest{"no args", "hello", "no args?(extra string=hello)"}, fmtTest{"no args", "hello", "no args%!(EXTRA string=hello)"},
fmtTest{"%s", nil, "%s(<nil>)"}, fmtTest{"%s", nil, "%!s(<nil>)"},
fmtTest{"%T", nil, "<nil>"}, fmtTest{"%T", nil, "<nil>"},
fmtTest{"%-1", 100, "%1(int=100)"}, fmtTest{"%-1", 100, "%!1(int=100)"},
} }
func TestSprintf(t *testing.T) { func TestSprintf(t *testing.T) {
@ -622,12 +622,12 @@ var startests = []starTest{
starTest{"%-*d", args(4, 42), "42 "}, starTest{"%-*d", args(4, 42), "42 "},
// erroneous // erroneous
starTest{"%*d", args(nil, 42), "%(badwidth)42"}, starTest{"%*d", args(nil, 42), "%!(BADWIDTH)42"},
starTest{"%.*d", args(nil, 42), "%(badprec)42"}, starTest{"%.*d", args(nil, 42), "%!(BADPREC)42"},
starTest{"%*d", args(5, "foo"), "%d(string= foo)"}, starTest{"%*d", args(5, "foo"), "%!d(string= foo)"},
starTest{"%*% %d", args(20, 5), "% 5"}, starTest{"%*% %d", args(20, 5), "% 5"},
starTest{"%*", args(4), "%(badwidth)%*(int=4)"}, starTest{"%*", args(4), "%!(BADWIDTH)%!*(int=4)"},
starTest{"%*d", args(int32(4), 42), "%(badwidth)42"}, starTest{"%*d", args(int32(4), 42), "%!(BADWIDTH)42"},
} }
// TODO: there's no conversion from []T to ...T, but we can fake it. These // TODO: there's no conversion from []T to ...T, but we can fake it. These

View File

@ -20,12 +20,12 @@ var (
nilParenBytes = []byte("(nil)") nilParenBytes = []byte("(nil)")
nilBytes = []byte("nil") nilBytes = []byte("nil")
mapBytes = []byte("map[") mapBytes = []byte("map[")
missingBytes = []byte("missing") missingBytes = []byte("(MISSING)")
extraBytes = []byte("?(extra ") extraBytes = []byte("%!(EXTRA ")
irparenBytes = []byte("i)") irparenBytes = []byte("i)")
bytesBytes = []byte("[]byte{") bytesBytes = []byte("[]byte{")
widthBytes = []byte("%(badwidth)") widthBytes = []byte("%!(BADWIDTH)")
precBytes = []byte("%(badprec)") precBytes = []byte("%!(BADPREC)")
) )
// State represents the printer state passed to custom formatters. // State represents the printer state passed to custom formatters.
@ -266,6 +266,7 @@ func (p *pp) unknownType(v interface{}) {
func (p *pp) badVerb(verb int, val interface{}) { func (p *pp) badVerb(verb int, val interface{}) {
p.add('%') p.add('%')
p.add('!')
p.add(verb) p.add(verb)
p.add('(') p.add('(')
if val == nil { if val == nil {