New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cmd/vet: false positive printf-like function detection for github.com/leonelquinteros/gotext #57288
Comments
(BTW, as for why I use gotext and not x/text/messages - I really insist on a file format with lots of good editing tools such as gettext's AND need to load them from an in-game VFS [possibly the user's file system just like other game data]; I couldn't figure out how to do that with x/text/messages). |
How exactly would you propose to fix this? |
IIRC go vet detects printf-like functions by being a transitive caller of a
printf function.
So maybe not detect the function as a printf function if the call to a
printf function is conditional?
…On Tue, Dec 13, 2022, 09:25 Than McIntosh ***@***.***> wrote:
How exactly would you propose to fix this?
—
Reply to this email directly, view it on GitHub
<#57288 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAB5NMHIFSYANPYYQRW3PDDWNCBMVANCNFSM6AAAAAAS5IE6GI>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
#57288 (comment) I think comes down to whether
I am curious what the author of this code (@leonelquinteros) thinks the decision should be. Is "gotext.Printf" a wrapper of "fmt.Sprintf" and it is desired for This does not necessarily dictate what vet should do, but I am curious. ** I doubt a
Right now the
Given the relative rarity of the issue, I think we should test candidate solutions before making a decision.
@divVerent An alternative stopgap is to have a function that
Not a great solution, but it may be preferable to |
Instead of using an artificial format string, I am now accessing the Get method on the Po object via the Translator interface. Accessing via an interface makes it impossible for go vet to detect if it's a printf-like function, thereby evading the false positives due to Get not acting like printf if it only gets one arg.
Obscuring the format string with an id() wrapper or similar won't work as then However I found a workaround - it seems like This is still dirty and against the philosophy in #17058 to NOT require explicit annotations of any form to evade false positives - instead go vet should not HAVE false positives. |
What speaks AGAINST skipping printf detection if the function is conditional is, of course, constructs like
(this SHOULD count as printf-like) from
(this is what this package is doing) The only real difference is that here in theory an analyzer could go through the entire thing and notice that the comparison is only based on the arguments and literals, while in the log.V case, a flag or otherwise external value is involved and the analyzer can prove nothing. |
The verbosity example you gave would be blocked by all of my suggestions. So none of those seem like the right call.
It might just be reading args or format in a context other than a matching call? Maybe it is just using one before a matching call? Another distinguishing characteristic is Worth also pointing out that someof printf's unit tests would likely need to be modified to support the types of restrictions needed: https://cs.opensource.google/go/x/tools/+/refs/tags/v0.4.0:go/analysis/passes/printf/testdata/src/b/b.go |
Another thing one could do is to take the name of the argument into account - and e.g. not vet if it is named OTOH, I would actually want most calls to Get be vetted here. If really no solution is feasible, I will maybe suggest to the author of the package to add four more methods (GetL, GetLN, GetLC, GetLNC) where L means literal (takes no further arguments), to be used when not formatting. Sadly they can't ditch the "1 arg means no formatting" rule anymore as they too have dependencies (but could introduce the literal versions now and ditch the 0/n distinction in next major version). For myself I have a working workaround now, although only thanks to lucky circumstances (that interface the package defines primarily so it can use both PO and MO files). |
Hello,
For the specific needs of validating format strings and function calls inside That said, I'm not sure how |
That sounds good - Based on the above, |
Related #26555. |
An alternative to |
It is a printf wrapper, since it may forward its arguments to printf. It does so only conditionally, but that's true of a lot of printf wrappers, such as loggers with a verbosity control. The only problem I see is that gotext.Printf interprets its arguments inconsistently: sometimes as a format string and sometimes literally. This seems just as likely to confuse human readers as mechanical ones: if a Go programmer calls a function named Printf and wants to see the output "100%", they will follow Go conventions and pass the argument I suggest you change gotext.Printf to delegate to fmt.Sprintf unconditionally. Or remove the "f" suffix and change its behavior to delegate to fmt.Print. Or if neither of those is appealing, change the function body to hide from vet the fact that it delegates. |
I disagree with this solution - yes, the function called Printf really should not be called that. But the functions I was about are NOT called Printf. They are called Get, GetC, GetN, GetNC. And apparently the author intended them to not take format strings if there is just one arg. In a /v3 of the package that may be something to reconsider - how do we handle this in /v2 though? Suggestion: can we maybe have a global variable (with a function to enable this mode) in the package that enables always-formatstring handling so I can migrate my project to that? |
It's not just the name, it's the behavior of the function that's the problem. 'Get' interprets its 'format' argument inconsistently: sometimes as a format string, and sometimes not. The convention throughout the Go libraries is that functions choose one or the other (and ideally reflect this choice in their name).
I'm asserting that this was a design mistake, since it confuses not only vet, but human readers too. (Perhaps vet should attempt to report "function interprets format string argument inconsistently"?)
If you want to disable vet checking for this function, then change its body as described above to obscure the delegation to printf. Or you could split Get into two functions, one Printf-like and one Print-like. If you can't change Get itself, you can write two wrappers, and use them instead. You can add
I'm not sure what you mean. If you can change the function, you can trick vet into treating it as as printf wrapper or not. If you can't change the function, there is no way to change vet's interpretation.
To be clear, I agree, it is sad that an API design mistake in one of your dependencies causes vet to report a false positive. In effect, vet imposes a stronger type on Get than Go itself, and the type of Get cannot be expressed in this stronger type system, and, like any type system, it rejects some programs that would run just fine. But since there is a design mistake in the code and there are simple workarounds, I don't think it's worth trying to change vet to accommodate this case. |
So the idea is that leonelquinteros puts in the ""+format hack into the library's /v2 as it is right now, and /v3 will use separate functions for literals and format string. @leonelquinteros is that OK with you? |
@divVerent I'm not sure about maintaining multiple versions of the package just for this change. About the design decision, it's not a mistake, it was made on purpose and it's documented, in fact, it's the only documentation for that function: https://pkg.go.dev/github.com/leonelquinteros/gotext#Printf , so readers should be safe. As I said before, if you need to make |
I can foresee a PRs are always welcome though :) we can start in a branch for your specific use case and move from there |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes: https://github.com/divVerent/aaaaxy/actions/runs/3685154878
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
Used "go vet" on a program that calls Get("100%") on a Po object. The method is defined here:
https://github.com/leonelquinteros/gotext/blob/6998e37f95499dad532f617cb0ff07fdbabc6ef7/po.go#L83
What did you expect to see?
This is valid, as the call goes through
https://github.com/leonelquinteros/gotext/blob/6998e37f95499dad532f617cb0ff07fdbabc6ef7/domain.go#L252
https://github.com/leonelquinteros/gotext/blob/6998e37f95499dad532f617cb0ff07fdbabc6ef7/helper.go#L37
I.e. this is only a printf-ish function if receiving more than one arg - if exactly one arg is provided, it is NOT interpreted as a format string.
What did you see instead?
Looks like as a workaround I have to call
po.Get("100%s", "%")
to get both the correct output AND no go vet errors. This is kinda sad. Not so great for the translator, who will needlessly see a format string.
On #17058 it has been agreed that we want NO annotation mechanism to silence vet warnings - so I guess this means we need to fix this false positive instead.
The text was updated successfully, but these errors were encountered: