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::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor};
8 use rustc::middle::expr_use_visitor::{LoanCause, MutateMode};
9 use rustc::middle::expr_use_visitor as euv;
10 use rustc::middle::mem_categorization::cmt_;
11 use rustc::middle::region;
12 use rustc::session::Session;
13 use rustc::ty::{self, Ty, TyCtxt};
14 use rustc::ty::subst::Substs;
16 use rustc_errors::{Applicability, DiagnosticBuilder};
17 use rustc::util::common::ErrorReported;
19 use rustc::hir::def::*;
20 use rustc::hir::def_id::DefId;
21 use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
22 use rustc::hir::{self, Pat, PatKind};
24 use smallvec::smallvec;
29 use syntax_pos::{Span, DUMMY_SP, MultiSpan};
31 pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
32 for def_id in tcx.body_owners() {
33 tcx.ensure().check_match(def_id);
35 tcx.sess.abort_if_errors();
38 pub(crate) fn check_match<'a, 'tcx>(
39 tcx: TyCtxt<'a, 'tcx, 'tcx>,
41 ) -> Result<(), ErrorReported> {
42 let body_id = if let Some(id) = tcx.hir().as_local_node_id(def_id) {
43 tcx.hir().body_owned_by(id)
48 tcx.sess.track_errors(|| {
51 tables: tcx.body_tables(body_id),
52 region_scope_tree: &tcx.region_scope_tree(def_id),
53 param_env: tcx.param_env(def_id),
54 identity_substs: Substs::identity_for_item(tcx, def_id),
55 }.visit_body(tcx.hir().body(body_id));
59 fn create_e0004<'a>(sess: &'a Session, sp: Span, error_message: String) -> DiagnosticBuilder<'a> {
60 struct_span_err!(sess, sp, E0004, "{}", &error_message)
63 struct MatchVisitor<'a, 'tcx: 'a> {
64 tcx: TyCtxt<'a, 'tcx, 'tcx>,
65 tables: &'a ty::TypeckTables<'tcx>,
66 param_env: ty::ParamEnv<'tcx>,
67 identity_substs: &'tcx Substs<'tcx>,
68 region_scope_tree: &'a region::ScopeTree,
71 impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> {
72 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
73 NestedVisitorMap::None
76 fn visit_expr(&mut self, ex: &'tcx hir::Expr) {
77 intravisit::walk_expr(self, ex);
80 hir::ExprKind::Match(ref scrut, ref arms, source) => {
81 self.check_match(scrut, arms, source);
87 fn visit_local(&mut self, loc: &'tcx hir::Local) {
88 intravisit::walk_local(self, loc);
90 self.check_irrefutable(&loc.pat, match loc.source {
91 hir::LocalSource::Normal => "local binding",
92 hir::LocalSource::ForLoopDesugar => "`for` loop binding",
95 // Check legality of move bindings and `@` patterns.
96 self.check_patterns(false, slice::from_ref(&loc.pat));
99 fn visit_body(&mut self, body: &'tcx hir::Body) {
100 intravisit::walk_body(self, body);
102 for arg in &body.arguments {
103 self.check_irrefutable(&arg.pat, "function argument");
104 self.check_patterns(false, slice::from_ref(&arg.pat));
110 impl<'a, 'tcx> PatternContext<'a, 'tcx> {
111 fn report_inlining_errors(&self, pat_span: Span) {
112 for error in &self.errors {
114 PatternError::StaticInPattern(span) => {
115 self.span_e0158(span, "statics cannot be referenced in patterns")
117 PatternError::AssociatedConstInPattern(span) => {
118 self.span_e0158(span, "associated consts cannot be referenced in patterns")
120 PatternError::FloatBug => {
121 // FIXME(#31407) this is only necessary because float parsing is buggy
122 ::rustc::mir::interpret::struct_error(
123 self.tcx.at(pat_span),
124 "could not evaluate float literal (see issue #31407)",
127 PatternError::NonConstPath(span) => {
128 ::rustc::mir::interpret::struct_error(
130 "runtime values cannot be referenced in patterns",
137 fn span_e0158(&self, span: Span, text: &str) {
138 span_err!(self.tcx.sess, span, E0158, "{}", text)
142 impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
143 fn check_patterns(&self, has_guard: bool, pats: &[P<Pat>]) {
144 check_legality_of_move_bindings(self, has_guard, pats);
146 check_legality_of_bindings_in_at_patterns(self, pat);
153 arms: &'tcx [hir::Arm],
154 source: hir::MatchSource)
157 // First, check legality of move bindings.
158 self.check_patterns(arm.guard.is_some(), &arm.pats);
160 // Second, if there is a guard on each arm, make sure it isn't
161 // assigning or borrowing anything mutably.
162 if let Some(ref guard) = arm.guard {
163 if self.tcx.check_for_mutation_in_guard_via_ast_walk() {
164 check_for_mutation_in_guard(self, &guard);
168 // Third, perform some lints.
169 for pat in &arm.pats {
170 check_for_bindings_named_same_as_variants(self, pat);
174 let module = self.tcx.hir().get_module_parent(scrut.id);
175 MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
176 let mut have_errors = false;
178 let inlined_arms : Vec<(Vec<_>, _)> = arms.iter().map(|arm| (
179 arm.pats.iter().map(|pat| {
180 let mut patcx = PatternContext::new(self.tcx,
181 self.param_env.and(self.identity_substs),
183 let pattern = expand_pattern(cx, patcx.lower_pattern(&pat));
184 if !patcx.errors.is_empty() {
185 patcx.report_inlining_errors(pat.span);
190 arm.guard.as_ref().map(|g| match g {
191 hir::Guard::If(ref e) => &**e,
195 // Bail out early if inlining failed.
200 // Fourth, check for unreachable arms.
201 check_arms(cx, &inlined_arms, source);
203 // Then, if the match has no arms, check whether the scrutinee
205 let pat_ty = self.tables.node_id_to_type(scrut.hir_id);
206 let module = self.tcx.hir().get_module_parent(scrut.id);
207 if inlined_arms.is_empty() {
208 let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns {
209 self.tcx.is_ty_uninhabited_from(module, pat_ty)
213 ty::Adt(def, _) => def.variants.is_empty(),
217 if !scrutinee_is_uninhabited {
218 // We know the type is inhabited, so this must be wrong
219 let mut err = create_e0004(self.tcx.sess, scrut.span,
220 format!("non-exhaustive patterns: type `{}` \
223 span_help!(&mut err, scrut.span,
224 "ensure that all possible cases are being handled, \
225 possibly by adding wildcards or more match arms");
228 // If the type *is* uninhabited, it's vacuously exhaustive
232 let matrix: Matrix = inlined_arms
234 .filter(|&&(_, guard)| guard.is_none())
235 .flat_map(|arm| &arm.0)
236 .map(|pat| smallvec![pat.0])
238 let scrut_ty = self.tables.node_id_to_type(scrut.hir_id);
239 check_exhaustive(cx, scrut_ty, scrut.span, &matrix);
243 fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str) {
244 let module = self.tcx.hir().get_module_parent(pat.id);
245 MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
246 let mut patcx = PatternContext::new(self.tcx,
247 self.param_env.and(self.identity_substs),
249 let pattern = patcx.lower_pattern(pat);
250 let pattern_ty = pattern.ty;
251 let pats: Matrix = vec![smallvec![
252 expand_pattern(cx, pattern)
253 ]].into_iter().collect();
255 let wild_pattern = Pattern {
258 kind: box PatternKind::Wild,
260 let witness = match is_useful(cx, &pats, &[&wild_pattern], ConstructWitness) {
261 UsefulWithWitness(witness) => witness,
266 let pattern_string = witness[0].single_pattern().to_string();
267 let mut diag = struct_span_err!(
268 self.tcx.sess, pat.span, E0005,
269 "refutable pattern in {}: `{}` not covered",
270 origin, pattern_string
272 let label_msg = match pat.node {
273 PatKind::Path(hir::QPath::Resolved(None, ref path))
274 if path.segments.len() == 1 && path.segments[0].args.is_none() => {
275 format!("interpreted as {} {} pattern, not new variable",
276 path.def.article(), path.def.kind_name())
278 _ => format!("pattern `{}` not covered", pattern_string),
280 diag.span_label(pat.span, label_msg);
286 fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor, pat: &Pat) {
288 if let PatKind::Binding(_, _, _, ident, None) = p.node {
289 if let Some(&bm) = cx.tables.pat_binding_modes().get(p.hir_id) {
290 if bm != ty::BindByValue(hir::MutImmutable) {
294 let pat_ty = cx.tables.pat_ty(p);
295 if let ty::Adt(edef, _) = pat_ty.sty {
296 if edef.is_enum() && edef.variants.iter().any(|variant| {
297 variant.ident == ident && variant.ctor_kind == CtorKind::Const
299 let ty_path = cx.tcx.item_path_str(edef.did);
300 let mut err = struct_span_warn!(cx.tcx.sess, p.span, E0170,
301 "pattern binding `{}` is named the same as one \
302 of the variants of the type `{}`",
306 "to match on the variant, qualify the path",
307 format!("{}::{}", ty_path, ident),
308 Applicability::MachineApplicable
314 cx.tcx.sess.delay_span_bug(p.span, "missing binding mode");
321 /// Checks for common cases of "catchall" patterns that may not be intended as such.
322 fn pat_is_catchall(pat: &Pat) -> bool {
324 PatKind::Binding(.., None) => true,
325 PatKind::Binding(.., Some(ref s)) => pat_is_catchall(s),
326 PatKind::Ref(ref s, _) => pat_is_catchall(s),
327 PatKind::Tuple(ref v, _) => v.iter().all(|p| {
334 // Check for unreachable patterns
335 fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
336 arms: &[(Vec<(&'a Pattern<'tcx>, &hir::Pat)>, Option<&hir::Expr>)],
337 source: hir::MatchSource)
339 let mut seen = Matrix::empty();
340 let mut catchall = None;
341 for (arm_index, &(ref pats, guard)) in arms.iter().enumerate() {
342 for &(pat, hir_pat) in pats {
343 let v = smallvec![pat];
345 match is_useful(cx, &seen, &v, LeaveOutWitness) {
348 hir::MatchSource::IfLetDesugar { .. } => {
350 lint::builtin::IRREFUTABLE_LET_PATTERNS,
353 "irrefutable if-let pattern",
357 hir::MatchSource::WhileLetDesugar => {
358 // check which arm we're on.
360 // The arm with the user-specified pattern.
363 lint::builtin::UNREACHABLE_PATTERNS,
364 hir_pat.id, pat.span,
365 "unreachable pattern");
367 // The arm with the wildcard pattern.
370 lint::builtin::IRREFUTABLE_LET_PATTERNS,
373 "irrefutable while-let pattern",
380 hir::MatchSource::ForLoopDesugar |
381 hir::MatchSource::Normal => {
382 let mut err = cx.tcx.struct_span_lint_node(
383 lint::builtin::UNREACHABLE_PATTERNS,
386 "unreachable pattern",
388 // if we had a catchall pattern, hint at that
389 if let Some(catchall) = catchall {
390 err.span_label(pat.span, "unreachable pattern");
391 err.span_label(catchall, "matches any value");
396 // Unreachable patterns in try expressions occur when one of the arms
397 // are an uninhabited type. Which is OK.
398 hir::MatchSource::TryDesugar => {}
402 UsefulWithWitness(_) => bug!()
406 if catchall.is_none() && pat_is_catchall(hir_pat) {
407 catchall = Some(pat.span);
414 fn check_exhaustive<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
417 matrix: &Matrix<'p, 'tcx>) {
418 let wild_pattern = Pattern {
421 kind: box PatternKind::Wild,
423 match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) {
424 UsefulWithWitness(pats) => {
425 let witnesses = if pats.is_empty() {
428 pats.iter().map(|w| w.single_pattern()).collect()
431 const LIMIT: usize = 3;
432 let joined_patterns = match witnesses.len() {
434 1 => format!("`{}`", witnesses[0]),
436 let (tail, head) = witnesses.split_last().unwrap();
437 let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
438 format!("`{}` and `{}`", head.join("`, `"), tail)
441 let (head, tail) = witnesses.split_at(LIMIT);
442 let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
443 format!("`{}` and {} more", head.join("`, `"), tail.len())
447 let label_text = match witnesses.len() {
448 1 => format!("pattern {} not covered", joined_patterns),
449 _ => format!("patterns {} not covered", joined_patterns)
451 create_e0004(cx.tcx.sess, sp,
452 format!("non-exhaustive patterns: {} not covered",
454 .span_label(sp, label_text)
458 // This is good, wildcard pattern isn't reachable
464 // Legality of move bindings checking
465 fn check_legality_of_move_bindings(cx: &MatchVisitor,
468 let mut by_ref_span = None;
470 pat.each_binding(|_, hir_id, span, _path| {
471 if let Some(&bm) = cx.tables.pat_binding_modes().get(hir_id) {
472 if let ty::BindByReference(..) = bm {
473 by_ref_span = Some(span);
476 cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode");
480 let span_vec = &mut Vec::new();
481 let check_move = |p: &Pat, sub: Option<&Pat>, span_vec: &mut Vec<Span>| {
482 // check legality of moving out of the enum
484 // x @ Foo(..) is legal, but x @ Foo(y) isn't.
485 if sub.map_or(false, |p| p.contains_bindings()) {
486 struct_span_err!(cx.tcx.sess, p.span, E0007,
487 "cannot bind by-move with sub-bindings")
488 .span_label(p.span, "binds an already bound by-move value by moving it")
490 } else if has_guard && !cx.tcx.allow_bind_by_move_patterns_with_guards() {
491 let mut err = struct_span_err!(cx.tcx.sess, p.span, E0008,
492 "cannot bind by-move into a pattern guard");
493 err.span_label(p.span, "moves value into pattern guard");
494 if cx.tcx.sess.opts.unstable_features.is_nightly_build() && cx.tcx.use_mir_borrowck() {
495 err.help("add #![feature(bind_by_move_pattern_guards)] to the \
496 crate attributes to enable");
499 } else if let Some(_by_ref_span) = by_ref_span {
500 span_vec.push(p.span);
506 if let PatKind::Binding(_, _, _, _, ref sub) = p.node {
507 if let Some(&bm) = cx.tables.pat_binding_modes().get(p.hir_id) {
509 ty::BindByValue(..) => {
510 let pat_ty = cx.tables.node_id_to_type(p.hir_id);
511 if !pat_ty.is_copy_modulo_regions(cx.tcx, cx.param_env, pat.span) {
512 check_move(p, sub.as_ref().map(|p| &**p), span_vec);
518 cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode");
524 if !span_vec.is_empty(){
525 let span = MultiSpan::from_spans(span_vec.clone());
526 let mut err = struct_span_err!(
530 "cannot bind by-move and by-ref in the same pattern",
532 err.span_label(by_ref_span.unwrap(), "both by-ref and by-move used");
533 for span in span_vec.iter(){
534 err.span_label(*span, "by-move pattern here");
540 /// Ensures that a pattern guard doesn't borrow by mutable reference or
543 /// FIXME: this should be done by borrowck.
544 fn check_for_mutation_in_guard(cx: &MatchVisitor, guard: &hir::Guard) {
545 let mut checker = MutationChecker {
549 hir::Guard::If(expr) =>
550 ExprUseVisitor::new(&mut checker,
553 cx.region_scope_tree,
555 None).walk_expr(expr),
559 struct MutationChecker<'a, 'tcx: 'a> {
560 cx: &'a MatchVisitor<'a, 'tcx>,
563 impl<'a, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'tcx> {
564 fn matched_pat(&mut self, _: &Pat, _: &cmt_, _: euv::MatchMode) {}
565 fn consume(&mut self, _: ast::NodeId, _: Span, _: &cmt_, _: ConsumeMode) {}
566 fn consume_pat(&mut self, _: &Pat, _: &cmt_, _: ConsumeMode) {}
572 kind:ty:: BorrowKind,
576 let mut err = struct_span_err!(self.cx.tcx.sess, span, E0301,
577 "cannot mutably borrow in a pattern guard");
578 err.span_label(span, "borrowed mutably in pattern guard");
579 if self.cx.tcx.sess.opts.unstable_features.is_nightly_build() &&
580 self.cx.tcx.use_mir_borrowck()
582 err.help("add #![feature(bind_by_move_pattern_guards)] to the \
583 crate attributes to enable");
587 ty::ImmBorrow | ty::UniqueImmBorrow => {}
590 fn decl_without_init(&mut self, _: ast::NodeId, _: Span) {}
591 fn mutate(&mut self, _: ast::NodeId, span: Span, _: &cmt_, mode: MutateMode) {
593 MutateMode::JustWrite | MutateMode::WriteAndRead => {
594 struct_span_err!(self.cx.tcx.sess, span, E0302, "cannot assign in a pattern guard")
595 .span_label(span, "assignment in pattern guard")
598 MutateMode::Init => {}
603 /// Forbids bindings in `@` patterns. This is necessary for memory safety,
604 /// because of the way rvalues are handled in the borrow check. (See issue
606 fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor, pat: &Pat) {
607 AtBindingPatternVisitor { cx: cx, bindings_allowed: true }.visit_pat(pat);
610 struct AtBindingPatternVisitor<'a, 'b:'a, 'tcx:'b> {
611 cx: &'a MatchVisitor<'b, 'tcx>,
612 bindings_allowed: bool
615 impl<'a, 'b, 'tcx, 'v> Visitor<'v> for AtBindingPatternVisitor<'a, 'b, 'tcx> {
616 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
617 NestedVisitorMap::None
620 fn visit_pat(&mut self, pat: &Pat) {
622 PatKind::Binding(.., ref subpat) => {
623 if !self.bindings_allowed {
624 struct_span_err!(self.cx.tcx.sess, pat.span, E0303,
625 "pattern bindings are not allowed after an `@`")
626 .span_label(pat.span, "not allowed after `@`")
630 if subpat.is_some() {
631 let bindings_were_allowed = self.bindings_allowed;
632 self.bindings_allowed = false;
633 intravisit::walk_pat(self, pat);
634 self.bindings_allowed = bindings_were_allowed;
637 _ => intravisit::walk_pat(self, pat),