Skip to content

Commit 4e834cf

Browse files
tznealrobpike
authored andcommitted
fmt: handle negative width/prec when supplied as an argument
Negative width arguments now left align the way a minus-width in the format string aligns. The minus in the format string overrides the sign of the argument as in C. Precision behavior is modified to include an error if the argument is negative. This differs from a negative precision in a format string which just terminates the format. Additional checks for large magnitude widths and precisions are added to make the runtime behavior (failure, but with different error messages), more consistent between format string specified width/precision and argument specified width/precision. Fixes #11376 Change-Id: I8c7ed21088e9c18128a45d4c487c5ab9fafd13ef Reviewed-on: https://go-review.googlesource.com/11405 Reviewed-by: Rob Pike <r@golang.org> Run-TryBot: Rob Pike <r@golang.org>
1 parent 2bcdb5a commit 4e834cf

File tree

2 files changed

+36
-4
lines changed

2 files changed

+36
-4
lines changed

src/fmt/fmt_test.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,10 @@ var reorderTests = []struct {
832832
{"%[5]d %[2]d %d", SE{1, 2, 3}, "%!d(BADINDEX) 2 3"},
833833
{"%d %[3]d %d", SE{1, 2}, "1 %!d(BADINDEX) 2"}, // Erroneous index does not affect sequence.
834834
{"%.[]", SE{}, "%!](BADINDEX)"}, // Issue 10675
835+
{"%.-3d", SE{42}, "%!-(int=42)3d"}, // TODO: Should this set return better error messages?
836+
{"%2147483648d", SE{42}, "%!(NOVERB)%!(EXTRA int=42)"},
837+
{"%-2147483648d", SE{42}, "%!(NOVERB)%!(EXTRA int=42)"},
838+
{"%.2147483648d", SE{42}, "%!(NOVERB)%!(EXTRA int=42)"},
835839
}
836840

837841
func TestReorder(t *testing.T) {
@@ -1158,14 +1162,20 @@ var startests = []struct {
11581162
out string
11591163
}{
11601164
{"%*d", args(4, 42), " 42"},
1165+
{"%-*d", args(4, 42), "42 "},
1166+
{"%*d", args(-4, 42), "42 "},
1167+
{"%-*d", args(-4, 42), "42 "},
11611168
{"%.*d", args(4, 42), "0042"},
11621169
{"%*.*d", args(8, 4, 42), " 0042"},
11631170
{"%0*d", args(4, 42), "0042"},
1164-
{"%-*d", args(4, 42), "42 "},
11651171

11661172
// erroneous
11671173
{"%*d", args(nil, 42), "%!(BADWIDTH)42"},
1174+
{"%*d", args(int(1e7), 42), "%!(BADWIDTH)42"},
1175+
{"%*d", args(int(-1e7), 42), "%!(BADWIDTH)42"},
11681176
{"%.*d", args(nil, 42), "%!(BADPREC)42"},
1177+
{"%.*d", args(-1, 42), "%!(BADPREC)42"},
1178+
{"%.*d", args(int(1e7), 42), "%!(BADPREC)42"},
11691179
{"%*d", args(5, "foo"), "%!d(string= foo)"},
11701180
{"%*% %d", args(20, 5), "% 5"},
11711181
{"%*", args(4), "%!(NOVERB)"},

src/fmt/print.go

+25-3
Original file line numberDiff line numberDiff line change
@@ -285,15 +285,20 @@ func getField(v reflect.Value, i int) reflect.Value {
285285
return val
286286
}
287287

288+
// tooLarge reports whether the magnitude of the integer is
289+
// too large to be used as a formatting width or precision.
290+
func tooLarge(x int) bool {
291+
const max int = 1e6
292+
return x > max || x < -max
293+
}
294+
288295
// parsenum converts ASCII to integer. num is 0 (and isnum is false) if no number present.
289296
func parsenum(s string, start, end int) (num int, isnum bool, newi int) {
290297
if start >= end {
291298
return 0, false, end
292299
}
293300
for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ {
294-
const maxInt32 = 1<<31 - 1 // 31 bits is plenty for a width.
295-
max := maxInt32/10 - 1
296-
if num > max {
301+
if tooLarge(num) {
297302
return 0, false, end // Overflow; crazy long number most likely.
298303
}
299304
num = num*10 + int(s[newi]-'0')
@@ -1025,6 +1030,10 @@ func intFromArg(a []interface{}, argNum int) (num int, isInt bool, newArgNum int
10251030
if argNum < len(a) {
10261031
num, isInt = a[argNum].(int)
10271032
newArgNum = argNum + 1
1033+
if tooLarge(num) {
1034+
num = 0
1035+
isInt = false
1036+
}
10281037
}
10291038
return
10301039
}
@@ -1119,9 +1128,17 @@ func (p *pp) doPrintf(format string, a []interface{}) {
11191128
if i < end && format[i] == '*' {
11201129
i++
11211130
p.fmt.wid, p.fmt.widPresent, argNum = intFromArg(a, argNum)
1131+
11221132
if !p.fmt.widPresent {
11231133
p.buf.Write(badWidthBytes)
11241134
}
1135+
1136+
// We have a negative width, so take its value and ensure
1137+
// that the minus flag is set
1138+
if p.fmt.wid < 0 {
1139+
p.fmt.wid = -p.fmt.wid
1140+
p.fmt.minus = true
1141+
}
11251142
afterIndex = false
11261143
} else {
11271144
p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end)
@@ -1140,6 +1157,11 @@ func (p *pp) doPrintf(format string, a []interface{}) {
11401157
if i < end && format[i] == '*' {
11411158
i++
11421159
p.fmt.prec, p.fmt.precPresent, argNum = intFromArg(a, argNum)
1160+
// Negative precision arguments don't make sense
1161+
if p.fmt.prec < 0 {
1162+
p.fmt.prec = 0
1163+
p.fmt.precPresent = false
1164+
}
11431165
if !p.fmt.precPresent {
11441166
p.buf.Write(badPrecBytes)
11451167
}

0 commit comments

Comments
 (0)