dominikh / go-tools

Staticcheck - The advanced Go linter
https://staticcheck.dev
MIT License
6.11k stars 368 forks source link

simple: suggest # flag in format strings when user manually recreates its effect #256

Open dmitshur opened 6 years ago

dmitshur commented 6 years ago

I recently (re)discovered a neat simplification to some of my format strings, because I forgot that the same effect could be achieved more simply with the # flag.

For example, I was doing things like:

fmt.Errorf("invalid signature: 0x%x", hdr.Signature)
// Which can be simplified to:
fmt.Errorf("invalid signature: %#x", hdr.Signature)

Note, it could (probably) also apply even if there are other flags being used. E.g.:

fmt.Printf("signature           0x%08x\n", hdr.Signature)
// Can also be simplified to:
fmt.Printf("signature           %#08x\n", hdr.Signature)
// Or:
fmt.Printf("signature           %0#8x\n", hdr.Signature)

And perhaps also for octal numbers:

fmt.Printf("perm: 0%v\n", fi.Mode().Perm())
fmt.Printf("perm: %#v\n", fi.Mode().Perm())

There's a chance it would be helpful if a tool could discover and make such suggestions. This issue is to consider that.

dgryski commented 6 years ago

I think even just 0x%x detection would be a win. I can't count the number of times I've written that both with and without 8/16 width zero padding.

dominikh commented 6 years ago

I am wondering if %#x is objectively better than 0x%x or if it's a personal choice.

dmitshur commented 6 years ago

I think it can argued that it's objectively better/simpler (in the general case).

The # flag is a part of the fmt package of the standard library of Go 1. The purpose/effect of using it on the %x verb is to add a 0x prefix:

#   alternate format: add leading 0 for octal (%#o), 0x for hex (%#x);
    0X for hex (%#X); suppress 0x for %p (%#p);

Given the two choices of using an existing feature of the standard library (especially since the rest of fmt functionality is already being used) or re-implementing the same feature yourself, I'd imagine it's better to do the former.

Similarly, if one wishes to use fmt to print a floating-point value with width 9, precision 2, it's better to just use %9.2f than to try to reimplement that same functionality in user code.

The only reason it's a harder decision for %#x vs 0x%x is because the custom implementation happens to be as easy as manually typing 2 characters rather than a much more significant amount of work.

dominikh commented 6 years ago

In the Go standard library, there are 74 matches for "0x%x" and 286 for "%#x", which makes this decision less obvious. Furthermore, it can be argued that "0x%x" is simpler to understand than "%#x" with regard to its outcome. I'll definitely need to think about this some more.

Another statistic: my Go corpus has 10126 matches for "0x%x" and 10390 for "%#x". Those numbers by themself don't mean anything, though.