From d84132cce77c7826a9b5d55eb385e71e0ad22ade Mon Sep 17 00:00:00 2001
From: Rob Pike <r@golang.org>
Date: Wed, 29 May 2013 11:29:29 -0400
Subject: [PATCH] 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
---
 src/pkg/fmt/doc.go      |  7 +++---
 src/pkg/fmt/fmt_test.go | 25 ++++++++++++------
 src/pkg/fmt/print.go    | 56 ++++++++++++++++++++++++++---------------
 3 files changed, 58 insertions(+), 30 deletions(-)

diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go
index a0c6795d81..2910198508 100644
--- a/src/pkg/fmt/doc.go
+++ b/src/pkg/fmt/doc.go
@@ -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
diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go
index a121c9c248..85173aa4bc 100644
--- a/src/pkg/fmt/fmt_test.go
+++ b/src/pkg/fmt/fmt_test.go
@@ -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)"},
 }
 
diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go
index 58ffe216e9..fa9eb52c6a 100644
--- a/src/pkg/fmt/print.go
+++ b/src/pkg/fmt/print.go
@@ -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('%')
-- 
2.30.9