Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions clippy_lints/src/matches/collapsible_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,17 @@ use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or};
pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], msrv: Msrv) {
if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
for arm in arms {
check_arm(cx, true, arm.pat, expr, arm.body, arm.guard, Some(els_arm.body), msrv);
check_arm(
cx,
true,
arm.pat,
expr,
arm.body,
arm.guard,
Some(els_arm.body),
Some(els_arm.pat),
msrv,
);
}
}
}
Expand All @@ -37,7 +47,7 @@ pub(super) fn check_if_let<'tcx>(
let_expr: &'tcx Expr<'_>,
msrv: Msrv,
) {
check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv);
check_arm(cx, false, pat, let_expr, body, None, else_expr, None, msrv);
}

#[expect(clippy::too_many_arguments, clippy::too_many_lines)]
Expand All @@ -49,6 +59,7 @@ fn check_arm<'tcx>(
outer_then_body: &'tcx Expr<'tcx>,
outer_guard: Option<&'tcx Expr<'tcx>>,
outer_else_body: Option<&'tcx Expr<'tcx>>,
outer_else_pat: Option<&'tcx Pat<'tcx>>,
msrv: Msrv,
) {
let inner_expr = peel_blocks_with_stmt(outer_then_body);
Expand Down Expand Up @@ -187,6 +198,15 @@ fn check_arm<'tcx>(
sugg.push((else_inner_span, String::new()));
}

// When collapsing into a match guard, the guard doesn't count toward
// exhaustiveness. If the else arm's pattern is not a true wildcard
// (e.g. `None`), we must replace it with `_` to maintain exhaustiveness.
if let Some(els_pat) = outer_else_pat
&& !matches!(els_pat.kind, PatKind::Wild | PatKind::Binding(..))
{
sugg.push((els_pat.span, "_".to_string()));
}

diag.multipart_suggestion("collapse nested if block", sugg, Applicability::MachineApplicable);
},
);
Expand Down
15 changes: 15 additions & 0 deletions tests/ui/collapsible_match_fixable.fixed
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
#![warn(clippy::collapsible_match)]
#![allow(clippy::single_match, clippy::redundant_guards)]

// issue #16860: collapsible_match suggestion causes non-exhaustive pattern error
// when the else arm is `None` (not a true wildcard)
fn issue16860() -> Option<i32> {
let x: Option<i32> = Some(1);
match x {
_ => {}
Some(v)
if v > 0 => {
//~^ collapsible_match
return Some(v);
}
}
None
}

fn issue16558() {
let opt = Some(1);
let _ = match opt {
Expand Down
16 changes: 16 additions & 0 deletions tests/ui/collapsible_match_fixable.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
#![warn(clippy::collapsible_match)]
#![allow(clippy::single_match, clippy::redundant_guards)]

// issue #16860: collapsible_match suggestion causes non-exhaustive pattern error
// when the else arm is `None` (not a true wildcard)
fn issue16860() -> Option<i32> {
let x: Option<i32> = Some(1);
match x {
None => {},
Some(v) => {
if v > 0 {
//~^ collapsible_match
return Some(v);
}
},
}
None
}

fn issue16558() {
let opt = Some(1);
let _ = match opt {
Expand Down
31 changes: 25 additions & 6 deletions tests/ui/collapsible_match_fixable.stderr
Original file line number Diff line number Diff line change
@@ -1,21 +1,40 @@
error: this `if` can be collapsed into the outer `match`
--> tests/ui/collapsible_match_fixable.rs:8:13
--> tests/ui/collapsible_match_fixable.rs:11:13
|
LL | if s == 1 { s } else { 1 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | / if v > 0 {
LL | |
LL | | return Some(v);
LL | | }
| |_____________^
|
= 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 ~ _ => {}
LL ~ Some(v)
LL ~ if v > 0 => {
LL |
LL | return Some(v);
LL ~ }
|

error: this `if` can be collapsed into the outer `match`
--> tests/ui/collapsible_match_fixable.rs:24:13
|
LL | if s == 1 { s } else { 1 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
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
--> tests/ui/collapsible_match_fixable.rs:32:13
|
LL | / (if s == 1 {
LL | |
Expand All @@ -33,7 +52,7 @@ LL ~ },
|

error: this `if` can be collapsed into the outer `match`
--> tests/ui/collapsible_match_fixable.rs:26:13
--> tests/ui/collapsible_match_fixable.rs:42:13
|
LL | if s == 1 { s } else { 1 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -46,5 +65,5 @@ LL |
LL ~ ,
|

error: aborting due to 3 previous errors
error: aborting due to 4 previous errors

Loading