Skip to content
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 exponentiation. #4541

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,7 @@ Released 2018-09-13
[`wrong_pub_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_pub_self_convention
[`wrong_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention
[`wrong_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_transmute
[`xor_used_as_pow`]: https://rust-lang.github.io/rust-clippy/master/index.html#xor_used_as_pow
[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.

[There are 317 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
[There are 318 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)

We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:

Expand Down
4 changes: 4 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ pub mod use_self;
pub mod vec;
pub mod wildcard_dependencies;
pub mod write;
pub mod xor_used_as_pow;
pub mod zero_div_zero;
// end lints modules, do not remove this comment, it’s used in `update_lints`

Expand Down Expand Up @@ -602,6 +603,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
reg.register_late_lint_pass(box inherent_to_string::InherentToString);
reg.register_late_lint_pass(box trait_bounds::TraitBounds);
reg.register_late_lint_pass(box comparison_chain::ComparisonChain);
reg.register_early_lint_pass(box xor_used_as_pow::XorUsedAsPow);

reg.register_lint_group("clippy::restriction", Some("clippy_restriction"), vec![
arithmetic::FLOAT_ARITHMETIC,
Expand Down Expand Up @@ -923,6 +925,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
write::WRITELN_EMPTY_STRING,
write::WRITE_LITERAL,
write::WRITE_WITH_NEWLINE,
xor_used_as_pow::XOR_USED_AS_POW,
zero_div_zero::ZERO_DIVIDED_BY_ZERO,
]);

Expand Down Expand Up @@ -1152,6 +1155,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
unicode::ZERO_WIDTH_SPACE,
unused_io_amount::UNUSED_IO_AMOUNT,
unwrap::PANICKING_UNWRAP,
xor_used_as_pow::XOR_USED_AS_POW,
]);

reg.register_lint_group("clippy::perf", Some("clippy_perf"), vec![
Expand Down
77 changes: 77 additions & 0 deletions clippy_lints/src/xor_used_as_pow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use crate::utils::{span_help_and_lint, span_lint_and_sugg};
use if_chain::if_chain;
use rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;
use syntax::ast::{BinOpKind, Expr, ExprKind, LitKind};

declare_clippy_lint! {
/// **What it does:** Checks for use of `^` operator when exponentiation was intended.
///
/// **Why is this bad?** This is most probably a typo.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust,ignore
/// // Bad
/// 2 ^ 16;
///
/// // Good
/// 1 << 16;
/// 2i32.pow(16);
/// ```
pub XOR_USED_AS_POW,
correctness,
"use of `^` operator when exponentiation was intended"
}

declare_lint_pass!(XorUsedAsPow => [XOR_USED_AS_POW]);

impl EarlyLintPass for XorUsedAsPow {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
if_chain! {
if !in_external_macro(cx.sess, expr.span);
if let ExprKind::Binary(op, left, right) = &expr.node;
if BinOpKind::BitXor == op.node;
if let ExprKind::Lit(lit) = &left.node;
if let LitKind::Int(lhs, _) = lit.node;
if let ExprKind::Lit(lit) = &right.node;
if let LitKind::Int(rhs, _) = lit.node;
then {
if lhs == 2 {
if rhs == 8 || rhs == 16 || rhs == 32 || rhs == 64 {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we account for u128 as well?

span_lint_and_sugg(
cx,
XOR_USED_AS_POW,
expr.span,
"it appears you are trying to get the maximum value of an integer, but `^` is not an exponentiation operator",
"try",
format!("std::u{}::MAX", rhs),
Applicability::MaybeIncorrect,
)
} else {
span_lint_and_sugg(
cx,
XOR_USED_AS_POW,
expr.span,
"it appears you are trying to get a power of two, but `^` is not an exponentiation operator",
"use a bitshift instead",
format!("1 << {}", rhs),
Applicability::MaybeIncorrect,
)
}
} else {
span_help_and_lint(
cx,
XOR_USED_AS_POW,
expr.span,
"`^` is not an exponentiation operator but appears to have been used as one",
"did you mean to use .pow()?"
)
}
}
}
}
}
9 changes: 8 additions & 1 deletion src/lintlist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub use lint::Lint;
pub use lint::LINT_LEVELS;

// begin lint list, do not remove this comment, it’s used in `update_lints`
pub const ALL_LINTS: [Lint; 317] = [
pub const ALL_LINTS: [Lint; 318] = [
Lint {
name: "absurd_extreme_comparisons",
group: "correctness",
Expand Down Expand Up @@ -2198,6 +2198,13 @@ pub const ALL_LINTS: [Lint; 317] = [
deprecation: None,
module: "transmute",
},
Lint {
name: "xor_used_as_pow",
group: "correctness",
desc: "use of `^` operator when exponentiation was intended",
deprecation: None,
module: "xor_used_as_pow",
},
Lint {
name: "zero_divided_by_zero",
group: "complexity",
Expand Down
15 changes: 15 additions & 0 deletions tests/ui/xor_used_as_pow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#![warn(clippy::xor_used_as_pow)]

fn main() {
// These should succeed
// With variables, it's not as clear whether the intention was exponentiation or not
let x = 15;
println!("{}", 2 ^ x);
let y = 2;
println!("{}", y ^ 16);

// These should fail
println!("{}", 2 ^ 16);
println!("{}", 2 ^ 7);
println!("{}", 9 ^ 3);
}
24 changes: 24 additions & 0 deletions tests/ui/xor_used_as_pow.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error: it appears you are trying to get the maximum value of an integer, but `^` is not an exponentiation operator
--> $DIR/xor_used_as_pow.rs:12:20
|
LL | println!("{}", 2 ^ 16);
| ^^^^^^ help: try: `std::u16::MAX`
|
= note: `-D clippy::xor-used-as-pow` implied by `-D warnings`

error: it appears you are trying to get a power of two, but `^` is not an exponentiation operator
--> $DIR/xor_used_as_pow.rs:13:20
|
LL | println!("{}", 2 ^ 7);
| ^^^^^ help: use a bitshift instead: `1 << 7`

error: `^` is not an exponentiation operator but appears to have been used as one
--> $DIR/xor_used_as_pow.rs:14:20
|
LL | println!("{}", 9 ^ 3);
| ^^^^^
|
= help: did you mean to use .pow()?

error: aborting due to 3 previous errors