Skip to content

Commit

Permalink
fmt: print byte stringers correctly
Browse files Browse the repository at this point in the history
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 golang#8360.

LGTM=r
R=r, dan.kortschak
CC=golang-codereviews
https://golang.org/cl/129330043
  • Loading branch information
adg authored and wheatman committed Jun 20, 2018
1 parent 4752e08 commit 3ba4b9f
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 2 deletions.
29 changes: 29 additions & 0 deletions src/pkg/fmt/fmt_test.go
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions src/pkg/fmt/print.go
Expand Up @@ -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) {
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 3ba4b9f

Please sign in to comment.