]> Cypherpunks repositories - gostls13.git/commitdiff
fmt: print byte stringers correctly
authorAndrew Gerrand <adg@golang.org>
Mon, 18 Aug 2014 22:52:52 +0000 (08:52 +1000)
committerAndrew Gerrand <adg@golang.org>
Mon, 18 Aug 2014 22:52:52 +0000 (08:52 +1000)
type T byte
func (T) String() string { return "X" }

fmt.Sprintf("%s", []T{97, 98, 99, 100}) == "abcd"
fmt.Sprintf("%x", []T{97, 98, 99, 100}) == "61626364"
fmt.Sprintf("%v", []T{97, 98, 99, 100}) == "[X X X X]"

This change makes the last case print correctly.
Before, it would have been "[97 98 99 100]".

Fixes #8360.

LGTM=r
R=r, dan.kortschak
CC=golang-codereviews
https://golang.org/cl/129330043

src/pkg/fmt/fmt_test.go
src/pkg/fmt/print.go

index 89227cce806eba888fdc751d3d646dad8a7ba704..8c577949a109e765d8dc2be9bb856f9f0dbd62b8 100644 (file)
@@ -108,6 +108,20 @@ func (p *P) String() string {
 var barray = [5]renamedUint8{1, 2, 3, 4, 5}
 var bslice = barray[:]
 
+type byteStringer byte
+
+func (byteStringer) String() string { return "X" }
+
+var byteStringerSlice = []byteStringer{97, 98, 99, 100}
+
+type byteFormatter byte
+
+func (byteFormatter) Format(f State, _ rune) {
+       Fprint(f, "X")
+}
+
+var byteFormatterSlice = []byteFormatter{97, 98, 99, 100}
+
 var b byte
 
 var fmtTests = []struct {
@@ -629,6 +643,21 @@ var fmtTests = []struct {
        {"%+010.2f", -104.66 + 440.51i, "(-000104.66+000440.51i)"},
        {"%+010.2f", +104.66 - 440.51i, "(+000104.66-000440.51i)"},
        {"%+010.2f", -104.66 - 440.51i, "(-000104.66-000440.51i)"},
+
+       // []T where type T is a byte with a Stringer method.
+       {"%v", byteStringerSlice, "[X X X X]"},
+       {"%s", byteStringerSlice, "abcd"},
+       {"%q", byteStringerSlice, "\"abcd\""},
+       {"%x", byteStringerSlice, "61626364"},
+       {"%#v", byteStringerSlice, "[]fmt_test.byteStringer{0x61, 0x62, 0x63, 0x64}"},
+
+       // And the same for Formatter.
+       {"%v", byteFormatterSlice, "[X X X X]"},
+       {"%s", byteFormatterSlice, "abcd"},
+       {"%q", byteFormatterSlice, "\"abcd\""},
+       {"%x", byteFormatterSlice, "61626364"},
+       // This next case seems wrong, but the docs say the Formatter wins here.
+       {"%#v", byteFormatterSlice, "[]fmt_test.byteFormatter{X, X, X, X}"},
 }
 
 // zeroFill generates zero-filled strings of the specified width. The length
index 302661f4c85fc46285f7e1ccc5e490b5ea1f3355..679c577dbd1aad806ff0c243a60ab82a011cf11e 100644 (file)
@@ -832,6 +832,8 @@ func (p *pp) printValue(value reflect.Value, verb rune, plus, goSyntax bool, dep
        return p.printReflectValue(value, verb, plus, goSyntax, depth)
 }
 
+var byteType = reflect.TypeOf(byte(0))
+
 // printReflectValue is the fallback for both printArg and printValue.
 // It uses reflect to print the value.
 func (p *pp) printReflectValue(value reflect.Value, verb rune, plus, goSyntax bool, depth int) (wasString bool) {
@@ -925,8 +927,12 @@ BigSwitch:
                        wasString = p.printValue(value, verb, plus, goSyntax, depth+1)
                }
        case reflect.Array, reflect.Slice:
-               // Byte slices are special.
-               if typ := f.Type(); typ.Elem().Kind() == reflect.Uint8 {
+               // Byte slices are special:
+               // - Handle []byte (== []uint8) with fmtBytes.
+               // - Handle []T, where T is a named byte type, with fmtBytes only
+               //   for the s, q, an x verbs. For other verbs, T might be a
+               //   Stringer, so we use printValue to print each element.
+               if typ := f.Type(); typ.Elem().Kind() == reflect.Uint8 && (typ.Elem() == byteType || verb == 's' || verb == 'q' || verb == 'x') {
                        var bytes []byte
                        if f.Kind() == reflect.Slice {
                                bytes = f.Bytes()