From: Andrew Gerrand Date: Mon, 18 Aug 2014 22:52:52 +0000 (+1000) Subject: fmt: print byte stringers correctly X-Git-Tag: go1.4beta1~803 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=326f48eb9cfcdaf7433756361b465df5b889dd46;p=gostls13.git 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 --- diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go index 89227cce80..8c577949a1 100644 --- a/src/pkg/fmt/fmt_test.go +++ b/src/pkg/fmt/fmt_test.go @@ -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 diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go index 302661f4c8..679c577dbd 100644 --- a/src/pkg/fmt/print.go +++ b/src/pkg/fmt/print.go @@ -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()