From 904a750f440fa3dbb9397955e26ac324cbd15d71 Mon Sep 17 00:00:00 2001 From: linshuy2 Date: Fri, 13 Feb 2026 01:00:27 +0000 Subject: [PATCH 1/2] Extend `collapsible_match` to cover `if-else`s --- clippy_lints/src/collapsible_if.rs | 4 +- clippy_lints/src/matches/collapsible_match.rs | 74 ++++++++++++++++++- tests/ui/collapsible_match_fixable.fixed | 30 ++++++++ tests/ui/collapsible_match_fixable.rs | 31 ++++++++ tests/ui/collapsible_match_fixable.stderr | 50 +++++++++++++ 5 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 tests/ui/collapsible_match_fixable.fixed create mode 100644 tests/ui/collapsible_match_fixable.rs create mode 100644 tests/ui/collapsible_match_fixable.stderr diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index be07ce1272bd..17e11b8b281d 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -307,7 +307,7 @@ fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { } /// If the expression is a `||`, suggest parentheses around it. -fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> { +pub(super) fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> { if let ExprKind::Binary(op, _, _) = expr.peel_drop_temps().kind && op.node == BinOpKind::Or { @@ -334,7 +334,7 @@ fn span_extract_keyword(sm: &SourceMap, span: Span, keyword: &str) -> Option (Span, Span, Span) { +pub(super) fn peel_parens(sm: &SourceMap, mut span: Span) -> (Span, Span, Span) { use crate::rustc_span::Pos; let start = span.shrink_to_lo(); diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 79f737f07eb1..5709348c5bcd 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,17 +1,19 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::higher::{If, IfLetOrMatch}; use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::snippet; +use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet}; use clippy_utils::visitors::is_local_used; use clippy_utils::{SpanlessEq, get_ref_operators, is_unit_expr, peel_blocks_with_stmt, peel_ref_operators}; use rustc_ast::BorrowKind; -use rustc_errors::MultiSpan; +use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; -use rustc_span::Span; use rustc_span::symbol::Ident; +use rustc_span::{BytePos, Span}; + +use crate::collapsible_if::{parens_around, peel_parens}; use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or}; @@ -119,6 +121,70 @@ fn check_arm<'tcx>( "the outer pattern can be modified to include the inner pattern", ); }); + } else if outer_is_match // Leave if-let to the `collapsible_if` lint + && let Some(inner) = If::hir(inner_expr) + && outer_pat.span.eq_ctxt(inner.cond.span) + && match (outer_else_body, inner.r#else) { + (None, None) => true, + (None, Some(e)) | (Some(e), None) => is_unit_expr(e), + (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), + } + { + span_lint_hir_and_then( + cx, + COLLAPSIBLE_MATCH, + inner_expr.hir_id, + inner_expr.span, + "this `if` can be collapsed into the outer `match`", + |diag| { + let outer_then_open_bracket = outer_then_body + .span + .split_at(1) + .0 + .with_leading_whitespace(cx) + .into_span(); + let outer_then_closing_bracket = { + let end = outer_then_body.span.shrink_to_hi(); + end.with_lo(end.lo() - BytePos(1)) + .with_leading_whitespace(cx) + .into_span() + }; + let outer_arrow_end = if let Some(outer_guard) = outer_guard { + outer_guard.span.shrink_to_hi() + } else { + outer_pat.span.shrink_to_hi() + }; + let (paren_start, inner_if_span, paren_end) = peel_parens(cx.tcx.sess.source_map(), inner_expr.span); + let inner_if = inner_if_span.split_at(2).0; + let mut sugg = vec![ + (inner.then.span.shrink_to_lo(), "=> ".to_string()), + (outer_arrow_end.to(outer_then_open_bracket), String::new()), + (outer_then_closing_bracket, String::new()), + ]; + + if let Some(outer_guard) = outer_guard { + sugg.extend(parens_around(outer_guard)); + sugg.push((inner_if, "&&".to_string())); + } + + if !paren_start.is_empty() { + sugg.push((paren_start, String::new())); + } + + if !paren_end.is_empty() { + sugg.push((paren_end, String::new())); + } + + sugg.extend(parens_around(inner.cond)); + + if let Some(else_inner) = inner.r#else { + let else_inner_span = inner.then.span.shrink_to_hi().to(else_inner.span); + sugg.push((else_inner_span, String::new())); + } + + diag.multipart_suggestion("collapse nested if block", sugg, Applicability::MachineApplicable); + }, + ); } } diff --git a/tests/ui/collapsible_match_fixable.fixed b/tests/ui/collapsible_match_fixable.fixed new file mode 100644 index 000000000000..db76530aee14 --- /dev/null +++ b/tests/ui/collapsible_match_fixable.fixed @@ -0,0 +1,30 @@ +#![warn(clippy::collapsible_match)] +#![allow(clippy::single_match, clippy::redundant_guards)] + +fn issue16558() { + let opt = Some(1); + let _ = match opt { + Some(s) + if s == 1 => { s } + //~^ collapsible_match + , + _ => 1, + }; + + match opt { + Some(s) + if s == 1 => { + //~^ collapsible_match + todo!() + }, + _ => {}, + }; + + let _ = match opt { + Some(s) if s > 2 + && s == 1 => { s } + //~^ collapsible_match + , + _ => 1, + }; +} diff --git a/tests/ui/collapsible_match_fixable.rs b/tests/ui/collapsible_match_fixable.rs new file mode 100644 index 000000000000..94bf1d6bfdfa --- /dev/null +++ b/tests/ui/collapsible_match_fixable.rs @@ -0,0 +1,31 @@ +#![warn(clippy::collapsible_match)] +#![allow(clippy::single_match, clippy::redundant_guards)] + +fn issue16558() { + let opt = Some(1); + let _ = match opt { + Some(s) => { + if s == 1 { s } else { 1 } + //~^ collapsible_match + }, + _ => 1, + }; + + match opt { + Some(s) => { + (if s == 1 { + //~^ collapsible_match + todo!() + }) + }, + _ => {}, + }; + + let _ = match opt { + Some(s) if s > 2 => { + if s == 1 { s } else { 1 } + //~^ collapsible_match + }, + _ => 1, + }; +} diff --git a/tests/ui/collapsible_match_fixable.stderr b/tests/ui/collapsible_match_fixable.stderr new file mode 100644 index 000000000000..4d501cbd0993 --- /dev/null +++ b/tests/ui/collapsible_match_fixable.stderr @@ -0,0 +1,50 @@ +error: this `if` can be collapsed into the outer `match` + --> tests/ui/collapsible_match_fixable.rs:8:13 + | +LL | if s == 1 { s } else { 1 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::collapsible-match` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::collapsible_match)]` +help: collapse nested if block + | +LL ~ Some(s) +LL ~ if s == 1 => { s } +LL | +LL ~ , + | + +error: this `if` can be collapsed into the outer `match` + --> tests/ui/collapsible_match_fixable.rs:16:13 + | +LL | / (if s == 1 { +LL | | +LL | | todo!() +LL | | }) + | |______________^ + | +help: collapse nested if block + | +LL ~ Some(s) +LL ~ if s == 1 => { +LL | +LL | todo!() +LL ~ }, + | + +error: this `if` can be collapsed into the outer `match` + --> tests/ui/collapsible_match_fixable.rs:26:13 + | +LL | if s == 1 { s } else { 1 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: collapse nested if block + | +LL ~ Some(s) if s > 2 +LL ~ && s == 1 => { s } +LL | +LL ~ , + | + +error: aborting due to 3 previous errors + From 5be3049e9cf3e41a1c15b280402e353d09c37ec5 Mon Sep 17 00:00:00 2001 From: linshuy2 Date: Fri, 13 Feb 2026 01:19:00 +0000 Subject: [PATCH 2/2] Apply `collapsible_match` to Clippy itself --- clippy_dev/src/new_lint.rs | 18 ++--- clippy_lints/src/casts/unnecessary_cast.rs | 6 +- clippy_lints/src/cognitive_complexity.rs | 6 +- clippy_lints/src/doc/mod.rs | 16 +--- clippy_lints/src/double_parens.rs | 74 +++++++++---------- clippy_lints/src/infinite_iter.rs | 6 +- clippy_lints/src/loops/utils.rs | 24 +++--- clippy_lints/src/manual_option_as_slice.rs | 39 ++++------ clippy_lints/src/manual_strip.rs | 21 +++--- clippy_lints/src/matches/collapsible_match.rs | 2 +- .../src/matches/match_single_binding.rs | 6 +- clippy_lints/src/methods/mod.rs | 9 ++- clippy_lints/src/methods/or_fun_call.rs | 8 +- .../src/methods/unnecessary_filter_map.rs | 14 ++-- .../src/methods/unnecessary_to_owned.rs | 12 +-- .../src/mixed_read_write_in_expression.rs | 18 ++--- .../src/multiple_unsafe_ops_per_block.rs | 6 +- .../src/non_send_fields_in_send_ty.rs | 24 +++--- clippy_lints/src/operators/bit_mask.rs | 16 ++-- clippy_lints/src/operators/identity_op.rs | 16 ++-- clippy_lints/src/operators/manual_div_ceil.rs | 11 ++- clippy_lints/src/operators/mod.rs | 4 +- .../src/redundant_static_lifetimes.rs | 30 +++----- clippy_lints/src/std_instead_of_core.rs | 9 +-- clippy_lints/src/strings.rs | 42 +++++------ clippy_utils/src/msrvs.rs | 10 +-- clippy_utils/src/ty/mod.rs | 9 +-- tests/missing-test-files.rs | 6 +- 28 files changed, 192 insertions(+), 270 deletions(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 72f281ca4d9d..2abe471bed2b 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -526,18 +526,14 @@ fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) { let mut captures = [Capture::EMPTY]; while let Some(name) = cursor.find_any_ident() { match cursor.get_text(name) { - "declare_clippy_lint" => { - if cursor.match_all(&[Bang, OpenBrace], &mut []) && cursor.find_pat(CloseBrace) { - decl_end = Some(cursor.pos()); - } + "declare_clippy_lint" if cursor.match_all(&[Bang, OpenBrace], &mut []) && cursor.find_pat(CloseBrace) => { + decl_end = Some(cursor.pos()); }, - "impl" => { - if cursor.match_all(&[Lt, Lifetime, Gt, CaptureIdent], &mut captures) { - match cursor.get_text(captures[0]) { - "LateLintPass" => context = Some("LateContext"), - "EarlyLintPass" => context = Some("EarlyContext"), - _ => {}, - } + "impl" if cursor.match_all(&[Lt, Lifetime, Gt, CaptureIdent], &mut captures) => { + match cursor.get_text(captures[0]) { + "LateLintPass" => context = Some("LateContext"), + "EarlyLintPass" => context = Some("EarlyContext"), + _ => {}, } }, _ => {}, diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 7bfe9201d812..7ecef955197a 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -39,10 +39,8 @@ pub(super) fn check<'tcx>( // Ignore casts to pointers that are aliases or cfg dependant, e.g. // - p as *const std::ffi::c_char (alias) // - p as *const std::os::raw::c_char (cfg dependant) - TyKind::Path(qpath) => { - if is_ty_alias(&qpath) || is_hir_ty_cfg_dependant(cx, to_pointee.ty) { - return false; - } + TyKind::Path(qpath) if is_ty_alias(&qpath) || is_hir_ty_cfg_dependant(cx, to_pointee.ty) => { + return false; }, // Ignore `p as *const _` TyKind::Infer(()) => return false, diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 595625c08bef..950ac863d755 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -81,10 +81,8 @@ fn check<'tcx>( } cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64; }, - ExprKind::Ret(_) => { - if !matches!(prev_expr, Some(ExprKind::Ret(_))) { - returns += 1; - } + ExprKind::Ret(_) if !matches!(prev_expr, Some(ExprKind::Ret(_))) => { + returns += 1; }, _ => {}, } diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index e7a984694831..0a2871f23964 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -763,19 +763,11 @@ fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) self.check_private_items, ); match item.kind { - ItemKind::Fn { sig, body, .. } => { + ItemKind::Fn { sig, body, .. } if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) - || item.span.in_external_macro(cx.tcx.sess.source_map())) - { - missing_headers::check( - cx, - item.owner_id, - sig, - headers, - Some(body), - self.check_private_items, - ); - } + || item.span.in_external_macro(cx.tcx.sess.source_map())) => + { + missing_headers::check(cx, item.owner_id, sig, headers, Some(body), self.check_private_items); }, ItemKind::Trait(_, _, unsafety, ..) => match (headers.safety, unsafety) { (false, Safety::Unsafe) => span_lint( diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 351d29d87432..acc3e4936e44 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -46,28 +46,28 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { // ((..)) // ^^^^^^ expr // ^^^^ inner - ExprKind::Paren(inner) if matches!(inner.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => { - if expr.span.eq_ctxt(inner.span) + ExprKind::Paren(inner) + if matches!(inner.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) + && expr.span.eq_ctxt(inner.span) && !expr.span.in_external_macro(cx.sess().source_map()) - && check_source(cx, inner) - { - // suggest removing the outer parens + && check_source(cx, inner) => + { + // suggest removing the outer parens - let mut applicability = Applicability::MachineApplicable; - // We don't need to use `snippet_with_context` here, because: - // - if `inner`'s `ctxt` is from macro, we don't lint in the first place (see the check above) - // - otherwise, calling `snippet_with_applicability` on a not-from-macro span is fine - let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); - span_lint_and_sugg( - cx, - DOUBLE_PARENS, - expr.span, - "unnecessary parentheses", - "remove them", - sugg.to_string(), - applicability, - ); - } + let mut applicability = Applicability::MachineApplicable; + // We don't need to use `snippet_with_context` here, because: + // - if `inner`'s `ctxt` is from macro, we don't lint in the first place (see the check above) + // - otherwise, calling `snippet_with_applicability` on a not-from-macro span is fine + let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + expr.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); }, // func((n)) @@ -76,26 +76,24 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { // ^ inner ExprKind::Call(_, args) | ExprKind::MethodCall(box MethodCall { args, .. }) if let [arg] = &**args - && let ExprKind::Paren(inner) = &arg.kind => - { - if expr.span.eq_ctxt(arg.span) + && let ExprKind::Paren(inner) = &arg.kind + && expr.span.eq_ctxt(arg.span) && !arg.span.in_external_macro(cx.sess().source_map()) - && check_source(cx, arg) - { - // suggest removing the inner parens + && check_source(cx, arg) => + { + // suggest removing the inner parens - let mut applicability = Applicability::MachineApplicable; - let sugg = snippet_with_context(cx.sess(), inner.span, arg.span.ctxt(), "_", &mut applicability).0; - span_lint_and_sugg( - cx, - DOUBLE_PARENS, - arg.span, - "unnecessary parentheses", - "remove them", - sugg.to_string(), - applicability, - ); - } + let mut applicability = Applicability::MachineApplicable; + let sugg = snippet_with_context(cx.sess(), inner.span, arg.span.ctxt(), "_", &mut applicability).0; + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + arg.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); }, _ => {}, } diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 8f6de9fc60bd..8b0a4b4d78d9 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -252,10 +252,8 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { } } }, - ExprKind::Binary(op, l, r) => { - if op.node.is_comparison() { - return is_infinite(cx, l).and(is_infinite(cx, r)).and(MaybeInfinite); - } + ExprKind::Binary(op, l, r) if op.node.is_comparison() => { + return is_infinite(cx, l).and(is_infinite(cx, r)).and(MaybeInfinite); }, // TODO: ExprKind::Loop + Match _ => (), } diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 56d535c4f262..2c37e2679d97 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -57,19 +57,17 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { } match parent.kind { - ExprKind::AssignOp(op, lhs, rhs) => { - if lhs.hir_id == expr.hir_id { - *state = if op.node == AssignOpKind::AddAssign - && is_integer_const(self.cx, rhs, 1) - && *state == IncrementVisitorVarState::Initial - && self.depth == 0 - { - IncrementVisitorVarState::IncrOnce - } else { - // Assigned some other value or assigned multiple times - IncrementVisitorVarState::DontWarn - }; - } + ExprKind::AssignOp(op, lhs, rhs) if lhs.hir_id == expr.hir_id => { + *state = if op.node == AssignOpKind::AddAssign + && is_integer_const(self.cx, rhs, 1) + && *state == IncrementVisitorVarState::Initial + && self.depth == 0 + { + IncrementVisitorVarState::IncrOnce + } else { + // Assigned some other value or assigned multiple times + IncrementVisitorVarState::DontWarn + }; }, ExprKind::Assign(lhs, _, _) if lhs.hir_id == expr.hir_id => { *state = IncrementVisitorVarState::DontWarn; diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index 5cf90eecaa97..cb451d8c0da3 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -59,12 +59,11 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { return; } match expr.kind { - ExprKind::Match(scrutinee, [arm1, arm2], _) => { + ExprKind::Match(scrutinee, [arm1, arm2], _) if is_none_pattern(cx, arm2.pat) && check_arms(cx, arm2, arm1) - || is_none_pattern(cx, arm1.pat) && check_arms(cx, arm1, arm2) - { - check_as_ref(cx, scrutinee, span, self.msrv); - } + || is_none_pattern(cx, arm1.pat) && check_arms(cx, arm1, arm2) => + { + check_as_ref(cx, scrutinee, span, self.msrv); }, ExprKind::If(cond, then, Some(other)) => { if let ExprKind::Let(let_expr) = cond.kind @@ -75,34 +74,24 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { check_as_ref(cx, let_expr.init, span, self.msrv); } }, - ExprKind::MethodCall(seg, callee, [], _) => { - if seg.ident.name == sym::unwrap_or_default { - check_map(cx, callee, span, self.msrv); - } + ExprKind::MethodCall(seg, callee, [], _) if seg.ident.name == sym::unwrap_or_default => { + check_map(cx, callee, span, self.msrv); }, ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name { - sym::unwrap_or => { - if is_empty_slice(cx, or) { - check_map(cx, callee, span, self.msrv); - } + sym::unwrap_or if is_empty_slice(cx, or) => { + check_map(cx, callee, span, self.msrv); }, - sym::unwrap_or_else => { - if returns_empty_slice(cx, or) { - check_map(cx, callee, span, self.msrv); - } + sym::unwrap_or_else if returns_empty_slice(cx, or) => { + check_map(cx, callee, span, self.msrv); }, _ => {}, }, ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name { - sym::map_or => { - if is_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { - check_as_ref(cx, callee, span, self.msrv); - } + sym::map_or if is_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) => { + check_as_ref(cx, callee, span, self.msrv); }, - sym::map_or_else => { - if returns_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { - check_as_ref(cx, callee, span, self.msrv); - } + sym::map_or_else if returns_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) => { + check_as_ref(cx, callee, span, self.msrv); }, _ => {}, }, diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index b668f391d67a..1cceecf3b61b 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -242,13 +242,13 @@ fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { && self.cx.qpath_res(path, ex.hir_id) == self.target { match (self.strip_kind, start, end) { - (StripKind::Prefix, Some(start), None) => { - if eq_pattern_length(self.cx, self.pattern, start, self.ctxt) { - self.results.push(ex); - return; - } + (StripKind::Prefix, Some(start), None) + if eq_pattern_length(self.cx, self.pattern, start, self.ctxt) => + { + self.results.push(ex); + return; }, - (StripKind::Suffix, None, Some(end)) => { + (StripKind::Suffix, None, Some(end)) if let ExprKind::Binary( Spanned { node: BinOpKind::Sub, .. @@ -259,11 +259,10 @@ fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { && let Some(left_arg) = len_arg(self.cx, left) && let ExprKind::Path(left_path) = &left_arg.kind && self.cx.qpath_res(left_path, left_arg.hir_id) == self.target - && eq_pattern_length(self.cx, self.pattern, right, self.ctxt) - { - self.results.push(ex); - return; - } + && eq_pattern_length(self.cx, self.pattern, right, self.ctxt) => + { + self.results.push(ex); + return; }, _ => {}, } diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 5709348c5bcd..c95a72da6e29 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -36,7 +36,7 @@ pub(super) fn check_if_let<'tcx>( check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv); } -#[expect(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments, clippy::too_many_lines)] fn check_arm<'tcx>( cx: &LateContext<'tcx>, outer_is_match: bool, diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index e40e21c490f3..3c0fad01835f 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -386,10 +386,8 @@ fn sugg_with_curlies<'a>( | Node::Expr(Expr { kind: ExprKind::Block(..) | ExprKind::ConstBlock(..), .. - }) => { - if needs_var_binding && is_var_binding_used_later { - add_curlies(); - } + }) if needs_var_binding && is_var_binding_used_later => { + add_curlies(); }, Node::Expr(..) | Node::AnonConst(..) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 264405e6c3fb..b0aab3eabb67 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5165,6 +5165,7 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { format_collect::check(cx, expr, m_arg, m_ident_span); }, Some((sym::take, take_self_arg, [take_arg], _, _)) => { + #[expect(clippy::collapsible_match)] if self.msrv.meets(cx, msrvs::STR_REPEAT) { manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); } @@ -5484,7 +5485,9 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { (sym::open, [_]) => { open_options::check(cx, expr, recv); }, - (sym::or_else, [arg]) => { + (sym::or_else, [arg]) => + { + #[expect(clippy::collapsible_match)] if !bind_instead_of_map::check_or_else_err(cx, expr, recv, arg) { unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } @@ -5589,7 +5592,9 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { (sym::try_into, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::TryInto) => { unnecessary_fallible_conversions::check_method(cx, expr); }, - (sym::to_owned, []) => { + (sym::to_owned, []) => + { + #[expect(clippy::collapsible_match)] if !suspicious_to_owned::check(cx, expr, span) { implicit_clone::check(cx, name, expr, recv); } diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index aed4a0075c2f..ef956697c5c4 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -84,10 +84,10 @@ pub(super) fn check<'tcx>( return ControlFlow::Break(()); } }, - hir::ExprKind::MethodCall(..) => { - if check_or_fn_call(cx, name, method_span, receiver, arg, Some(lambda), expr.span, None) { - return ControlFlow::Break(()); - } + hir::ExprKind::MethodCall(..) + if check_or_fn_call(cx, name, method_span, receiver, arg, Some(lambda), expr.span, None) => + { + return ControlFlow::Break(()); }, _ => {}, } diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 72f1c42da2ee..54f3fe410519 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -131,17 +131,13 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc } (true, true) }, - hir::ExprKind::MethodCall(segment, recv, [arg], _) => { + hir::ExprKind::MethodCall(segment, recv, [arg], _) if segment.ident.name == sym::then_some && cx.typeck_results().expr_ty(recv).is_bool() - && arg.res_local_id() == Some(arg_id) - { - // bool.then_some(arg_id) - (false, true) - } else { - // bool.then_some(not arg_id) - (true, true) - } + && arg.res_local_id() == Some(arg_id) => + { + // bool.then_some(arg_id) + (false, true) }, hir::ExprKind::Block(block, _) => block .expr diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 607aaef9627b..bc7e2cd6ec1c 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -482,15 +482,11 @@ fn get_input_traits_and_projections<'tcx>( let mut projection_predicates = Vec::new(); for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() { match predicate.kind().skip_binder() { - ClauseKind::Trait(trait_predicate) => { - if trait_predicate.trait_ref.self_ty() == input { - trait_predicates.push(trait_predicate); - } + ClauseKind::Trait(trait_predicate) if trait_predicate.trait_ref.self_ty() == input => { + trait_predicates.push(trait_predicate); }, - ClauseKind::Projection(projection_predicate) => { - if projection_predicate.projection_term.self_ty() == input { - projection_predicates.push(projection_predicate); - } + ClauseKind::Projection(projection_predicate) if projection_predicate.projection_term.self_ty() == input => { + projection_predicates.push(projection_predicate); }, _ => {}, } diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index ddd4271960e1..53f97abe0b44 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -154,18 +154,14 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) { match e.kind { // fix #10776 ExprKind::Block(block, ..) => match (block.stmts, block.expr) { - (stmts, Some(e)) => { - if stmts.iter().all(|stmt| !stmt_might_diverge(stmt)) { - self.visit_expr(e); - } + (stmts, Some(e)) if stmts.iter().all(|stmt| !stmt_might_diverge(stmt)) => { + self.visit_expr(e); }, - ([first @ .., stmt], None) => { - if first.iter().all(|stmt| !stmt_might_diverge(stmt)) { - match stmt.kind { - StmtKind::Expr(e) | StmtKind::Semi(e) => self.visit_expr(e), - _ => {}, - } - } + ([first @ .., stmt], None) + if first.iter().all(|stmt| !stmt_might_diverge(stmt)) + && let StmtKind::Expr(e) | StmtKind::Semi(e) = stmt.kind => + { + self.visit_expr(e); }, _ => {}, }, diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 42dc9f2f1fa8..ebf7ccfd5ba7 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -165,10 +165,8 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { return self.visit_expr(inner); }, - ExprKind::Field(e, _) => { - if self.typeck_results.expr_ty(e).is_union() { - self.insert_span(expr.span, "union field access occurs here"); - } + ExprKind::Field(e, _) if self.typeck_results.expr_ty(e).is_union() => { + self.insert_span(expr.span, "union field access occurs here"); }, ExprKind::Path(QPath::Resolved( diff --git a/clippy_lints/src/non_send_fields_in_send_ty.rs b/clippy_lints/src/non_send_fields_in_send_ty.rs index fd5562f310e4..3c4dfade3439 100644 --- a/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -205,17 +205,13 @@ fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t .iter() .all(|ty| ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait)), ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, *ty, send_trait), - ty::Adt(_, args) => { - if contains_pointer_like(cx, ty) { - // descends only if ADT contains any raw pointers - args.iter().all(|generic_arg| match generic_arg.kind() { - GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait), - // Lifetimes and const generics are not solid part of ADT and ignored - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => true, - }) - } else { - false - } + ty::Adt(_, args) if contains_pointer_like(cx, ty) => { + // descends only if ADT contains any raw pointers + args.iter().all(|generic_arg| match generic_arg.kind() { + GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait), + // Lifetimes and const generics are not solid part of ADT and ignored + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => true, + }) }, // Raw pointers are `!Send` but allowed by the heuristic ty::RawPtr(_, _) => true, @@ -231,10 +227,8 @@ fn contains_pointer_like<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> b ty::RawPtr(_, _) => { return true; }, - ty::Adt(adt_def, _) => { - if cx.tcx.is_diagnostic_item(sym::NonNull, adt_def.did()) { - return true; - } + ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::NonNull, adt_def.did()) => { + return true; }, _ => (), } diff --git a/clippy_lints/src/operators/bit_mask.rs b/clippy_lints/src/operators/bit_mask.rs index d6af0234f010..7f6dea573de8 100644 --- a/clippy_lints/src/operators/bit_mask.rs +++ b/clippy_lints/src/operators/bit_mask.rs @@ -71,15 +71,13 @@ fn check_bit_mask( span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero"); } }, - BinOpKind::BitOr => { - if mask_value | cmp_value != cmp_value { - span_lint( - cx, - BAD_BIT_MASK, - span, - format!("incompatible bit mask: `_ | {mask_value}` can never be equal to `{cmp_value}`"), - ); - } + BinOpKind::BitOr if mask_value | cmp_value != cmp_value => { + span_lint( + cx, + BAD_BIT_MASK, + span, + format!("incompatible bit mask: `_ | {mask_value}` can never be equal to `{cmp_value}`"), + ); }, _ => (), }, diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs index 43c62e1e131a..f179672b44c7 100644 --- a/clippy_lints/src/operators/identity_op.rs +++ b/clippy_lints/src/operators/identity_op.rs @@ -52,11 +52,9 @@ pub(crate) fn check<'tcx>( span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, - BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - if is_redundant_op(cx, right, 0, ctxt) { - let paren = needs_parenthesis(cx, expr, left); - span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); - } + BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub if is_redundant_op(cx, right, 0, ctxt) => { + let paren = needs_parenthesis(cx, expr, left); + span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); }, BinOpKind::Mul => { if is_redundant_op(cx, left, 1, ctxt) { @@ -67,11 +65,9 @@ pub(crate) fn check<'tcx>( span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, - BinOpKind::Div => { - if is_redundant_op(cx, right, 1, ctxt) { - let paren = needs_parenthesis(cx, expr, left); - span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); - } + BinOpKind::Div if is_redundant_op(cx, right, 1, ctxt) => { + let paren = needs_parenthesis(cx, expr, left); + span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); }, BinOpKind::BitAnd => { if is_redundant_op(cx, left, -1, ctxt) { diff --git a/clippy_lints/src/operators/manual_div_ceil.rs b/clippy_lints/src/operators/manual_div_ceil.rs index 5ed923d719bc..6751532ecbc7 100644 --- a/clippy_lints/src/operators/manual_div_ceil.rs +++ b/clippy_lints/src/operators/manual_div_ceil.rs @@ -73,14 +73,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: & build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability); } }, - ExprKind::MethodCall(method, receiver, [next_multiple_of_arg], _) => { - // x.next_multiple_of(Y) / Y + ExprKind::MethodCall(method, receiver, [next_multiple_of_arg], _) if method.ident.name == sym::next_multiple_of && check_int_ty(cx.typeck_results().expr_ty(receiver)) - && check_eq_expr(cx, next_multiple_of_arg, rhs) - { - build_suggestion(cx, expr, receiver, rhs, &mut applicability); - } + && check_eq_expr(cx, next_multiple_of_arg, rhs) => + { + // x.next_multiple_of(Y) / Y + build_suggestion(cx, expr, receiver, rhs, &mut applicability); }, ExprKind::Call(callee, [receiver, next_multiple_of_arg]) => { // int_type::next_multiple_of(x, Y) / Y diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 383b135dfa50..5b00f1b0bd13 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -1064,7 +1064,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { assign_op_pattern::check(cx, e, lhs, rhs, self.msrv); self_assignment::check(cx, e, lhs, rhs); }, - ExprKind::Unary(op, arg) => { + ExprKind::Unary(op, arg) => + { + #[expect(clippy::collapsible_match)] if op == UnOp::Neg { self.arithmetic_context.check_negate(cx, e, arg); } diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index b4e1f70d1535..da85125730a8 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -66,25 +66,19 @@ fn visit_type(ty: &Ty, cx: &EarlyContext<'_>, reason: &'static str) { // Match the 'static lifetime if let Some(lifetime) = *optional_lifetime { match borrow_type.ty.kind { - TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => { - if lifetime.ident.name == kw::StaticLifetime { - let snip = snippet(cx, borrow_type.ty.span, ""); - let sugg = format!("&{}{snip}", borrow_type.mutbl.prefix_str()); - span_lint_and_then( - cx, - REDUNDANT_STATIC_LIFETIMES, - lifetime.ident.span, - reason, - |diag| { - diag.span_suggestion( - ty.span, - "consider removing `'static`", - sugg, - Applicability::MachineApplicable, //snippet - ); - }, + TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) + if lifetime.ident.name == kw::StaticLifetime => + { + let snip = snippet(cx, borrow_type.ty.span, ""); + let sugg = format!("&{}{snip}", borrow_type.mutbl.prefix_str()); + span_lint_and_then(cx, REDUNDANT_STATIC_LIFETIMES, lifetime.ident.span, reason, |diag| { + diag.span_suggestion( + ty.span, + "consider removing `'static`", + sugg, + Applicability::MachineApplicable, //snippet ); - } + }); }, _ => {}, } diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index 8c4a50041e67..143be5137038 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -138,14 +138,7 @@ fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { return; }, }, - sym::alloc => { - if cx.tcx.crate_name(def_id.krate) == sym::core { - (ALLOC_INSTEAD_OF_CORE, "alloc", "core") - } else { - self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); - return; - } - }, + sym::alloc if cx.tcx.crate_name(def_id.krate) == sym::core => (ALLOC_INSTEAD_OF_CORE, "alloc", "core"), _ => { self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); return; diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 509ad4e4fcb3..9d17779419fb 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -155,36 +155,32 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { }, left, _, - ) => { - if is_string(cx, left) { - if !is_lint_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) { - let parent = get_parent_expr(cx, e); - if let Some(p) = parent + ) if is_string(cx, left) => { + if !is_lint_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) { + let parent = get_parent_expr(cx, e); + if let Some(p) = parent && let ExprKind::Assign(target, _, _) = p.kind // avoid duplicate matches && SpanlessEq::new(cx).eq_expr(target, left) - { - return; - } + { + return; } - span_lint( - cx, - STRING_ADD, - e.span, - "you added something to a string. Consider using `String::push_str()` instead", - ); } + span_lint( + cx, + STRING_ADD, + e.span, + "you added something to a string. Consider using `String::push_str()` instead", + ); }, - ExprKind::Assign(target, src, _) => { - if is_string(cx, target) && is_add(cx, src, target) { - span_lint( - cx, - STRING_ADD_ASSIGN, - e.span, - "you assigned the result of adding something to this string. Consider using \ + ExprKind::Assign(target, src, _) if is_string(cx, target) && is_add(cx, src, target) => { + span_lint( + cx, + STRING_ADD_ASSIGN, + e.span, + "you assigned the result of adding something to this string. Consider using \ `String::push_str()` instead", - ); - } + ); }, ExprKind::Index(target, _idx, _) => { let e_ty = cx.typeck_results().expr_ty_adjusted(target).peel_refs(); diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 18fab6035f28..5aa457b9d19c 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -142,12 +142,10 @@ pub fn read_cargo(&mut self, sess: &Session) { match (self.0, cargo_msrv) { (None, Some(cargo_msrv)) => self.0 = Some(cargo_msrv), - (Some(clippy_msrv), Some(cargo_msrv)) => { - if clippy_msrv != cargo_msrv { - sess.dcx().warn(format!( - "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" - )); - } + (Some(clippy_msrv), Some(cargo_msrv)) if clippy_msrv != cargo_msrv => { + sess.dcx().warn(format!( + "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" + )); }, _ => {}, } diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 639492b75747..bd56a3d86b44 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -114,16 +114,15 @@ fn contains_ty_adt_constructor_opaque_inner<'tcx>( match predicate.kind().skip_binder() { // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through // and check substitutions to find `U`. - ty::ClauseKind::Trait(trait_predicate) => { + ty::ClauseKind::Trait(trait_predicate) if trait_predicate .trait_ref .args .types() .skip(1) // Skip the implicit `Self` generic parameter - .any(|ty| contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen)) - { - return true; - } + .any(|ty| contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen)) => + { + return true; }, // For `impl Trait`, it will register a predicate of `::Assoc = U`, // so we check the term for `U`. diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index 9fff3132498d..dbb25ac545fc 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -53,10 +53,8 @@ fn explore_directory(dir: &Path) -> Vec { if let Some(ext) = path.extension() { match ext.to_str().unwrap() { "rs" | "toml" => current_file.clone_from(&file_prefix), - "stderr" | "stdout" => { - if file_prefix != current_file { - missing_files.push(path.to_str().unwrap().to_string()); - } + "stderr" | "stdout" if file_prefix != current_file => { + missing_files.push(path.to_str().unwrap().to_string()); }, _ => {}, }