1 use super::_match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful};
2 use super::_match::Usefulness::*;
3 use super::_match::WitnessPreference::*;
5 use super::{Pattern, PatternContext, PatternError, PatternKind};
7 use rustc::middle::borrowck::SignalledError;
8 use rustc::session::Session;
9 use rustc::ty::{self, Ty, TyCtxt};
10 use rustc::ty::subst::{InternalSubsts, SubstsRef};
12 use rustc_errors::{Applicability, DiagnosticBuilder};
14 use rustc::hir::def::*;
15 use rustc::hir::def_id::DefId;
16 use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
17 use rustc::hir::ptr::P;
18 use rustc::hir::{self, Pat, PatKind};
20 use smallvec::smallvec;
23 use syntax_pos::{Span, DUMMY_SP, MultiSpan};
25 crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) -> SignalledError {
26 let body_id = if let Some(id) = tcx.hir().as_local_hir_id(def_id) {
27 tcx.hir().body_owned_by(id)
29 return SignalledError::NoErrorsSeen;
32 let mut visitor = MatchVisitor {
34 tables: tcx.body_tables(body_id),
35 param_env: tcx.param_env(def_id),
36 identity_substs: InternalSubsts::identity_for_item(tcx, def_id),
37 signalled_error: SignalledError::NoErrorsSeen,
39 visitor.visit_body(tcx.hir().body(body_id));
40 visitor.signalled_error
43 fn create_e0004(sess: &Session, sp: Span, error_message: String) -> DiagnosticBuilder<'_> {
44 struct_span_err!(sess, sp, E0004, "{}", &error_message)
47 struct MatchVisitor<'a, 'tcx> {
49 tables: &'a ty::TypeckTables<'tcx>,
50 param_env: ty::ParamEnv<'tcx>,
51 identity_substs: SubstsRef<'tcx>,
52 signalled_error: SignalledError,
55 impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
56 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
57 NestedVisitorMap::None
60 fn visit_expr(&mut self, ex: &'tcx hir::Expr) {
61 intravisit::walk_expr(self, ex);
63 if let hir::ExprKind::Match(ref scrut, ref arms, source) = ex.node {
64 self.check_match(scrut, arms, source);
68 fn visit_local(&mut self, loc: &'tcx hir::Local) {
69 intravisit::walk_local(self, loc);
71 self.check_irrefutable(&loc.pat, match loc.source {
72 hir::LocalSource::Normal => "local binding",
73 hir::LocalSource::ForLoopDesugar => "`for` loop binding",
74 hir::LocalSource::AsyncFn => "async fn binding",
75 hir::LocalSource::AwaitDesugar => "`await` future binding",
78 // Check legality of move bindings and `@` patterns.
79 self.check_patterns(false, slice::from_ref(&loc.pat));
82 fn visit_body(&mut self, body: &'tcx hir::Body) {
83 intravisit::walk_body(self, body);
85 for param in &body.params {
86 self.check_irrefutable(¶m.pat, "function argument");
87 self.check_patterns(false, slice::from_ref(¶m.pat));
92 impl PatternContext<'_, '_> {
93 fn report_inlining_errors(&self, pat_span: Span) {
94 for error in &self.errors {
96 PatternError::StaticInPattern(span) => {
97 self.span_e0158(span, "statics cannot be referenced in patterns")
99 PatternError::AssocConstInPattern(span) => {
100 self.span_e0158(span, "associated consts cannot be referenced in patterns")
102 PatternError::FloatBug => {
103 // FIXME(#31407) this is only necessary because float parsing is buggy
104 ::rustc::mir::interpret::struct_error(
105 self.tcx.at(pat_span),
106 "could not evaluate float literal (see issue #31407)",
109 PatternError::NonConstPath(span) => {
110 ::rustc::mir::interpret::struct_error(
112 "runtime values cannot be referenced in patterns",
119 fn span_e0158(&self, span: Span, text: &str) {
120 span_err!(self.tcx.sess, span, E0158, "{}", text)
124 impl<'tcx> MatchVisitor<'_, 'tcx> {
125 fn check_patterns(&mut self, has_guard: bool, pats: &[P<Pat>]) {
126 check_legality_of_move_bindings(self, has_guard, pats);
128 check_legality_of_bindings_in_at_patterns(self, pat);
135 arms: &'tcx [hir::Arm],
136 source: hir::MatchSource
139 // First, check legality of move bindings.
140 self.check_patterns(arm.guard.is_some(), &arm.top_pats_hack());
142 // Second, if there is a guard on each arm, make sure it isn't
143 // assigning or borrowing anything mutably.
144 if arm.guard.is_some() {
145 self.signalled_error = SignalledError::SawSomeError;
148 // Third, perform some lints.
149 for pat in arm.top_pats_hack() {
150 check_for_bindings_named_same_as_variants(self, pat);
154 let module = self.tcx.hir().get_module_parent(scrut.hir_id);
155 MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
156 let mut have_errors = false;
158 let inlined_arms : Vec<(Vec<_>, _)> = arms.iter().map(|arm| (
159 arm.top_pats_hack().iter().map(|pat| {
160 let mut patcx = PatternContext::new(self.tcx,
161 self.param_env.and(self.identity_substs),
163 patcx.include_lint_checks();
164 let pattern = expand_pattern(cx, patcx.lower_pattern(&pat));
165 if !patcx.errors.is_empty() {
166 patcx.report_inlining_errors(pat.span);
171 arm.guard.as_ref().map(|g| match g {
172 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
200 missing_variants = def.variants.iter()
201 .map(|variant| variant.ident)
205 let is_non_exhaustive_and_non_local =
206 def.is_variant_list_non_exhaustive() && !def.did.is_local();
208 !(is_non_exhaustive_and_non_local) && def.variants.is_empty()
213 if !scrutinee_is_uninhabited {
214 // We know the type is inhabited, so this must be wrong
215 let mut err = create_e0004(
218 format!("non-exhaustive patterns: {}", match missing_variants.len() {
219 0 => format!("type `{}` is non-empty", pat_ty),
221 "pattern `{}` of type `{}` is not handled",
222 missing_variants[0].name,
225 _ => format!("multiple patterns of type `{}` are not handled", pat_ty),
228 err.help("ensure that all possible cases are being handled, \
229 possibly by adding wildcards or more match arms");
230 if let Some(sp) = def_span {
231 err.span_label(sp, format!("`{}` defined here", pat_ty));
233 // point at the definition of non-covered enum variants
234 for variant in &missing_variants {
235 err.span_label(variant.span, "variant not covered");
239 // If the type *is* uninhabited, it's vacuously exhaustive
243 let matrix: Matrix<'_, '_> = inlined_arms
245 .filter(|&&(_, guard)| guard.is_none())
246 .flat_map(|arm| &arm.0)
247 .map(|pat| smallvec![pat.0])
249 let scrut_ty = self.tables.node_type(scrut.hir_id);
250 check_exhaustive(cx, scrut_ty, scrut.span, &matrix);
254 fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str) {
255 let module = self.tcx.hir().get_module_parent(pat.hir_id);
256 MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
257 let mut patcx = PatternContext::new(self.tcx,
258 self.param_env.and(self.identity_substs),
260 patcx.include_lint_checks();
261 let pattern = patcx.lower_pattern(pat);
262 let pattern_ty = pattern.ty;
263 let pats: Matrix<'_, '_> = vec![smallvec![
264 expand_pattern(cx, pattern)
265 ]].into_iter().collect();
267 let witnesses = match check_not_useful(cx, pattern_ty, &pats) {
272 let joined_patterns = joined_uncovered_patterns(&witnesses);
273 let mut err = struct_span_err!(
274 self.tcx.sess, pat.span, E0005,
275 "refutable pattern in {}: {} not covered",
276 origin, joined_patterns
278 err.span_label(pat.span, match &pat.node {
279 PatKind::Path(hir::QPath::Resolved(None, path))
280 if path.segments.len() == 1 && path.segments[0].args.is_none() => {
281 format!("interpreted as {} {} pattern, not new variable",
282 path.res.article(), path.res.descr())
284 _ => pattern_not_convered_label(&witnesses, &joined_patterns),
286 adt_defined_here(cx, &mut err, pattern_ty, &witnesses);
292 fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pat) {
294 if let PatKind::Binding(_, _, ident, None) = p.node {
295 if let Some(&bm) = cx.tables.pat_binding_modes().get(p.hir_id) {
296 if bm != ty::BindByValue(hir::MutImmutable) {
300 let pat_ty = cx.tables.pat_ty(p);
301 if let ty::Adt(edef, _) = pat_ty.sty {
302 if edef.is_enum() && edef.variants.iter().any(|variant| {
303 variant.ident == ident && variant.ctor_kind == CtorKind::Const
305 let ty_path = cx.tcx.def_path_str(edef.did);
306 let mut err = struct_span_warn!(cx.tcx.sess, p.span, E0170,
307 "pattern binding `{}` is named the same as one \
308 of the variants of the type `{}`",
312 "to match on the variant, qualify the path",
313 format!("{}::{}", ty_path, ident),
314 Applicability::MachineApplicable
320 cx.tcx.sess.delay_span_bug(p.span, "missing binding mode");
327 /// Checks for common cases of "catchall" patterns that may not be intended as such.
328 fn pat_is_catchall(pat: &Pat) -> bool {
330 PatKind::Binding(.., None) => true,
331 PatKind::Binding(.., Some(ref s)) => pat_is_catchall(s),
332 PatKind::Ref(ref s, _) => pat_is_catchall(s),
333 PatKind::Tuple(ref v, _) => v.iter().all(|p| {
340 // Check for unreachable patterns
342 cx: &mut MatchCheckCtxt<'_, 'tcx>,
343 arms: &[(Vec<(&Pattern<'tcx>, &hir::Pat)>, Option<&hir::Expr>)],
344 source: hir::MatchSource,
346 let mut seen = Matrix::empty();
347 let mut catchall = None;
348 for (arm_index, &(ref pats, guard)) in arms.iter().enumerate() {
349 for &(pat, hir_pat) in pats {
350 let v = smallvec![pat];
352 match is_useful(cx, &seen, &v, LeaveOutWitness) {
355 hir::MatchSource::IfDesugar { .. } |
356 hir::MatchSource::WhileDesugar => bug!(),
357 hir::MatchSource::IfLetDesugar { .. } => {
359 lint::builtin::IRREFUTABLE_LET_PATTERNS,
362 "irrefutable if-let pattern",
366 hir::MatchSource::WhileLetDesugar => {
367 // check which arm we're on.
369 // The arm with the user-specified pattern.
372 lint::builtin::UNREACHABLE_PATTERNS,
373 hir_pat.hir_id, pat.span,
374 "unreachable pattern");
376 // The arm with the wildcard pattern.
379 lint::builtin::IRREFUTABLE_LET_PATTERNS,
382 "irrefutable while-let pattern",
389 hir::MatchSource::ForLoopDesugar |
390 hir::MatchSource::Normal => {
391 let mut err = cx.tcx.struct_span_lint_hir(
392 lint::builtin::UNREACHABLE_PATTERNS,
395 "unreachable pattern",
397 // if we had a catchall pattern, hint at that
398 if let Some(catchall) = catchall {
399 err.span_label(pat.span, "unreachable pattern");
400 err.span_label(catchall, "matches any value");
405 // Unreachable patterns in try and await expressions occur when one of
406 // the arms are an uninhabited type. Which is OK.
407 hir::MatchSource::AwaitDesugar |
408 hir::MatchSource::TryDesugar => {}
412 UsefulWithWitness(_) => bug!()
416 if catchall.is_none() && pat_is_catchall(hir_pat) {
417 catchall = Some(pat.span);
425 cx: &mut MatchCheckCtxt<'_, 'tcx>,
427 matrix: &Matrix<'_, 'tcx>,
428 ) -> Result<(), Vec<Pattern<'tcx>>> {
429 let wild_pattern = Pattern { ty, span: DUMMY_SP, kind: box PatternKind::Wild };
430 match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) {
431 NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
432 UsefulWithWitness(pats) => Err(if pats.is_empty() {
435 pats.into_iter().map(|w| w.single_pattern()).collect()
441 fn check_exhaustive<'tcx>(
442 cx: &mut MatchCheckCtxt<'_, 'tcx>,
445 matrix: &Matrix<'_, 'tcx>,
447 let witnesses = match check_not_useful(cx, scrut_ty, matrix) {
452 let joined_patterns = joined_uncovered_patterns(&witnesses);
453 let mut err = create_e0004(
455 format!("non-exhaustive patterns: {} not covered", joined_patterns),
457 err.span_label(sp, pattern_not_convered_label(&witnesses, &joined_patterns));
458 adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
460 "ensure that all possible cases are being handled, \
461 possibly by adding wildcards or more match arms"
466 fn joined_uncovered_patterns(witnesses: &[Pattern<'_>]) -> String {
467 const LIMIT: usize = 3;
470 [witness] => format!("`{}`", witness),
471 [head @ .., tail] if head.len() < LIMIT => {
472 let head: Vec<_> = head.iter().map(<_>::to_string).collect();
473 format!("`{}` and `{}`", head.join("`, `"), tail)
476 let (head, tail) = witnesses.split_at(LIMIT);
477 let head: Vec<_> = head.iter().map(<_>::to_string).collect();
478 format!("`{}` and {} more", head.join("`, `"), tail.len())
483 fn pattern_not_convered_label(witnesses: &[Pattern<'_>], joined_patterns: &str) -> String {
484 format!("pattern{} {} not covered", rustc_errors::pluralise!(witnesses.len()), joined_patterns)
487 /// Point at the definition of non-covered `enum` variants.
489 cx: &MatchCheckCtxt<'_, '_>,
490 err: &mut DiagnosticBuilder<'_>,
492 witnesses: &[Pattern<'_>],
494 let ty = ty.peel_refs();
495 if let ty::Adt(def, _) = ty.sty {
496 if let Some(sp) = cx.tcx.hir().span_if_local(def.did) {
497 err.span_label(sp, format!("`{}` defined here", ty));
500 if witnesses.len() < 4 {
501 for sp in maybe_point_at_variant(ty, &witnesses) {
502 err.span_label(sp, "not covered");
508 fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[Pattern<'_>]) -> Vec<Span> {
509 let mut covered = vec![];
510 if let ty::Adt(def, _) = ty.sty {
511 // Don't point at variants that have already been covered due to other patterns to avoid
513 for pattern in patterns {
514 use PatternKind::{AscribeUserType, Deref, Variant, Or, Leaf};
515 match &*pattern.kind {
516 AscribeUserType { subpattern, .. } | Deref { subpattern } => {
517 covered.extend(maybe_point_at_variant(ty, slice::from_ref(&subpattern)));
519 Variant { adt_def, variant_index, subpatterns, .. } if adt_def.did == def.did => {
520 let sp = def.variants[*variant_index].ident.span;
521 if covered.contains(&sp) {
526 let pats = subpatterns.iter()
527 .map(|field_pattern| field_pattern.pattern.clone())
528 .collect::<Box<[_]>>();
529 covered.extend(maybe_point_at_variant(ty, &pats));
531 Leaf { subpatterns } => {
532 let pats = subpatterns.iter()
533 .map(|field_pattern| field_pattern.pattern.clone())
534 .collect::<Box<[_]>>();
535 covered.extend(maybe_point_at_variant(ty, &pats));
538 let pats = pats.iter().cloned().collect::<Box<[_]>>();
539 covered.extend(maybe_point_at_variant(ty, &pats));
548 // Legality of move bindings checking
549 fn check_legality_of_move_bindings(
550 cx: &mut MatchVisitor<'_, '_>,
554 let mut by_ref_span = None;
556 pat.each_binding(|_, hir_id, span, _path| {
557 if let Some(&bm) = cx.tables.pat_binding_modes().get(hir_id) {
558 if let ty::BindByReference(..) = bm {
559 by_ref_span = Some(span);
562 cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode");
566 let span_vec = &mut Vec::new();
568 cx: &mut MatchVisitor<'_, '_>,
571 span_vec: &mut Vec<Span>,
573 // check legality of moving out of the enum
575 // x @ Foo(..) is legal, but x @ Foo(y) isn't.
576 if sub.map_or(false, |p| p.contains_bindings()) {
577 struct_span_err!(cx.tcx.sess, p.span, E0007,
578 "cannot bind by-move with sub-bindings")
579 .span_label(p.span, "binds an already bound by-move value by moving it")
581 } else if !has_guard {
582 if let Some(_by_ref_span) = by_ref_span {
583 span_vec.push(p.span);
590 if let PatKind::Binding(_, _, _, ref sub) = p.node {
591 if let Some(&bm) = cx.tables.pat_binding_modes().get(p.hir_id) {
593 ty::BindByValue(..) => {
594 let pat_ty = cx.tables.node_type(p.hir_id);
595 if !pat_ty.is_copy_modulo_regions(cx.tcx, cx.param_env, pat.span) {
596 check_move(cx, p, sub.as_ref().map(|p| &**p), span_vec);
602 cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode");
608 if !span_vec.is_empty(){
609 let span = MultiSpan::from_spans(span_vec.clone());
610 let mut err = struct_span_err!(
614 "cannot bind by-move and by-ref in the same pattern",
616 if let Some(by_ref_span) = by_ref_span {
617 err.span_label(by_ref_span, "both by-ref and by-move used");
619 for span in span_vec.iter(){
620 err.span_label(*span, "by-move pattern here");
626 /// Forbids bindings in `@` patterns. This is necessary for memory safety,
627 /// because of the way rvalues are handled in the borrow check. (See issue
629 fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat) {
630 AtBindingPatternVisitor { cx: cx, bindings_allowed: true }.visit_pat(pat);
633 struct AtBindingPatternVisitor<'a, 'b, 'tcx> {
634 cx: &'a MatchVisitor<'b, 'tcx>,
635 bindings_allowed: bool
638 impl<'v> Visitor<'v> for AtBindingPatternVisitor<'_, '_, '_> {
639 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
640 NestedVisitorMap::None
643 fn visit_pat(&mut self, pat: &Pat) {
645 PatKind::Binding(.., ref subpat) => {
646 if !self.bindings_allowed {
647 struct_span_err!(self.cx.tcx.sess, pat.span, E0303,
648 "pattern bindings are not allowed after an `@`")
649 .span_label(pat.span, "not allowed after `@`")
653 if subpat.is_some() {
654 let bindings_were_allowed = self.bindings_allowed;
655 self.bindings_allowed = false;
656 intravisit::walk_pat(self, pat);
657 self.bindings_allowed = bindings_were_allowed;
660 _ => intravisit::walk_pat(self, pat),