mirror of
https://github.com/golang/go
synced 2024-11-23 00:10:07 -07:00
fmt: change evalutation of indexed arg to match docs
The old code put the index before the period in the precision; it should be after so it's always before the star, as documented. A little trickier to do in one pass but compensated for by more tests and catching a couple of other error cases. R=rsc CC=golang-dev https://golang.org/cl/9751044
This commit is contained in:
parent
ca986a2c81
commit
d84132cce7
@ -131,7 +131,7 @@
|
||||
For example,
|
||||
fmt.Sprintf("%[2]d %[1]d\n", 11, 22)
|
||||
will yield "22, 11", while
|
||||
fmt.Sprintf("%[3]*[2].*[1]f", 12.0, 2, 6),
|
||||
fmt.Sprintf("%[3]*.[2]*[1]f", 12.0, 2, 6),
|
||||
equivalent to
|
||||
fmt.Sprintf("%6.2f", 12.0),
|
||||
will yield " 12.00". Because an explicit index affects subsequent verbs,
|
||||
@ -155,8 +155,9 @@
|
||||
Non-int for width or precision: %!(BADWIDTH) or %!(BADPREC)
|
||||
Printf("%*s", 4.5, "hi"): %!(BADWIDTH)hi
|
||||
Printf("%.*s", 4.5, "hi"): %!(BADPREC)hi
|
||||
Invalid or out-of-range argument index: %!(BADARGNUM)
|
||||
Printf("%*[2]d", 7): %d(BADARGNUM)
|
||||
Invalid or invalid use of argument index: %!(BADINDEX)
|
||||
Printf("%*[2]d", 7): %d(BADINDEX)
|
||||
Printf("%.[2]d", 7): %d(BADINDEX)
|
||||
|
||||
All errors begin with the string "%!" followed sometimes
|
||||
by a single character (the verb) and end with a parenthesized
|
||||
|
@ -550,18 +550,29 @@ var reorderTests = []struct {
|
||||
{"%[2]d", SE{2, 1}, "1"},
|
||||
{"%[2]d %[1]d", SE{1, 2}, "2 1"},
|
||||
{"%[2]*[1]d", SE{2, 5}, " 2"},
|
||||
{"%6.2f", SE{12.0}, " 12.00"},
|
||||
{"%[3]*[2].*[1]f", SE{12.0, 2, 6}, " 12.00"},
|
||||
{"%[1]*[2].*[3]f", SE{6, 2, 12.0}, " 12.00"},
|
||||
{"%6.2f", SE{12.0}, " 12.00"}, // Explicit version of next line.
|
||||
{"%[3]*.[2]*[1]f", SE{12.0, 2, 6}, " 12.00"},
|
||||
{"%[1]*.[2]*[3]f", SE{6, 2, 12.0}, " 12.00"},
|
||||
{"%10f", SE{12.0}, " 12.000000"},
|
||||
{"%[1]*[3]f", SE{10, 99, 12.0}, " 12.000000"},
|
||||
{"%.6f", SE{12.0}, "12.000000"}, // Explicit version of next line.
|
||||
{"%.[1]*[3]f", SE{6, 99, 12.0}, "12.000000"},
|
||||
{"%6.f", SE{12.0}, " 12"}, // // Explicit version of next line; empty precision means zero.
|
||||
{"%[1]*.[3]f", SE{6, 3, 12.0}, " 12"},
|
||||
// An actual use! Print the same arguments twice.
|
||||
{"%d %d %d %#[1]o %#o %#o", SE{11, 12, 13}, "11 12 13 013 014 015"},
|
||||
|
||||
// Erroneous cases.
|
||||
{"%[]d", SE{2, 1}, "%d(BADARGNUM)"},
|
||||
{"%[-3]d", SE{2, 1}, "%d(BADARGNUM)"},
|
||||
{"%[x]d", SE{2, 1}, "%d(BADARGNUM)"},
|
||||
{"%[23]d", SE{2, 1}, "%d(BADARGNUM)"},
|
||||
{"%[d", SE{2, 1}, "%d(BADINDEX)"},
|
||||
{"%]d", SE{2, 1}, "%!](int=2)d%!(EXTRA int=1)"},
|
||||
{"%[]d", SE{2, 1}, "%d(BADINDEX)"},
|
||||
{"%[-3]d", SE{2, 1}, "%d(BADINDEX)"},
|
||||
{"%[99]d", SE{2, 1}, "%d(BADINDEX)"},
|
||||
{"%[3]", SE{2, 1}, "%!(NOVERB)"},
|
||||
{"%[1].2d", SE{5, 6}, "%d(BADINDEX)"},
|
||||
{"%[1]2d", SE{2, 1}, "%d(BADINDEX)"},
|
||||
{"%3.[2]d", SE{7}, "%d(BADINDEX)"},
|
||||
{"%.[2]d", SE{7}, "%d(BADINDEX)"},
|
||||
{"%d %d %d %#[1]o %#o %#o %#o", SE{11, 12, 13}, "11 12 13 013 014 015 %o(MISSING)"},
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ var (
|
||||
nilBytes = []byte("nil")
|
||||
mapBytes = []byte("map[")
|
||||
missingBytes = []byte("(MISSING)")
|
||||
badArgNum = []byte("(BADARGNUM)")
|
||||
badIndexBytes = []byte("(BADINDEX)")
|
||||
panicBytes = []byte("(PANIC=")
|
||||
extraBytes = []byte("%!(EXTRA ")
|
||||
irparenBytes = []byte("i)")
|
||||
@ -117,7 +117,7 @@ type pp struct {
|
||||
value reflect.Value
|
||||
// reordered records whether the format string used argument reordering.
|
||||
reordered bool
|
||||
// goodArgNum records whether the last reordering directive was valid.
|
||||
// goodArgNum records whether all reordering directives were valid.
|
||||
goodArgNum bool
|
||||
runeBuf [utf8.UTFMax]byte
|
||||
fmt fmt
|
||||
@ -1021,11 +1021,11 @@ BigSwitch:
|
||||
}
|
||||
|
||||
// intFromArg gets the argNumth element of a. On return, isInt reports whether the argument has type int.
|
||||
func intFromArg(a []interface{}, end, i, argNum int) (num int, isInt bool, newi, newArgNum int) {
|
||||
newi, newArgNum = end, argNum
|
||||
if i < end && argNum < len(a) {
|
||||
func intFromArg(a []interface{}, argNum int) (num int, isInt bool, newArgNum int) {
|
||||
newArgNum = argNum
|
||||
if argNum < len(a) {
|
||||
num, isInt = a[argNum].(int)
|
||||
newi, newArgNum = i+1, argNum+1
|
||||
newArgNum = argNum + 1
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -1053,24 +1053,25 @@ func parseArgNumber(format string) (index int, wid int, ok bool) {
|
||||
// argNumber returns the next argument to evaluate, which is either the value of the passed-in
|
||||
// argNum or the value of the bracketed integer that begins format[i:]. It also returns
|
||||
// the new value of i, that is, the index of the next byte of the format to process.
|
||||
func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int) {
|
||||
p.goodArgNum = true
|
||||
func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int, found bool) {
|
||||
if len(format) <= i || format[i] != '[' {
|
||||
return argNum, i
|
||||
return argNum, i, false
|
||||
}
|
||||
p.reordered = true
|
||||
index, wid, ok := parseArgNumber(format[i:])
|
||||
if ok && 0 <= index && index < numArgs {
|
||||
return index, i + wid
|
||||
return index, i + wid, true
|
||||
}
|
||||
p.goodArgNum = false
|
||||
return argNum, i + wid
|
||||
return argNum, i + wid, true
|
||||
}
|
||||
|
||||
func (p *pp) doPrintf(format string, a []interface{}) {
|
||||
end := len(format)
|
||||
argNum := 0 // we process one argument per non-trivial format
|
||||
argNum := 0 // we process one argument per non-trivial format
|
||||
afterIndex := false // previous item in format was an index like [3].
|
||||
p.reordered = false
|
||||
p.goodArgNum = true
|
||||
for i := 0; i < end; {
|
||||
lasti := i
|
||||
for i < end && format[i] != '%' {
|
||||
@ -1108,35 +1109,50 @@ func (p *pp) doPrintf(format string, a []interface{}) {
|
||||
}
|
||||
|
||||
// Do we have an explicit argument index?
|
||||
argNum, i = p.argNumber(argNum, format, i, len(a))
|
||||
argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
|
||||
|
||||
// Do we have width?
|
||||
if i < end && format[i] == '*' {
|
||||
p.fmt.wid, p.fmt.widPresent, i, argNum = intFromArg(a, end, i, argNum)
|
||||
i++
|
||||
p.fmt.wid, p.fmt.widPresent, argNum = intFromArg(a, argNum)
|
||||
if !p.fmt.widPresent {
|
||||
p.buf.Write(badWidthBytes)
|
||||
}
|
||||
argNum, i = p.argNumber(argNum, format, i, len(a)) // We consumed []; another can follow here.
|
||||
afterIndex = false
|
||||
} else {
|
||||
p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end)
|
||||
if afterIndex && p.fmt.widPresent { // "%[3]2d"
|
||||
p.goodArgNum = false
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have precision?
|
||||
if i+1 < end && format[i] == '.' {
|
||||
if format[i+1] == '*' {
|
||||
p.fmt.prec, p.fmt.precPresent, i, argNum = intFromArg(a, end, i+1, argNum)
|
||||
i++
|
||||
if afterIndex { // "%[3].2d"
|
||||
p.goodArgNum = false
|
||||
}
|
||||
argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
|
||||
if format[i] == '*' {
|
||||
i++
|
||||
p.fmt.prec, p.fmt.precPresent, argNum = intFromArg(a, argNum)
|
||||
if !p.fmt.precPresent {
|
||||
p.buf.Write(badPrecBytes)
|
||||
}
|
||||
argNum, i = p.argNumber(argNum, format, i, len(a)) // We consumed []; another can follow here.
|
||||
afterIndex = false
|
||||
} else {
|
||||
p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end)
|
||||
p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i, end)
|
||||
if !p.fmt.precPresent {
|
||||
p.fmt.prec = 0
|
||||
p.fmt.precPresent = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !afterIndex {
|
||||
argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
|
||||
}
|
||||
|
||||
if i >= end {
|
||||
p.buf.Write(noVerbBytes)
|
||||
continue
|
||||
@ -1151,7 +1167,7 @@ func (p *pp) doPrintf(format string, a []interface{}) {
|
||||
if !p.goodArgNum {
|
||||
p.buf.WriteByte('%')
|
||||
p.add(c)
|
||||
p.buf.Write(badArgNum)
|
||||
p.buf.Write(badIndexBytes)
|
||||
continue
|
||||
} else if argNum >= len(a) { // out of operands
|
||||
p.buf.WriteByte('%')
|
||||
|
Loading…
Reference in New Issue
Block a user