-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Add lint for use of ^ operator as pow. #4213
Conversation
b009aef
to
76dc98e
Compare
76dc98e
to
f9687e0
Compare
Citing #4205 (comment)
This lint should not trigger on hex or binary literals, since these are probably intended. |
println!("{}", 2 ^ 16); | ||
// Should be allowed | ||
let x = 16; | ||
println!("{}", 2 ^ x); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should also get linted, shouldn't it?
So? It's still very unlikely that someone intentionally wrote |
expr.span, | ||
"`^` is not an exponentiation operator but was used as one", | ||
"did you mean to write", | ||
format!("1 << {}", right), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the suggestion be about .pow()
instead? The premise is, quote, "^
is not an exponentiation operator but was used as one", so it might make more sense to suggest an actual exponentiation operator than a seemingly completely unrelated bit shift?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2^x == 1 << x
(That's only true for powers of 2). You don't want to use pow
for powers of 2, because of performance reasons.
If we add a suggestion for 10^x
, the lint should obviously suggest 10.pow(x)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nod, I'm aware of how the bitshift works for powers of two, was just wondering if that makes for a somewhat confusing suggestion - telling the user they probably intended exponentiation, and then suggesting a bitshift. Especially, but not limited to, if the mistake is made by a beginner, the suggestion might seem strange 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! I would suggest the bitshift, but write this in the documentation of the lint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Additionally, arguably, why should bitshift be more efficient than .pow()
, especially for known-at-compile-time values, shouldn't that be a compiler optimization that the programmer doesn't need to worry about? 🤔 )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another interesting thought here for consideration, perhaps recommending the std::_::MAX
values, if applicable 🤔 rust-lang/rust#61934 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't that be a compiler optimization
Yeah it should be one for pow(some_power_of_two, x)
. But using a bitshift is always performant (and a pretty standard thing to do). Relying on the compiler might not always produce performant code. And Clippy should always suggest the best possible code IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I end up picking this up (assuming that's desired or needed), would it be worthwhile to put out a different error based on whether the number is 2 or not? If it's two, output the suggestion of bitshift, if it's not, mention pow
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need an extra lint for this, I think. But you can do something like:
// NOTE: This is pseudo code and the messages need improvement. ;)
if lhs == 2 {
span_lint(LINT, "this looks like you want to pow", "try bitshift");
} else {
span_lint(LINT, "..", "try pow");
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's akin to what I was thinking. I think I'll also look at incorporating Walther's suggestion (and the comment from this thread) to recommend std::_::MAX
in the case of rhs being an Int literal of 8, 16, 32, or 64.
if let LitKind::Int(2, _) = lit.node; | ||
if let ExprKind::Lit(lit) = &right.node; | ||
if let LitKind::Int(right, _) = lit.node; | ||
then { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also make sure that it doesn't trigger in external macros (using in_external_macro
)
☔ The latest upstream changes (presumably #4259) made this pull request unmergeable. Please resolve the merge conflicts. |
@xfix what's the status of this PR? |
@flip1995 @kornelski As this has been closed as inactive, does anybody mind if I pick this up and run with it? If that sounds good, with this PR being unmergeable, is it worth trying to pull in xfix's branch and continue forward, or should I go ahead and just create a fresh branch to make these changes and address some of the comments at the same time? |
Sure, you can pick it up, thanks! I think the best thing to do would be to start with the current clippy master branch and cherry-pick the commit of @xfix on top of it*. The merge conflicts should only be in files with auto generated code (with * if you need help on how to do this, let me know. :) |
Thanks Philipp. Got the commit cherry-picked just fine. You were correct that the only conflicts were from auto-generated files. I'll get a PR opened tomorrow (Saturday) for this. |
Resolves #4205
Inspired by https://twitter.com/jfbastien/status/1139298419988549632. Only checking for
2^
is intentional, I was considering checking for10^
as well, but more often than not, the incorrect code is using2^
.changelog: Add lint for using xor operator when exponentiation was intended.