Commit 344600f6 authored by Rob Pike's avatar Rob Pike

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

R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/2198044
parent d181625b
...@@ -69,8 +69,8 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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.
......
...@@ -193,8 +193,8 @@ var fmttests = []fmtTest{ ...@@ -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{ ...@@ -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{ ...@@ -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{ ...@@ -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{ ...@@ -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
......
...@@ -20,12 +20,12 @@ var ( ...@@ -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{}) { ...@@ -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 {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment