Skip to content

Commit

Permalink
Add a machine-applicable suggestion to "unreachable pattern"
Browse files Browse the repository at this point in the history
  • Loading branch information
Nadrieril committed Sep 13, 2024
1 parent 0307e40 commit 1f69638
Show file tree
Hide file tree
Showing 19 changed files with 502 additions and 98 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_mir_build/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ mir_build_unreachable_pattern = unreachable pattern
.unreachable_covered_by_catchall = matches any value
.unreachable_covered_by_one = matches all the relevant values
.unreachable_covered_by_many = multiple earlier patterns match some of the same values
.suggestion = remove the match arm
mir_build_unsafe_fn_safe_body = an unsafe function restricts its caller, but its body is safe by default
mir_build_unsafe_not_inherited = items do not inherit unsafety from separate enclosing items
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_mir_build/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,8 @@ pub(crate) struct UnreachablePattern<'tcx> {
#[note(mir_build_unreachable_covered_by_many)]
pub(crate) covered_by_many: Option<MultiSpan>,
pub(crate) covered_by_many_n_more_count: usize,
#[suggestion(code = "", applicability = "machine-applicable")]
pub(crate) suggest_remove: Option<Span>,
}

#[derive(Diagnostic)]
Expand Down
35 changes: 30 additions & 5 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
// Emit lints in the order in which they occur in the file.
redundant_subpats.sort_unstable_by_key(|(pat, _)| pat.data().span);
for (pat, explanation) in redundant_subpats {
report_unreachable_pattern(cx, arm.arm_data, pat, &explanation)
report_unreachable_pattern(cx, arm.arm_data, pat, &explanation, None)
}
}
}
Expand Down Expand Up @@ -474,7 +474,11 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
hir::MatchSource::ForLoopDesugar
| hir::MatchSource::Postfix
| hir::MatchSource::Normal
| hir::MatchSource::FormatArgs => report_arm_reachability(&cx, &report),
| hir::MatchSource::FormatArgs => {
let is_match_arm =
matches!(source, hir::MatchSource::Postfix | hir::MatchSource::Normal);
report_arm_reachability(&cx, &report, is_match_arm);
}
// Unreachable patterns in try and await expressions occur when one of
// the arms are an uninhabited type. Which is OK.
hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar(_) => {}
Expand Down Expand Up @@ -626,7 +630,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
) -> Result<RefutableFlag, ErrorGuaranteed> {
let (cx, report) = self.analyze_binding(pat, Refutable, scrut)?;
// Report if the pattern is unreachable, which can only occur when the type is uninhabited.
report_arm_reachability(&cx, &report);
report_arm_reachability(&cx, &report, false);
// If the list of witnesses is empty, the match is exhaustive, i.e. the `if let` pattern is
// irrefutable.
Ok(if report.non_exhaustiveness_witnesses.is_empty() { Irrefutable } else { Refutable })
Expand Down Expand Up @@ -916,6 +920,7 @@ fn report_unreachable_pattern<'p, 'tcx>(
hir_id: HirId,
pat: &DeconstructedPat<'p, 'tcx>,
explanation: &RedundancyExplanation<'p, 'tcx>,
whole_arm_span: Option<Span>,
) {
static CAP_COVERED_BY_MANY: usize = 4;
let pat_span = pat.data().span;
Expand All @@ -928,13 +933,15 @@ fn report_unreachable_pattern<'p, 'tcx>(
covered_by_one: None,
covered_by_many: None,
covered_by_many_n_more_count: 0,
suggest_remove: None,
};
match explanation.covered_by.as_slice() {
[] => {
// Empty pattern; we report the uninhabited type that caused the emptiness.
lint.span = None; // Don't label the pattern itself
lint.uninhabited_note = Some(()); // Give a link about empty types
lint.matches_no_values = Some(pat_span);
lint.suggest_remove = whole_arm_span; // Suggest to remove the match arm
pat.walk(&mut |subpat| {
let ty = **subpat.ty();
if cx.is_uninhabited(ty) {
Expand Down Expand Up @@ -982,10 +989,28 @@ fn report_unreachable_pattern<'p, 'tcx>(
}

/// Report unreachable arms, if any.
fn report_arm_reachability<'p, 'tcx>(cx: &PatCtxt<'p, 'tcx>, report: &UsefulnessReport<'p, 'tcx>) {
fn report_arm_reachability<'p, 'tcx>(
cx: &PatCtxt<'p, 'tcx>,
report: &UsefulnessReport<'p, 'tcx>,
is_match_arm: bool,
) {
let sm = cx.tcx.sess.source_map();
for (arm, is_useful) in report.arm_usefulness.iter() {
if let Usefulness::Redundant(explanation) = is_useful {
report_unreachable_pattern(cx, arm.arm_data, arm.pat, explanation)
let hir_id = arm.arm_data;
let arm_span = cx.tcx.hir().span(hir_id);
let whole_arm_span = if is_match_arm {
// If the arm is followed by a comma, extend the span to include it.
let with_whitespace = sm.span_extend_while_whitespace(arm_span);
if let Some(comma) = sm.span_look_ahead(with_whitespace, ",", Some(1)) {
Some(arm_span.to(comma))
} else {
Some(arm_span)
}
} else {
None
};
report_unreachable_pattern(cx, hir_id, arm.pat, explanation, whole_arm_span)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:17:9
|
LL | _ => {}
| ^ matches no values because `EmptyEnum` is uninhabited
| ^------
| |
| matches no values because `EmptyEnum` is uninhabited
| help: remove the match arm
|
= note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types
note: the lint level is defined here
Expand All @@ -15,23 +18,32 @@ error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:22:9
|
LL | _ if false => {}
| ^ matches no values because `EmptyEnum` is uninhabited
| ^---------------
| |
| matches no values because `EmptyEnum` is uninhabited
| help: remove the match arm
|
= note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types

error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:31:9
|
LL | _ => {}
| ^ matches no values because `EmptyForeignEnum` is uninhabited
| ^------
| |
| matches no values because `EmptyForeignEnum` is uninhabited
| help: remove the match arm
|
= note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types

error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:36:9
|
LL | _ if false => {}
| ^ matches no values because `EmptyForeignEnum` is uninhabited
| ^---------------
| |
| matches no values because `EmptyForeignEnum` is uninhabited
| help: remove the match arm
|
= note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types

Expand Down
20 changes: 16 additions & 4 deletions tests/ui/pattern/usefulness/empty-match-check-notes.normal.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:17:9
|
LL | _ => {}
| ^ matches no values because `EmptyEnum` is uninhabited
| ^------
| |
| matches no values because `EmptyEnum` is uninhabited
| help: remove the match arm
|
= note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types
note: the lint level is defined here
Expand All @@ -15,23 +18,32 @@ error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:22:9
|
LL | _ if false => {}
| ^ matches no values because `EmptyEnum` is uninhabited
| ^---------------
| |
| matches no values because `EmptyEnum` is uninhabited
| help: remove the match arm
|
= note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types

error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:31:9
|
LL | _ => {}
| ^ matches no values because `EmptyForeignEnum` is uninhabited
| ^------
| |
| matches no values because `EmptyForeignEnum` is uninhabited
| help: remove the match arm
|
= note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types

error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:36:9
|
LL | _ if false => {}
| ^ matches no values because `EmptyForeignEnum` is uninhabited
| ^---------------
| |
| matches no values because `EmptyForeignEnum` is uninhabited
| help: remove the match arm
|
= note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types

Expand Down
Loading

0 comments on commit 1f69638

Please sign in to comment.