golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
124k stars 17.67k forks source link

fmt: error verb rune 'w', overriding/breaking fmt.formatter implementation #56562

Open splace opened 2 years ago

splace commented 2 years ago

https://go.dev/play/p/eM3d8y2L768

What did you expect to see?

s
w

What did you see instead?

s
%!w(main.T2={})
robpike commented 2 years ago

This looks like a bug in the checking of 'w', which occurs before seeing if the type implements Formatter.

neild commented 2 years ago

fmt.Errorf will convert 'w' to 'v' before calling a Format method, since 'w' is intended to be a synonym for 'v' other than indicating that an operand be wrapped. We didn't want existing errors that implement Formatter to need to check for 'w'.

There's also a vet check that verifies that the operand to 'w' is an error, and that '%w' is not used with any formatting function other than Errorf.

Perhaps the bug is that we don't document that it is not valid to use '%w' with any function other than Errorf.

splace commented 2 years ago

This looks like a bug in the checking of 'w', which occurs before seeing if the type implements Formatter.

reported because seemed like it had to be that, because its hidden by its rarity and also because it didn't seem too 'invasive' to fix.

ericlagergren commented 2 years ago

Perhaps the bug is that we don't document that it is not valid to use '%w' with any function other than Errorf.

Why is '%w' special in this regard?

neild commented 2 years ago

'%w' is the error wrapping verb. It indicates that fmt.Errorf should return an error that unwraps to the target of '%w', and is otherwise a synonym for '%v'.

Passing '%w' to any formatting function other than fmt.Errorf is generally indicative of a mistake; either the user intended to wrap the error (in which case they need to use Errorf) or they didn't (in which case '%v' is a clearer indication of intent). There's a vet check for passing '%w' to a formatter other than Errorf, so I'd expect this to occur rarely if ever in practice.)

splace commented 1 year ago

just tried to use rune 'p' in a bespoke formatter (for percentage)

also seems to be blocked from working because of its use in the non-overridden default code. as pointer type formatter!

why implementing fmt.Formatter doesn't simply override the default is surprising.