about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-10-31 00:28:59 +0000
committerbors <bors@rust-lang.org>2022-10-31 00:28:59 +0000
commit37d338c1efd7840fa96c5eaeaa5785094efdab07 (patch)
treee4bd279df1ba7588c6bdb59f315879c836f07bb7
parent60d171873a761feeb2270be248a944a65fa658aa (diff)
parent151395d74e703600a724ecfc219c3f0fb1fe384a (diff)
downloadrust-37d338c1efd7840fa96c5eaeaa5785094efdab07.tar.gz
rust-37d338c1efd7840fa96c5eaeaa5785094efdab07.zip
Auto merge of #9506 - blyxyas:master, r=giraffate
Add lint for confusing use of `^` instead of `.pow`

fixes #4205
Adds a lint named [`confusing_xor_and_pow`], it warns the user when `a ^ b` is used as the `.pow()` function, it doesn't warn for Hex, Binary... etc.

---

changelog: New lint: [`confusing_xor_and_pow`]
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/declared_lints.rs1
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/suspicious_xor_used_as_pow.rs53
-rw-r--r--tests/ui/eq_op.rs1
-rw-r--r--tests/ui/eq_op.stderr56
-rw-r--r--tests/ui/suspicious_xor_used_as_pow.rs34
-rw-r--r--tests/ui/suspicious_xor_used_as_pow.stderr51
8 files changed, 171 insertions, 28 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b47e80f6979..35864e85673 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4251,6 +4251,7 @@ Released 2018-09-13
 [`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
 [`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned
 [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
+[`suspicious_xor_used_as_pow`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_xor_used_as_pow
 [`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref
 [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
 [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index fff26307e34..f4fc2bd3330 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -544,6 +544,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO,
     crate::suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL_INFO,
     crate::suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL_INFO,
+    crate::suspicious_xor_used_as_pow::SUSPICIOUS_XOR_USED_AS_POW_INFO,
     crate::swap::ALMOST_SWAPPED_INFO,
     crate::swap::MANUAL_SWAP_INFO,
     crate::swap_ptr_to_ref::SWAP_PTR_TO_REF_INFO,
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 01da11958e2..197d8f2dc08 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -268,6 +268,7 @@ mod strings;
 mod strlen_on_c_strings;
 mod suspicious_operation_groupings;
 mod suspicious_trait_impl;
+mod suspicious_xor_used_as_pow;
 mod swap;
 mod swap_ptr_to_ref;
 mod tabs_in_doc_comments;
@@ -916,6 +917,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields));
     store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods));
     store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
+    store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_lints/src/suspicious_xor_used_as_pow.rs b/clippy_lints/src/suspicious_xor_used_as_pow.rs
new file mode 100644
index 00000000000..301aa5798bf
--- /dev/null
+++ b/clippy_lints/src/suspicious_xor_used_as_pow.rs
@@ -0,0 +1,53 @@
+use clippy_utils::{numeric_literal::NumericLiteral, source::snippet_with_context};
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Warns for a Bitwise XOR (`^`) operator being probably confused as a powering. It will not trigger if any of the numbers are not in decimal.
+    /// ### Why is this bad?
+    ///	It's most probably a typo and may lead to unexpected behaviours.
+    /// ### Example
+    /// ```rust
+    /// let x = 3_i32 ^ 4_i32;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let x = 3_i32.pow(4);
+    /// ```
+    #[clippy::version = "1.66.0"]
+    pub SUSPICIOUS_XOR_USED_AS_POW,
+    restriction,
+    "XOR (`^`) operator possibly used as exponentiation operator"
+}
+declare_lint_pass!(ConfusingXorAndPow => [SUSPICIOUS_XOR_USED_AS_POW]);
+
+impl LateLintPass<'_> for ConfusingXorAndPow {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        if !in_external_macro(cx.sess(), expr.span) &&
+        	let ExprKind::Binary(op, left, right) = &expr.kind &&
+            op.node == BinOpKind::BitXor &&
+            left.span.ctxt() == right.span.ctxt() &&
+            let ExprKind::Lit(lit_left) = &left.kind &&
+            let ExprKind::Lit(lit_right) = &right.kind &&
+            let snip_left = snippet_with_context(cx, lit_left.span, lit_left.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) &&
+            let snip_right = snippet_with_context(cx, lit_right.span, lit_right.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) &&
+            let Some(left_val) = NumericLiteral::from_lit_kind(&snip_left.0, &lit_left.node) &&
+            let Some(right_val) = NumericLiteral::from_lit_kind(&snip_right.0, &lit_right.node) &&
+			left_val.is_decimal() &&
+			right_val.is_decimal() {
+					clippy_utils::diagnostics::span_lint_and_sugg(
+					        cx,
+					        SUSPICIOUS_XOR_USED_AS_POW,
+					        expr.span,
+					        "`^` is not the exponentiation operator",
+					        "did you mean to write",
+					        format!("{}.pow({})", left_val.format(), right_val.format()),
+					        Applicability::MaybeIncorrect,
+					    );
+		}
+    }
+}
diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs
index 422f9486503..e7379550265 100644
--- a/tests/ui/eq_op.rs
+++ b/tests/ui/eq_op.rs
@@ -2,6 +2,7 @@
 
 #![warn(clippy::eq_op)]
 #![allow(clippy::double_parens, clippy::identity_op, clippy::nonminimal_bool)]
+#![allow(clippy::suspicious_xor_used_as_pow)]
 
 fn main() {
     // simple values and comparisons
diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr
index 313ceed2b41..d365ab27edc 100644
--- a/tests/ui/eq_op.stderr
+++ b/tests/ui/eq_op.stderr
@@ -1,5 +1,5 @@
 error: equal expressions as operands to `==`
-  --> $DIR/eq_op.rs:8:13
+  --> $DIR/eq_op.rs:9:13
    |
 LL |     let _ = 1 == 1;
    |             ^^^^^^
@@ -7,163 +7,163 @@ LL |     let _ = 1 == 1;
    = note: `-D clippy::eq-op` implied by `-D warnings`
 
 error: equal expressions as operands to `==`
-  --> $DIR/eq_op.rs:9:13
+  --> $DIR/eq_op.rs:10:13
    |
 LL |     let _ = "no" == "no";
    |             ^^^^^^^^^^^^
 
 error: equal expressions as operands to `!=`
-  --> $DIR/eq_op.rs:11:13
+  --> $DIR/eq_op.rs:12:13
    |
 LL |     let _ = false != false;
    |             ^^^^^^^^^^^^^^
 
 error: equal expressions as operands to `<`
-  --> $DIR/eq_op.rs:12:13
+  --> $DIR/eq_op.rs:13:13
    |
 LL |     let _ = 1.5 < 1.5;
    |             ^^^^^^^^^
 
 error: equal expressions as operands to `>=`
-  --> $DIR/eq_op.rs:13:13
+  --> $DIR/eq_op.rs:14:13
    |
 LL |     let _ = 1u64 >= 1u64;
    |             ^^^^^^^^^^^^
 
 error: equal expressions as operands to `&`
-  --> $DIR/eq_op.rs:16:13
+  --> $DIR/eq_op.rs:17:13
    |
 LL |     let _ = (1u32 as u64) & (1u32 as u64);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: equal expressions as operands to `^`
-  --> $DIR/eq_op.rs:19:17
+  --> $DIR/eq_op.rs:20:17
    |
 LL |         let _ = 1 ^ ((((((1))))));
    |                 ^^^^^^^^^^^^^^^^^
 
 error: equal expressions as operands to `<`
-  --> $DIR/eq_op.rs:23:13
+  --> $DIR/eq_op.rs:24:13
    |
 LL |     let _ = (-(2) < -(2));
    |             ^^^^^^^^^^^^^
 
 error: equal expressions as operands to `==`
-  --> $DIR/eq_op.rs:24:13
+  --> $DIR/eq_op.rs:25:13
    |
 LL |     let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: equal expressions as operands to `&`
-  --> $DIR/eq_op.rs:24:14
+  --> $DIR/eq_op.rs:25:14
    |
 LL |     let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
    |              ^^^^^^^^^^^^^^^^^
 
 error: equal expressions as operands to `&`
-  --> $DIR/eq_op.rs:24:35
+  --> $DIR/eq_op.rs:25:35
    |
 LL |     let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
    |                                   ^^^^^^^^^^^^^^^^^
 
 error: equal expressions as operands to `==`
-  --> $DIR/eq_op.rs:25:13
+  --> $DIR/eq_op.rs:26:13
    |
 LL |     let _ = (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: equal expressions as operands to `!=`
-  --> $DIR/eq_op.rs:28:13
+  --> $DIR/eq_op.rs:29:13
    |
 LL |     let _ = ([1] != [1]);
    |             ^^^^^^^^^^^^
 
 error: equal expressions as operands to `!=`
-  --> $DIR/eq_op.rs:29:13
+  --> $DIR/eq_op.rs:30:13
    |
 LL |     let _ = ((1, 2) != (1, 2));
    |             ^^^^^^^^^^^^^^^^^^
 
 error: equal expressions as operands to `==`
-  --> $DIR/eq_op.rs:33:13
+  --> $DIR/eq_op.rs:34:13
    |
 LL |     let _ = 1 + 1 == 2;
    |             ^^^^^^^^^^
 
 error: equal expressions as operands to `==`
-  --> $DIR/eq_op.rs:34:13
+  --> $DIR/eq_op.rs:35:13
    |
 LL |     let _ = 1 - 1 == 0;
    |             ^^^^^^^^^^
 
 error: equal expressions as operands to `-`
-  --> $DIR/eq_op.rs:34:13
+  --> $DIR/eq_op.rs:35:13
    |
 LL |     let _ = 1 - 1 == 0;
    |             ^^^^^
 
 error: equal expressions as operands to `-`
-  --> $DIR/eq_op.rs:36:13
+  --> $DIR/eq_op.rs:37:13
    |
 LL |     let _ = 1 - 1;
    |             ^^^^^
 
 error: equal expressions as operands to `/`
-  --> $DIR/eq_op.rs:37:13
+  --> $DIR/eq_op.rs:38:13
    |
 LL |     let _ = 1 / 1;
    |             ^^^^^
 
 error: equal expressions as operands to `&&`
-  --> $DIR/eq_op.rs:38:13
+  --> $DIR/eq_op.rs:39:13
    |
 LL |     let _ = true && true;
    |             ^^^^^^^^^^^^
 
 error: equal expressions as operands to `||`
-  --> $DIR/eq_op.rs:40:13
+  --> $DIR/eq_op.rs:41:13
    |
 LL |     let _ = true || true;
    |             ^^^^^^^^^^^^
 
 error: equal expressions as operands to `&&`
-  --> $DIR/eq_op.rs:45:13
+  --> $DIR/eq_op.rs:46:13
    |
 LL |     let _ = a == b && b == a;
    |             ^^^^^^^^^^^^^^^^
 
 error: equal expressions as operands to `&&`
-  --> $DIR/eq_op.rs:46:13
+  --> $DIR/eq_op.rs:47:13
    |
 LL |     let _ = a != b && b != a;
    |             ^^^^^^^^^^^^^^^^
 
 error: equal expressions as operands to `&&`
-  --> $DIR/eq_op.rs:47:13
+  --> $DIR/eq_op.rs:48:13
    |
 LL |     let _ = a < b && b > a;
    |             ^^^^^^^^^^^^^^
 
 error: equal expressions as operands to `&&`
-  --> $DIR/eq_op.rs:48:13
+  --> $DIR/eq_op.rs:49:13
    |
 LL |     let _ = a <= b && b >= a;
    |             ^^^^^^^^^^^^^^^^
 
 error: equal expressions as operands to `==`
-  --> $DIR/eq_op.rs:51:13
+  --> $DIR/eq_op.rs:52:13
    |
 LL |     let _ = a == a;
    |             ^^^^^^
 
 error: equal expressions as operands to `/`
-  --> $DIR/eq_op.rs:61:20
+  --> $DIR/eq_op.rs:62:20
    |
 LL |     const D: u32 = A / A;
    |                    ^^^^^
 
 error: equal expressions as operands to `==`
-  --> $DIR/eq_op.rs:92:5
+  --> $DIR/eq_op.rs:93:5
    |
 LL |     (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/suspicious_xor_used_as_pow.rs b/tests/ui/suspicious_xor_used_as_pow.rs
new file mode 100644
index 00000000000..eb9fc63fb1d
--- /dev/null
+++ b/tests/ui/suspicious_xor_used_as_pow.rs
@@ -0,0 +1,34 @@
+#![allow(unused)]
+#![warn(clippy::suspicious_xor_used_as_pow)]
+#![allow(clippy::eq_op)]
+
+macro_rules! macro_test {
+    () => {
+        13
+    };
+}
+
+macro_rules! macro_test_inside {
+    () => {
+        1 ^ 2 // should warn even if inside macro
+    };
+}
+
+fn main() {
+    // Should warn:
+    let _ = 2 ^ 5;
+    let _ = 2i32 ^ 9i32;
+    let _ = 2i32 ^ 2i32;
+    let _ = 50i32 ^ 3i32;
+    let _ = 5i32 ^ 8i32;
+    let _ = 2i32 ^ 32i32;
+    macro_test_inside!();
+
+    // Should not warn:
+    let x = 0x02;
+    let _ = x ^ 2;
+    let _ = 2 ^ x;
+    let _ = x ^ 5;
+    let _ = 10 ^ 0b0101;
+    let _ = 2i32 ^ macro_test!();
+}
diff --git a/tests/ui/suspicious_xor_used_as_pow.stderr b/tests/ui/suspicious_xor_used_as_pow.stderr
new file mode 100644
index 00000000000..8bb3c8fbeeb
--- /dev/null
+++ b/tests/ui/suspicious_xor_used_as_pow.stderr
@@ -0,0 +1,51 @@
+error: `^` is not the exponentiation operator
+  --> $DIR/suspicious_xor_used_as_pow.rs:19:13
+   |
+LL |     let _ = 2 ^ 5;
+   |             ^^^^^ help: did you mean to write: `2.pow(5)`
+   |
+   = note: `-D clippy::suspicious-xor-used-as-pow` implied by `-D warnings`
+
+error: `^` is not the exponentiation operator
+  --> $DIR/suspicious_xor_used_as_pow.rs:20:13
+   |
+LL |     let _ = 2i32 ^ 9i32;
+   |             ^^^^^^^^^^^ help: did you mean to write: `2_i32.pow(9_i32)`
+
+error: `^` is not the exponentiation operator
+  --> $DIR/suspicious_xor_used_as_pow.rs:21:13
+   |
+LL |     let _ = 2i32 ^ 2i32;
+   |             ^^^^^^^^^^^ help: did you mean to write: `2_i32.pow(2_i32)`
+
+error: `^` is not the exponentiation operator
+  --> $DIR/suspicious_xor_used_as_pow.rs:22:13
+   |
+LL |     let _ = 50i32 ^ 3i32;
+   |             ^^^^^^^^^^^^ help: did you mean to write: `50_i32.pow(3_i32)`
+
+error: `^` is not the exponentiation operator
+  --> $DIR/suspicious_xor_used_as_pow.rs:23:13
+   |
+LL |     let _ = 5i32 ^ 8i32;
+   |             ^^^^^^^^^^^ help: did you mean to write: `5_i32.pow(8_i32)`
+
+error: `^` is not the exponentiation operator
+  --> $DIR/suspicious_xor_used_as_pow.rs:24:13
+   |
+LL |     let _ = 2i32 ^ 32i32;
+   |             ^^^^^^^^^^^^ help: did you mean to write: `2_i32.pow(32_i32)`
+
+error: `^` is not the exponentiation operator
+  --> $DIR/suspicious_xor_used_as_pow.rs:13:9
+   |
+LL |         1 ^ 2 // should warn even if inside macro
+   |         ^^^^^ help: did you mean to write: `1.pow(2)`
+...
+LL |     macro_test_inside!();
+   |     -------------------- in this macro invocation
+   |
+   = note: this error originates in the macro `macro_test_inside` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 7 previous errors
+