Skip to content

Commit bed8b70

Browse files
Fix suggestions for match non-exhaustiveness
1 parent 9d116e8 commit bed8b70

File tree

5 files changed

+54
-34
lines changed

5 files changed

+54
-34
lines changed

compiler/rustc_middle/src/thir.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
1212
use rustc_errors::{DiagArgValue, IntoDiagArg};
1313
use rustc_hir as hir;
1414
use rustc_hir::def_id::DefId;
15-
use rustc_hir::{BindingAnnotation, ByRef, RangeEnd};
15+
use rustc_hir::{BindingAnnotation, ByRef, MatchSource, RangeEnd};
1616
use rustc_index::newtype_index;
1717
use rustc_index::IndexVec;
1818
use rustc_middle::middle::region;
@@ -358,6 +358,7 @@ pub enum ExprKind<'tcx> {
358358
scrutinee: ExprId,
359359
scrutinee_hir_id: hir::HirId,
360360
arms: Box<[ArmId]>,
361+
match_source: MatchSource,
361362
},
362363
/// A block.
363364
Block {

compiler/rustc_mir_build/src/errors.rs

+8-15
Original file line numberDiff line numberDiff line change
@@ -456,16 +456,16 @@ pub enum UnusedUnsafeEnclosing {
456456

457457
pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
458458
pub cx: &'m RustcPatCtxt<'p, 'tcx>,
459-
pub expr_span: Span,
460-
pub span: Span,
459+
pub scrut_span: Span,
460+
pub braces_span: Option<Span>,
461461
pub ty: Ty<'tcx>,
462462
}
463463

464464
impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
465465
fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'_, G> {
466466
let mut diag =
467467
Diag::new(dcx, level, fluent::mir_build_non_exhaustive_patterns_type_not_empty);
468-
diag.span(self.span);
468+
diag.span(self.scrut_span);
469469
diag.code(E0004);
470470
let peeled_ty = self.ty.peel_refs();
471471
diag.arg("ty", self.ty);
@@ -502,26 +502,19 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo
502502
}
503503
}
504504

505-
let mut suggestion = None;
506505
let sm = self.cx.tcx.sess.source_map();
507-
if self.span.eq_ctxt(self.expr_span) {
506+
if let Some(braces_span) = self.braces_span {
508507
// Get the span for the empty match body `{}`.
509-
let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.span) {
508+
let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.scrut_span)
509+
{
510510
(format!("\n{snippet}"), " ")
511511
} else {
512512
(" ".to_string(), "")
513513
};
514-
suggestion = Some((
515-
self.span.shrink_to_hi().with_hi(self.expr_span.hi()),
516-
format!(" {{{indentation}{more}_ => todo!(),{indentation}}}",),
517-
));
518-
}
519-
520-
if let Some((span, sugg)) = suggestion {
521514
diag.span_suggestion_verbose(
522-
span,
515+
braces_span,
523516
fluent::mir_build_suggestion,
524-
sugg,
517+
format!(" {{{indentation}{more}_ => todo!(),{indentation}}}"),
525518
Applicability::HasPlaceholders,
526519
);
527520
} else {

compiler/rustc_mir_build/src/thir/cx/expr.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -716,10 +716,11 @@ impl<'tcx> Cx<'tcx> {
716716
then: self.mirror_expr(then),
717717
else_opt: else_opt.map(|el| self.mirror_expr(el)),
718718
},
719-
hir::ExprKind::Match(discr, arms, _) => ExprKind::Match {
719+
hir::ExprKind::Match(discr, arms, match_source) => ExprKind::Match {
720720
scrutinee: self.mirror_expr(discr),
721721
scrutinee_hir_id: discr.hir_id,
722722
arms: arms.iter().map(|a| self.convert_arm(a)).collect(),
723+
match_source,
723724
},
724725
hir::ExprKind::Loop(body, ..) => {
725726
let block_ty = self.typeck_results().node_type(body.hir_id);

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

+41-16
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,8 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
144144
});
145145
return;
146146
}
147-
ExprKind::Match { scrutinee, scrutinee_hir_id, box ref arms } => {
148-
let source = match ex.span.desugaring_kind() {
149-
Some(DesugaringKind::ForLoop) => hir::MatchSource::ForLoopDesugar,
150-
Some(DesugaringKind::QuestionMark) => {
151-
hir::MatchSource::TryDesugar(scrutinee_hir_id)
152-
}
153-
Some(DesugaringKind::Await) => hir::MatchSource::AwaitDesugar,
154-
_ => hir::MatchSource::Normal,
155-
};
156-
self.check_match(scrutinee, arms, source, ex.span);
147+
ExprKind::Match { scrutinee, scrutinee_hir_id: _, box ref arms, match_source } => {
148+
self.check_match(scrutinee, arms, match_source, ex.span);
157149
}
158150
ExprKind::Let { box ref pat, expr } => {
159151
self.check_let(pat, Some(expr), ex.span);
@@ -505,8 +497,41 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
505497
None,
506498
);
507499
} else {
500+
// span after scrutinee, or after `.match`. That is, the braces, arms,
501+
// and any whitespace preceding the braces.
502+
let braces_span = match source {
503+
hir::MatchSource::Normal => scrut
504+
.span
505+
.find_ancestor_in_same_ctxt(expr_span)
506+
.map(|scrut_span| scrut_span.shrink_to_hi().with_hi(expr_span.hi())),
507+
hir::MatchSource::Postfix => {
508+
// This is horrendous, and we should deal with it by just
509+
// stashing the span of the braces somewhere (like in the match source).
510+
scrut.span.find_ancestor_in_same_ctxt(expr_span).and_then(|scrut_span| {
511+
let sm = self.tcx.sess.source_map();
512+
let brace_span = sm.span_extend_to_next_char(scrut_span, '{', true);
513+
if sm.span_to_snippet(sm.next_point(brace_span)).as_deref() == Ok("{") {
514+
let sp = brace_span.shrink_to_hi().with_hi(expr_span.hi());
515+
// We also need to extend backwards for whitespace
516+
sm.span_extend_prev_while(sp, |c| c.is_whitespace()).ok()
517+
} else {
518+
None
519+
}
520+
})
521+
}
522+
hir::MatchSource::ForLoopDesugar
523+
| hir::MatchSource::TryDesugar(_)
524+
| hir::MatchSource::AwaitDesugar
525+
| hir::MatchSource::FormatArgs => None,
526+
};
508527
self.error = Err(report_non_exhaustive_match(
509-
&cx, self.thir, scrut.ty, scrut.span, witnesses, arms, expr_span,
528+
&cx,
529+
self.thir,
530+
scrut.ty,
531+
scrut.span,
532+
witnesses,
533+
arms,
534+
braces_span,
510535
));
511536
}
512537
}
@@ -929,7 +954,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
929954
sp: Span,
930955
witnesses: Vec<WitnessPat<'p, 'tcx>>,
931956
arms: &[ArmId],
932-
expr_span: Span,
957+
braces_span: Option<Span>,
933958
) -> ErrorGuaranteed {
934959
let is_empty_match = arms.is_empty();
935960
let non_empty_enum = match scrut_ty.kind() {
@@ -941,8 +966,8 @@ fn report_non_exhaustive_match<'p, 'tcx>(
941966
if is_empty_match && !non_empty_enum {
942967
return cx.tcx.dcx().emit_err(NonExhaustivePatternsTypeNotEmpty {
943968
cx,
944-
expr_span,
945-
span: sp,
969+
scrut_span: sp,
970+
braces_span,
946971
ty: scrut_ty,
947972
});
948973
}
@@ -1028,15 +1053,15 @@ fn report_non_exhaustive_match<'p, 'tcx>(
10281053
let mut suggestion = None;
10291054
let sm = cx.tcx.sess.source_map();
10301055
match arms {
1031-
[] if sp.eq_ctxt(expr_span) => {
1056+
[] if let Some(braces_span) = braces_span => {
10321057
// Get the span for the empty match body `{}`.
10331058
let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) {
10341059
(format!("\n{snippet}"), " ")
10351060
} else {
10361061
(" ".to_string(), "")
10371062
};
10381063
suggestion = Some((
1039-
sp.shrink_to_hi().with_hi(expr_span.hi()),
1064+
braces_span,
10401065
format!(" {{{indentation}{more}{suggested_arm},{indentation}}}",),
10411066
));
10421067
}

tests/ui/match/postfix-match/match-after-as.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ LL | 1 as i32.match {};
1818
= note: the matched value is of type `i32`
1919
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
2020
|
21-
LL ~ 1 as i32 {
21+
LL ~ 1 as i32.match {
2222
LL + _ => todo!(),
2323
LL ~ };
2424
|

0 commit comments

Comments
 (0)