Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a machine-applicable suggestion to "unreachable pattern" #128991

Merged
merged 1 commit into from
Sep 14, 2024
Merged
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
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
Loading