Commit 326f48eb authored by Andrew Gerrand's avatar Andrew Gerrand

fmt: print byte stringers correctly

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
parent 6d248cec
......@@ -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
......
......@@ -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()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment