]> Cypherpunks repositories - gostls13.git/commitdiff
fmt: treat reflect.Value specially - as the value it holds
authorRob Pike <r@golang.org>
Thu, 9 Apr 2015 23:30:48 +0000 (16:30 -0700)
committerRob Pike <r@golang.org>
Wed, 15 Apr 2015 15:59:39 +0000 (15:59 +0000)
When a reflect.Value is passed to Printf (etc.), fmt called the
String method, which does not disclose its contents. To get the
contents, one could call Value.Interface(), but that is illegal
if the Value is not exported or otherwise forbidden.

This CL improves the situation with a trivial change to the
fmt package: when we see a reflect.Value as an argument,
we treat it exactly as we treat a reflect.Value we make inside
the package. This means that we always print the
contents of the Value as if _that_ was the argument to Printf.

This is arguably a breaking change but I think it is a genuine
improvement and no greater a break than many other tweaks
we have made to formatted output from this package.

Fixes #8965.

Change-Id: Ifc2a4ce3c1134ad5160e101d2196c22f1542faab
Reviewed-on: https://go-review.googlesource.com/8731
Reviewed-by: roger peppe <rogpeppe@gmail.com>
Reviewed-by: Russ Cox <rsc@golang.org>
src/fmt/doc.go
src/fmt/fmt_test.go
src/fmt/print.go
src/reflect/value.go

index 9ba11f4a2ba41ee9317731ac31ff5fe8781afefc..cbca6ab4920e6a4ff67bf92aff73280a77266a5a 100644 (file)
        formatting considerations apply for operands that implement
        certain interfaces. In order of application:
 
-       1. If an operand implements the Formatter interface, it will
+       1. If the operand is a reflect.Value, the concrete value it
+       holds is printed as if it was the operand.
+
+       2. If an operand implements the Formatter interface, it will
        be invoked. Formatter provides fine control of formatting.
 
-       2. If the %v verb is used with the # flag (%#v) and the operand
+       3. If the %v verb is used with the # flag (%#v) and the operand
        implements the GoStringer interface, that will be invoked.
 
        If the format (which is implicitly %v for Println etc.) is valid
        for a string (%s %q %v %x %X), the following two rules apply:
 
-       3. If an operand implements the error interface, the Error method
+       4. If an operand implements the error interface, the Error method
        will be invoked to convert the object to a string, which will then
        be formatted as required by the verb (if any).
 
-       4. If an operand implements method String() string, that method
+       5. If an operand implements method String() string, that method
        will be invoked to convert the object to a string, which will then
        be formatted as required by the verb (if any).
 
index 146977ace117c4086bd1a51af0c1ecbdaa647cb1..96505b82716abc926aaf72641ad3ca7e56735aa4 100644 (file)
@@ -9,6 +9,7 @@ import (
        . "fmt"
        "io"
        "math"
+       "reflect"
        "runtime"
        "strings"
        "testing"
@@ -679,6 +680,12 @@ var fmtTests = []struct {
        {"%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}"},
+
+       // reflect.Value handled specially in Go 1.5, making it possible to
+       // see inside non-exported fields (which cannot be accessed with Interface()).
+       // Issue 8965.
+       {"%v", reflect.ValueOf(A{}).Field(0).String(), "<int Value>"}, // Equivalent to the old way.
+       {"%v", reflect.ValueOf(A{}).Field(0), "0"},                    // Sees inside the field.
 }
 
 // zeroFill generates zero-filled strings of the specified width. The length
index 59a30d221e92aa55d0dbce98eba35a5936560ad6..c8038f09a8818b7ed0457a6387d1f3531aeabb7d 100644 (file)
@@ -789,6 +789,8 @@ func (p *pp) printArg(arg interface{}, verb rune, depth int) (wasString bool) {
        case []byte:
                p.fmtBytes(f, verb, nil, depth)
                wasString = verb == 's'
+       case reflect.Value:
+               return p.printReflectValue(f, verb, depth)
        default:
                // If the type is not simple, it might have methods.
                if handled := p.handleMethods(verb, depth); handled {
index 27f9c2dc8c0d87c74095a50eca7375b844f1c312..0b22efb027d9f619be78c4d8ccd03c11c0befc37 100644 (file)
@@ -1622,6 +1622,8 @@ func (v Value) Slice3(i, j, k int) Value {
 // String is a special case because of Go's String method convention.
 // Unlike the other getters, it does not panic if v's Kind is not String.
 // Instead, it returns a string of the form "<T value>" where T is v's type.
+// The fmt package treats Values specially. It does not call their String
+// method implicitly but instead prints the concrete values they hold.
 func (v Value) String() string {
        switch k := v.kind(); k {
        case Invalid: