Skip to content

Commit a50a89f

Browse files
authored
Rollup merge of rust-lang#137409 - estebank:match-arm, r=compiler-errors
Tweak comma handling of "missing match arm" suggestion and fix "remove this arm" suggestion, and make suggestion verbose Better track trailing commas in match arms. Do not suggest adding trailing comma to match arm with block body. Better heuristic for "is this match in one line". When encountering an unreachable match arm, (correctly) suggest removing the entire arm: ``` error: a never pattern is always unreachable --> $DIR/ICE-130779-never-arm-no-oatherwise-block.rs:10:20 | LL | ! if true => {} | ^^ this will never be executed | help: remove the unreachable match arm | LL - ! if true => {} | ``` Noticed in rust-lang#137343 (comment). r? `@compiler-errors` The first commit is independent of the second, but to make the second one produce accurate suggestions the span needs to include the trailing comma, hence the grouping of both changes in this PR.
2 parents fc997fb + 88398eb commit a50a89f

File tree

98 files changed

+1993
-848
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+1993
-848
lines changed

compiler/rustc_ast_lowering/messages.ftl

+2-2
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,11 @@ ast_lowering_misplaced_relax_trait_bound =
133133
ast_lowering_never_pattern_with_body =
134134
a never pattern is always unreachable
135135
.label = this will never be executed
136-
.suggestion = remove this expression
136+
.suggestion = remove the match arm expression
137137
138138
ast_lowering_never_pattern_with_guard =
139139
a guard on a never pattern will never be run
140-
.suggestion = remove this guard
140+
.suggestion = remove the match arm guard
141141
142142
ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed in argument-position `impl Trait`
143143

compiler/rustc_ast_lowering/src/errors.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ pub(crate) struct MisplacedRelaxTraitBound {
335335
pub(crate) struct MatchArmWithNoBody {
336336
#[primary_span]
337337
pub span: Span,
338-
#[suggestion(code = " => todo!(),", applicability = "has-placeholders")]
338+
#[suggestion(code = " => todo!(),", applicability = "has-placeholders", style = "verbose")]
339339
pub suggestion: Span,
340340
}
341341

@@ -344,16 +344,18 @@ pub(crate) struct MatchArmWithNoBody {
344344
pub(crate) struct NeverPatternWithBody {
345345
#[primary_span]
346346
#[label]
347-
#[suggestion(code = "", applicability = "maybe-incorrect")]
348347
pub span: Span,
348+
#[suggestion(code = ",", applicability = "maybe-incorrect", style = "verbose")]
349+
pub removal_span: Span,
349350
}
350351

351352
#[derive(Diagnostic)]
352353
#[diag(ast_lowering_never_pattern_with_guard)]
353354
pub(crate) struct NeverPatternWithGuard {
354355
#[primary_span]
355-
#[suggestion(code = "", applicability = "maybe-incorrect")]
356356
pub span: Span,
357+
#[suggestion(code = ",", applicability = "maybe-incorrect", style = "verbose")]
358+
pub removal_span: Span,
357359
}
358360

359361
#[derive(Diagnostic)]

compiler/rustc_ast_lowering/src/expr.rs

+18-2
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
676676
{
677677
self.lower_expr(body)
678678
} else {
679+
let removal_span = |removal_span: Span| {
680+
// Seek upwards in the macro call sites to see if we find the place where
681+
// `pat!()` was called so that we can get the right span to remove.
682+
let Some(pat_span) = pat.span.find_ancestor_in_same_ctxt(arm.span) else {
683+
return removal_span;
684+
};
685+
// - pat!() => {}
686+
// + pat!(),
687+
pat_span.shrink_to_hi().with_hi(arm.span.hi())
688+
};
679689
// Either `body.is_none()` or `is_never_pattern` here.
680690
if !is_never_pattern {
681691
if self.tcx.features().never_patterns() {
@@ -684,9 +694,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
684694
self.dcx().emit_err(MatchArmWithNoBody { span, suggestion });
685695
}
686696
} else if let Some(body) = &arm.body {
687-
self.dcx().emit_err(NeverPatternWithBody { span: body.span });
697+
self.dcx().emit_err(NeverPatternWithBody {
698+
span: body.span,
699+
removal_span: removal_span(body.span),
700+
});
688701
} else if let Some(g) = &arm.guard {
689-
self.dcx().emit_err(NeverPatternWithGuard { span: g.span });
702+
self.dcx().emit_err(NeverPatternWithGuard {
703+
span: g.span,
704+
removal_span: removal_span(g.span),
705+
});
690706
}
691707

692708
// We add a fake `loop {}` arm body so that it typecks to `!`. The mir lowering of never

compiler/rustc_ast_lowering/src/pat.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
2020
self.arena.alloc(self.lower_pat_mut(pattern))
2121
}
2222

23-
fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> {
23+
fn lower_pat_mut(&mut self, outer_pattern: &Pat) -> hir::Pat<'hir> {
24+
let mut pattern = outer_pattern;
2425
ensure_sufficient_stack(|| {
2526
// loop here to avoid recursion
2627
let pat_hir_id = self.lower_node_id(pattern.id);
@@ -147,7 +148,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
147148
}
148149
};
149150

150-
self.pat_with_node_id_of(pattern, node, pat_hir_id)
151+
self.pat_with_node_id_of(outer_pattern, node, pat_hir_id)
151152
})
152153
}
153154

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

+61-25
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use rustc_session::lint::builtin::{
2424
};
2525
use rustc_span::edit_distance::find_best_match_for_name;
2626
use rustc_span::hygiene::DesugaringKind;
27-
use rustc_span::{Ident, Span};
27+
use rustc_span::{ExpnKind, Ident, Span};
2828
use rustc_trait_selection::infer::InferCtxtExt;
2929
use tracing::instrument;
3030

@@ -543,6 +543,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
543543
witnesses,
544544
arms,
545545
braces_span,
546+
expr_span,
546547
));
547548
}
548549
}
@@ -1210,6 +1211,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
12101211
witnesses: Vec<WitnessPat<'p, 'tcx>>,
12111212
arms: &[ArmId],
12121213
braces_span: Option<Span>,
1214+
expr_span: Span,
12131215
) -> ErrorGuaranteed {
12141216
let is_empty_match = arms.is_empty();
12151217
let non_empty_enum = match scrut_ty.kind() {
@@ -1350,43 +1352,59 @@ fn report_non_exhaustive_match<'p, 'tcx>(
13501352
format!(" {{{indentation}{more}{suggested_arm},{indentation}}}",),
13511353
));
13521354
}
1353-
[only] => {
1355+
[only] if let Some(braces_span) = braces_span => {
13541356
let only = &thir[*only];
1355-
let (pre_indentation, is_multiline) = if let Some(snippet) =
1356-
sm.indentation_before(only.span)
1357-
&& let Ok(with_trailing) =
1358-
sm.span_extend_while(only.span, |c| c.is_whitespace() || c == ',')
1359-
&& sm.is_multiline(with_trailing)
1360-
{
1361-
(format!("\n{snippet}"), true)
1362-
} else {
1363-
(" ".to_string(), false)
1364-
};
13651357
let only_body = &thir[only.body];
1366-
let comma = if matches!(only_body.kind, ExprKind::Block { .. })
1367-
&& only.span.eq_ctxt(only_body.span)
1368-
&& is_multiline
1358+
let pre_indentation = if let Some(snippet) = sm.indentation_before(only.span)
1359+
&& sm.is_multiline(braces_span)
13691360
{
1370-
""
1361+
format!("\n{snippet}")
13711362
} else {
1372-
","
1363+
" ".to_string()
1364+
};
1365+
let comma = match only_body.kind {
1366+
ExprKind::Block { .. } if only_body.span.eq_ctxt(sp) => "",
1367+
ExprKind::Scope { value, .. }
1368+
if let expr = &thir[value]
1369+
&& let ExprKind::Block { .. } = expr.kind
1370+
&& expr.span.eq_ctxt(sp) =>
1371+
{
1372+
""
1373+
}
1374+
_ if sm
1375+
.span_to_snippet(only.span)
1376+
.map_or(false, |snippet| snippet.ends_with(",")) =>
1377+
{
1378+
""
1379+
}
1380+
_ => ",",
13731381
};
13741382
suggestion = Some((
13751383
only.span.shrink_to_hi(),
13761384
format!("{comma}{pre_indentation}{suggested_arm}"),
13771385
));
13781386
}
1379-
[.., prev, last] => {
1387+
[.., prev, last] if braces_span.is_some() => {
13801388
let prev = &thir[*prev];
13811389
let last = &thir[*last];
13821390
if prev.span.eq_ctxt(last.span) {
13831391
let last_body = &thir[last.body];
1384-
let comma = if matches!(last_body.kind, ExprKind::Block { .. })
1385-
&& last.span.eq_ctxt(last_body.span)
1386-
{
1387-
""
1388-
} else {
1389-
","
1392+
let comma = match last_body.kind {
1393+
ExprKind::Block { .. } if last_body.span.eq_ctxt(sp) => "",
1394+
ExprKind::Scope { value, .. }
1395+
if let expr = &thir[value]
1396+
&& let ExprKind::Block { .. } = expr.kind
1397+
&& expr.span.eq_ctxt(sp) =>
1398+
{
1399+
""
1400+
}
1401+
_ if sm
1402+
.span_to_snippet(last.span)
1403+
.map_or(false, |snippet| snippet.ends_with(",")) =>
1404+
{
1405+
""
1406+
}
1407+
_ => ",",
13901408
};
13911409
let spacing = if sm.is_multiline(prev.span.between(last.span)) {
13921410
sm.indentation_before(last.span).map(|indent| format!("\n{indent}"))
@@ -1401,7 +1419,25 @@ fn report_non_exhaustive_match<'p, 'tcx>(
14011419
}
14021420
}
14031421
}
1404-
_ => {}
1422+
_ => {
1423+
if let Some(data) = expr_span.macro_backtrace().next()
1424+
&& let ExpnKind::Macro(macro_kind, name) = data.kind
1425+
{
1426+
let macro_kind = macro_kind.descr();
1427+
// We don't want to point at the macro invocation place as that is already shown
1428+
// or talk about macro-backtrace and the macro's name, as we are already doing
1429+
// that as part of this note.
1430+
let mut span: MultiSpan = expr_span.with_ctxt(data.call_site.ctxt()).into();
1431+
span.push_span_label(data.def_site, "");
1432+
err.span_note(
1433+
span,
1434+
format!(
1435+
"within {macro_kind} `{name}`, this `match` expression doesn't expand to \
1436+
cover all patterns",
1437+
),
1438+
);
1439+
}
1440+
}
14051441
}
14061442

14071443
let msg = format!(

compiler/rustc_parse/src/parser/expr.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -3074,6 +3074,7 @@ impl<'a> Parser<'a> {
30743074
let (pat, guard) = this.parse_match_arm_pat_and_guard()?;
30753075

30763076
let span_before_body = this.prev_token.span;
3077+
let mut comma = None;
30773078
let arm_body;
30783079
let is_fat_arrow = this.check(exp!(FatArrow));
30793080
let is_almost_fat_arrow =
@@ -3143,6 +3144,7 @@ impl<'a> Parser<'a> {
31433144
{
31443145
let body = this.mk_expr_err(span, guar);
31453146
arm_body = Some(body);
3147+
let _ = this.eat(exp!(Comma));
31463148
Ok(Recovered::Yes(guar))
31473149
} else {
31483150
let expr_span = expr.span;
@@ -3183,8 +3185,12 @@ impl<'a> Parser<'a> {
31833185
})
31843186
}
31853187
};
3188+
if let TokenKind::Comma = this.prev_token.kind {
3189+
comma = Some(this.prev_token.span);
3190+
}
31863191

3187-
let hi_span = arm_body.as_ref().map_or(span_before_body, |body| body.span);
3192+
let hi_span =
3193+
comma.unwrap_or(arm_body.as_ref().map_or(span_before_body, |body| body.span));
31883194
let arm_span = lo.to(hi_span);
31893195

31903196
// We want to recover:

src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ pub(super) fn check<'tcx>(
7171
{
7272
let mut applicability = Applicability::MachineApplicable;
7373
let mut pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability);
74-
if matches!(pat.kind, PatKind::Or(..)) {
74+
if matches!(pat.kind, PatKind::Or(..)) && !pat_snip.starts_with("(") {
7575
pat_snip = format!("({pat_snip})").into();
7676
}
7777
let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);

src/tools/clippy/tests/ui/match_same_arms.stderr

+8-8
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ note: `_` wildcard arm here
99
--> tests/ui/match_same_arms.rs:14:9
1010
|
1111
LL | _ => 0,
12-
| ^^^^^^
12+
| ^^^^^^^
1313
= note: `-D clippy::match-same-arms` implied by `-D warnings`
1414
= help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]`
1515

1616
error: this match arm has an identical body to another arm
1717
--> tests/ui/match_same_arms.rs:18:9
1818
|
1919
LL | (1, .., 3) => 42,
20-
| ^^^^^^^^^^^^^^^^
20+
| ^^^^^^^^^^^^^^^^^
2121
|
2222
= help: try changing either arm body
2323
help: or try merging the arm patterns and removing the obsolete arm
@@ -30,7 +30,7 @@ error: this match arm has an identical body to another arm
3030
--> tests/ui/match_same_arms.rs:25:9
3131
|
3232
LL | 51 => 1,
33-
| ^^^^^^^
33+
| ^^^^^^^^
3434
|
3535
= help: try changing either arm body
3636
help: or try merging the arm patterns and removing the obsolete arm
@@ -44,7 +44,7 @@ error: this match arm has an identical body to another arm
4444
--> tests/ui/match_same_arms.rs:26:9
4545
|
4646
LL | 41 => 2,
47-
| ^^^^^^^
47+
| ^^^^^^^^
4848
|
4949
= help: try changing either arm body
5050
help: or try merging the arm patterns and removing the obsolete arm
@@ -57,7 +57,7 @@ error: this match arm has an identical body to another arm
5757
--> tests/ui/match_same_arms.rs:33:9
5858
|
5959
LL | 2 => 2,
60-
| ^^^^^^
60+
| ^^^^^^^
6161
|
6262
= help: try changing either arm body
6363
help: or try merging the arm patterns and removing the obsolete arm
@@ -71,7 +71,7 @@ error: this match arm has an identical body to another arm
7171
--> tests/ui/match_same_arms.rs:35:9
7272
|
7373
LL | 3 => 2,
74-
| ^^^^^^
74+
| ^^^^^^^
7575
|
7676
= help: try changing either arm body
7777
help: or try merging the arm patterns and removing the obsolete arm
@@ -85,7 +85,7 @@ error: this match arm has an identical body to another arm
8585
--> tests/ui/match_same_arms.rs:33:9
8686
|
8787
LL | 2 => 2,
88-
| ^^^^^^
88+
| ^^^^^^^
8989
|
9090
= help: try changing either arm body
9191
help: or try merging the arm patterns and removing the obsolete arm
@@ -99,7 +99,7 @@ error: this match arm has an identical body to another arm
9999
--> tests/ui/match_same_arms.rs:52:17
100100
|
101101
LL | CommandInfo::External { name, .. } => name.to_string(),
102-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
102+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
103103
|
104104
= help: try changing either arm body
105105
help: or try merging the arm patterns and removing the obsolete arm

0 commit comments

Comments
 (0)