diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 2eb15928f25a..344e68a6df8f 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -165,11 +165,21 @@ fn check_arm<'tcx>( let (paren_start, inner_if_span, paren_end) = peel_parens(cx, 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())]; + if matches!(outer_then_body.kind, ExprKind::Block(..)) { - let outer_then_open_bracket = outer_then_body - .span - .split_at(1) - .0 + let block_span = outer_then_body.span; + // The Block Span can start with a label so finding actual opening braces instead of assuming it's + // first Byte + let block_snippet = snippet(cx, block_span, ""); + let Some(brace_offset) = block_snippet.find('{') else { + return; + }; + let brace_pos = block_span.lo() + BytePos(u32::try_from(brace_offset).unwrap()); + + let label_prefix = block_snippet[..brace_offset].trim(); + let outer_then_open_bracket = block_span + .with_lo(brace_pos) + .with_hi(brace_pos + BytePos(1)) .with_leading_whitespace(cx) .into_span(); let outer_then_closing_bracket = { @@ -180,6 +190,10 @@ fn check_arm<'tcx>( }; sugg.push((outer_arrow_end.to(outer_then_open_bracket), String::new())); sugg.push((outer_then_closing_bracket, String::new())); + + if !label_prefix.is_empty() { + sugg[0].1 = format!("=> {label_prefix} "); + } } else { sugg.push((outer_arrow_end.until(inner_if), " ".to_string())); } diff --git a/tests/ui/collapsible_match_fixable.fixed b/tests/ui/collapsible_match_fixable.fixed index 671a785abdbe..2e34d0564f02 100644 --- a/tests/ui/collapsible_match_fixable.fixed +++ b/tests/ui/collapsible_match_fixable.fixed @@ -75,3 +75,20 @@ fn issue16894() { //~^ collapsible_match }; } + +// https://github.com/rust-lang/rust-clippy/issues/17427 +// `collapsible_match` suggested wrong spans when the outer arm's block has a +// label, deleting the label's leading `'` instead of preserving it after `=>`. +fn issue17427() { + let x: Result, ()> = Ok(Some(1)); + match x { + Ok(Some(_c)) + if true => 'label: { + //~^ collapsible_match + let Some(_y) = Some(0) else { + break 'label; + }; + }, + _ => (), + } +} diff --git a/tests/ui/collapsible_match_fixable.rs b/tests/ui/collapsible_match_fixable.rs index 536f9e2e1a55..8d709b4c038f 100644 --- a/tests/ui/collapsible_match_fixable.rs +++ b/tests/ui/collapsible_match_fixable.rs @@ -77,3 +77,21 @@ fn issue16894() { //~^ collapsible_match }; } + +// https://github.com/rust-lang/rust-clippy/issues/17427 +// `collapsible_match` suggested wrong spans when the outer arm's block has a +// label, deleting the label's leading `'` instead of preserving it after `=>`. +fn issue17427() { + let x: Result, ()> = Ok(Some(1)); + match x { + Ok(Some(_c)) => 'label: { + if true { + //~^ collapsible_match + let Some(_y) = Some(0) else { + break 'label; + }; + } + }, + _ => (), + } +} diff --git a/tests/ui/collapsible_match_fixable.stderr b/tests/ui/collapsible_match_fixable.stderr index ef078b2cb1fd..cf6072d87edd 100644 --- a/tests/ui/collapsible_match_fixable.stderr +++ b/tests/ui/collapsible_match_fixable.stderr @@ -112,5 +112,26 @@ LL - 11 => 0, 12 => if a == b { 1 } else { 0 }, _ => 0, LL + 11 => 0, 12 if a == b => { 1 }, _ => 0, | -error: aborting due to 8 previous errors +error: this `if` can be collapsed into the outer `match` + --> tests/ui/collapsible_match_fixable.rs:88:13 + | +LL | / if true { +LL | | +LL | | let Some(_y) = Some(0) else { +LL | | break 'label; +LL | | }; +LL | | } + | |_____________^ + | +help: collapse nested if block + | +LL ~ Ok(Some(_c)) +LL ~ if true => 'label: { +LL | +... +LL | }; +LL ~ }, + | + +error: aborting due to 9 previous errors