diff --git a/CHANGELOG.md b/CHANGELOG.md index d06a72d9c415..177ec94af43a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7006,8 +7006,10 @@ Released 2018-09-13 [`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else [`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str [`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map +[`manual_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_max [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_midpoint`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_midpoint +[`manual_min`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_min [`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_noop_waker`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_noop_waker diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 640ea0e3b9d5..dc00e9d1d939 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -314,6 +314,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::manual_is_power_of_two::MANUAL_IS_POWER_OF_TWO_INFO, crate::manual_let_else::MANUAL_LET_ELSE_INFO, crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO, + crate::manual_max::MANUAL_MAX_INFO, + crate::manual_max::MANUAL_MIN_INFO, crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO, crate::manual_noop_waker::MANUAL_NOOP_WAKER_INFO, crate::manual_option_as_slice::MANUAL_OPTION_AS_SLICE_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4a7659a9cc21..4be26c57d17b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -213,6 +213,7 @@ mod manual_is_ascii_check; mod manual_is_power_of_two; mod manual_let_else; mod manual_main_separator_str; +mod manual_max; mod manual_non_exhaustive; mod manual_noop_waker; mod manual_option_as_slice; @@ -760,6 +761,7 @@ rustc_lint::late_lint_methods!( PartialeqToNone: partialeq_to_none::PartialeqToNone = partialeq_to_none::PartialeqToNone, ManualAbsDiff: manual_abs_diff::ManualAbsDiff = manual_abs_diff::ManualAbsDiff::new(conf), ManualClamp: manual_clamp::ManualClamp = manual_clamp::ManualClamp::new(conf), + ManualMax: manual_max::ManualMax = manual_max::ManualMax::new(conf), ManualStringNew: manual_string_new::ManualStringNew = manual_string_new::ManualStringNew, UnusedPeekable: unused_peekable::UnusedPeekable = unused_peekable::UnusedPeekable, BoolToIntWithIf: bool_to_int_with_if::BoolToIntWithIf = bool_to_int_with_if::BoolToIntWithIf, diff --git a/clippy_lints/src/manual_max.rs b/clippy_lints/src/manual_max.rs new file mode 100644 index 000000000000..f36d1ef387f4 --- /dev/null +++ b/clippy_lints/src/manual_max.rs @@ -0,0 +1,279 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher::If; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::implements_trait; +use clippy_utils::{eq_expr_value, is_in_const_context, peel_blocks, peel_blocks_with_stmt, sym}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for a single `if`/`else` (or guarding `if`) that picks the greater of + /// two values, where [`Ord::max`] would be clearer. + /// + /// ### Why is this bad? + /// `a.max(b)` is shorter, has no branch, and states the intent directly. Unlike + /// [`MANUAL_CLAMP`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp), + /// which only triggers when both a lower *and* an upper bound are applied, this lint + /// catches the common single-sided "floor" case. + /// + /// ### Known problems + /// On a tie the two forms can return different operands: `Ord::max` returns the + /// *second* argument when the operands compare equal, whereas `if a < b { b } else { a }` + /// returns the *first* (`a`). For types whose `Eq`-equal values are observationally + /// distinct (e.g. ordered by a key field), the rewrite changes which value is selected, + /// so the suggestion is `MaybeIncorrect`. + /// + /// ### Example + /// ```no_run + /// # let (a, b) = (1, 2); + /// let _ = if a < b { b } else { a }; + /// + /// let mut cores = a; + /// if cores < b { + /// cores = b; + /// } + /// ``` + /// Use instead: + /// ```no_run + /// # let (a, b) = (1, 2); + /// let _ = a.max(b); + /// + /// let mut cores = a; + /// cores = cores.max(b); + /// ``` + #[clippy::version = "1.98.0"] + pub MANUAL_MAX, + complexity, + "an `if`/`else` that could be written as a call to `Ord::max`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for a single `if`/`else` (or guarding `if`) that picks the lesser of + /// two values, where [`Ord::min`] would be clearer. + /// + /// ### Why is this bad? + /// `a.min(b)` is shorter, has no branch, and states the intent directly. Unlike + /// [`MANUAL_CLAMP`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp), + /// which only triggers when both a lower *and* an upper bound are applied, this lint + /// catches the common single-sided "ceiling" case. + /// + /// ### Example + /// ```no_run + /// # let (a, b) = (1, 2); + /// let _ = if a > b { b } else { a }; + /// + /// let mut cores = a; + /// if cores > b { + /// cores = b; + /// } + /// ``` + /// Use instead: + /// ```no_run + /// # let (a, b) = (1, 2); + /// let _ = a.min(b); + /// + /// let mut cores = a; + /// cores = cores.min(b); + /// ``` + #[clippy::version = "1.98.0"] + pub MANUAL_MIN, + complexity, + "an `if`/`else` that could be written as a call to `Ord::min`" +} + +impl_lint_pass!(ManualMax => [MANUAL_MAX, MANUAL_MIN]); + +pub struct ManualMax { + msrv: Msrv, +} + +impl ManualMax { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} + +#[derive(Clone, Copy)] +enum MinMax { + Max, + Min, +} + +impl MinMax { + fn method(self) -> &'static str { + match self { + MinMax::Max => "max", + MinMax::Min => "min", + } + } + + fn lint(self) -> &'static rustc_lint::Lint { + match self { + MinMax::Max => MANUAL_MAX, + MinMax::Min => MANUAL_MIN, + } + } +} + +/// `lhs OP rhs` where `OP` is a comparison; used to reason about which operand the +/// condition selects when it holds. +struct Cmp<'tcx> { + op: BinOpKind, + lhs: &'tcx Expr<'tcx>, + rhs: &'tcx Expr<'tcx>, +} + +impl<'tcx> Cmp<'tcx> { + fn new(cond: &'tcx Expr<'tcx>) -> Option { + if let ExprKind::Binary(op, lhs, rhs) = peel_blocks(cond).kind + && matches!(op.node, BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge) + { + Some(Self { op: op.node, lhs, rhs }) + } else { + None + } + } + + /// Returns the operator rewritten so that `var` sits on the left-hand side, i.e. the + /// direction of the comparison as seen from `var`. Returns `None` if `var` is not one + /// of the operands. + fn orient(&self, cx: &LateContext<'tcx>, var: &Expr<'tcx>, ctxt: rustc_span::SyntaxContext) -> Option { + if eq_expr_value(cx, ctxt, var, self.lhs) { + Some(self.op) + } else if eq_expr_value(cx, ctxt, var, self.rhs) { + Some(flip(self.op)) + } else { + None + } + } +} + +fn flip(op: BinOpKind) -> BinOpKind { + match op { + BinOpKind::Lt => BinOpKind::Gt, + BinOpKind::Le => BinOpKind::Ge, + BinOpKind::Gt => BinOpKind::Lt, + BinOpKind::Ge => BinOpKind::Le, + other => other, + } +} + +impl<'tcx> LateLintPass<'tcx> for ManualMax { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + // `Ord::max`/`Ord::min` were stabilized in 1.21.0. + if expr.span.from_expansion() || is_in_const_context(cx) || !self.msrv.meets(cx, msrvs::ORD_MAX_MIN) { + return; + } + if let Some(If { cond, then, r#else }) = If::hir(expr) + && let Some(cmp) = Cmp::new(cond) + // Only `Ord` types: `f32`/`f64` implement `PartialOrd` only, and `a.max(b)` + // differs from the branching form when an operand is `NaN`. Both operands must be + // `Ord` (and hence the same type) for `lhs.max(rhs)` to type-check. + && is_ord(cx, cmp.lhs) + && is_ord(cx, cmp.rhs) + { + let ctxt = expr.span.ctxt(); + let found = match r#else { + Some(r#else) => match_select(cx, &cmp, peel_blocks(then), peel_blocks(r#else), ctxt), + None => match_guard(cx, &cmp, peel_blocks_with_stmt(then), ctxt), + }; + if let Some((kind, recv, arg, assign_to)) = found { + emit(cx, kind, expr, recv, arg, assign_to); + } + } + } +} + +/// Matches the value-returning form `if lhs OP rhs { x } else { y }`, where `{x, y}` are +/// the two operands in either order. +fn match_select<'tcx>( + cx: &LateContext<'tcx>, + cmp: &Cmp<'tcx>, + then: &'tcx Expr<'tcx>, + r#else: &'tcx Expr<'tcx>, + ctxt: rustc_span::SyntaxContext, +) -> Option<(MinMax, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Option<&'tcx Expr<'tcx>>)> { + let picks_lhs = eq_expr_value(cx, ctxt, then, cmp.lhs) && eq_expr_value(cx, ctxt, r#else, cmp.rhs); + let picks_rhs = eq_expr_value(cx, ctxt, then, cmp.rhs) && eq_expr_value(cx, ctxt, r#else, cmp.lhs); + // When the condition holds: `Lt`/`Le` means `lhs` is the smaller operand, `Gt`/`Ge` + // means `lhs` is the larger one. Picking the larger operand => `max`, else `min`. + let lhs_is_greater_when_true = matches!(cmp.op, BinOpKind::Gt | BinOpKind::Ge); + let kind = match (picks_lhs, picks_rhs) { + (true, false) if lhs_is_greater_when_true => MinMax::Max, + (true, false) => MinMax::Min, + (false, true) if lhs_is_greater_when_true => MinMax::Min, + (false, true) => MinMax::Max, + _ => return None, + }; + Some((kind, cmp.lhs, cmp.rhs, None)) +} + +/// Matches the guarding form `if x OP bound { x = bound; }` (no `else`), equivalent to +/// `x = x.max(bound)` / `x = x.min(bound)`. +fn match_guard<'tcx>( + cx: &LateContext<'tcx>, + cmp: &Cmp<'tcx>, + then: &'tcx Expr<'tcx>, + ctxt: rustc_span::SyntaxContext, +) -> Option<(MinMax, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Option<&'tcx Expr<'tcx>>)> { + if let ExprKind::Assign(target, value, _) = then.kind + // The assigned value must be the bound that is compared against, and the assignment + // target the clamped variable. + && let Some(oriented) = cmp.orient(cx, target, ctxt) + && (eq_expr_value(cx, ctxt, value, cmp.lhs) || eq_expr_value(cx, ctxt, value, cmp.rhs)) + && !eq_expr_value(cx, ctxt, value, target) + { + // `if x < bound { x = bound }` raises `x` to a floor => `max`; + // `if x > bound { x = bound }` lowers `x` to a ceiling => `min`. + let kind = match oriented { + BinOpKind::Lt | BinOpKind::Le => MinMax::Max, + BinOpKind::Gt | BinOpKind::Ge => MinMax::Min, + _ => return None, + }; + return Some((kind, target, value, Some(target))); + } + None +} + +fn is_ord<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + cx.tcx + .get_diagnostic_item(sym::Ord) + .is_some_and(|ord| implements_trait(cx, ty, ord, &[])) +} + +fn emit<'tcx>( + cx: &LateContext<'tcx>, + kind: MinMax, + expr: &Expr<'tcx>, + recv: &Expr<'tcx>, + arg: &Expr<'tcx>, + assign_to: Option<&Expr<'tcx>>, +) { + let recv = Sugg::hir(cx, recv, "..").maybe_paren(); + let arg = Sugg::hir(cx, arg, ".."); + let call = format!("{recv}.{}({arg})", kind.method()); + let sugg = match assign_to { + // The guard form replaces a whole `if` *statement*, so the assignment needs its + // own terminating `;`; the value-returning form is an expression and must not. + Some(target) => format!("{} = {call};", Sugg::hir(cx, target, "..")), + None => call, + }; + span_lint_and_sugg( + cx, + kind.lint(), + expr.span, + format!("this `if` expression is a manual `{}`", kind.method()), + "replace with", + sugg, + // The branching form re-evaluates the selected operand, so operands with side + // effects could behave differently. + Applicability::MaybeIncorrect, + ); +} diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 5151351ed2ad..a9e9def1287d 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -81,6 +81,7 @@ msrv_aliases! { 1,27,0 { ITERATOR_TRY_FOLD, DOUBLE_ENDED_ITERATOR_RFIND, DURATION_FROM_NANOS_MICROS } 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN, POINTER_ADD_SUB_METHODS } 1,24,0 { IS_ASCII_DIGIT, PTR_NULL } + 1,21,0 { ORD_MAX_MIN } 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT, RESULT_UNWRAP_OR_DEFAULT } diff --git a/tests/ui/manual_clamp.fixed b/tests/ui/manual_clamp.fixed index 4cd6fe60f381..b32261ea5992 100644 --- a/tests/ui/manual_clamp.fixed +++ b/tests/ui/manual_clamp.fixed @@ -1,4 +1,5 @@ #![warn(clippy::manual_clamp)] +#![allow(clippy::manual_max, clippy::manual_min)] #![expect(clippy::if_same_then_else, clippy::needless_match)] use std::cmp::{max as cmp_max, min as cmp_min}; diff --git a/tests/ui/manual_clamp.rs b/tests/ui/manual_clamp.rs index f0613501a8e8..fb0d423d9e7e 100644 --- a/tests/ui/manual_clamp.rs +++ b/tests/ui/manual_clamp.rs @@ -1,4 +1,5 @@ #![warn(clippy::manual_clamp)] +#![allow(clippy::manual_max, clippy::manual_min)] #![expect(clippy::if_same_then_else, clippy::needless_match)] use std::cmp::{max as cmp_max, min as cmp_min}; diff --git a/tests/ui/manual_clamp.stderr b/tests/ui/manual_clamp.stderr index 4876e2eb1cd1..79726e66fbae 100644 --- a/tests/ui/manual_clamp.stderr +++ b/tests/ui/manual_clamp.stderr @@ -1,5 +1,5 @@ error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:206:5 + --> tests/ui/manual_clamp.rs:207:5 | LL | / if x9 < CONST_MIN { LL | | @@ -15,7 +15,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::manual_clamp)]` error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:224:5 + --> tests/ui/manual_clamp.rs:225:5 | LL | / if x11 > CONST_MAX { LL | | @@ -29,7 +29,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:234:5 + --> tests/ui/manual_clamp.rs:235:5 | LL | / if CONST_MIN > x12 { LL | | @@ -43,7 +43,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:244:5 + --> tests/ui/manual_clamp.rs:245:5 | LL | / if CONST_MAX < x13 { LL | | @@ -57,7 +57,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:357:5 + --> tests/ui/manual_clamp.rs:358:5 | LL | / if CONST_MAX < x35 { LL | | @@ -71,7 +71,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:138:14 + --> tests/ui/manual_clamp.rs:139:14 | LL | let x0 = if CONST_MAX < input { | ______________^ @@ -86,7 +86,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:148:14 + --> tests/ui/manual_clamp.rs:149:14 | LL | let x1 = if input > CONST_MAX { | ______________^ @@ -101,7 +101,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:158:14 + --> tests/ui/manual_clamp.rs:159:14 | LL | let x2 = if input < CONST_MIN { | ______________^ @@ -116,7 +116,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:168:14 + --> tests/ui/manual_clamp.rs:169:14 | LL | let x3 = if CONST_MIN > input { | ______________^ @@ -131,7 +131,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:178:14 + --> tests/ui/manual_clamp.rs:179:14 | LL | let x4 = input.max(CONST_MIN).min(CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -139,7 +139,7 @@ LL | let x4 = input.max(CONST_MIN).min(CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:181:14 + --> tests/ui/manual_clamp.rs:182:14 | LL | let x5 = input.min(CONST_MAX).max(CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -147,7 +147,7 @@ LL | let x5 = input.min(CONST_MAX).max(CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:184:14 + --> tests/ui/manual_clamp.rs:185:14 | LL | let x6 = match input { | ______________^ @@ -161,7 +161,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:191:14 + --> tests/ui/manual_clamp.rs:192:14 | LL | let x7 = match input { | ______________^ @@ -175,7 +175,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:198:14 + --> tests/ui/manual_clamp.rs:199:14 | LL | let x8 = match input { | ______________^ @@ -189,7 +189,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:215:15 + --> tests/ui/manual_clamp.rs:216:15 | LL | let x10 = match input { | _______________^ @@ -203,7 +203,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:253:15 + --> tests/ui/manual_clamp.rs:254:15 | LL | let x14 = if input > CONST_MAX { | _______________^ @@ -218,7 +218,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:264:19 + --> tests/ui/manual_clamp.rs:265:19 | LL | let x15 = if input > CONST_F64_MAX { | ___________________^ @@ -234,7 +234,7 @@ LL | | }; = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:277:19 + --> tests/ui/manual_clamp.rs:278:19 | LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -242,7 +242,7 @@ LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:280:19 + --> tests/ui/manual_clamp.rs:281:19 | LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -250,7 +250,7 @@ LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:283:19 + --> tests/ui/manual_clamp.rs:284:19 | LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -258,7 +258,7 @@ LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:286:19 + --> tests/ui/manual_clamp.rs:287:19 | LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -266,7 +266,7 @@ LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:289:19 + --> tests/ui/manual_clamp.rs:290:19 | LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -274,7 +274,7 @@ LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:292:19 + --> tests/ui/manual_clamp.rs:293:19 | LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -282,7 +282,7 @@ LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:295:19 + --> tests/ui/manual_clamp.rs:296:19 | LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -290,7 +290,7 @@ LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:298:19 + --> tests/ui/manual_clamp.rs:299:19 | LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -298,7 +298,7 @@ LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:302:19 + --> tests/ui/manual_clamp.rs:303:19 | LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -307,7 +307,7 @@ LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:305:19 + --> tests/ui/manual_clamp.rs:306:19 | LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -316,7 +316,7 @@ LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:308:19 + --> tests/ui/manual_clamp.rs:309:19 | LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -325,7 +325,7 @@ LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:311:19 + --> tests/ui/manual_clamp.rs:312:19 | LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -334,7 +334,7 @@ LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:314:19 + --> tests/ui/manual_clamp.rs:315:19 | LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -343,7 +343,7 @@ LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:317:19 + --> tests/ui/manual_clamp.rs:318:19 | LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -352,7 +352,7 @@ LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:320:19 + --> tests/ui/manual_clamp.rs:321:19 | LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -361,7 +361,7 @@ LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:323:19 + --> tests/ui/manual_clamp.rs:324:19 | LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -370,7 +370,7 @@ LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:327:5 + --> tests/ui/manual_clamp.rs:328:5 | LL | / if x32 < CONST_MIN { LL | | @@ -384,7 +384,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:519:13 + --> tests/ui/manual_clamp.rs:520:13 | LL | let _ = if input > CONST_MAX { | _____________^ diff --git a/tests/ui/manual_max.fixed b/tests/ui/manual_max.fixed new file mode 100644 index 000000000000..b612aa19309e --- /dev/null +++ b/tests/ui/manual_max.fixed @@ -0,0 +1,89 @@ +#![warn(clippy::manual_max)] +#![allow(clippy::manual_min, clippy::needless_late_init, unused_assignments)] + +// Ordered solely by the first field, so two `Eq`-equal values can still be distinguished by +// the second. Exercises the tie-breaking note in the lint docs. +#[derive(Clone, Copy, PartialEq, Eq)] +struct ByKey(u32, &'static str); + +impl PartialOrd for ByKey { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ByKey { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.0.cmp(&other.0) + } +} + +// Const context: `Ord::max` is not const-callable, so this must not lint. +const fn in_const(a: i32, b: i32) -> i32 { + if a < b { b } else { a } +} + +fn main() { + let (a, b) = (1, 2); + + // Value-returning `if`/`else`, both comparison directions. + let _ = a.max(b); + //~^ manual_max + let _ = a.max(b); + //~^ manual_max + let _ = a.max(b); + //~^ manual_max + let _ = a.max(b); + //~^ manual_max + + // Operand needing parentheses in the receiver position. + let _ = (a + 1).max(b); + //~^ manual_max + + // Guarding `if` that raises a value to a floor. + let mut cores = a; + cores = cores.max(b); + //~^^^ manual_max + + let mut limit = a; + limit = limit.max(b); + //~^^^ manual_max + + // The `nproc` idiom from uutils/coreutils#12753 (floor of an expression). + let ignore = 1; + let mut n = 8; + n = n.max(ignore); + //~^^^ manual_max + + // Tie-sensitive `Ord` type: still lints, but the rewrite changes which operand is + // returned when the keys are equal (hence `MaybeIncorrect`). + let (k1, k2) = (ByKey(1, "a"), ByKey(2, "b")); + let _ = k1.max(k2); + //~^ manual_max + + // Negatives: not a manual max. + let _ = if a < b { a } else { b }; // this is a min + let _ = if a < b { b } else { 0 }; // else is not an operand + let f = 1.0_f64; + let g = 2.0_f64; + let _ = if f < g { g } else { f }; // float: NaN semantics differ, no lint + let mut x = a; + if x < b { + x = b; + println!("changed"); + } // extra statement, not a pure guard +} + +// `Ord::max` was stabilized in 1.21, so an older MSRV must suppress the lint. +#[clippy::msrv = "1.20"] +fn below_msrv() { + let (a, b) = (1, 2); + let _ = if a < b { b } else { a }; +} + +#[clippy::msrv = "1.21"] +fn meets_msrv() { + let (a, b) = (1, 2); + let _ = a.max(b); + //~^ manual_max +} diff --git a/tests/ui/manual_max.rs b/tests/ui/manual_max.rs new file mode 100644 index 000000000000..d9932d8f7dce --- /dev/null +++ b/tests/ui/manual_max.rs @@ -0,0 +1,95 @@ +#![warn(clippy::manual_max)] +#![allow(clippy::manual_min, clippy::needless_late_init, unused_assignments)] + +// Ordered solely by the first field, so two `Eq`-equal values can still be distinguished by +// the second. Exercises the tie-breaking note in the lint docs. +#[derive(Clone, Copy, PartialEq, Eq)] +struct ByKey(u32, &'static str); + +impl PartialOrd for ByKey { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ByKey { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.0.cmp(&other.0) + } +} + +// Const context: `Ord::max` is not const-callable, so this must not lint. +const fn in_const(a: i32, b: i32) -> i32 { + if a < b { b } else { a } +} + +fn main() { + let (a, b) = (1, 2); + + // Value-returning `if`/`else`, both comparison directions. + let _ = if a < b { b } else { a }; + //~^ manual_max + let _ = if a <= b { b } else { a }; + //~^ manual_max + let _ = if a > b { a } else { b }; + //~^ manual_max + let _ = if a >= b { a } else { b }; + //~^ manual_max + + // Operand needing parentheses in the receiver position. + let _ = if a + 1 < b { b } else { a + 1 }; + //~^ manual_max + + // Guarding `if` that raises a value to a floor. + let mut cores = a; + if cores < b { + cores = b; + } + //~^^^ manual_max + + let mut limit = a; + if limit <= b { + limit = b; + } + //~^^^ manual_max + + // The `nproc` idiom from uutils/coreutils#12753 (floor of an expression). + let ignore = 1; + let mut n = 8; + if n < ignore { + n = ignore; + } + //~^^^ manual_max + + // Tie-sensitive `Ord` type: still lints, but the rewrite changes which operand is + // returned when the keys are equal (hence `MaybeIncorrect`). + let (k1, k2) = (ByKey(1, "a"), ByKey(2, "b")); + let _ = if k1 < k2 { k2 } else { k1 }; + //~^ manual_max + + // Negatives: not a manual max. + let _ = if a < b { a } else { b }; // this is a min + let _ = if a < b { b } else { 0 }; // else is not an operand + let f = 1.0_f64; + let g = 2.0_f64; + let _ = if f < g { g } else { f }; // float: NaN semantics differ, no lint + let mut x = a; + if x < b { + x = b; + println!("changed"); + } // extra statement, not a pure guard +} + +// `Ord::max` was stabilized in 1.21, so an older MSRV must suppress the lint. +#[clippy::msrv = "1.20"] +fn below_msrv() { + let (a, b) = (1, 2); + let _ = if a < b { b } else { a }; +} + +#[clippy::msrv = "1.21"] +fn meets_msrv() { + let (a, b) = (1, 2); + let _ = if a < b { b } else { a }; + //~^ manual_max +} diff --git a/tests/ui/manual_max.stderr b/tests/ui/manual_max.stderr new file mode 100644 index 000000000000..09302d1792be --- /dev/null +++ b/tests/ui/manual_max.stderr @@ -0,0 +1,71 @@ +error: this `if` expression is a manual `max` + --> tests/ui/manual_max.rs:30:13 + | +LL | let _ = if a < b { b } else { a }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `a.max(b)` + | + = note: `-D clippy::manual-max` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_max)]` + +error: this `if` expression is a manual `max` + --> tests/ui/manual_max.rs:32:13 + | +LL | let _ = if a <= b { b } else { a }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `a.max(b)` + +error: this `if` expression is a manual `max` + --> tests/ui/manual_max.rs:34:13 + | +LL | let _ = if a > b { a } else { b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `a.max(b)` + +error: this `if` expression is a manual `max` + --> tests/ui/manual_max.rs:36:13 + | +LL | let _ = if a >= b { a } else { b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `a.max(b)` + +error: this `if` expression is a manual `max` + --> tests/ui/manual_max.rs:40:13 + | +LL | let _ = if a + 1 < b { b } else { a + 1 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(a + 1).max(b)` + +error: this `if` expression is a manual `max` + --> tests/ui/manual_max.rs:45:5 + | +LL | / if cores < b { +LL | | cores = b; +LL | | } + | |_____^ help: replace with: `cores = cores.max(b);` + +error: this `if` expression is a manual `max` + --> tests/ui/manual_max.rs:51:5 + | +LL | / if limit <= b { +LL | | limit = b; +LL | | } + | |_____^ help: replace with: `limit = limit.max(b);` + +error: this `if` expression is a manual `max` + --> tests/ui/manual_max.rs:59:5 + | +LL | / if n < ignore { +LL | | n = ignore; +LL | | } + | |_____^ help: replace with: `n = n.max(ignore);` + +error: this `if` expression is a manual `max` + --> tests/ui/manual_max.rs:67:13 + | +LL | let _ = if k1 < k2 { k2 } else { k1 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `k1.max(k2)` + +error: this `if` expression is a manual `max` + --> tests/ui/manual_max.rs:93:13 + | +LL | let _ = if a < b { b } else { a }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `a.max(b)` + +error: aborting due to 10 previous errors + diff --git a/tests/ui/manual_min.fixed b/tests/ui/manual_min.fixed new file mode 100644 index 000000000000..798da1379dc0 --- /dev/null +++ b/tests/ui/manual_min.fixed @@ -0,0 +1,31 @@ +#![warn(clippy::manual_min)] +#![allow(clippy::manual_max, clippy::needless_late_init, unused_assignments)] + +fn main() { + let (a, b) = (1, 2); + + // Value-returning `if`/`else`, both comparison directions. + let _ = a.min(b); + //~^ manual_min + let _ = a.min(b); + //~^ manual_min + let _ = a.min(b); + //~^ manual_min + let _ = a.min(b); + //~^ manual_min + + // Guarding `if` that lowers a value to a ceiling. + let mut cores = a; + cores = cores.min(b); + //~^^^ manual_min + + let mut limit = a; + limit = limit.min(b); + //~^^^ manual_min + + // Negatives: not a manual min. + let _ = if a > b { a } else { b }; // this is a max + let f = 1.0_f64; + let g = 2.0_f64; + let _ = if f > g { g } else { f }; // float: NaN semantics differ, no lint +} diff --git a/tests/ui/manual_min.rs b/tests/ui/manual_min.rs new file mode 100644 index 000000000000..ce584ba48fb7 --- /dev/null +++ b/tests/ui/manual_min.rs @@ -0,0 +1,35 @@ +#![warn(clippy::manual_min)] +#![allow(clippy::manual_max, clippy::needless_late_init, unused_assignments)] + +fn main() { + let (a, b) = (1, 2); + + // Value-returning `if`/`else`, both comparison directions. + let _ = if a > b { b } else { a }; + //~^ manual_min + let _ = if a >= b { b } else { a }; + //~^ manual_min + let _ = if a < b { a } else { b }; + //~^ manual_min + let _ = if a <= b { a } else { b }; + //~^ manual_min + + // Guarding `if` that lowers a value to a ceiling. + let mut cores = a; + if cores > b { + cores = b; + } + //~^^^ manual_min + + let mut limit = a; + if limit >= b { + limit = b; + } + //~^^^ manual_min + + // Negatives: not a manual min. + let _ = if a > b { a } else { b }; // this is a max + let f = 1.0_f64; + let g = 2.0_f64; + let _ = if f > g { g } else { f }; // float: NaN semantics differ, no lint +} diff --git a/tests/ui/manual_min.stderr b/tests/ui/manual_min.stderr new file mode 100644 index 000000000000..94739ee6efaf --- /dev/null +++ b/tests/ui/manual_min.stderr @@ -0,0 +1,45 @@ +error: this `if` expression is a manual `min` + --> tests/ui/manual_min.rs:8:13 + | +LL | let _ = if a > b { b } else { a }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `a.min(b)` + | + = note: `-D clippy::manual-min` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_min)]` + +error: this `if` expression is a manual `min` + --> tests/ui/manual_min.rs:10:13 + | +LL | let _ = if a >= b { b } else { a }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `a.min(b)` + +error: this `if` expression is a manual `min` + --> tests/ui/manual_min.rs:12:13 + | +LL | let _ = if a < b { a } else { b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `a.min(b)` + +error: this `if` expression is a manual `min` + --> tests/ui/manual_min.rs:14:13 + | +LL | let _ = if a <= b { a } else { b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `a.min(b)` + +error: this `if` expression is a manual `min` + --> tests/ui/manual_min.rs:19:5 + | +LL | / if cores > b { +LL | | cores = b; +LL | | } + | |_____^ help: replace with: `cores = cores.min(b);` + +error: this `if` expression is a manual `min` + --> tests/ui/manual_min.rs:25:5 + | +LL | / if limit >= b { +LL | | limit = b; +LL | | } + | |_____^ help: replace with: `limit = limit.min(b);` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/redundant_closure_call_fixable.fixed b/tests/ui/redundant_closure_call_fixable.fixed index d10063251acc..ed5f405b08e4 100644 --- a/tests/ui/redundant_closure_call_fixable.fixed +++ b/tests/ui/redundant_closure_call_fixable.fixed @@ -1,6 +1,7 @@ #![expect(unused)] #![expect(incomplete_features)] #![feature(ergonomic_clones)] +#![allow(clippy::manual_max)] async fn something() -> u32 { 21 diff --git a/tests/ui/redundant_closure_call_fixable.rs b/tests/ui/redundant_closure_call_fixable.rs index eccfb2e3c3a7..60c7b4d043bb 100644 --- a/tests/ui/redundant_closure_call_fixable.rs +++ b/tests/ui/redundant_closure_call_fixable.rs @@ -1,6 +1,7 @@ #![expect(unused)] #![expect(incomplete_features)] #![feature(ergonomic_clones)] +#![allow(clippy::manual_max)] async fn something() -> u32 { 21 diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr index d9d2335ef903..807979a811cd 100644 --- a/tests/ui/redundant_closure_call_fixable.stderr +++ b/tests/ui/redundant_closure_call_fixable.stderr @@ -1,5 +1,5 @@ error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:14:13 + --> tests/ui/redundant_closure_call_fixable.rs:15:13 | LL | let a = (|| 42)(); | ^^^^^^^^^ help: try doing something like: `42` @@ -8,7 +8,7 @@ LL | let a = (|| 42)(); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure_call)]` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:16:13 + --> tests/ui/redundant_closure_call_fixable.rs:17:13 | LL | let b = (async || { | _____________^ @@ -30,7 +30,7 @@ LL ~ }; | error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:22:13 + --> tests/ui/redundant_closure_call_fixable.rs:23:13 | LL | let c = (|| { | _____________^ @@ -52,13 +52,13 @@ LL ~ }; | error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:28:13 + --> tests/ui/redundant_closure_call_fixable.rs:29:13 | LL | let d = (async || something().await)(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:38:13 + --> tests/ui/redundant_closure_call_fixable.rs:39:13 | LL | (|| m!())() | ^^^^^^^^^^^ help: try doing something like: `m!()` @@ -69,7 +69,7 @@ LL | m2!(); = note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info) error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:33:13 + --> tests/ui/redundant_closure_call_fixable.rs:34:13 | LL | (|| 0)() | ^^^^^^^^ help: try doing something like: `0` @@ -80,115 +80,115 @@ LL | m2!(); = note: this error originates in the macro `m` which comes from the expansion of the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info) error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:48:16 + --> tests/ui/redundant_closure_call_fixable.rs:49:16 | LL | assert_eq!((|| || 43)()(), 42); | ^^^^^^^^^^^^^^ help: try doing something like: `43` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:58:10 + --> tests/ui/redundant_closure_call_fixable.rs:59:10 | LL | dbg!((|| 42)()); | ^^^^^^^^^ help: try doing something like: `42` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:62:13 + --> tests/ui/redundant_closure_call_fixable.rs:63:13 | LL | let a = (|| || || 123)(); | ^^^^^^^^^^^^^^^^ help: try doing something like: `|| || 123` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:67:13 + --> tests/ui/redundant_closure_call_fixable.rs:68:13 | LL | let a = (|| || || || async || 1)()()()()(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { 1 }` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:78:13 + --> tests/ui/redundant_closure_call_fixable.rs:79:13 | LL | let a = (|| echo!(|| echo!(|| 1)))()()(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `1` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:81:13 + --> tests/ui/redundant_closure_call_fixable.rs:82:13 | LL | let a = (|| echo!((|| 123)))()(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `123` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:96:11 + --> tests/ui/redundant_closure_call_fixable.rs:97:11 | LL | bar()((|| || 42)()(), 5); | ^^^^^^^^^^^^^^ help: try doing something like: `42` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:98:9 + --> tests/ui/redundant_closure_call_fixable.rs:99:9 | LL | foo((|| || 42)()(), 5); | ^^^^^^^^^^^^^^ help: try doing something like: `42` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:103:5 + --> tests/ui/redundant_closure_call_fixable.rs:104:5 | LL | (|| async {})().await; | ^^^^^^^^^^^^^^^ help: try doing something like: `async {}` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:113:18 + --> tests/ui/redundant_closure_call_fixable.rs:114:18 | LL | spawn_on((|| async move {})()); | ^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async move {}` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:119:18 + --> tests/ui/redundant_closure_call_fixable.rs:120:18 | LL | spawn_on((|| async move { drop(format!("hello, {name}")) })()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async move { drop(format!("hello, {name}")) }` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:125:18 + --> tests/ui/redundant_closure_call_fixable.rs:126:18 | LL | spawn_on((async move || drop(format!("hello, {name}")))()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async move { drop(format!("hello, {name}")) }` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:138:18 + --> tests/ui/redundant_closure_call_fixable.rs:139:18 | LL | spawn_on((|| async use { drop(format!("hello, {name:?}")) })()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async use { drop(format!("hello, {name:?}")) }` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:144:18 + --> tests/ui/redundant_closure_call_fixable.rs:145:18 | LL | spawn_on((async use || drop(format!("hello, {name}")))()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async use { drop(format!("hello, {name}")) }` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:150:28 + --> tests/ui/redundant_closure_call_fixable.rs:151:28 | LL | std::convert::identity((|| 13_i32 + 36_i32)()).leading_zeros(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `13_i32 + 36_i32` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:182:5 + --> tests/ui/redundant_closure_call_fixable.rs:183:5 | LL | (|| { Some(true) })() == Some(true); | ^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(true)` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:184:5 + --> tests/ui/redundant_closure_call_fixable.rs:185:5 | LL | (|| Some(true))() == Some(true); | ^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(true)` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:186:5 + --> tests/ui/redundant_closure_call_fixable.rs:187:5 | LL | (|| { Some(if 1 > 2 {1} else {2}) })() == Some(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(if 1 > 2 {1} else {2})` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:188:5 + --> tests/ui/redundant_closure_call_fixable.rs:189:5 | LL | (|| { Some( 1 > 2 ) })() == Some(true); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some( 1 > 2 )`