Commit 8e231567 authored by Martin Möhrmann's avatar Martin Möhrmann Committed by Rob Pike

fmt: refactor pointer formatting and improve tests

Uses a switch statement for direct format function selection
similar to other types verb handling in fmt.

Applies padding also to nil pointers formatted with %v.

Guards against "slice bounds out of range" panic in TestSprintf
when a pointer test results in a formatted string s
that is shorter than the index i the pointer should appear in.

Adds more and rearranges tests.

Fixes #14712
Fixes #14714

Change-Id: Iaf5ae37b7e6ba7d27d528d199f2b2eb9d5829b8c
Reviewed-on: https://go-review.googlesource.com/20371
Run-TryBot: Rob Pike <r@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRob Pike <r@golang.org>
parent bbd3ffbd
...@@ -48,19 +48,23 @@ func TestFmtInterface(t *testing.T) { ...@@ -48,19 +48,23 @@ func TestFmtInterface(t *testing.T) {
} }
} }
const (
b32 uint32 = 1<<32 - 1
b64 uint64 = 1<<64 - 1
)
var ( var (
NaN = math.NaN() NaN = math.NaN()
posInf = math.Inf(1) posInf = math.Inf(1)
negInf = math.Inf(-1) negInf = math.Inf(-1)
)
const b32 uint32 = 1<<32 - 1 intVar = 0
const b64 uint64 = 1<<64 - 1
var array = [5]int{1, 2, 3, 4, 5} array = [5]int{1, 2, 3, 4, 5}
var iarray = [4]interface{}{1, "hello", 2.5, nil} iarray = [4]interface{}{1, "hello", 2.5, nil}
var slice = array[:] slice = array[:]
var islice = iarray[:] islice = iarray[:]
)
type A struct { type A struct {
i int i int
...@@ -130,8 +134,6 @@ func (byteFormatter) Format(f State, _ rune) { ...@@ -130,8 +134,6 @@ func (byteFormatter) Format(f State, _ rune) {
var byteFormatterSlice = []byteFormatter{97, 98, 99, 100} var byteFormatterSlice = []byteFormatter{97, 98, 99, 100}
var b byte
var fmtTests = []struct { var fmtTests = []struct {
fmt string fmt string
val interface{} val interface{}
...@@ -598,7 +600,7 @@ var fmtTests = []struct { ...@@ -598,7 +600,7 @@ var fmtTests = []struct {
// go syntax // go syntax
{"%#v", A{1, 2, "a", []int{1, 2}}, `fmt_test.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}`}, {"%#v", A{1, 2, "a", []int{1, 2}}, `fmt_test.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}`},
{"%#v", &b, "(*uint8)(0xPTR)"}, {"%#v", new(byte), "(*uint8)(0xPTR)"},
{"%#v", TestFmtInterface, "(func(*testing.T))(0xPTR)"}, {"%#v", TestFmtInterface, "(func(*testing.T))(0xPTR)"},
{"%#v", make(chan int), "(chan int)(0xPTR)"}, {"%#v", make(chan int), "(chan int)(0xPTR)"},
{"%#v", uint64(1<<64 - 1), "0xffffffffffffffff"}, {"%#v", uint64(1<<64 - 1), "0xffffffffffffffff"},
...@@ -729,31 +731,40 @@ var fmtTests = []struct { ...@@ -729,31 +731,40 @@ var fmtTests = []struct {
{"%10T", nil, " <nil>"}, {"%10T", nil, " <nil>"},
{"%-10T", nil, "<nil> "}, {"%-10T", nil, "<nil> "},
// %p // %p with pointers
{"p0=%p", new(int), "p0=0xPTR"}, {"%p", (*int)(nil), "0x0"},
{"p1=%s", &pValue, "p1=String(p)"}, // String method... {"%#p", (*int)(nil), "0"},
{"p2=%p", &pValue, "p2=0xPTR"}, // ... not called with %p {"%p", &intVar, "0xPTR"},
{"p3=%p", (*int)(nil), "p3=0x0"}, {"%#p", &intVar, "PTR"},
{"p4=%#p", new(int), "p4=PTR"}, {"%p", &array, "0xPTR"},
{"%p", &slice, "0xPTR"},
{"%8.2p", (*int)(nil), " 0x00"},
{"%-20.16p", &intVar, "0xPTR "},
// %p on non-pointers // %p on non-pointers
{"%p", make(chan int), "0xPTR"}, {"%p", make(chan int), "0xPTR"},
{"%p", make(map[int]int), "0xPTR"}, {"%p", make(map[int]int), "0xPTR"},
{"%p", make([]int, 1), "0xPTR"}, {"%p", func() {}, "0xPTR"},
{"%p", 27, "%!p(int=27)"}, // not a pointer at all {"%p", 27, "%!p(int=27)"}, // not a pointer at all
{"%p", nil, "%!p(<nil>)"}, // nil on its own has no type ...
// %q on pointers {"%#p", nil, "%!p(<nil>)"}, // ... and hence is not a pointer type.
{"%q", (*int)(nil), "%!q(*int=<nil>)"}, // pointers with specified base
{"%q", new(int), "%!q(*int=0xPTR)"}, {"%b", &intVar, "PTR_b"},
{"%d", &intVar, "PTR_d"},
// %v on pointers formats 0 as <nil> {"%o", &intVar, "PTR_o"},
{"%x", &intVar, "PTR_x"},
{"%X", &intVar, "PTR_X"},
// %v on pointers
{"%v", nil, "<nil>"},
{"%#v", nil, "<nil>"},
{"%v", (*int)(nil), "<nil>"}, {"%v", (*int)(nil), "<nil>"},
{"%v", new(int), "0xPTR"}, {"%#v", (*int)(nil), "(*int)(nil)"},
{"%v", &intVar, "0xPTR"},
// %d etc. pointers use specified base. {"%#v", &intVar, "(*int)(0xPTR)"},
{"%d", new(int), "PTR_d"}, {"%8.2v", (*int)(nil), " <nil>"},
{"%o", new(int), "PTR_o"}, {"%-20.16v", &intVar, "0xPTR "},
{"%x", new(int), "PTR_x"}, // string method on pointer
{"%s", &pValue, "String(p)"}, // String method...
{"%p", &pValue, "0xPTR"}, // ... is not called with %p.
// %d on Stringer should give integer if possible // %d on Stringer should give integer if possible
{"%s", time.Time{}.Month(), "January"}, {"%s", time.Time{}.Month(), "January"},
...@@ -961,6 +972,9 @@ var fmtTests = []struct { ...@@ -961,6 +972,9 @@ var fmtTests = []struct {
{"%☠", float32(1.2345678), "%!☠(float32=1.2345678)"}, {"%☠", float32(1.2345678), "%!☠(float32=1.2345678)"},
{"%☠", 1.2345678 + 1.2345678i, "%!☠(complex128=(1.2345678+1.2345678i))"}, {"%☠", 1.2345678 + 1.2345678i, "%!☠(complex128=(1.2345678+1.2345678i))"},
{"%☠", complex64(1.2345678 + 1.2345678i), "%!☠(complex64=(1.2345678+1.2345678i))"}, {"%☠", complex64(1.2345678 + 1.2345678i), "%!☠(complex64=(1.2345678+1.2345678i))"},
{"%☠", &intVar, "%!☠(*int=0xPTR)"},
{"%☠", make(chan int), "%!☠(chan int=0xPTR)"},
{"%☠", func() {}, "%!☠(func()=0xPTR)"},
} }
// zeroFill generates zero-filled strings of the specified width. The length // zeroFill generates zero-filled strings of the specified width. The length
...@@ -972,27 +986,37 @@ func zeroFill(prefix string, width int, suffix string) string { ...@@ -972,27 +986,37 @@ func zeroFill(prefix string, width int, suffix string) string {
func TestSprintf(t *testing.T) { func TestSprintf(t *testing.T) {
for _, tt := range fmtTests { for _, tt := range fmtTests {
s := Sprintf(tt.fmt, tt.val) s := Sprintf(tt.fmt, tt.val)
if i := strings.Index(tt.out, "PTR"); i >= 0 { i := strings.Index(tt.out, "PTR")
pattern := "PTR" if i >= 0 && i < len(s) {
chars := "0123456789abcdefABCDEF" var pattern, chars string
switch { switch {
case strings.HasPrefix(tt.out[i:], "PTR_d"): case strings.HasPrefix(tt.out[i:], "PTR_b"):
pattern = "PTR_d" pattern = "PTR_b"
chars = chars[:10] chars = "01"
case strings.HasPrefix(tt.out[i:], "PTR_o"): case strings.HasPrefix(tt.out[i:], "PTR_o"):
pattern = "PTR_o" pattern = "PTR_o"
chars = chars[:8] chars = "01234567"
case strings.HasPrefix(tt.out[i:], "PTR_d"):
pattern = "PTR_d"
chars = "0123456789"
case strings.HasPrefix(tt.out[i:], "PTR_x"): case strings.HasPrefix(tt.out[i:], "PTR_x"):
pattern = "PTR_x" pattern = "PTR_x"
} chars = "0123456789abcdef"
j := i case strings.HasPrefix(tt.out[i:], "PTR_X"):
for ; j < len(s); j++ { pattern = "PTR_X"
c := s[j] chars = "0123456789ABCDEF"
if !strings.ContainsRune(chars, rune(c)) { default:
pattern = "PTR"
chars = "0123456789abcdefABCDEF"
}
p := s[:i] + pattern
for j := i; j < len(s); j++ {
if !strings.ContainsRune(chars, rune(s[j])) {
p += s[j:]
break break
} }
} }
s = s[0:i] + pattern + s[j:] s = p
} }
if s != tt.out { if s != tt.out {
if _, ok := tt.val.(string); ok { if _, ok := tt.val.(string); ok {
......
...@@ -541,18 +541,6 @@ func (p *pp) fmtBytes(v []byte, verb rune, typeString string) { ...@@ -541,18 +541,6 @@ func (p *pp) fmtBytes(v []byte, verb rune, typeString string) {
} }
func (p *pp) fmtPointer(value reflect.Value, verb rune) { func (p *pp) fmtPointer(value reflect.Value, verb rune) {
use0x64 := true
switch verb {
case 'p', 'v':
// ok
case 'b', 'd', 'o', 'x', 'X':
use0x64 = false
// ok
default:
p.badVerb(verb)
return
}
var u uintptr var u uintptr
switch value.Kind() { switch value.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer: case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
...@@ -562,6 +550,8 @@ func (p *pp) fmtPointer(value reflect.Value, verb rune) { ...@@ -562,6 +550,8 @@ func (p *pp) fmtPointer(value reflect.Value, verb rune) {
return return
} }
switch verb {
case 'v':
if p.fmt.sharpV { if p.fmt.sharpV {
p.buf.WriteByte('(') p.buf.WriteByte('(')
p.buf.WriteString(value.Type().String()) p.buf.WriteString(value.Type().String())
...@@ -572,14 +562,19 @@ func (p *pp) fmtPointer(value reflect.Value, verb rune) { ...@@ -572,14 +562,19 @@ func (p *pp) fmtPointer(value reflect.Value, verb rune) {
p.fmt0x64(uint64(u), true) p.fmt0x64(uint64(u), true)
} }
p.buf.WriteByte(')') p.buf.WriteByte(')')
} else if verb == 'v' && u == 0 {
p.buf.WriteString(nilAngleString)
} else { } else {
if use0x64 { if u == 0 {
p.fmt0x64(uint64(u), !p.fmt.sharp) p.fmt.padString(nilAngleString)
} else { } else {
p.fmtUint64(uint64(u), verb) p.fmt0x64(uint64(u), !p.fmt.sharp)
}
} }
case 'p':
p.fmt0x64(uint64(u), !p.fmt.sharp)
case 'b', 'o', 'd', 'x', 'X':
p.fmtUint64(uint64(u), verb)
default:
p.badVerb(verb)
} }
} }
......
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