summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir_build/src/thir/pattern/check_match.rs')
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs119
1 files changed, 75 insertions, 44 deletions
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 8f58db504..1e51cb9aa 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -26,8 +26,8 @@ use rustc_session::Session;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::Span;
-pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) {
- let Ok((thir, expr)) = tcx.thir_body(ty::WithOptConstParam::unknown(def_id)) else { return };
+pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
+ let (thir, expr) = tcx.thir_body(def_id)?;
let thir = thir.borrow();
let pattern_arena = TypedArena::default();
let mut visitor = MatchVisitor {
@@ -37,13 +37,16 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) {
lint_level: tcx.hir().local_def_id_to_hir_id(def_id),
let_source: LetSource::None,
pattern_arena: &pattern_arena,
+ error: Ok(()),
};
visitor.visit_expr(&thir[expr]);
+
for param in thir.params.iter() {
if let Some(box ref pattern) = param.pat {
visitor.check_irrefutable(pattern, "function argument", None);
}
}
+ visitor.error
}
fn create_e0004(
@@ -77,6 +80,7 @@ struct MatchVisitor<'a, 'p, 'tcx> {
lint_level: HirId,
let_source: LetSource,
pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
+ error: Result<(), ErrorGuaranteed>,
}
impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
@@ -86,35 +90,34 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
#[instrument(level = "trace", skip(self))]
fn visit_arm(&mut self, arm: &Arm<'tcx>) {
- match arm.guard {
- Some(Guard::If(expr)) => {
- self.with_let_source(LetSource::IfLetGuard, |this| {
- this.visit_expr(&this.thir[expr])
- });
- }
- Some(Guard::IfLet(ref pat, expr)) => {
- self.with_let_source(LetSource::IfLetGuard, |this| {
- this.check_let(pat, expr, LetSource::IfLetGuard, pat.span);
- this.visit_pat(pat);
- this.visit_expr(&this.thir[expr]);
- });
+ self.with_lint_level(arm.lint_level, |this| {
+ match arm.guard {
+ Some(Guard::If(expr)) => {
+ this.with_let_source(LetSource::IfLetGuard, |this| {
+ this.visit_expr(&this.thir[expr])
+ });
+ }
+ Some(Guard::IfLet(ref pat, expr)) => {
+ this.with_let_source(LetSource::IfLetGuard, |this| {
+ this.check_let(pat, expr, LetSource::IfLetGuard, pat.span);
+ this.visit_pat(pat);
+ this.visit_expr(&this.thir[expr]);
+ });
+ }
+ None => {}
}
- None => {}
- }
- self.visit_pat(&arm.pattern);
- self.visit_expr(&self.thir[arm.body]);
+ this.visit_pat(&arm.pattern);
+ this.visit_expr(&self.thir[arm.body]);
+ });
}
#[instrument(level = "trace", skip(self))]
fn visit_expr(&mut self, ex: &Expr<'tcx>) {
match ex.kind {
ExprKind::Scope { value, lint_level, .. } => {
- let old_lint_level = self.lint_level;
- if let LintLevel::Explicit(hir_id) = lint_level {
- self.lint_level = hir_id;
- }
- self.visit_expr(&self.thir[value]);
- self.lint_level = old_lint_level;
+ self.with_lint_level(lint_level, |this| {
+ this.visit_expr(&this.thir[value]);
+ });
return;
}
ExprKind::If { cond, then, else_opt, if_then_scope: _ } => {
@@ -186,6 +189,17 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
self.let_source = old_let_source;
}
+ fn with_lint_level(&mut self, new_lint_level: LintLevel, f: impl FnOnce(&mut Self)) {
+ if let LintLevel::Explicit(hir_id) = new_lint_level {
+ let old_lint_level = self.lint_level;
+ self.lint_level = hir_id;
+ f(self);
+ self.lint_level = old_lint_level;
+ } else {
+ f(self);
+ }
+ }
+
fn check_patterns(&self, pat: &Pat<'tcx>, rf: RefutableFlag) {
pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
check_for_bindings_named_same_as_variants(self, pat, rf);
@@ -232,7 +246,9 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
for &arm in arms {
// Check the arm for some things unrelated to exhaustiveness.
let arm = &self.thir.arms[arm];
- self.check_patterns(&arm.pattern, Refutable);
+ self.with_lint_level(arm.lint_level, |this| {
+ this.check_patterns(&arm.pattern, Refutable);
+ });
}
let tarms: Vec<_> = arms
@@ -276,9 +292,9 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
let [pat_field] = &subpatterns[..] else { bug!() };
self.check_irrefutable(&pat_field.pattern, "`for` loop binding", None);
} else {
- non_exhaustive_match(
+ self.error = Err(non_exhaustive_match(
&cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span,
- );
+ ));
}
}
}
@@ -406,7 +422,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
}
#[instrument(level = "trace", skip(self))]
- fn check_irrefutable(&self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
+ fn check_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
let mut cx = self.new_cx(self.lint_level, false);
let pattern = self.lower_pattern(&mut cx, pat);
@@ -475,18 +491,36 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
AdtDefinedHere { adt_def_span, ty, variants }
};
- self.tcx.sess.emit_err(PatternNotCovered {
+ // Emit an extra note if the first uncovered witness would be uninhabited
+ // if we disregard visibility.
+ let witness_1_is_privately_uninhabited =
+ if cx.tcx.features().exhaustive_patterns
+ && let Some(witness_1) = witnesses.get(0)
+ && let ty::Adt(adt, substs) = witness_1.ty().kind()
+ && adt.is_enum()
+ && let Constructor::Variant(variant_index) = witness_1.ctor()
+ {
+ let variant = adt.variant(*variant_index);
+ let inhabited = variant.inhabited_predicate(cx.tcx, *adt).subst(cx.tcx, substs);
+ assert!(inhabited.apply(cx.tcx, cx.param_env, cx.module));
+ !inhabited.apply_ignore_module(cx.tcx, cx.param_env)
+ } else {
+ false
+ };
+
+ self.error = Err(self.tcx.sess.emit_err(PatternNotCovered {
span: pat.span,
origin,
uncovered: Uncovered::new(pat.span, &cx, witnesses),
inform,
interpreted_as_const,
+ witness_1_is_privately_uninhabited: witness_1_is_privately_uninhabited.then_some(()),
_p: (),
pattern_ty,
let_suggestion,
misc_suggestion,
adt_defined_here,
- });
+ }));
}
}
@@ -628,7 +662,7 @@ fn non_exhaustive_match<'p, 'tcx>(
witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
arms: &[ArmId],
expr_span: Span,
-) {
+) -> ErrorGuaranteed {
let is_empty_match = arms.is_empty();
let non_empty_enum = match scrut_ty.kind() {
ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(),
@@ -640,13 +674,12 @@ fn non_exhaustive_match<'p, 'tcx>(
let pattern;
let patterns_len;
if is_empty_match && !non_empty_enum {
- cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
+ return cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
cx,
expr_span,
span: sp,
ty: scrut_ty,
});
- return;
} else {
// FIXME: migration of this diagnostic will require list support
let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
@@ -668,13 +701,11 @@ fn non_exhaustive_match<'p, 'tcx>(
};
};
- let is_variant_list_non_exhaustive = match scrut_ty.kind() {
- ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local() => true,
- _ => false,
- };
+ let is_variant_list_non_exhaustive = matches!(scrut_ty.kind(),
+ ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local());
adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
- err.note(&format!(
+ err.note(format!(
"the matched value is of type `{}`{}",
scrut_ty,
if is_variant_list_non_exhaustive { ", which is marked as non-exhaustive" } else { "" }
@@ -684,13 +715,13 @@ fn non_exhaustive_match<'p, 'tcx>(
&& witnesses.len() == 1
&& matches!(witnesses[0].ctor(), Constructor::NonExhaustive)
{
- err.note(&format!(
+ err.note(format!(
"`{}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
exhaustively",
scrut_ty,
));
if cx.tcx.sess.is_nightly_build() {
- err.help(&format!(
+ err.help(format!(
"add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
enable precise `{}` matching",
scrut_ty,
@@ -795,11 +826,11 @@ fn non_exhaustive_match<'p, 'tcx>(
},
);
if let Some((span, sugg)) = suggestion {
- err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders);
+ err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders);
} else {
- err.help(&msg);
+ err.help(msg);
}
- err.emit();
+ err.emit()
}
pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
@@ -859,7 +890,7 @@ fn adt_defined_here<'p, 'tcx>(
for pat in spans {
span.push_span_label(pat, "not covered");
}
- err.span_note(span, &format!("`{}` defined here", ty));
+ err.span_note(span, format!("`{}` defined here", ty));
}
}