Skip to content

Commit

Permalink
attributes: format invalid argument for '%p' to string in style `<typ…
Browse files Browse the repository at this point in the history
…e=value>`, not `%!verb(type=value)`

#6574 (comment)
  • Loading branch information
searKing committed Aug 30, 2023
1 parent 1114f39 commit dbe2270
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 10 deletions.
53 changes: 50 additions & 3 deletions attributes/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ func (a *Attributes) String() string {
return sb.String()
}

const nilAngleString = "<nil>"

func str(x any) (s string) {
defer func() {
if r := recover(); r != nil {
Expand All @@ -131,7 +133,7 @@ func str(x any) (s string) {
//
// Adapted from the code in fmt/print.go.
if v := reflect.ValueOf(x); v.Kind() == reflect.Pointer && v.IsNil() {
s = "<nil>"
s = nilAngleString
return
}

Expand All @@ -140,13 +142,58 @@ func str(x any) (s string) {
}
}()
if x == nil { // NOTE: typed nils will not be caught by this check
return "<nil>"
return nilAngleString
} else if v, ok := x.(fmt.Stringer); ok {
return v.String()
} else if v, ok := x.(string); ok {
return v
}
return fmt.Sprintf("<%p>", x)
value := reflect.ValueOf(x)
switch value.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.Slice, reflect.UnsafePointer:
return fmt.Sprintf("<%p>", x)
default:
// This will call badVerb to print as "<%p>", but without leading "%!(" and tailing ")"
return badVerb(x, value)
}
}

// badVerb is like fmt.Sprintf("%p", arg), but with
// leading "%!verb(" replaced by "<" and tailing ")" replaced by ">".
// If an invalid argument is given for a '%p', such as providing
// an int to %p, the generated string will contain a
// description of the problem, as in these examples:
//
// # our style
//
// Wrong type or unknown verb: <type=value>
// Printf("%p", 1): <int=1>
//
// # fmt style as `fmt.Sprintf("%p", arg)`
//
// Wrong type or unknown verb: %!verb(type=value)
// Printf("%p", 1): %!d(int=1)
//
// Adapted from the code in fmt/print.go.
func badVerb(arg any, value reflect.Value) string {
var buf strings.Builder
switch {
case arg != nil:
buf.WriteByte('<')
buf.WriteString(reflect.TypeOf(arg).String())
buf.WriteByte('=')
_, _ = fmt.Fprintf(&buf, "%v", arg)
buf.WriteByte('>')
case value.IsValid():
buf.WriteByte('<')
buf.WriteString(value.Type().String())
buf.WriteByte('=')
_, _ = fmt.Fprintf(&buf, "%v", 0)
buf.WriteByte('>')
default:
buf.WriteString(nilAngleString)
}
return buf.String()
}

// MarshalJSON helps implement the json.Marshaler interface, thereby rendering
Expand Down
17 changes: 10 additions & 7 deletions attributes/attributes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,24 @@ func ExampleAttributes_String() {
a5 := attributes.New(key{}, 1)
a6 := attributes.New(key{}, stringerVal{s: "two"})
a7 := attributes.New(key{}, stringVal{s: "two"})
a8 := attributes.New(1, true)
fmt.Println("a1:", a1.String())
fmt.Println("a2:", a2.String())
fmt.Println("a3:", a3.String())
fmt.Println("a4:", a4.String())
fmt.Println("a5:", a5.String())
fmt.Println("a6:", a6.String())
fmt.Println("a7:", a7.String())
fmt.Println("a8:", a8.String())
// Output:
// a1: {"<%!p(attributes_test.key={})>": "<nil>" }
// a2: {"<%!p(attributes_test.key={})>": "<nil>" }
// a3: {"<%!p(attributes_test.key={})>": "<0x0>" }
// a4: {"<%!p(attributes_test.key={})>": "<nil>" }
// a5: {"<%!p(attributes_test.key={})>": "<%!p(int=1)>" }
// a6: {"<%!p(attributes_test.key={})>": "two" }
// a7: {"<%!p(attributes_test.key={})>": "<%!p(attributes_test.stringVal={two})>" }
// a1: {"<attributes_test.key={}>": "<nil>" }
// a2: {"<attributes_test.key={}>": "<nil>" }
// a3: {"<attributes_test.key={}>": "<0x0>" }
// a4: {"<attributes_test.key={}>": "<nil>" }
// a5: {"<attributes_test.key={}>": "<int=1>" }
// a6: {"<attributes_test.key={}>": "two" }
// a7: {"<attributes_test.key={}>": "<attributes_test.stringVal={two}>" }
// a8: {"<int=1>": "<bool=true>" }
}

// Test that two attributes with the same content are Equal.
Expand Down

0 comments on commit dbe2270

Please sign in to comment.