@@ -122,6 +122,8 @@ func (a *Attributes) String() string {
122122 return sb .String ()
123123}
124124
125+ const nilAngleString = "<nil>"
126+
125127func str (x any ) (s string ) {
126128 defer func () {
127129 if r := recover (); r != nil {
@@ -131,7 +133,7 @@ func str(x any) (s string) {
131133 //
132134 // Adapted from the code in fmt/print.go.
133135 if v := reflect .ValueOf (x ); v .Kind () == reflect .Pointer && v .IsNil () {
134- s = "<nil>"
136+ s = nilAngleString
135137 return
136138 }
137139
@@ -140,13 +142,58 @@ func str(x any) (s string) {
140142 }
141143 }()
142144 if x == nil { // NOTE: typed nils will not be caught by this check
143- return "<nil>"
145+ return nilAngleString
144146 } else if v , ok := x .(fmt.Stringer ); ok {
145147 return v .String ()
146148 } else if v , ok := x .(string ); ok {
147149 return v
148150 }
149- return fmt .Sprintf ("<%p>" , x )
151+ value := reflect .ValueOf (x )
152+ switch value .Kind () {
153+ case reflect .Chan , reflect .Func , reflect .Map , reflect .Pointer , reflect .Slice , reflect .UnsafePointer :
154+ return fmt .Sprintf ("<%p>" , x )
155+ default :
156+ // This will call badVerb to print as "<%p>", but without leading "%!(" and tailing ")"
157+ return badVerb (x , value )
158+ }
159+ }
160+
161+ // badVerb is like fmt.Sprintf("%p", arg), but with
162+ // leading "%!verb(" replaced by "<" and tailing ")" replaced by ">".
163+ // If an invalid argument is given for a '%p', such as providing
164+ // an int to %p, the generated string will contain a
165+ // description of the problem, as in these examples:
166+ //
167+ // # our style
168+ //
169+ // Wrong type or unknown verb: <type=value>
170+ // Printf("%p", 1): <int=1>
171+ //
172+ // # fmt style as `fmt.Sprintf("%p", arg)`
173+ //
174+ // Wrong type or unknown verb: %!verb(type=value)
175+ // Printf("%p", 1): %!d(int=1)
176+ //
177+ // Adapted from the code in fmt/print.go.
178+ func badVerb (arg any , value reflect.Value ) string {
179+ var buf strings.Builder
180+ switch {
181+ case arg != nil :
182+ buf .WriteByte ('<' )
183+ buf .WriteString (reflect .TypeOf (arg ).String ())
184+ buf .WriteByte ('=' )
185+ _ , _ = fmt .Fprintf (& buf , "%v" , arg )
186+ buf .WriteByte ('>' )
187+ case value .IsValid ():
188+ buf .WriteByte ('<' )
189+ buf .WriteString (value .Type ().String ())
190+ buf .WriteByte ('=' )
191+ _ , _ = fmt .Fprintf (& buf , "%v" , 0 )
192+ buf .WriteByte ('>' )
193+ default :
194+ buf .WriteString (nilAngleString )
195+ }
196+ return buf .String ()
150197}
151198
152199// MarshalJSON helps implement the json.Marshaler interface, thereby rendering
0 commit comments