1 use super::_match::Usefulness::*;
2 use super::_match::WitnessPreference::*;
3 use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack};
5 use super::{PatCtxt, PatKind, PatternError};
8 use rustc::session::Session;
9 use rustc::ty::subst::{InternalSubsts, SubstsRef};
10 use rustc::ty::{self, Ty, TyCtxt};
11 use rustc_errors::{Applicability, DiagnosticBuilder};
13 use rustc::hir::def::*;
14 use rustc::hir::def_id::DefId;
15 use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
16 use rustc::hir::HirId;
17 use rustc::hir::{self, Pat};
21 use syntax_pos::{MultiSpan, Span};
23 crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
24 let body_id = match tcx.hir().as_local_hir_id(def_id) {
26 Some(id) => tcx.hir().body_owned_by(id),
29 let mut visitor = MatchVisitor {
31 tables: tcx.body_tables(body_id),
32 param_env: tcx.param_env(def_id),
33 identity_substs: InternalSubsts::identity_for_item(tcx, def_id),
35 visitor.visit_body(tcx.hir().body(body_id));
38 fn create_e0004(sess: &Session, sp: Span, error_message: String) -> DiagnosticBuilder<'_> {
39 struct_span_err!(sess, sp, E0004, "{}", &error_message)
42 struct MatchVisitor<'a, 'tcx> {
44 tables: &'a ty::TypeckTables<'tcx>,
45 param_env: ty::ParamEnv<'tcx>,
46 identity_substs: SubstsRef<'tcx>,
49 impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
50 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
51 NestedVisitorMap::None
54 fn visit_expr(&mut self, ex: &'tcx hir::Expr) {
55 intravisit::walk_expr(self, ex);
57 if let hir::ExprKind::Match(ref scrut, ref arms, source) = ex.kind {
58 self.check_match(scrut, arms, source);
62 fn visit_local(&mut self, loc: &'tcx hir::Local) {
63 intravisit::walk_local(self, loc);
65 let (msg, sp) = match loc.source {
66 hir::LocalSource::Normal => ("local binding", Some(loc.span)),
67 hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None),
68 hir::LocalSource::AsyncFn => ("async fn binding", None),
69 hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
71 self.check_irrefutable(&loc.pat, msg, sp);
73 // Check legality of move bindings and `@` patterns.
74 self.check_patterns(false, &loc.pat);
77 fn visit_body(&mut self, body: &'tcx hir::Body) {
78 intravisit::walk_body(self, body);
80 for param in &body.params {
81 self.check_irrefutable(¶m.pat, "function argument", None);
82 self.check_patterns(false, ¶m.pat);
87 impl PatCtxt<'_, '_> {
88 fn report_inlining_errors(&self, pat_span: Span) {
89 for error in &self.errors {
91 PatternError::StaticInPattern(span) => {
92 self.span_e0158(span, "statics cannot be referenced in patterns")
94 PatternError::AssocConstInPattern(span) => {
95 self.span_e0158(span, "associated consts cannot be referenced in patterns")
97 PatternError::FloatBug => {
98 // FIXME(#31407) this is only necessary because float parsing is buggy
99 ::rustc::mir::interpret::struct_error(
100 self.tcx.at(pat_span),
101 "could not evaluate float literal (see issue #31407)",
105 PatternError::NonConstPath(span) => {
106 ::rustc::mir::interpret::struct_error(
108 "runtime values cannot be referenced in patterns",
116 fn span_e0158(&self, span: Span, text: &str) {
117 span_err!(self.tcx.sess, span, E0158, "{}", text)
121 impl<'tcx> MatchVisitor<'_, 'tcx> {
122 fn check_patterns(&mut self, has_guard: bool, pat: &Pat) {
123 check_legality_of_move_bindings(self, has_guard, pat);
124 check_legality_of_bindings_in_at_patterns(self, pat);
127 fn check_match(&mut self, scrut: &hir::Expr, arms: &'tcx [hir::Arm], source: hir::MatchSource) {
129 // First, check legality of move bindings.
130 self.check_patterns(arm.guard.is_some(), &arm.pat);
132 // Second, perform some lints.
133 check_for_bindings_named_same_as_variants(self, &arm.pat);
136 let module = self.tcx.hir().get_module_parent(scrut.hir_id);
137 MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
138 let mut have_errors = false;
140 let inlined_arms: Vec<(Vec<_>, _)> = arms
144 // HACK(or_patterns; Centril | dlrobertson): Remove this and
145 // correctly handle exhaustiveness checking for nested or-patterns.
146 match &arm.pat.kind {
147 hir::PatKind::Or(pats) => pats,
148 _ => std::slice::from_ref(&arm.pat),
152 let mut patcx = PatCtxt::new(
154 self.param_env.and(self.identity_substs),
157 patcx.include_lint_checks();
160 .alloc(expand_pattern(cx, patcx.lower_pattern(&pat)))
162 if !patcx.errors.is_empty() {
163 patcx.report_inlining_errors(pat.span);
169 arm.guard.as_ref().map(|g| match g {
170 hir::Guard::If(ref e) => &**e,
176 // Bail out early if inlining failed.
181 // Fourth, check for unreachable arms.
182 check_arms(cx, &inlined_arms, source);
184 // Then, if the match has no arms, check whether the scrutinee
186 let pat_ty = self.tables.node_type(scrut.hir_id);
187 let module = self.tcx.hir().get_module_parent(scrut.hir_id);
188 let mut def_span = None;
189 let mut missing_variants = vec![];
190 if inlined_arms.is_empty() {
191 let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns {
192 self.tcx.is_ty_uninhabited_from(module, pat_ty)
197 def_span = self.tcx.hir().span_if_local(def.did);
198 if def.variants.len() < 4 && !def.variants.is_empty() {
199 // keep around to point at the definition of non-covered variants
201 def.variants.iter().map(|variant| variant.ident).collect();
204 let is_non_exhaustive_and_non_local =
205 def.is_variant_list_non_exhaustive() && !def.did.is_local();
207 !(is_non_exhaustive_and_non_local) && def.variants.is_empty()
212 if !scrutinee_is_uninhabited {
213 // We know the type is inhabited, so this must be wrong
214 let mut err = create_e0004(
218 "non-exhaustive patterns: {}",
219 match missing_variants.len() {
220 0 => format!("type `{}` is non-empty", pat_ty),
222 "pattern `{}` of type `{}` is not handled",
223 missing_variants[0].name, pat_ty,
226 "multiple patterns of type `{}` are not handled",
233 "ensure that all possible cases are being handled, \
234 possibly by adding wildcards or more match arms",
236 if let Some(sp) = def_span {
237 err.span_label(sp, format!("`{}` defined here", pat_ty));
239 // point at the definition of non-covered enum variants
240 for variant in &missing_variants {
241 err.span_label(variant.span, "variant not covered");
245 // If the type *is* uninhabited, it's vacuously exhaustive
249 let matrix: Matrix<'_, '_> = inlined_arms
251 .filter(|&&(_, guard)| guard.is_none())
252 .flat_map(|arm| &arm.0)
253 .map(|pat| PatStack::from_pattern(pat.0))
255 let scrut_ty = self.tables.node_type(scrut.hir_id);
256 check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id);
260 fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str, sp: Option<Span>) {
261 let module = self.tcx.hir().get_module_parent(pat.hir_id);
262 MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
264 PatCtxt::new(self.tcx, self.param_env.and(self.identity_substs), self.tables);
265 patcx.include_lint_checks();
266 let pattern = patcx.lower_pattern(pat);
267 let pattern_ty = pattern.ty;
268 let pattern = expand_pattern(cx, pattern);
269 let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(&pattern)].into_iter().collect();
271 let witnesses = match check_not_useful(cx, pattern_ty, &pats, pat.hir_id) {
276 let joined_patterns = joined_uncovered_patterns(&witnesses);
277 let mut err = struct_span_err!(
281 "refutable pattern in {}: {} not covered",
285 let suggest_if_let = match &pat.kind {
286 hir::PatKind::Path(hir::QPath::Resolved(None, path))
287 if path.segments.len() == 1 && path.segments[0].args.is_none() =>
289 const_not_var(&mut err, cx.tcx, pat, path);
295 pattern_not_covered_label(&witnesses, &joined_patterns),
301 if let (Some(span), true) = (sp, suggest_if_let) {
303 "`let` bindings require an \"irrefutable pattern\", like a `struct` or \
304 an `enum` with only one variant",
306 if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
309 "you might want to use `if let` to ignore the variant that isn't matched",
310 format!("if {} {{ /* */ }}", &snippet[..snippet.len() - 1]),
311 Applicability::HasPlaceholders,
315 "for more information, visit \
316 https://doc.rust-lang.org/book/ch18-02-refutability.html",
320 adt_defined_here(cx, &mut err, pattern_ty, &witnesses);
326 /// A path pattern was interpreted as a constant, not a new variable.
327 /// This caused an irrefutable match failure in e.g. `let`.
328 fn const_not_var(err: &mut DiagnosticBuilder<'_>, tcx: TyCtxt<'_>, pat: &Pat, path: &hir::Path) {
329 let descr = path.res.descr();
332 format!("interpreted as {} {} pattern, not a new variable", path.res.article(), descr,),
337 "introduce a variable instead",
338 format!("{}_var", path.segments[0].ident).to_lowercase(),
339 // Cannot use `MachineApplicable` as it's not really *always* correct
340 // because there may be such an identifier in scope or the user maybe
341 // really wanted to match against the constant. This is quite unlikely however.
342 Applicability::MaybeIncorrect,
345 if let Some(span) = tcx.hir().res_span(path.res) {
346 err.span_label(span, format!("{} defined here", descr));
350 fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pat) {
352 if let hir::PatKind::Binding(_, _, ident, None) = p.kind {
353 if let Some(&bm) = cx.tables.pat_binding_modes().get(p.hir_id) {
354 if bm != ty::BindByValue(hir::Mutability::Immutable) {
358 let pat_ty = cx.tables.pat_ty(p);
359 if let ty::Adt(edef, _) = pat_ty.kind {
361 && edef.variants.iter().any(|variant| {
362 variant.ident == ident && variant.ctor_kind == CtorKind::Const
365 let ty_path = cx.tcx.def_path_str(edef.did);
366 let mut err = struct_span_warn!(
370 "pattern binding `{}` is named the same as one \
371 of the variants of the type `{}`",
377 "to match on the variant, qualify the path",
378 format!("{}::{}", ty_path, ident),
379 Applicability::MachineApplicable,
385 cx.tcx.sess.delay_span_bug(p.span, "missing binding mode");
392 /// Checks for common cases of "catchall" patterns that may not be intended as such.
393 fn pat_is_catchall(pat: &Pat) -> bool {
395 hir::PatKind::Binding(.., None) => true,
396 hir::PatKind::Binding(.., Some(ref s)) => pat_is_catchall(s),
397 hir::PatKind::Ref(ref s, _) => pat_is_catchall(s),
398 hir::PatKind::Tuple(ref v, _) => v.iter().all(|p| pat_is_catchall(&p)),
403 // Check for unreachable patterns
405 cx: &mut MatchCheckCtxt<'_, 'tcx>,
406 arms: &[(Vec<(&super::Pat<'tcx>, &hir::Pat)>, Option<&hir::Expr>)],
407 source: hir::MatchSource,
409 let mut seen = Matrix::empty();
410 let mut catchall = None;
411 for (arm_index, &(ref pats, guard)) in arms.iter().enumerate() {
412 for &(pat, hir_pat) in pats {
413 let v = PatStack::from_pattern(pat);
415 match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id) {
418 hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => {
421 hir::MatchSource::IfLetDesugar { .. } => {
423 lint::builtin::IRREFUTABLE_LET_PATTERNS,
426 "irrefutable if-let pattern",
430 hir::MatchSource::WhileLetDesugar => {
431 // check which arm we're on.
433 // The arm with the user-specified pattern.
436 lint::builtin::UNREACHABLE_PATTERNS,
439 "unreachable pattern",
442 // The arm with the wildcard pattern.
445 lint::builtin::IRREFUTABLE_LET_PATTERNS,
448 "irrefutable while-let pattern",
455 hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
456 let mut err = cx.tcx.struct_span_lint_hir(
457 lint::builtin::UNREACHABLE_PATTERNS,
460 "unreachable pattern",
462 // if we had a catchall pattern, hint at that
463 if let Some(catchall) = catchall {
464 err.span_label(pat.span, "unreachable pattern");
465 err.span_label(catchall, "matches any value");
470 // Unreachable patterns in try and await expressions occur when one of
471 // the arms are an uninhabited type. Which is OK.
472 hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
476 UsefulWithWitness(_) => bug!(),
480 if catchall.is_none() && pat_is_catchall(hir_pat) {
481 catchall = Some(pat.span);
489 cx: &mut MatchCheckCtxt<'_, 'tcx>,
491 matrix: &Matrix<'_, 'tcx>,
493 ) -> Result<(), Vec<super::Pat<'tcx>>> {
494 let wild_pattern = super::Pat::wildcard_from_ty(ty);
495 match is_useful(cx, matrix, &PatStack::from_pattern(&wild_pattern), ConstructWitness, hir_id) {
496 NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
497 UsefulWithWitness(pats) => Err(if pats.is_empty() {
500 pats.into_iter().map(|w| w.single_pattern()).collect()
506 fn check_exhaustive<'tcx>(
507 cx: &mut MatchCheckCtxt<'_, 'tcx>,
510 matrix: &Matrix<'_, 'tcx>,
513 let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) {
518 let joined_patterns = joined_uncovered_patterns(&witnesses);
519 let mut err = create_e0004(
522 format!("non-exhaustive patterns: {} not covered", joined_patterns),
524 err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
525 adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
527 "ensure that all possible cases are being handled, \
528 possibly by adding wildcards or more match arms",
533 fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String {
534 const LIMIT: usize = 3;
537 [witness] => format!("`{}`", witness),
538 [head @ .., tail] if head.len() < LIMIT => {
539 let head: Vec<_> = head.iter().map(<_>::to_string).collect();
540 format!("`{}` and `{}`", head.join("`, `"), tail)
543 let (head, tail) = witnesses.split_at(LIMIT);
544 let head: Vec<_> = head.iter().map(<_>::to_string).collect();
545 format!("`{}` and {} more", head.join("`, `"), tail.len())
550 fn pattern_not_covered_label(witnesses: &[super::Pat<'_>], joined_patterns: &str) -> String {
551 format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
554 /// Point at the definition of non-covered `enum` variants.
556 cx: &MatchCheckCtxt<'_, '_>,
557 err: &mut DiagnosticBuilder<'_>,
559 witnesses: &[super::Pat<'_>],
561 let ty = ty.peel_refs();
562 if let ty::Adt(def, _) = ty.kind {
563 if let Some(sp) = cx.tcx.hir().span_if_local(def.did) {
564 err.span_label(sp, format!("`{}` defined here", ty));
567 if witnesses.len() < 4 {
568 for sp in maybe_point_at_variant(ty, &witnesses) {
569 err.span_label(sp, "not covered");
575 fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec<Span> {
576 let mut covered = vec![];
577 if let ty::Adt(def, _) = ty.kind {
578 // Don't point at variants that have already been covered due to other patterns to avoid
580 for pattern in patterns {
581 use PatKind::{AscribeUserType, Deref, Leaf, Or, Variant};
582 match &*pattern.kind {
583 AscribeUserType { subpattern, .. } | Deref { subpattern } => {
584 covered.extend(maybe_point_at_variant(ty, slice::from_ref(&subpattern)));
586 Variant { adt_def, variant_index, subpatterns, .. } if adt_def.did == def.did => {
587 let sp = def.variants[*variant_index].ident.span;
588 if covered.contains(&sp) {
593 let pats = subpatterns
595 .map(|field_pattern| field_pattern.pattern.clone())
596 .collect::<Box<[_]>>();
597 covered.extend(maybe_point_at_variant(ty, &pats));
599 Leaf { subpatterns } => {
600 let pats = subpatterns
602 .map(|field_pattern| field_pattern.pattern.clone())
603 .collect::<Box<[_]>>();
604 covered.extend(maybe_point_at_variant(ty, &pats));
607 let pats = pats.iter().cloned().collect::<Box<[_]>>();
608 covered.extend(maybe_point_at_variant(ty, &pats));
617 // Check the legality of legality of by-move bindings.
618 fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: bool, pat: &Pat) {
619 let mut by_ref_span = None;
620 pat.each_binding(|_, hir_id, span, _| {
621 if let Some(&bm) = cx.tables.pat_binding_modes().get(hir_id) {
622 if let ty::BindByReference(..) = bm {
623 by_ref_span = Some(span);
626 cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode");
630 let span_vec = &mut Vec::new();
631 let mut check_move = |p: &Pat, sub: Option<&Pat>| {
632 // Check legality of moving out of the enum.
634 // `x @ Foo(..)` is legal, but `x @ Foo(y)` isn't.
635 if sub.map_or(false, |p| p.contains_bindings()) {
636 struct_span_err!(cx.tcx.sess, p.span, E0007, "cannot bind by-move with sub-bindings")
637 .span_label(p.span, "binds an already bound by-move value by moving it")
639 } else if !has_guard && by_ref_span.is_some() {
640 span_vec.push(p.span);
645 if let hir::PatKind::Binding(.., sub) = &p.kind {
646 if let Some(&bm) = cx.tables.pat_binding_modes().get(p.hir_id) {
647 if let ty::BindByValue(..) = bm {
648 let pat_ty = cx.tables.node_type(p.hir_id);
649 if !pat_ty.is_copy_modulo_regions(cx.tcx, cx.param_env, pat.span) {
650 check_move(p, sub.as_deref());
654 cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode");
660 if !span_vec.is_empty() {
661 let mut err = struct_span_err!(
663 MultiSpan::from_spans(span_vec.clone()),
665 "cannot bind by-move and by-ref in the same pattern",
667 if let Some(by_ref_span) = by_ref_span {
668 err.span_label(by_ref_span, "both by-ref and by-move used");
670 for span in span_vec.iter() {
671 err.span_label(*span, "by-move pattern here");
677 /// Forbids bindings in `@` patterns. This is necessary for memory safety,
678 /// because of the way rvalues are handled in the borrow check. (See issue
680 fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat) {
681 AtBindingPatternVisitor { cx, bindings_allowed: true }.visit_pat(pat);
684 struct AtBindingPatternVisitor<'a, 'b, 'tcx> {
685 cx: &'a MatchVisitor<'b, 'tcx>,
686 bindings_allowed: bool,
689 impl<'v> Visitor<'v> for AtBindingPatternVisitor<'_, '_, '_> {
690 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
691 NestedVisitorMap::None
694 fn visit_pat(&mut self, pat: &Pat) {
696 hir::PatKind::Binding(.., ref subpat) => {
697 if !self.bindings_allowed {
702 "pattern bindings are not allowed after an `@`"
704 .span_label(pat.span, "not allowed after `@`")
708 if subpat.is_some() {
709 let bindings_were_allowed = self.bindings_allowed;
710 self.bindings_allowed = false;
711 intravisit::walk_pat(self, pat);
712 self.bindings_allowed = bindings_were_allowed;
715 _ => intravisit::walk_pat(self, pat),