diff --git a/CHANGELOG.md b/CHANGELOG.md index ca0eb65ca3d8..1ebfe58d60d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index f944e716e9aa..2f7032af87d0 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ed47b6a18576..7ff1925338d4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -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` @@ -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, @@ -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, ]); @@ -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![ diff --git a/clippy_lints/src/xor_used_as_pow.rs b/clippy_lints/src/xor_used_as_pow.rs new file mode 100644 index 000000000000..3388d0fe1d80 --- /dev/null +++ b/clippy_lints/src/xor_used_as_pow.rs @@ -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 { + 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()?" + ) + } + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1b838b17f7f7..198f4d9346d5 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -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", @@ -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", diff --git a/tests/ui/xor_used_as_pow.rs b/tests/ui/xor_used_as_pow.rs new file mode 100644 index 000000000000..4e0305d32e47 --- /dev/null +++ b/tests/ui/xor_used_as_pow.rs @@ -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); +} diff --git a/tests/ui/xor_used_as_pow.stderr b/tests/ui/xor_used_as_pow.stderr new file mode 100644 index 000000000000..dc3d6351908f --- /dev/null +++ b/tests/ui/xor_used_as_pow.stderr @@ -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 +