about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSamuel Tardieu <sam@rfc1149.net>2025-03-24 19:01:18 +0100
committerSamuel Tardieu <sam@rfc1149.net>2025-04-14 22:05:44 +0200
commit1cab0b412ec5e73f08e142cae14cff695ac6945b (patch)
tree9b63c516fb0c200295c5e10af168db4cbc384057
parent89385c135d647c7d1e712484a5d6509bb15fa661 (diff)
downloadrust-1cab0b412ec5e73f08e142cae14cff695ac6945b.tar.gz
rust-1cab0b412ec5e73f08e142cae14cff695ac6945b.zip
Do not lint result from macro expansion
If parts of the expression comes from macro expansion, it may match an
expression equivalent to `is_power_of_two()` by chance only.
-rw-r--r--clippy_lints/src/manual_is_power_of_two.rs22
-rw-r--r--tests/ui/manual_is_power_of_two.fixed17
-rw-r--r--tests/ui/manual_is_power_of_two.rs17
-rw-r--r--tests/ui/manual_is_power_of_two.stderr14
4 files changed, 56 insertions, 14 deletions
diff --git a/clippy_lints/src/manual_is_power_of_two.rs b/clippy_lints/src/manual_is_power_of_two.rs
index e4eeeb59ee0..067354ae5b2 100644
--- a/clippy_lints/src/manual_is_power_of_two.rs
+++ b/clippy_lints/src/manual_is_power_of_two.rs
@@ -35,8 +35,8 @@ declare_lint_pass!(ManualIsPowerOfTwo => [MANUAL_IS_POWER_OF_TWO]);
 
 impl<'tcx> LateLintPass<'tcx> for ManualIsPowerOfTwo {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
-        if let ExprKind::Binary(bin_op, lhs, rhs) = expr.kind
-            && bin_op.node == BinOpKind::Eq
+        if !expr.span.from_expansion()
+            && let Some((lhs, rhs)) = unexpanded_binop_operands(expr, BinOpKind::Eq)
         {
             if let Some(a) = count_ones_receiver(cx, lhs)
                 && is_integer_literal(rhs, 1)
@@ -92,8 +92,7 @@ fn is_one_less<'tcx>(
     greater: &'tcx Expr<'tcx>,
     smaller: &Expr<'tcx>,
 ) -> Option<&'tcx Expr<'tcx>> {
-    if let ExprKind::Binary(op, lhs, rhs) = smaller.kind
-        && op.node == BinOpKind::Sub
+    if let Some((lhs, rhs)) = unexpanded_binop_operands(smaller, BinOpKind::Sub)
         && SpanlessEq::new(cx).eq_expr(greater, lhs)
         && is_integer_literal(rhs, 1)
         && matches!(cx.typeck_results().expr_ty_adjusted(greater).kind(), ty::Uint(_))
@@ -106,10 +105,19 @@ fn is_one_less<'tcx>(
 
 /// Return `v` if `expr` is `v & (v - 1)` or `(v - 1) & v`
 fn is_and_minus_one<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
-    if let ExprKind::Binary(op, lhs, rhs) = expr.kind
-        && op.node == BinOpKind::BitAnd
+    let (lhs, rhs) = unexpanded_binop_operands(expr, BinOpKind::BitAnd)?;
+    is_one_less(cx, lhs, rhs).or_else(|| is_one_less(cx, rhs, lhs))
+}
+
+/// Return the operands of the `expr` binary operation if the operator is `op` and none of the
+/// operands come from expansion.
+fn unexpanded_binop_operands<'hir>(expr: &Expr<'hir>, op: BinOpKind) -> Option<(&'hir Expr<'hir>, &'hir Expr<'hir>)> {
+    if let ExprKind::Binary(binop, lhs, rhs) = expr.kind
+        && binop.node == op
+        && !lhs.span.from_expansion()
+        && !rhs.span.from_expansion()
     {
-        is_one_less(cx, lhs, rhs).or_else(|| is_one_less(cx, rhs, lhs))
+        Some((lhs, rhs))
     } else {
         None
     }
diff --git a/tests/ui/manual_is_power_of_two.fixed b/tests/ui/manual_is_power_of_two.fixed
index d301b7dd186..2f18f566c58 100644
--- a/tests/ui/manual_is_power_of_two.fixed
+++ b/tests/ui/manual_is_power_of_two.fixed
@@ -1,4 +1,17 @@
 #![warn(clippy::manual_is_power_of_two)]
+#![allow(clippy::precedence)]
+
+macro_rules! binop {
+    ($a: expr, equal, $b: expr) => {
+        $a == $b
+    };
+    ($a: expr, and, $b: expr) => {
+        $a & $b
+    };
+    ($a: expr, minus, $b: expr) => {
+        $a - $b
+    };
+}
 
 fn main() {
     let a = 16_u64;
@@ -27,4 +40,8 @@ fn main() {
     let i: i32 = 3;
     let _ = (i as u32).is_power_of_two();
     //~^ manual_is_power_of_two
+
+    let _ = binop!(a.count_ones(), equal, 1);
+    let _ = binop!(a, and, a - 1) == 0;
+    let _ = a & binop!(a, minus, 1) == 0;
 }
diff --git a/tests/ui/manual_is_power_of_two.rs b/tests/ui/manual_is_power_of_two.rs
index fd7c27abcb5..ef219e92412 100644
--- a/tests/ui/manual_is_power_of_two.rs
+++ b/tests/ui/manual_is_power_of_two.rs
@@ -1,4 +1,17 @@
 #![warn(clippy::manual_is_power_of_two)]
+#![allow(clippy::precedence)]
+
+macro_rules! binop {
+    ($a: expr, equal, $b: expr) => {
+        $a == $b
+    };
+    ($a: expr, and, $b: expr) => {
+        $a & $b
+    };
+    ($a: expr, minus, $b: expr) => {
+        $a - $b
+    };
+}
 
 fn main() {
     let a = 16_u64;
@@ -27,4 +40,8 @@ fn main() {
     let i: i32 = 3;
     let _ = i as u32 & (i as u32 - 1) == 0;
     //~^ manual_is_power_of_two
+
+    let _ = binop!(a.count_ones(), equal, 1);
+    let _ = binop!(a, and, a - 1) == 0;
+    let _ = a & binop!(a, minus, 1) == 0;
 }
diff --git a/tests/ui/manual_is_power_of_two.stderr b/tests/ui/manual_is_power_of_two.stderr
index c4535031c8c..73b4b282c8b 100644
--- a/tests/ui/manual_is_power_of_two.stderr
+++ b/tests/ui/manual_is_power_of_two.stderr
@@ -1,5 +1,5 @@
 error: manually reimplementing `is_power_of_two`
-  --> tests/ui/manual_is_power_of_two.rs:6:13
+  --> tests/ui/manual_is_power_of_two.rs:19:13
    |
 LL |     let _ = a.count_ones() == 1;
    |             ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()`
@@ -8,37 +8,37 @@ LL |     let _ = a.count_ones() == 1;
    = help: to override `-D warnings` add `#[allow(clippy::manual_is_power_of_two)]`
 
 error: manually reimplementing `is_power_of_two`
-  --> tests/ui/manual_is_power_of_two.rs:8:13
+  --> tests/ui/manual_is_power_of_two.rs:21:13
    |
 LL |     let _ = a & (a - 1) == 0;
    |             ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()`
 
 error: manually reimplementing `is_power_of_two`
-  --> tests/ui/manual_is_power_of_two.rs:12:13
+  --> tests/ui/manual_is_power_of_two.rs:25:13
    |
 LL |     let _ = 1 == a.count_ones();
    |             ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()`
 
 error: manually reimplementing `is_power_of_two`
-  --> tests/ui/manual_is_power_of_two.rs:14:13
+  --> tests/ui/manual_is_power_of_two.rs:27:13
    |
 LL |     let _ = (a - 1) & a == 0;
    |             ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()`
 
 error: manually reimplementing `is_power_of_two`
-  --> tests/ui/manual_is_power_of_two.rs:16:13
+  --> tests/ui/manual_is_power_of_two.rs:29:13
    |
 LL |     let _ = 0 == a & (a - 1);
    |             ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()`
 
 error: manually reimplementing `is_power_of_two`
-  --> tests/ui/manual_is_power_of_two.rs:18:13
+  --> tests/ui/manual_is_power_of_two.rs:31:13
    |
 LL |     let _ = 0 == (a - 1) & a;
    |             ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()`
 
 error: manually reimplementing `is_power_of_two`
-  --> tests/ui/manual_is_power_of_two.rs:28:13
+  --> tests/ui/manual_is_power_of_two.rs:41:13
    |
 LL |     let _ = i as u32 & (i as u32 - 1) == 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `(i as u32).is_power_of_two()`