-use super::_match::Usefulness::*;
-use super::_match::WitnessPreference::*;
-use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack};
-use super::{PatCtxt, PatKind, PatternError};
+use super::usefulness::{
+ compute_match_usefulness, expand_pattern, is_wildcard, MatchArm, MatchCheckCtxt, Reachability,
+ UsefulnessReport,
+};
+use super::{PatCtxt, PatternError};
use rustc_arena::TypedArena;
use rustc_ast::Mutability;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{HirId, Pat};
+use rustc_middle::thir::PatKind;
use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_session::config::nightly_options;
use rustc_session::lint::builtin::BINDINGS_WITH_VARIANT_NAME;
use rustc_session::lint::builtin::{IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS};
use rustc_session::parse::feature_err;
for arm in arms {
// Check the arm for some things unrelated to exhaustiveness.
self.check_patterns(&arm.pat);
+ if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
+ self.check_patterns(pat);
+ }
}
let mut cx = self.new_cx(scrut.hir_id);
+ for arm in arms {
+ if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
+ let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
+ check_if_let_guard(&mut cx, &tpat, pat.hir_id);
+ }
+ }
+
let mut have_errors = false;
- let inlined_arms: Vec<_> = arms
+ let arms: Vec<_> = arms
.iter()
- .map(|hir::Arm { pat, guard, .. }| {
- (self.lower_pattern(&mut cx, pat, &mut have_errors).0, pat.hir_id, guard.is_some())
+ .map(|hir::Arm { pat, guard, .. }| MatchArm {
+ pat: self.lower_pattern(&mut cx, pat, &mut have_errors).0,
+ hir_id: pat.hir_id,
+ has_guard: guard.is_some(),
})
.collect();
- // Bail out early if inlining failed.
+ // Bail out early if lowering failed.
if have_errors {
return;
}
- // Fourth, check for unreachable arms.
- let matrix = check_arms(&mut cx, &inlined_arms, source);
+ let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut);
+ let report = compute_match_usefulness(&cx, &arms, scrut.hir_id, scrut_ty);
+
+ // Report unreachable arms.
+ report_arm_reachability(&cx, &report, source);
- // Fifth, check if the match is exhaustive.
+ // Check if the match is exhaustive.
// Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
// since an empty matrix can occur when there are arms, if those arms all have guards.
- let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut);
- let is_empty_match = inlined_arms.is_empty();
- check_exhaustive(&mut cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match);
+ let is_empty_match = arms.is_empty();
+ let witnesses = report.non_exhaustiveness_witnesses;
+ if !witnesses.is_empty() {
+ non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, is_empty_match);
+ }
}
fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
let mut cx = self.new_cx(pat.hir_id);
let (pattern, pattern_ty) = self.lower_pattern(&mut cx, pat, &mut false);
- let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect();
-
- let witnesses = match check_not_useful(&mut cx, pattern_ty, &pats, pat.hir_id) {
- Ok(_) => return,
- Err(err) => err,
- };
+ let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }];
+ let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty);
+
+ // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
+ // only care about exhaustiveness here.
+ let witnesses = report.non_exhaustiveness_witnesses;
+ if witnesses.is_empty() {
+ // The pattern is irrefutable.
+ return;
+ }
let joined_patterns = joined_uncovered_patterns(&witnesses);
let mut err = struct_span_err!(
/// Checks for common cases of "catchall" patterns that may not be intended as such.
fn pat_is_catchall(pat: &super::Pat<'_>) -> bool {
- use super::PatKind::*;
+ use PatKind::*;
match &*pat.kind {
Binding { subpattern: None, .. } => true,
Binding { subpattern: Some(s), .. } | Deref { subpattern: s } => pat_is_catchall(s),
}
fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::MatchSource) {
- tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| {
- let msg = match source {
- hir::MatchSource::IfLetDesugar { .. } => "irrefutable if-let pattern",
- hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern",
- _ => bug!(),
- };
- lint.build(msg).emit()
+ tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| match source {
+ hir::MatchSource::IfLetDesugar { .. } => {
+ let mut diag = lint.build("irrefutable `if let` pattern");
+ diag.note("this pattern will always match, so the `if let` is useless");
+ diag.help("consider replacing the `if let` with a `let`");
+ diag.emit()
+ }
+ hir::MatchSource::WhileLetDesugar => {
+ let mut diag = lint.build("irrefutable `while let` pattern");
+ diag.note("this pattern will always match, so the loop will never exit");
+ diag.help("consider instead using a `loop { ... }` with a `let` inside it");
+ diag.emit()
+ }
+ hir::MatchSource::IfLetGuardDesugar => {
+ let mut diag = lint.build("irrefutable `if let` guard pattern");
+ diag.note("this pattern will always match, so the guard is useless");
+ diag.help("consider removing the guard and adding a `let` inside the match arm");
+ diag.emit()
+ }
+ _ => {
+ bug!(
+ "expected `if let`, `while let`, or `if let` guard HIR match source, found {:?}",
+ source,
+ )
+ }
});
}
-/// Check for unreachable patterns.
-fn check_arms<'p, 'tcx>(
+fn check_if_let_guard<'p, 'tcx>(
cx: &mut MatchCheckCtxt<'p, 'tcx>,
- arms: &[(&'p super::Pat<'tcx>, HirId, bool)],
+ pat: &'p super::Pat<'tcx>,
+ pat_id: HirId,
+) {
+ let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
+ let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty);
+ report_arm_reachability(&cx, &report, hir::MatchSource::IfLetGuardDesugar);
+
+ if report.non_exhaustiveness_witnesses.is_empty() {
+ // The match is exhaustive, i.e. the `if let` pattern is irrefutable.
+ irrefutable_let_pattern(cx.tcx, pat.span, pat_id, hir::MatchSource::IfLetGuardDesugar)
+ }
+}
+
+/// Report unreachable arms, if any.
+fn report_arm_reachability<'p, 'tcx>(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ report: &UsefulnessReport<'p, 'tcx>,
source: hir::MatchSource,
-) -> Matrix<'p, 'tcx> {
- let mut seen = Matrix::empty();
+) {
+ use Reachability::*;
let mut catchall = None;
- for (arm_index, (pat, id, has_guard)) in arms.iter().copied().enumerate() {
- let v = PatStack::from_pattern(pat);
- match is_useful(cx, &seen, &v, LeaveOutWitness, id, has_guard, true) {
- NotUseful => {
+ for (arm_index, (arm, is_useful)) in report.arm_usefulness.iter().enumerate() {
+ match is_useful {
+ Unreachable => {
match source {
- hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(),
+ hir::MatchSource::WhileDesugar => bug!(),
hir::MatchSource::IfLetDesugar { .. } | hir::MatchSource::WhileLetDesugar => {
// Check which arm we're on.
match arm_index {
// The arm with the user-specified pattern.
- 0 => unreachable_pattern(cx.tcx, pat.span, id, None),
+ 0 => unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, None),
// The arm with the wildcard pattern.
- 1 => irrefutable_let_pattern(cx.tcx, pat.span, id, source),
+ 1 => irrefutable_let_pattern(cx.tcx, arm.pat.span, arm.hir_id, source),
_ => bug!(),
}
}
+ hir::MatchSource::IfLetGuardDesugar => {
+ assert_eq!(arm_index, 0);
+ unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, None);
+ }
+
hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
- unreachable_pattern(cx.tcx, pat.span, id, catchall);
+ unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, catchall);
}
// Unreachable patterns in try and await expressions occur when one of
hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
}
}
- Useful(unreachables) => {
- let mut unreachables: Vec<_> = unreachables.into_iter().flatten().collect();
+ Reachable(unreachables) if unreachables.is_empty() => {}
+ // The arm is reachable, but contains unreachable subpatterns (from or-patterns).
+ Reachable(unreachables) => {
+ let mut unreachables = unreachables.clone();
// Emit lints in the order in which they occur in the file.
unreachables.sort_unstable();
for span in unreachables {
- unreachable_pattern(cx.tcx, span, id, None);
+ unreachable_pattern(cx.tcx, span, arm.hir_id, None);
}
}
- UsefulWithWitness(_) => bug!(),
}
- if !has_guard {
- seen.push(v);
- if catchall.is_none() && pat_is_catchall(pat) {
- catchall = Some(pat.span);
- }
+ if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
+ catchall = Some(arm.pat.span);
}
}
- seen
-}
-
-fn check_not_useful<'p, 'tcx>(
- cx: &mut MatchCheckCtxt<'p, 'tcx>,
- ty: Ty<'tcx>,
- matrix: &Matrix<'p, 'tcx>,
- hir_id: HirId,
-) -> Result<(), Vec<super::Pat<'tcx>>> {
- let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty));
- let v = PatStack::from_pattern(wild_pattern);
-
- // false is given for `is_under_guard` argument due to the wildcard
- // pattern not having a guard
- match is_useful(cx, matrix, &v, ConstructWitness, hir_id, false, true) {
- NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
- UsefulWithWitness(pats) => Err(if pats.is_empty() {
- bug!("Exhaustiveness check returned no witnesses")
- } else {
- pats.into_iter().map(|w| w.single_pattern()).collect()
- }),
- Useful(_) => bug!(),
- }
}
-fn check_exhaustive<'p, 'tcx>(
- cx: &mut MatchCheckCtxt<'p, 'tcx>,
+/// Report that a match is not exhaustive.
+fn non_exhaustive_match<'p, 'tcx>(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
scrut_ty: Ty<'tcx>,
sp: Span,
- matrix: &Matrix<'p, 'tcx>,
- hir_id: HirId,
+ witnesses: Vec<super::Pat<'tcx>>,
is_empty_match: bool,
) {
- // In the absence of the `exhaustive_patterns` feature, empty matches are not detected by
- // `is_useful` to exhaustively match uninhabited types, so we manually check here.
- if is_empty_match && !cx.tcx.features().exhaustive_patterns {
- let scrutinee_is_visibly_uninhabited = match scrut_ty.kind() {
- ty::Never => true,
- ty::Adt(def, _) => {
- def.is_enum()
- && def.variants.is_empty()
- && !cx.is_foreign_non_exhaustive_enum(scrut_ty)
- }
- _ => false,
- };
- if scrutinee_is_visibly_uninhabited {
- // If the type *is* uninhabited, an empty match is vacuously exhaustive.
- return;
- }
- }
-
- let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) {
- Ok(_) => return,
- Err(err) => err,
- };
-
let non_empty_enum = match scrut_ty.kind() {
ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(),
_ => false,
err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
};
+ 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,
+ };
+
adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
err.help(
"ensure that all possible cases are being handled, \
possibly by adding wildcards or more match arms",
);
- err.note(&format!("the matched value is of type `{}`", scrut_ty));
+ err.note(&format!(
+ "the matched value is of type `{}`{}",
+ scrut_ty,
+ if is_variant_list_non_exhaustive { ", which is marked as non-exhaustive" } else { "" }
+ ));
if (scrut_ty == cx.tcx.types.usize || scrut_ty == cx.tcx.types.isize)
&& !is_empty_match
&& witnesses.len() == 1
- && witnesses[0].is_wildcard()
+ && is_wildcard(&witnesses[0])
{
err.note(&format!(
"`{}` does not have a fixed maximum value, \
so a wildcard `_` is necessary to match exhaustively",
scrut_ty,
));
- if nightly_options::is_nightly_build() {
+ if cx.tcx.sess.is_nightly_build() {
err.help(&format!(
"add `#![feature(precise_pointer_size_matching)]` \
to the crate attributes to enable precise `{}` matching",
));
}
}
+ if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() {
+ if cx.tcx.is_ty_uninhabited_from(cx.module, sub_ty, cx.param_env) {
+ err.note("references are always considered inhabited");
+ }
+ }
err.emit();
}