]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/hair/pattern/check_match.rs
Rollup merge of #61413 - davidtwco:async-argument-order-in-a-sane-way, r=eddyb
[rust.git] / src / librustc_mir / hair / pattern / check_match.rs
1 use super::_match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful};
2 use super::_match::Usefulness::*;
3 use super::_match::WitnessPreference::*;
4
5 use super::{Pattern, PatternContext, PatternError, PatternKind};
6
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::{InternalSubsts, SubstsRef};
15 use rustc::lint;
16 use rustc_errors::{Applicability, DiagnosticBuilder};
17
18 use rustc::hir::def::*;
19 use rustc::hir::def_id::DefId;
20 use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
21 use rustc::hir::{self, Pat, PatKind};
22
23 use smallvec::smallvec;
24 use std::slice;
25
26 use syntax::ptr::P;
27 use syntax_pos::{Span, DUMMY_SP, MultiSpan};
28
29 pub(crate) fn check_match<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
30     let body_id = if let Some(id) = tcx.hir().as_local_hir_id(def_id) {
31         tcx.hir().body_owned_by(id)
32     } else {
33         return;
34     };
35
36     MatchVisitor {
37         tcx,
38         body_owner: def_id,
39         tables: tcx.body_tables(body_id),
40         region_scope_tree: &tcx.region_scope_tree(def_id),
41         param_env: tcx.param_env(def_id),
42         identity_substs: InternalSubsts::identity_for_item(tcx, def_id),
43     }.visit_body(tcx.hir().body(body_id));
44 }
45
46 fn create_e0004<'a>(sess: &'a Session, sp: Span, error_message: String) -> DiagnosticBuilder<'a> {
47     struct_span_err!(sess, sp, E0004, "{}", &error_message)
48 }
49
50 struct MatchVisitor<'a, 'tcx: 'a> {
51     tcx: TyCtxt<'a, 'tcx, 'tcx>,
52     body_owner: DefId,
53     tables: &'a ty::TypeckTables<'tcx>,
54     param_env: ty::ParamEnv<'tcx>,
55     identity_substs: SubstsRef<'tcx>,
56     region_scope_tree: &'a region::ScopeTree,
57 }
58
59 impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> {
60     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
61         NestedVisitorMap::None
62     }
63
64     fn visit_expr(&mut self, ex: &'tcx hir::Expr) {
65         intravisit::walk_expr(self, ex);
66
67         match ex.node {
68             hir::ExprKind::Match(ref scrut, ref arms, source) => {
69                 self.check_match(scrut, arms, source);
70             }
71             _ => {}
72         }
73     }
74
75     fn visit_local(&mut self, loc: &'tcx hir::Local) {
76         intravisit::walk_local(self, loc);
77
78         self.check_irrefutable(&loc.pat, match loc.source {
79             hir::LocalSource::Normal => "local binding",
80             hir::LocalSource::ForLoopDesugar => "`for` loop binding",
81             hir::LocalSource::AsyncFn => "async fn binding",
82             hir::LocalSource::AwaitDesugar => "`await` future binding",
83         });
84
85         // Check legality of move bindings and `@` patterns.
86         self.check_patterns(false, slice::from_ref(&loc.pat));
87     }
88
89     fn visit_body(&mut self, body: &'tcx hir::Body) {
90         intravisit::walk_body(self, body);
91
92         for arg in &body.arguments {
93             self.check_irrefutable(&arg.pat, "function argument");
94             self.check_patterns(false, slice::from_ref(&arg.pat));
95         }
96     }
97 }
98
99
100 impl<'a, 'tcx> PatternContext<'a, 'tcx> {
101     fn report_inlining_errors(&self, pat_span: Span) {
102         for error in &self.errors {
103             match *error {
104                 PatternError::StaticInPattern(span) => {
105                     self.span_e0158(span, "statics cannot be referenced in patterns")
106                 }
107                 PatternError::AssocConstInPattern(span) => {
108                     self.span_e0158(span, "associated consts cannot be referenced in patterns")
109                 }
110                 PatternError::FloatBug => {
111                     // FIXME(#31407) this is only necessary because float parsing is buggy
112                     ::rustc::mir::interpret::struct_error(
113                         self.tcx.at(pat_span),
114                         "could not evaluate float literal (see issue #31407)",
115                     ).emit();
116                 }
117                 PatternError::NonConstPath(span) => {
118                     ::rustc::mir::interpret::struct_error(
119                         self.tcx.at(span),
120                         "runtime values cannot be referenced in patterns",
121                     ).emit();
122                 }
123             }
124         }
125     }
126
127     fn span_e0158(&self, span: Span, text: &str) {
128         span_err!(self.tcx.sess, span, E0158, "{}", text)
129     }
130 }
131
132 impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
133     fn check_patterns(&self, has_guard: bool, pats: &[P<Pat>]) {
134         check_legality_of_move_bindings(self, has_guard, pats);
135         for pat in pats {
136             check_legality_of_bindings_in_at_patterns(self, pat);
137         }
138     }
139
140     fn check_match(
141         &self,
142         scrut: &hir::Expr,
143         arms: &'tcx [hir::Arm],
144         source: hir::MatchSource)
145     {
146         for arm in arms {
147             // First, check legality of move bindings.
148             self.check_patterns(arm.guard.is_some(), &arm.pats);
149
150             // Second, if there is a guard on each arm, make sure it isn't
151             // assigning or borrowing anything mutably.
152             if let Some(ref guard) = arm.guard {
153                 if !self.tcx.features().bind_by_move_pattern_guards {
154                     check_for_mutation_in_guard(self, &guard);
155                 }
156             }
157
158             // Third, perform some lints.
159             for pat in &arm.pats {
160                 check_for_bindings_named_same_as_variants(self, pat);
161             }
162         }
163
164         let module = self.tcx.hir().get_module_parent_by_hir_id(scrut.hir_id);
165         MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
166             let mut have_errors = false;
167
168             let inlined_arms : Vec<(Vec<_>, _)> = arms.iter().map(|arm| (
169                 arm.pats.iter().map(|pat| {
170                     let mut patcx = PatternContext::new(self.tcx,
171                                                         self.param_env.and(self.identity_substs),
172                                                         self.tables);
173                     let pattern = expand_pattern(cx, patcx.lower_pattern(&pat));
174                     if !patcx.errors.is_empty() {
175                         patcx.report_inlining_errors(pat.span);
176                         have_errors = true;
177                     }
178                     (pattern, &**pat)
179                 }).collect(),
180                 arm.guard.as_ref().map(|g| match g {
181                     hir::Guard::If(ref e) => &**e,
182                 })
183             )).collect();
184
185             // Bail out early if inlining failed.
186             if have_errors {
187                 return;
188             }
189
190             // Fourth, check for unreachable arms.
191             check_arms(cx, &inlined_arms, source);
192
193             // Then, if the match has no arms, check whether the scrutinee
194             // is uninhabited.
195             let pat_ty = self.tables.node_type(scrut.hir_id);
196             let module = self.tcx.hir().get_module_parent_by_hir_id(scrut.hir_id);
197             let mut def_span = None;
198             let mut missing_variants = vec![];
199             if inlined_arms.is_empty() {
200                 let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns {
201                     self.tcx.is_ty_uninhabited_from(module, pat_ty)
202                 } else {
203                     match pat_ty.sty {
204                         ty::Never => true,
205                         ty::Adt(def, _) => {
206                             def_span = self.tcx.hir().span_if_local(def.did);
207                             if def.variants.len() < 4 && !def.variants.is_empty() {
208                                 // keep around to point at the definition of non-covered variants
209                                 missing_variants = def.variants.iter()
210                                     .map(|variant| variant.ident)
211                                     .collect();
212                             }
213
214                             let is_non_exhaustive_and_non_local =
215                                 def.is_variant_list_non_exhaustive() && !def.did.is_local();
216
217                             !(is_non_exhaustive_and_non_local) && def.variants.is_empty()
218                         },
219                         _ => false
220                     }
221                 };
222                 if !scrutinee_is_uninhabited {
223                     // We know the type is inhabited, so this must be wrong
224                     let mut err = create_e0004(
225                         self.tcx.sess,
226                         scrut.span,
227                         format!("non-exhaustive patterns: {}", match missing_variants.len() {
228                             0 => format!("type `{}` is non-empty", pat_ty),
229                             1 => format!(
230                                 "pattern `{}` of type `{}` is not handled",
231                                 missing_variants[0].name,
232                                 pat_ty,
233                             ),
234                             _ => format!("multiple patterns of type `{}` are not handled", pat_ty),
235                         }),
236                     );
237                     err.help("ensure that all possible cases are being handled, \
238                               possibly by adding wildcards or more match arms");
239                     if let Some(sp) = def_span {
240                         err.span_label(sp, format!("`{}` defined here", pat_ty));
241                     }
242                     // point at the definition of non-covered enum variants
243                     for variant in &missing_variants {
244                         err.span_label(variant.span, "variant not covered");
245                     }
246                     err.emit();
247                 }
248                 // If the type *is* uninhabited, it's vacuously exhaustive
249                 return;
250             }
251
252             let matrix: Matrix<'_, '_> = inlined_arms
253                 .iter()
254                 .filter(|&&(_, guard)| guard.is_none())
255                 .flat_map(|arm| &arm.0)
256                 .map(|pat| smallvec![pat.0])
257                 .collect();
258             let scrut_ty = self.tables.node_type(scrut.hir_id);
259             check_exhaustive(cx, scrut_ty, scrut.span, &matrix);
260         })
261     }
262
263     fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str) {
264         let module = self.tcx.hir().get_module_parent_by_hir_id(pat.hir_id);
265         MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
266             let mut patcx = PatternContext::new(self.tcx,
267                                                 self.param_env.and(self.identity_substs),
268                                                 self.tables);
269             let pattern = patcx.lower_pattern(pat);
270             let pattern_ty = pattern.ty;
271             let pats: Matrix<'_, '_> = vec![smallvec![
272                 expand_pattern(cx, pattern)
273             ]].into_iter().collect();
274
275             let wild_pattern = Pattern {
276                 ty: pattern_ty,
277                 span: DUMMY_SP,
278                 kind: box PatternKind::Wild,
279             };
280             let witness = match is_useful(cx, &pats, &[&wild_pattern], ConstructWitness) {
281                 UsefulWithWitness(witness) => witness,
282                 NotUseful => return,
283                 Useful => bug!()
284             };
285
286             let pattern_string = witness[0].single_pattern().to_string();
287             let mut err = struct_span_err!(
288                 self.tcx.sess, pat.span, E0005,
289                 "refutable pattern in {}: `{}` not covered",
290                 origin, pattern_string
291             );
292             let label_msg = match pat.node {
293                 PatKind::Path(hir::QPath::Resolved(None, ref path))
294                         if path.segments.len() == 1 && path.segments[0].args.is_none() => {
295                     format!("interpreted as {} {} pattern, not new variable",
296                             path.res.article(), path.res.descr())
297                 }
298                 _ => format!("pattern `{}` not covered", pattern_string),
299             };
300             err.span_label(pat.span, label_msg);
301             if let ty::Adt(def, _) = pattern_ty.sty {
302                 if let Some(sp) = self.tcx.hir().span_if_local(def.did){
303                     err.span_label(sp, format!("`{}` defined here", pattern_ty));
304                 }
305             }
306             err.emit();
307         });
308     }
309 }
310
311 fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pat) {
312     pat.walk(|p| {
313         if let PatKind::Binding(_, _, ident, None) = p.node {
314             if let Some(&bm) = cx.tables.pat_binding_modes().get(p.hir_id) {
315                 if bm != ty::BindByValue(hir::MutImmutable) {
316                     // Nothing to check.
317                     return true;
318                 }
319                 let pat_ty = cx.tables.pat_ty(p);
320                 if let ty::Adt(edef, _) = pat_ty.sty {
321                     if edef.is_enum() && edef.variants.iter().any(|variant| {
322                         variant.ident == ident && variant.ctor_kind == CtorKind::Const
323                     }) {
324                         let ty_path = cx.tcx.def_path_str(edef.did);
325                         let mut err = struct_span_warn!(cx.tcx.sess, p.span, E0170,
326                             "pattern binding `{}` is named the same as one \
327                             of the variants of the type `{}`",
328                             ident, ty_path);
329                         err.span_suggestion(
330                             p.span,
331                             "to match on the variant, qualify the path",
332                             format!("{}::{}", ty_path, ident),
333                             Applicability::MachineApplicable
334                         );
335                         err.emit();
336                     }
337                 }
338             } else {
339                 cx.tcx.sess.delay_span_bug(p.span, "missing binding mode");
340             }
341         }
342         true
343     });
344 }
345
346 /// Checks for common cases of "catchall" patterns that may not be intended as such.
347 fn pat_is_catchall(pat: &Pat) -> bool {
348     match pat.node {
349         PatKind::Binding(.., None) => true,
350         PatKind::Binding(.., Some(ref s)) => pat_is_catchall(s),
351         PatKind::Ref(ref s, _) => pat_is_catchall(s),
352         PatKind::Tuple(ref v, _) => v.iter().all(|p| {
353             pat_is_catchall(&p)
354         }),
355         _ => false
356     }
357 }
358
359 // Check for unreachable patterns
360 fn check_arms<'a, 'tcx>(
361     cx: &mut MatchCheckCtxt<'a, 'tcx>,
362     arms: &[(Vec<(&'a Pattern<'tcx>, &hir::Pat)>, Option<&hir::Expr>)],
363     source: hir::MatchSource,
364 ) {
365     let mut seen = Matrix::empty();
366     let mut catchall = None;
367     for (arm_index, &(ref pats, guard)) in arms.iter().enumerate() {
368         for &(pat, hir_pat) in pats {
369             let v = smallvec![pat];
370
371             match is_useful(cx, &seen, &v, LeaveOutWitness) {
372                 NotUseful => {
373                     match source {
374                         hir::MatchSource::IfDesugar { .. } => bug!(),
375                         hir::MatchSource::IfLetDesugar { .. } => {
376                             cx.tcx.lint_hir(
377                                 lint::builtin::IRREFUTABLE_LET_PATTERNS,
378                                 hir_pat.hir_id,
379                                 pat.span,
380                                 "irrefutable if-let pattern",
381                             );
382                         }
383
384                         hir::MatchSource::WhileLetDesugar => {
385                             // check which arm we're on.
386                             match arm_index {
387                                 // The arm with the user-specified pattern.
388                                 0 => {
389                                     cx.tcx.lint_hir(
390                                         lint::builtin::UNREACHABLE_PATTERNS,
391                                         hir_pat.hir_id, pat.span,
392                                         "unreachable pattern");
393                                 },
394                                 // The arm with the wildcard pattern.
395                                 1 => {
396                                     cx.tcx.lint_hir(
397                                         lint::builtin::IRREFUTABLE_LET_PATTERNS,
398                                         hir_pat.hir_id,
399                                         pat.span,
400                                         "irrefutable while-let pattern",
401                                     );
402                                 },
403                                 _ => bug!(),
404                             }
405                         }
406
407                         hir::MatchSource::ForLoopDesugar |
408                         hir::MatchSource::Normal => {
409                             let mut err = cx.tcx.struct_span_lint_hir(
410                                 lint::builtin::UNREACHABLE_PATTERNS,
411                                 hir_pat.hir_id,
412                                 pat.span,
413                                 "unreachable pattern",
414                             );
415                             // if we had a catchall pattern, hint at that
416                             if let Some(catchall) = catchall {
417                                 err.span_label(pat.span, "unreachable pattern");
418                                 err.span_label(catchall, "matches any value");
419                             }
420                             err.emit();
421                         }
422
423                         // Unreachable patterns in try and await expressions occur when one of
424                         // the arms are an uninhabited type. Which is OK.
425                         hir::MatchSource::AwaitDesugar |
426                         hir::MatchSource::TryDesugar => {}
427                     }
428                 }
429                 Useful => (),
430                 UsefulWithWitness(_) => bug!()
431             }
432             if guard.is_none() {
433                 seen.push(v);
434                 if catchall.is_none() && pat_is_catchall(hir_pat) {
435                     catchall = Some(pat.span);
436                 }
437             }
438         }
439     }
440 }
441
442 fn check_exhaustive<'p, 'a: 'p, 'tcx: 'a>(
443     cx: &mut MatchCheckCtxt<'a, 'tcx>,
444     scrut_ty: Ty<'tcx>,
445     sp: Span,
446     matrix: &Matrix<'p, 'tcx>,
447 ) {
448     let wild_pattern = Pattern {
449         ty: scrut_ty,
450         span: DUMMY_SP,
451         kind: box PatternKind::Wild,
452     };
453     match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) {
454         UsefulWithWitness(pats) => {
455             let witnesses = if pats.is_empty() {
456                 vec![&wild_pattern]
457             } else {
458                 pats.iter().map(|w| w.single_pattern()).collect()
459             };
460
461             const LIMIT: usize = 3;
462             let joined_patterns = match witnesses.len() {
463                 0 => bug!(),
464                 1 => format!("`{}`", witnesses[0]),
465                 2..=LIMIT => {
466                     let (tail, head) = witnesses.split_last().unwrap();
467                     let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
468                     format!("`{}` and `{}`", head.join("`, `"), tail)
469                 }
470                 _ => {
471                     let (head, tail) = witnesses.split_at(LIMIT);
472                     let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
473                     format!("`{}` and {} more", head.join("`, `"), tail.len())
474                 }
475             };
476
477             let label_text = match witnesses.len() {
478                 1 => format!("pattern {} not covered", joined_patterns),
479                 _ => format!("patterns {} not covered", joined_patterns),
480             };
481             let mut err = create_e0004(cx.tcx.sess, sp, format!(
482                 "non-exhaustive patterns: {} not covered",
483                 joined_patterns,
484             ));
485             err.span_label(sp, label_text);
486             // point at the definition of non-covered enum variants
487             if let ty::Adt(def, _) = scrut_ty.sty {
488                 if let Some(sp) = cx.tcx.hir().span_if_local(def.did){
489                     err.span_label(sp, format!("`{}` defined here", scrut_ty));
490                 }
491             }
492             let patterns = witnesses.iter().map(|p| (**p).clone()).collect::<Vec<Pattern<'_>>>();
493             if patterns.len() < 4 {
494                 for sp in maybe_point_at_variant(cx, scrut_ty, patterns.as_slice()) {
495                     err.span_label(sp, "not covered");
496                 }
497             }
498             err.help("ensure that all possible cases are being handled, \
499                       possibly by adding wildcards or more match arms");
500             err.emit();
501         }
502         NotUseful => {
503             // This is good, wildcard pattern isn't reachable
504         }
505         _ => bug!()
506     }
507 }
508
509 fn maybe_point_at_variant(
510     cx: &mut MatchCheckCtxt<'a, 'tcx>,
511     ty: Ty<'tcx>,
512     patterns: &[Pattern<'_>],
513 ) -> Vec<Span> {
514     let mut covered = vec![];
515     if let ty::Adt(def, _) = ty.sty {
516         // Don't point at variants that have already been covered due to other patterns to avoid
517         // visual clutter
518         for pattern in patterns {
519             let pk: &PatternKind<'_> = &pattern.kind;
520             if let PatternKind::Variant { adt_def, variant_index, subpatterns, .. } = pk {
521                 if adt_def.did == def.did {
522                     let sp = def.variants[*variant_index].ident.span;
523                     if covered.contains(&sp) {
524                         continue;
525                     }
526                     covered.push(sp);
527                     let subpatterns = subpatterns.iter()
528                         .map(|field_pattern| field_pattern.pattern.clone())
529                         .collect::<Vec<_>>();
530                     covered.extend(
531                         maybe_point_at_variant(cx, ty, subpatterns.as_slice()),
532                     );
533                 }
534             }
535             if let PatternKind::Leaf { subpatterns } = pk {
536                 let subpatterns = subpatterns.iter()
537                     .map(|field_pattern| field_pattern.pattern.clone())
538                     .collect::<Vec<_>>();
539                 covered.extend(maybe_point_at_variant(cx, ty, subpatterns.as_slice()));
540             }
541         }
542     }
543     covered
544 }
545
546 // Legality of move bindings checking
547 fn check_legality_of_move_bindings(
548     cx: &MatchVisitor<'_, '_>,
549     has_guard: bool,
550     pats: &[P<Pat>],
551 ) {
552     let mut by_ref_span = None;
553     for pat in pats {
554         pat.each_binding(|_, hir_id, span, _path| {
555             if let Some(&bm) = cx.tables.pat_binding_modes().get(hir_id) {
556                 if let ty::BindByReference(..) = bm {
557                     by_ref_span = Some(span);
558                 }
559             } else {
560                 cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode");
561             }
562         })
563     }
564     let span_vec = &mut Vec::new();
565     let check_move = |p: &Pat, sub: Option<&Pat>, span_vec: &mut Vec<Span>| {
566         // check legality of moving out of the enum
567
568         // x @ Foo(..) is legal, but x @ Foo(y) isn't.
569         if sub.map_or(false, |p| p.contains_bindings()) {
570             struct_span_err!(cx.tcx.sess, p.span, E0007,
571                              "cannot bind by-move with sub-bindings")
572                 .span_label(p.span, "binds an already bound by-move value by moving it")
573                 .emit();
574         } else if has_guard && !cx.tcx.features().bind_by_move_pattern_guards {
575             let mut err = struct_span_err!(cx.tcx.sess, p.span, E0008,
576                                            "cannot bind by-move into a pattern guard");
577             err.span_label(p.span, "moves value into pattern guard");
578             if cx.tcx.sess.opts.unstable_features.is_nightly_build() {
579                 err.help("add #![feature(bind_by_move_pattern_guards)] to the \
580                           crate attributes to enable");
581             }
582             err.emit();
583         } else if let Some(_by_ref_span) = by_ref_span {
584             span_vec.push(p.span);
585         }
586     };
587
588     for pat in pats {
589         pat.walk(|p| {
590             if let PatKind::Binding(_, _, _, ref sub) = p.node {
591                 if let Some(&bm) = cx.tables.pat_binding_modes().get(p.hir_id) {
592                     match bm {
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(p, sub.as_ref().map(|p| &**p), span_vec);
597                             }
598                         }
599                         _ => {}
600                     }
601                 } else {
602                     cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode");
603                 }
604             }
605             true
606         });
607     }
608     if !span_vec.is_empty(){
609         let span = MultiSpan::from_spans(span_vec.clone());
610         let mut err = struct_span_err!(
611             cx.tcx.sess,
612             span,
613             E0009,
614             "cannot bind by-move and by-ref in the same pattern",
615         );
616         if let Some(by_ref_span) = by_ref_span {
617             err.span_label(by_ref_span, "both by-ref and by-move used");
618         }
619         for span in span_vec.iter(){
620             err.span_label(*span, "by-move pattern here");
621         }
622         err.emit();
623     }
624 }
625
626 /// Ensures that a pattern guard doesn't borrow by mutable reference or assign.
627 //
628 // FIXME: this should be done by borrowck.
629 fn check_for_mutation_in_guard(cx: &MatchVisitor<'_, '_>, guard: &hir::Guard) {
630     let mut checker = MutationChecker {
631         cx,
632     };
633     match guard {
634         hir::Guard::If(expr) =>
635             ExprUseVisitor::new(&mut checker,
636                                 cx.tcx,
637                                 cx.body_owner,
638                                 cx.param_env,
639                                 cx.region_scope_tree,
640                                 cx.tables,
641                                 None).walk_expr(expr),
642     };
643 }
644
645 struct MutationChecker<'a, 'tcx: 'a> {
646     cx: &'a MatchVisitor<'a, 'tcx>,
647 }
648
649 impl<'a, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'tcx> {
650     fn matched_pat(&mut self, _: &Pat, _: &cmt_<'_>, _: euv::MatchMode) {}
651     fn consume(&mut self, _: hir::HirId, _: Span, _: &cmt_<'_>, _: ConsumeMode) {}
652     fn consume_pat(&mut self, _: &Pat, _: &cmt_<'_>, _: ConsumeMode) {}
653     fn borrow(&mut self,
654               _: hir::HirId,
655               span: Span,
656               _: &cmt_<'_>,
657               _: ty::Region<'tcx>,
658               kind:ty:: BorrowKind,
659               _: LoanCause) {
660         match kind {
661             ty::MutBorrow => {
662                 let mut err = struct_span_err!(self.cx.tcx.sess, span, E0301,
663                           "cannot mutably borrow in a pattern guard");
664                 err.span_label(span, "borrowed mutably in pattern guard");
665                 if self.cx.tcx.sess.opts.unstable_features.is_nightly_build() {
666                     err.help("add #![feature(bind_by_move_pattern_guards)] to the \
667                               crate attributes to enable");
668                 }
669                 err.emit();
670             }
671             ty::ImmBorrow | ty::UniqueImmBorrow => {}
672         }
673     }
674     fn decl_without_init(&mut self, _: hir::HirId, _: Span) {}
675     fn mutate(&mut self, _: hir::HirId, span: Span, _: &cmt_<'_>, mode: MutateMode) {
676         match mode {
677             MutateMode::JustWrite | MutateMode::WriteAndRead => {
678                 struct_span_err!(self.cx.tcx.sess, span, E0302, "cannot assign in a pattern guard")
679                     .span_label(span, "assignment in pattern guard")
680                     .emit();
681             }
682             MutateMode::Init => {}
683         }
684     }
685 }
686
687 /// Forbids bindings in `@` patterns. This is necessary for memory safety,
688 /// because of the way rvalues are handled in the borrow check. (See issue
689 /// #14587.)
690 fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat) {
691     AtBindingPatternVisitor { cx: cx, bindings_allowed: true }.visit_pat(pat);
692 }
693
694 struct AtBindingPatternVisitor<'a, 'b:'a, 'tcx:'b> {
695     cx: &'a MatchVisitor<'b, 'tcx>,
696     bindings_allowed: bool
697 }
698
699 impl<'a, 'b, 'tcx, 'v> Visitor<'v> for AtBindingPatternVisitor<'a, 'b, 'tcx> {
700     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
701         NestedVisitorMap::None
702     }
703
704     fn visit_pat(&mut self, pat: &Pat) {
705         match pat.node {
706             PatKind::Binding(.., ref subpat) => {
707                 if !self.bindings_allowed {
708                     struct_span_err!(self.cx.tcx.sess, pat.span, E0303,
709                                      "pattern bindings are not allowed after an `@`")
710                         .span_label(pat.span,  "not allowed after `@`")
711                         .emit();
712                 }
713
714                 if subpat.is_some() {
715                     let bindings_were_allowed = self.bindings_allowed;
716                     self.bindings_allowed = false;
717                     intravisit::walk_pat(self, pat);
718                     self.bindings_allowed = bindings_were_allowed;
719                 }
720             }
721             _ => intravisit::walk_pat(self, pat),
722         }
723     }
724 }