]> Cypherpunks repositories - gostls13.git/commitdiff
fmt: allow any type in a format's width argument
authorRob Pike <r@golang.org>
Thu, 10 Sep 2015 20:41:03 +0000 (13:41 -0700)
committerRob Pike <r@golang.org>
Thu, 10 Sep 2015 20:53:22 +0000 (20:53 +0000)
The construction
fmt.Printf("%*d", n, 4)
reads the argument n as a width specifier to use when printing 4.
Until now, only strict int type was accepted here and it couldn't
be fixed because the fix, using reflection, broke escape analysis
and added an extra allocation in every Printf call, even those that
do not use this feature.

The compiler has been fixed, although I am not sure when exactly,
so let's fix Printf and then write

Fixes #10732.

Change-Id: I79cf0c4fadd876265aa39d3cb62867247b36ab65
Reviewed-on: https://go-review.googlesource.com/14491
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/fmt/fmt_test.go
src/fmt/print.go

index 90a4031d5bdafca3465ff9bc923d2ff5dcfcdd74..26383f6d1e5ad99bb48e6c4d34ab5ce0f51a5f01 100644 (file)
@@ -1188,6 +1188,11 @@ var startests = []struct {
        {"%.*d", args(4, 42), "0042"},
        {"%*.*d", args(8, 4, 42), "    0042"},
        {"%0*d", args(4, 42), "0042"},
+       // Some non-int types for width. (Issue 10732).
+       {"%0*d", args(uint(4), 42), "0042"},
+       {"%0*d", args(uint64(4), 42), "0042"},
+       {"%0*d", args('\x04', 42), "0042"},
+       {"%0*d", args(uintptr(4), 42), "0042"},
 
        // erroneous
        {"%*d", args(nil, 42), "%!(BADWIDTH)42"},
@@ -1196,17 +1201,19 @@ var startests = []struct {
        {"%.*d", args(nil, 42), "%!(BADPREC)42"},
        {"%.*d", args(-1, 42), "%!(BADPREC)42"},
        {"%.*d", args(int(1e7), 42), "%!(BADPREC)42"},
+       {"%.*d", args(uint(1e7), 42), "%!(BADPREC)42"},
+       {"%.*d", args(uint64(1<<63), 42), "%!(BADPREC)42"},   // Huge negative (-inf).
+       {"%.*d", args(uint64(1<<64-1), 42), "%!(BADPREC)42"}, // Small negative (-1).
        {"%*d", args(5, "foo"), "%!d(string=  foo)"},
        {"%*% %d", args(20, 5), "% 5"},
        {"%*", args(4), "%!(NOVERB)"},
-       {"%*d", args(int32(4), 42), "%!(BADWIDTH)42"},
 }
 
 func TestWidthAndPrecision(t *testing.T) {
-       for _, tt := range startests {
+       for i, tt := range startests {
                s := Sprintf(tt.fmt, tt.in...)
                if s != tt.out {
-                       t.Errorf("%q: got %q expected %q", tt.fmt, s, tt.out)
+                       t.Errorf("#%d: %q: got %q expected %q", i, tt.fmt, s, tt.out)
                }
        }
 }
index 8d3e97c3ab1b7da8959a71218302a73ceef89f57..ebfa13e4d370a16515f064c3d7930fbf3379463b 100644 (file)
@@ -1024,11 +1024,30 @@ BigSwitch:
        return wasString
 }
 
-// intFromArg gets the argNumth element of a. On return, isInt reports whether the argument has type int.
+// intFromArg gets the argNumth element of a. On return, isInt reports whether the argument has integer type.
 func intFromArg(a []interface{}, argNum int) (num int, isInt bool, newArgNum int) {
        newArgNum = argNum
        if argNum < len(a) {
-               num, isInt = a[argNum].(int)
+               num, isInt = a[argNum].(int) // Almost always OK.
+               if !isInt {
+                       // Work harder.
+                       switch v := reflect.ValueOf(a[argNum]); v.Kind() {
+                       case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+                               n := v.Int()
+                               if int64(int(n)) == n {
+                                       num = int(n)
+                                       isInt = true
+                               }
+                       case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+                               n := v.Uint()
+                               if int64(n) >= 0 && uint64(int(n)) == n {
+                                       num = int(n)
+                                       isInt = true
+                               }
+                       default:
+                               // Already 0, false.
+                       }
+               }
                newArgNum = argNum + 1
                if tooLarge(num) {
                        num = 0