about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlejandra González <blyxyas@gmail.com>2025-06-20 09:43:53 +0000
committerGitHub <noreply@github.com>2025-06-20 09:43:53 +0000
commit2c1c746ebd4e49e3578a452a02b9757e33512585 (patch)
tree043509d5f154a2d35c8763923264bc0df5a28989
parent8ec6f1a96f471a72e7cfed072b5eb4f4b85bc6d6 (diff)
parent6ffff5f009d951098d4762af1ec449f296787fe4 (diff)
downloadrust-2c1c746ebd4e49e3578a452a02b9757e33512585.tar.gz
rust-2c1c746ebd4e49e3578a452a02b9757e33512585.zip
New lint: `manual_is_multiple_of` (#14292)
~~I've added a `min_divisor` configuration option, default to 4, to not
trigger on smaller divisibility operations. I would prefer not to lint
`if a & 1 == 0` as `if a.is_multiple_of(2)` by default because the
former is very idiomatic in systems (and embedded) programming.~~

~~A `min_and_mask_size` option, defaulting to 3, sets the default bits
to be and-masked before this lint triggers; that would be `n` in `v &
((1 << n) - 1) == 0`. The form `v % 10 == 0` is always linted.~~

~~This PR will stay in draft mode until the next rustup which will mark
`unsigned_is_multiple_of` stable for Rust 1.87.0, and the feature flags
will be removed.~~

What should its category be? I've used "complexity", but "pedantic"
might be suitable as well.

Close rust-lang/rust-clippy#14289
changelog: [`manual_is_multiple_of`]: new lint
r? ghost
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/casts/cast_sign_loss.rs2
-rw-r--r--clippy_lints/src/declared_lints.rs1
-rw-r--r--clippy_lints/src/if_not_else.rs11
-rw-r--r--clippy_lints/src/operators/identity_op.rs6
-rw-r--r--clippy_lints/src/operators/manual_is_multiple_of.rs66
-rw-r--r--clippy_lints/src/operators/mod.rs33
-rw-r--r--clippy_utils/src/consts.rs15
-rw-r--r--clippy_utils/src/msrvs.rs2
-rw-r--r--tests/ui/box_default.fixed2
-rw-r--r--tests/ui/box_default.rs2
-rw-r--r--tests/ui/infinite_iter.rs2
-rw-r--r--tests/ui/infinite_iter.stderr4
-rw-r--r--tests/ui/iter_kv_map.fixed20
-rw-r--r--tests/ui/iter_kv_map.rs20
-rw-r--r--tests/ui/iter_kv_map.stderr64
-rw-r--r--tests/ui/let_unit.fixed2
-rw-r--r--tests/ui/let_unit.rs2
-rw-r--r--tests/ui/let_unit.stderr2
-rw-r--r--tests/ui/manual_contains.fixed2
-rw-r--r--tests/ui/manual_contains.rs2
-rw-r--r--tests/ui/manual_find_fixable.fixed4
-rw-r--r--tests/ui/manual_find_fixable.rs4
-rw-r--r--tests/ui/manual_find_fixable.stderr4
-rw-r--r--tests/ui/manual_is_multiple_of.fixed25
-rw-r--r--tests/ui/manual_is_multiple_of.rs25
-rw-r--r--tests/ui/manual_is_multiple_of.stderr41
-rw-r--r--tests/ui/manual_is_variant_and.fixed8
-rw-r--r--tests/ui/manual_is_variant_and.rs8
-rw-r--r--tests/ui/manual_is_variant_and.stderr12
30 files changed, 303 insertions, 89 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9f15b16b5c9..5ad1a7177eb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5968,6 +5968,7 @@ Released 2018-09-13
 [`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
 [`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite
 [`manual_is_infinite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_infinite
+[`manual_is_multiple_of`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_multiple_of
 [`manual_is_power_of_two`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two
 [`manual_is_variant_and`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_variant_and
 [`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs
index 9a1ad8a7473..a70bd886191 100644
--- a/clippy_lints/src/casts/cast_sign_loss.rs
+++ b/clippy_lints/src/casts/cast_sign_loss.rs
@@ -168,7 +168,7 @@ fn pow_call_result_sign(cx: &LateContext<'_>, base: &Expr<'_>, exponent: &Expr<'
 
     // Rust's integer pow() functions take an unsigned exponent.
     let exponent_val = get_const_unsigned_int_eval(cx, exponent, None);
-    let exponent_is_even = exponent_val.map(|val| val % 2 == 0);
+    let exponent_is_even = exponent_val.map(|val| val.is_multiple_of(2));
 
     match (base_sign, exponent_is_even) {
         // Non-negative bases always return non-negative results, ignoring overflow.
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index aac30d29a90..c3f8e02b4c0 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -591,6 +591,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
     crate::operators::IMPOSSIBLE_COMPARISONS_INFO,
     crate::operators::INEFFECTIVE_BIT_MASK_INFO,
     crate::operators::INTEGER_DIVISION_INFO,
+    crate::operators::MANUAL_IS_MULTIPLE_OF_INFO,
     crate::operators::MANUAL_MIDPOINT_INFO,
     crate::operators::MISREFACTORED_ASSIGN_OP_INFO,
     crate::operators::MODULO_ARITHMETIC_INFO,
diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs
index 45f9aa0a53e..ab7a965b367 100644
--- a/clippy_lints/src/if_not_else.rs
+++ b/clippy_lints/src/if_not_else.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{ConstEvalCtxt, Constant};
+use clippy_utils::consts::is_zero_integer_const;
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 use clippy_utils::is_else_clause;
 use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet};
@@ -48,13 +48,6 @@ declare_clippy_lint! {
 
 declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]);
 
-fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool {
-    if let Some(value) = ConstEvalCtxt::new(cx).eval_simple(expr) {
-        return Constant::Int(0) == value;
-    }
-    false
-}
-
 impl LateLintPass<'_> for IfNotElse {
     fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
         if let ExprKind::If(cond, cond_inner, Some(els)) = e.kind
@@ -68,7 +61,7 @@ impl LateLintPass<'_> for IfNotElse {
                 ),
                 // Don't lint on `… != 0`, as these are likely to be bit tests.
                 // For example, `if foo & 0x0F00 != 0 { … } else { … }` is already in the "proper" order.
-                ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_const(rhs, cx) => (
+                ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs) => (
                     "unnecessary `!=` operation",
                     "change to `==` and swap the blocks of the `if`/`else`",
                 ),
diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs
index 7e515e83cc9..3efbb896358 100644
--- a/clippy_lints/src/operators/identity_op.rs
+++ b/clippy_lints/src/operators/identity_op.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt};
+use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt, integer_const, is_zero_integer_const};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{ExprUseNode, clip, expr_use_ctxt, peel_hir_expr_refs, unsext};
@@ -186,9 +186,7 @@ fn is_allowed<'tcx>(
     cx.typeck_results().expr_ty(left).peel_refs().is_integral()
         && cx.typeck_results().expr_ty(right).peel_refs().is_integral()
         // `1 << 0` is a common pattern in bit manipulation code
-        && !(cmp == BinOpKind::Shl
-            && ConstEvalCtxt::new(cx).eval_simple(right) == Some(Constant::Int(0))
-            && ConstEvalCtxt::new(cx).eval_simple(left) == Some(Constant::Int(1)))
+        && !(cmp == BinOpKind::Shl && is_zero_integer_const(cx, right) && integer_const(cx, left) == Some(1))
 }
 
 fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) {
diff --git a/clippy_lints/src/operators/manual_is_multiple_of.rs b/clippy_lints/src/operators/manual_is_multiple_of.rs
new file mode 100644
index 00000000000..821178a4315
--- /dev/null
+++ b/clippy_lints/src/operators/manual_is_multiple_of.rs
@@ -0,0 +1,66 @@
+use clippy_utils::consts::is_zero_integer_const;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::sugg::Sugg;
+use rustc_ast::BinOpKind;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+
+use super::MANUAL_IS_MULTIPLE_OF;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &Expr<'_>,
+    op: BinOpKind,
+    lhs: &'tcx Expr<'tcx>,
+    rhs: &'tcx Expr<'tcx>,
+    msrv: Msrv,
+) {
+    if msrv.meets(cx, msrvs::UNSIGNED_IS_MULTIPLE_OF)
+        && let Some(operand) = uint_compare_to_zero(cx, op, lhs, rhs)
+        && let ExprKind::Binary(operand_op, operand_left, operand_right) = operand.kind
+        && operand_op.node == BinOpKind::Rem
+    {
+        let mut app = Applicability::MachineApplicable;
+        let divisor = Sugg::hir_with_applicability(cx, operand_right, "_", &mut app);
+        span_lint_and_sugg(
+            cx,
+            MANUAL_IS_MULTIPLE_OF,
+            expr.span,
+            "manual implementation of `.is_multiple_of()`",
+            "replace with",
+            format!(
+                "{}{}.is_multiple_of({divisor})",
+                if op == BinOpKind::Eq { "" } else { "!" },
+                Sugg::hir_with_applicability(cx, operand_left, "_", &mut app).maybe_paren()
+            ),
+            app,
+        );
+    }
+}
+
+// If we have a `x == 0`, `x != 0` or `x > 0` (or the reverted ones), return the non-zero operand
+fn uint_compare_to_zero<'tcx>(
+    cx: &LateContext<'tcx>,
+    op: BinOpKind,
+    lhs: &'tcx Expr<'tcx>,
+    rhs: &'tcx Expr<'tcx>,
+) -> Option<&'tcx Expr<'tcx>> {
+    let operand = if matches!(lhs.kind, ExprKind::Binary(..))
+        && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Gt)
+        && is_zero_integer_const(cx, rhs)
+    {
+        lhs
+    } else if matches!(rhs.kind, ExprKind::Binary(..))
+        && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Lt)
+        && is_zero_integer_const(cx, lhs)
+    {
+        rhs
+    } else {
+        return None;
+    };
+
+    matches!(cx.typeck_results().expr_ty_adjusted(operand).kind(), ty::Uint(_)).then_some(operand)
+}
diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs
index 2f4e8e99588..bdbbb3475cd 100644
--- a/clippy_lints/src/operators/mod.rs
+++ b/clippy_lints/src/operators/mod.rs
@@ -11,6 +11,7 @@ mod float_cmp;
 mod float_equality_without_abs;
 mod identity_op;
 mod integer_division;
+mod manual_is_multiple_of;
 mod manual_midpoint;
 mod misrefactored_assign_op;
 mod modulo_arithmetic;
@@ -830,12 +831,42 @@ declare_clippy_lint! {
     "manual implementation of `midpoint` which can overflow"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for manual implementation of `.is_multiple_of()` on
+    /// unsigned integer types.
+    ///
+    /// ### Why is this bad?
+    /// `a.is_multiple_of(b)` is a clearer way to check for divisibility
+    /// of `a` by `b`. This expression can never panic.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// # let (a, b) = (3u64, 4u64);
+    /// if a % b == 0 {
+    ///     println!("{a} is divisible by {b}");
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// # let (a, b) = (3u64, 4u64);
+    /// if a.is_multiple_of(b) {
+    ///     println!("{a} is divisible by {b}");
+    /// }
+    /// ```
+    #[clippy::version = "1.89.0"]
+    pub MANUAL_IS_MULTIPLE_OF,
+    complexity,
+    "manual implementation of `.is_multiple_of()`"
+}
+
 pub struct Operators {
     arithmetic_context: numeric_arithmetic::Context,
     verbose_bit_mask_threshold: u64,
     modulo_arithmetic_allow_comparison_to_zero: bool,
     msrv: Msrv,
 }
+
 impl Operators {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
@@ -874,6 +905,7 @@ impl_lint_pass!(Operators => [
     NEEDLESS_BITWISE_BOOL,
     SELF_ASSIGNMENT,
     MANUAL_MIDPOINT,
+    MANUAL_IS_MULTIPLE_OF,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Operators {
@@ -891,6 +923,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
                     identity_op::check(cx, e, op.node, lhs, rhs);
                     needless_bitwise_bool::check(cx, e, op.node, lhs, rhs);
                     manual_midpoint::check(cx, e, op.node, lhs, rhs, self.msrv);
+                    manual_is_multiple_of::check(cx, e, op.node, lhs, rhs, self.msrv);
                 }
                 self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
                 bit_mask::check(cx, e, op.node, lhs, rhs);
diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs
index 1ec5d11384f..aaa071fd5c9 100644
--- a/clippy_utils/src/consts.rs
+++ b/clippy_utils/src/consts.rs
@@ -958,3 +958,18 @@ fn field_of_struct<'tcx>(
         None
     }
 }
+
+/// If `expr` evaluates to an integer constant, return its value.
+pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
+    if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_simple(expr) {
+        Some(value)
+    } else {
+        None
+    }
+}
+
+/// Check if `expr` evaluates to an integer constant of 0.
+#[inline]
+pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    integer_const(cx, expr) == Some(0)
+}
diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs
index 26f6bbc2227..7a0bef1a9bb 100644
--- a/clippy_utils/src/msrvs.rs
+++ b/clippy_utils/src/msrvs.rs
@@ -24,7 +24,7 @@ macro_rules! msrv_aliases {
 // names may refer to stabilized feature flags or library items
 msrv_aliases! {
     1,88,0 { LET_CHAINS }
-    1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT }
+    1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF }
     1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL }
     1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR }
     1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP }
diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed
index 80000f5de4f..ed00494433b 100644
--- a/tests/ui/box_default.fixed
+++ b/tests/ui/box_default.fixed
@@ -126,7 +126,7 @@ fn issue_10381() {
     impl Bar for Foo {}
 
     fn maybe_get_bar(i: u32) -> Option<Box<dyn Bar>> {
-        if i % 2 == 0 {
+        if i.is_multiple_of(2) {
             Some(Box::new(Foo::default()))
         } else {
             None
diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs
index 4681016d7cd..801d92f5c29 100644
--- a/tests/ui/box_default.rs
+++ b/tests/ui/box_default.rs
@@ -126,7 +126,7 @@ fn issue_10381() {
     impl Bar for Foo {}
 
     fn maybe_get_bar(i: u32) -> Option<Box<dyn Bar>> {
-        if i % 2 == 0 {
+        if i.is_multiple_of(2) {
             Some(Box::new(Foo::default()))
         } else {
             None
diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs
index 002a791a657..701a86534ba 100644
--- a/tests/ui/infinite_iter.rs
+++ b/tests/ui/infinite_iter.rs
@@ -38,7 +38,7 @@ fn infinite_iters() {
     //~^ infinite_iter
 
     // infinite iter
-    (0_u64..).filter(|x| x % 2 == 0).last();
+    (0_u64..).filter(|x| x.is_multiple_of(2)).last();
     //~^ infinite_iter
 
     // not an infinite, because ranges are double-ended
diff --git a/tests/ui/infinite_iter.stderr b/tests/ui/infinite_iter.stderr
index 47133a2ea62..b9e7c008f93 100644
--- a/tests/ui/infinite_iter.stderr
+++ b/tests/ui/infinite_iter.stderr
@@ -42,8 +42,8 @@ LL |     (0_usize..).flat_map(|x| 0..x).product::<usize>();
 error: infinite iteration detected
   --> tests/ui/infinite_iter.rs:41:5
    |
-LL |     (0_u64..).filter(|x| x % 2 == 0).last();
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     (0_u64..).filter(|x| x.is_multiple_of(2)).last();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: possible infinite iteration detected
   --> tests/ui/infinite_iter.rs:53:5
diff --git a/tests/ui/iter_kv_map.fixed b/tests/ui/iter_kv_map.fixed
index 874f749b33d..b18dda35887 100644
--- a/tests/ui/iter_kv_map.fixed
+++ b/tests/ui/iter_kv_map.fixed
@@ -30,15 +30,19 @@ fn main() {
 
     let _ = map.clone().values().collect::<Vec<_>>();
     //~^ iter_kv_map
-    let _ = map.keys().filter(|x| *x % 2 == 0).count();
+    let _ = map.keys().filter(|x| x.is_multiple_of(2)).count();
     //~^ iter_kv_map
 
     // Don't lint
-    let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
+    let _ = map
+        .iter()
+        .filter(|(_, val)| val.is_multiple_of(2))
+        .map(|(key, _)| key)
+        .count();
     let _ = map.iter().map(get_key).collect::<Vec<_>>();
 
     // Linting the following could be an improvement to the lint
-    // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
+    // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count();
 
     // Lint
     let _ = map.keys().map(|key| key * 9).count();
@@ -84,15 +88,19 @@ fn main() {
 
     let _ = map.clone().values().collect::<Vec<_>>();
     //~^ iter_kv_map
-    let _ = map.keys().filter(|x| *x % 2 == 0).count();
+    let _ = map.keys().filter(|x| x.is_multiple_of(2)).count();
     //~^ iter_kv_map
 
     // Don't lint
-    let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
+    let _ = map
+        .iter()
+        .filter(|(_, val)| val.is_multiple_of(2))
+        .map(|(key, _)| key)
+        .count();
     let _ = map.iter().map(get_key).collect::<Vec<_>>();
 
     // Linting the following could be an improvement to the lint
-    // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
+    // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count();
 
     // Lint
     let _ = map.keys().map(|key| key * 9).count();
diff --git a/tests/ui/iter_kv_map.rs b/tests/ui/iter_kv_map.rs
index f570e3c32cb..729e4e8a266 100644
--- a/tests/ui/iter_kv_map.rs
+++ b/tests/ui/iter_kv_map.rs
@@ -30,15 +30,19 @@ fn main() {
 
     let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
     //~^ iter_kv_map
-    let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
+    let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count();
     //~^ iter_kv_map
 
     // Don't lint
-    let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
+    let _ = map
+        .iter()
+        .filter(|(_, val)| val.is_multiple_of(2))
+        .map(|(key, _)| key)
+        .count();
     let _ = map.iter().map(get_key).collect::<Vec<_>>();
 
     // Linting the following could be an improvement to the lint
-    // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
+    // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count();
 
     // Lint
     let _ = map.iter().map(|(key, _value)| key * 9).count();
@@ -86,15 +90,19 @@ fn main() {
 
     let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
     //~^ iter_kv_map
-    let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
+    let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count();
     //~^ iter_kv_map
 
     // Don't lint
-    let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
+    let _ = map
+        .iter()
+        .filter(|(_, val)| val.is_multiple_of(2))
+        .map(|(key, _)| key)
+        .count();
     let _ = map.iter().map(get_key).collect::<Vec<_>>();
 
     // Linting the following could be an improvement to the lint
-    // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
+    // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count();
 
     // Lint
     let _ = map.iter().map(|(key, _value)| key * 9).count();
diff --git a/tests/ui/iter_kv_map.stderr b/tests/ui/iter_kv_map.stderr
index 31ee76c25b7..8f73541f503 100644
--- a/tests/ui/iter_kv_map.stderr
+++ b/tests/ui/iter_kv_map.stderr
@@ -52,29 +52,29 @@ LL |     let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
 error: iterating on a map's keys
   --> tests/ui/iter_kv_map.rs:33:13
    |
-LL |     let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
+LL |     let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
 
 error: iterating on a map's keys
-  --> tests/ui/iter_kv_map.rs:44:13
+  --> tests/ui/iter_kv_map.rs:48:13
    |
 LL |     let _ = map.iter().map(|(key, _value)| key * 9).count();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:46:13
+  --> tests/ui/iter_kv_map.rs:50:13
    |
 LL |     let _ = map.iter().map(|(_key, value)| value * 17).count();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:50:13
+  --> tests/ui/iter_kv_map.rs:54:13
    |
 LL |     let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:54:13
+  --> tests/ui/iter_kv_map.rs:58:13
    |
 LL |       let _ = map
    |  _____________^
@@ -97,85 +97,85 @@ LL +         })
    |
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:65:13
+  --> tests/ui/iter_kv_map.rs:69:13
    |
 LL |     let _ = map.clone().into_iter().map(|(_, mut val)| val).count();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
 
 error: iterating on a map's keys
-  --> tests/ui/iter_kv_map.rs:70:13
+  --> tests/ui/iter_kv_map.rs:74:13
    |
 LL |     let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:72:13
+  --> tests/ui/iter_kv_map.rs:76:13
    |
 LL |     let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:74:13
+  --> tests/ui/iter_kv_map.rs:78:13
    |
 LL |     let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)`
 
 error: iterating on a map's keys
-  --> tests/ui/iter_kv_map.rs:77:13
+  --> tests/ui/iter_kv_map.rs:81:13
    |
 LL |     let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()`
 
 error: iterating on a map's keys
-  --> tests/ui/iter_kv_map.rs:79:13
+  --> tests/ui/iter_kv_map.rs:83:13
    |
 LL |     let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:82:13
+  --> tests/ui/iter_kv_map.rs:86:13
    |
 LL |     let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:84:13
+  --> tests/ui/iter_kv_map.rs:88:13
    |
 LL |     let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:87:13
+  --> tests/ui/iter_kv_map.rs:91:13
    |
 LL |     let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()`
 
 error: iterating on a map's keys
-  --> tests/ui/iter_kv_map.rs:89:13
+  --> tests/ui/iter_kv_map.rs:93:13
    |
-LL |     let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
+LL |     let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
 
 error: iterating on a map's keys
-  --> tests/ui/iter_kv_map.rs:100:13
+  --> tests/ui/iter_kv_map.rs:108:13
    |
 LL |     let _ = map.iter().map(|(key, _value)| key * 9).count();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:102:13
+  --> tests/ui/iter_kv_map.rs:110:13
    |
 LL |     let _ = map.iter().map(|(_key, value)| value * 17).count();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:106:13
+  --> tests/ui/iter_kv_map.rs:114:13
    |
 LL |     let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:110:13
+  --> tests/ui/iter_kv_map.rs:118:13
    |
 LL |       let _ = map
    |  _____________^
@@ -198,73 +198,73 @@ LL +         })
    |
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:121:13
+  --> tests/ui/iter_kv_map.rs:129:13
    |
 LL |     let _ = map.clone().into_iter().map(|(_, mut val)| val).count();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
 
 error: iterating on a map's keys
-  --> tests/ui/iter_kv_map.rs:137:13
+  --> tests/ui/iter_kv_map.rs:145:13
    |
 LL |     let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:140:13
+  --> tests/ui/iter_kv_map.rs:148:13
    |
 LL |     let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:143:13
+  --> tests/ui/iter_kv_map.rs:151:13
    |
 LL |     let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)`
 
 error: iterating on a map's keys
-  --> tests/ui/iter_kv_map.rs:152:13
+  --> tests/ui/iter_kv_map.rs:160:13
    |
 LL |     let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()`
 
 error: iterating on a map's keys
-  --> tests/ui/iter_kv_map.rs:155:13
+  --> tests/ui/iter_kv_map.rs:163:13
    |
 LL |     let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:158:13
+  --> tests/ui/iter_kv_map.rs:166:13
    |
 LL |     let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:161:13
+  --> tests/ui/iter_kv_map.rs:169:13
    |
 LL |     let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)`
 
 error: iterating on a map's keys
-  --> tests/ui/iter_kv_map.rs:164:13
+  --> tests/ui/iter_kv_map.rs:172:13
    |
 LL |     let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:167:13
+  --> tests/ui/iter_kv_map.rs:175:13
    |
 LL |     let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:170:13
+  --> tests/ui/iter_kv_map.rs:178:13
    |
 LL |     let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)`
 
 error: iterating on a map's values
-  --> tests/ui/iter_kv_map.rs:185:13
+  --> tests/ui/iter_kv_map.rs:193:13
    |
 LL |     let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::<Vec<_>>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.as_ref().values()`
diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed
index 5e7a2ad37a8..304eacecd94 100644
--- a/tests/ui/let_unit.fixed
+++ b/tests/ui/let_unit.fixed
@@ -61,7 +61,7 @@ fn multiline_sugg() {
         //~^ let_unit_value
         .into_iter()
         .map(|i| i * 2)
-        .filter(|i| i % 2 == 0)
+        .filter(|i| i.is_multiple_of(2))
         .map(|_| ())
         .next()
         .unwrap();
diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs
index 7b06f694012..a02cb346ff9 100644
--- a/tests/ui/let_unit.rs
+++ b/tests/ui/let_unit.rs
@@ -61,7 +61,7 @@ fn multiline_sugg() {
         //~^ let_unit_value
         .into_iter()
         .map(|i| i * 2)
-        .filter(|i| i % 2 == 0)
+        .filter(|i| i.is_multiple_of(2))
         .map(|_| ())
         .next()
         .unwrap();
diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr
index d7d01d304ca..d743110c99d 100644
--- a/tests/ui/let_unit.stderr
+++ b/tests/ui/let_unit.stderr
@@ -25,7 +25,7 @@ LL ~     v
 LL +
 LL +         .into_iter()
 LL +         .map(|i| i * 2)
-LL +         .filter(|i| i % 2 == 0)
+LL +         .filter(|i| i.is_multiple_of(2))
 LL +         .map(|_| ())
 LL +         .next()
 LL +         .unwrap();
diff --git a/tests/ui/manual_contains.fixed b/tests/ui/manual_contains.fixed
index d26c948a781..18171f0b2b4 100644
--- a/tests/ui/manual_contains.fixed
+++ b/tests/ui/manual_contains.fixed
@@ -58,7 +58,7 @@ fn should_not_lint() {
 
     let vec: Vec<u32> = vec![1, 2, 3, 4, 5, 6];
     let values = &vec[..];
-    let _ = values.iter().any(|&v| v % 2 == 0);
+    let _ = values.iter().any(|&v| v.is_multiple_of(2));
     let _ = values.iter().any(|&v| v * 2 == 6);
     let _ = values.iter().any(|&v| v == v);
     let _ = values.iter().any(|&v| 4 == 4);
diff --git a/tests/ui/manual_contains.rs b/tests/ui/manual_contains.rs
index fe67d2ee5d5..918f4d6b8dd 100644
--- a/tests/ui/manual_contains.rs
+++ b/tests/ui/manual_contains.rs
@@ -58,7 +58,7 @@ fn should_not_lint() {
 
     let vec: Vec<u32> = vec![1, 2, 3, 4, 5, 6];
     let values = &vec[..];
-    let _ = values.iter().any(|&v| v % 2 == 0);
+    let _ = values.iter().any(|&v| v.is_multiple_of(2));
     let _ = values.iter().any(|&v| v * 2 == 6);
     let _ = values.iter().any(|&v| v == v);
     let _ = values.iter().any(|&v| 4 == 4);
diff --git a/tests/ui/manual_find_fixable.fixed b/tests/ui/manual_find_fixable.fixed
index 01b3ebacbeb..c69b0cb11e3 100644
--- a/tests/ui/manual_find_fixable.fixed
+++ b/tests/ui/manual_find_fixable.fixed
@@ -11,7 +11,7 @@ fn lookup(n: u32) -> Option<u32> {
 }
 
 fn with_pat(arr: Vec<(u32, u32)>) -> Option<u32> {
-    arr.into_iter().map(|(a, _)| a).find(|&a| a % 2 == 0)
+    arr.into_iter().map(|(a, _)| a).find(|&a| a.is_multiple_of(2))
 }
 
 struct Data {
@@ -63,7 +63,7 @@ fn with_side_effects(arr: Vec<u32>) -> Option<u32> {
 
 fn with_else(arr: Vec<u32>) -> Option<u32> {
     for el in arr {
-        if el % 2 == 0 {
+        if el.is_multiple_of(2) {
             return Some(el);
         } else {
             println!("{}", el);
diff --git a/tests/ui/manual_find_fixable.rs b/tests/ui/manual_find_fixable.rs
index ce62a4beba1..db7092f020c 100644
--- a/tests/ui/manual_find_fixable.rs
+++ b/tests/ui/manual_find_fixable.rs
@@ -19,7 +19,7 @@ fn lookup(n: u32) -> Option<u32> {
 fn with_pat(arr: Vec<(u32, u32)>) -> Option<u32> {
     for (a, _) in arr {
         //~^ manual_find
-        if a % 2 == 0 {
+        if a.is_multiple_of(2) {
             return Some(a);
         }
     }
@@ -111,7 +111,7 @@ fn with_side_effects(arr: Vec<u32>) -> Option<u32> {
 
 fn with_else(arr: Vec<u32>) -> Option<u32> {
     for el in arr {
-        if el % 2 == 0 {
+        if el.is_multiple_of(2) {
             return Some(el);
         } else {
             println!("{}", el);
diff --git a/tests/ui/manual_find_fixable.stderr b/tests/ui/manual_find_fixable.stderr
index 020635d90bb..0c05c0d2c44 100644
--- a/tests/ui/manual_find_fixable.stderr
+++ b/tests/ui/manual_find_fixable.stderr
@@ -17,11 +17,11 @@ error: manual implementation of `Iterator::find`
    |
 LL | /     for (a, _) in arr {
 LL | |
-LL | |         if a % 2 == 0 {
+LL | |         if a.is_multiple_of(2) {
 LL | |             return Some(a);
 ...  |
 LL | |     None
-   | |________^ help: replace with an iterator: `arr.into_iter().map(|(a, _)| a).find(|&a| a % 2 == 0)`
+   | |________^ help: replace with an iterator: `arr.into_iter().map(|(a, _)| a).find(|&a| a.is_multiple_of(2))`
 
 error: manual implementation of `Iterator::find`
   --> tests/ui/manual_find_fixable.rs:34:5
diff --git a/tests/ui/manual_is_multiple_of.fixed b/tests/ui/manual_is_multiple_of.fixed
new file mode 100644
index 00000000000..6735b99f298
--- /dev/null
+++ b/tests/ui/manual_is_multiple_of.fixed
@@ -0,0 +1,25 @@
+//@aux-build: proc_macros.rs
+#![warn(clippy::manual_is_multiple_of)]
+
+fn main() {}
+
+#[clippy::msrv = "1.87"]
+fn f(a: u64, b: u64) {
+    let _ = a.is_multiple_of(b); //~ manual_is_multiple_of
+    let _ = (a + 1).is_multiple_of(b + 1); //~ manual_is_multiple_of
+    let _ = !a.is_multiple_of(b); //~ manual_is_multiple_of
+    let _ = !(a + 1).is_multiple_of(b + 1); //~ manual_is_multiple_of
+
+    let _ = !a.is_multiple_of(b); //~ manual_is_multiple_of
+    let _ = !a.is_multiple_of(b); //~ manual_is_multiple_of
+
+    proc_macros::external! {
+        let a: u64 = 23424;
+        let _ = a % 4096 == 0;
+    }
+}
+
+#[clippy::msrv = "1.86"]
+fn g(a: u64, b: u64) {
+    let _ = a % b == 0;
+}
diff --git a/tests/ui/manual_is_multiple_of.rs b/tests/ui/manual_is_multiple_of.rs
new file mode 100644
index 00000000000..00b638e4fd9
--- /dev/null
+++ b/tests/ui/manual_is_multiple_of.rs
@@ -0,0 +1,25 @@
+//@aux-build: proc_macros.rs
+#![warn(clippy::manual_is_multiple_of)]
+
+fn main() {}
+
+#[clippy::msrv = "1.87"]
+fn f(a: u64, b: u64) {
+    let _ = a % b == 0; //~ manual_is_multiple_of
+    let _ = (a + 1) % (b + 1) == 0; //~ manual_is_multiple_of
+    let _ = a % b != 0; //~ manual_is_multiple_of
+    let _ = (a + 1) % (b + 1) != 0; //~ manual_is_multiple_of
+
+    let _ = a % b > 0; //~ manual_is_multiple_of
+    let _ = 0 < a % b; //~ manual_is_multiple_of
+
+    proc_macros::external! {
+        let a: u64 = 23424;
+        let _ = a % 4096 == 0;
+    }
+}
+
+#[clippy::msrv = "1.86"]
+fn g(a: u64, b: u64) {
+    let _ = a % b == 0;
+}
diff --git a/tests/ui/manual_is_multiple_of.stderr b/tests/ui/manual_is_multiple_of.stderr
new file mode 100644
index 00000000000..0b1ae70c2a7
--- /dev/null
+++ b/tests/ui/manual_is_multiple_of.stderr
@@ -0,0 +1,41 @@
+error: manual implementation of `.is_multiple_of()`
+  --> tests/ui/manual_is_multiple_of.rs:8:13
+   |
+LL |     let _ = a % b == 0;
+   |             ^^^^^^^^^^ help: replace with: `a.is_multiple_of(b)`
+   |
+   = note: `-D clippy::manual-is-multiple-of` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::manual_is_multiple_of)]`
+
+error: manual implementation of `.is_multiple_of()`
+  --> tests/ui/manual_is_multiple_of.rs:9:13
+   |
+LL |     let _ = (a + 1) % (b + 1) == 0;
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(a + 1).is_multiple_of(b + 1)`
+
+error: manual implementation of `.is_multiple_of()`
+  --> tests/ui/manual_is_multiple_of.rs:10:13
+   |
+LL |     let _ = a % b != 0;
+   |             ^^^^^^^^^^ help: replace with: `!a.is_multiple_of(b)`
+
+error: manual implementation of `.is_multiple_of()`
+  --> tests/ui/manual_is_multiple_of.rs:11:13
+   |
+LL |     let _ = (a + 1) % (b + 1) != 0;
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `!(a + 1).is_multiple_of(b + 1)`
+
+error: manual implementation of `.is_multiple_of()`
+  --> tests/ui/manual_is_multiple_of.rs:13:13
+   |
+LL |     let _ = a % b > 0;
+   |             ^^^^^^^^^ help: replace with: `!a.is_multiple_of(b)`
+
+error: manual implementation of `.is_multiple_of()`
+  --> tests/ui/manual_is_multiple_of.rs:14:13
+   |
+LL |     let _ = 0 < a % b;
+   |             ^^^^^^^^^ help: replace with: `!a.is_multiple_of(b)`
+
+error: aborting due to 6 previous errors
+
diff --git a/tests/ui/manual_is_variant_and.fixed b/tests/ui/manual_is_variant_and.fixed
index 18a72188ab5..6425f32c09c 100644
--- a/tests/ui/manual_is_variant_and.fixed
+++ b/tests/ui/manual_is_variant_and.fixed
@@ -77,7 +77,7 @@ fn option_methods() {
     let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint
 
     // Should not lint.
-    let _ = Foo::<u32>(0).map(|x| x % 2 == 0) == Some(true);
+    let _ = Foo::<u32>(0).map(|x| x.is_multiple_of(2)) == Some(true);
     let _ = Some(2).map(|x| x % 2 == 0) != foo();
     let _ = mac!(eq Some(2).map(|x| x % 2 == 0), Some(true));
     let _ = mac!(some 2).map(|x| x % 2 == 0) == Some(true);
@@ -96,11 +96,11 @@ fn result_methods() {
     });
     let _ = res.is_ok_and(|x| x > 1);
 
-    let _ = Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0);
+    let _ = Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2));
     //~^ manual_is_variant_and
-    let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0);
+    let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2));
     //~^ manual_is_variant_and
-    let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0);
+    let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2));
     //~^ manual_is_variant_and
 
     // won't fix because the return type of the closure is not `bool`
diff --git a/tests/ui/manual_is_variant_and.rs b/tests/ui/manual_is_variant_and.rs
index a92f7c04369..e069e97a04d 100644
--- a/tests/ui/manual_is_variant_and.rs
+++ b/tests/ui/manual_is_variant_and.rs
@@ -83,7 +83,7 @@ fn option_methods() {
     let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint
 
     // Should not lint.
-    let _ = Foo::<u32>(0).map(|x| x % 2 == 0) == Some(true);
+    let _ = Foo::<u32>(0).map(|x| x.is_multiple_of(2)) == Some(true);
     let _ = Some(2).map(|x| x % 2 == 0) != foo();
     let _ = mac!(eq Some(2).map(|x| x % 2 == 0), Some(true));
     let _ = mac!(some 2).map(|x| x % 2 == 0) == Some(true);
@@ -105,11 +105,11 @@ fn result_methods() {
     //~^ manual_is_variant_and
         .unwrap_or_default();
 
-    let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) == Ok(true);
+    let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) == Ok(true);
     //~^ manual_is_variant_and
-    let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
+    let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) != Ok(true);
     //~^ manual_is_variant_and
-    let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
+    let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) != Ok(true);
     //~^ manual_is_variant_and
 
     // won't fix because the return type of the closure is not `bool`
diff --git a/tests/ui/manual_is_variant_and.stderr b/tests/ui/manual_is_variant_and.stderr
index 1fb437a8bc7..f770319a268 100644
--- a/tests/ui/manual_is_variant_and.stderr
+++ b/tests/ui/manual_is_variant_and.stderr
@@ -105,20 +105,20 @@ LL | |         .unwrap_or_default();
 error: called `.map() == Ok()`
   --> tests/ui/manual_is_variant_and.rs:108:13
    |
-LL |     let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) == Ok(true);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)`
+LL |     let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) == Ok(true);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2))`
 
 error: called `.map() != Ok()`
   --> tests/ui/manual_is_variant_and.rs:110:13
    |
-LL |     let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)`
+LL |     let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) != Ok(true);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2))`
 
 error: called `.map() != Ok()`
   --> tests/ui/manual_is_variant_and.rs:112:13
    |
-LL |     let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)`
+LL |     let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) != Ok(true);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2))`
 
 error: called `map(<f>).unwrap_or_default()` on a `Result` value
   --> tests/ui/manual_is_variant_and.rs:119:18