]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/hair/pattern/check_match.rs
or-patterns: use `top_pats_hack` to make things compile.
[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::borrowck::SignalledError;
8 use rustc::session::Session;
9 use rustc::ty::{self, Ty, TyCtxt};
10 use rustc::ty::subst::{InternalSubsts, SubstsRef};
11 use rustc::lint;
12 use rustc_errors::{Applicability, DiagnosticBuilder};
13
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};
19
20 use smallvec::smallvec;
21 use std::slice;
22
23 use syntax_pos::{Span, DUMMY_SP, MultiSpan};
24
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)
28     } else {
29         return SignalledError::NoErrorsSeen;
30     };
31
32     let mut visitor = MatchVisitor {
33         tcx,
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,
38     };
39     visitor.visit_body(tcx.hir().body(body_id));
40     visitor.signalled_error
41 }
42
43 fn create_e0004(sess: &Session, sp: Span, error_message: String) -> DiagnosticBuilder<'_> {
44     struct_span_err!(sess, sp, E0004, "{}", &error_message)
45 }
46
47 struct MatchVisitor<'a, 'tcx> {
48     tcx: TyCtxt<'tcx>,
49     tables: &'a ty::TypeckTables<'tcx>,
50     param_env: ty::ParamEnv<'tcx>,
51     identity_substs: SubstsRef<'tcx>,
52     signalled_error: SignalledError,
53 }
54
55 impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
56     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
57         NestedVisitorMap::None
58     }
59
60     fn visit_expr(&mut self, ex: &'tcx hir::Expr) {
61         intravisit::walk_expr(self, ex);
62
63         if let hir::ExprKind::Match(ref scrut, ref arms, source) = ex.node {
64             self.check_match(scrut, arms, source);
65         }
66     }
67
68     fn visit_local(&mut self, loc: &'tcx hir::Local) {
69         intravisit::walk_local(self, loc);
70
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",
76         });
77
78         // Check legality of move bindings and `@` patterns.
79         self.check_patterns(false, slice::from_ref(&loc.pat));
80     }
81
82     fn visit_body(&mut self, body: &'tcx hir::Body) {
83         intravisit::walk_body(self, body);
84
85         for param in &body.params {
86             self.check_irrefutable(&param.pat, "function argument");
87             self.check_patterns(false, slice::from_ref(&param.pat));
88         }
89     }
90 }
91
92 impl PatternContext<'_, '_> {
93     fn report_inlining_errors(&self, pat_span: Span) {
94         for error in &self.errors {
95             match *error {
96                 PatternError::StaticInPattern(span) => {
97                     self.span_e0158(span, "statics cannot be referenced in patterns")
98                 }
99                 PatternError::AssocConstInPattern(span) => {
100                     self.span_e0158(span, "associated consts cannot be referenced in patterns")
101                 }
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)",
107                     ).emit();
108                 }
109                 PatternError::NonConstPath(span) => {
110                     ::rustc::mir::interpret::struct_error(
111                         self.tcx.at(span),
112                         "runtime values cannot be referenced in patterns",
113                     ).emit();
114                 }
115             }
116         }
117     }
118
119     fn span_e0158(&self, span: Span, text: &str) {
120         span_err!(self.tcx.sess, span, E0158, "{}", text)
121     }
122 }
123
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);
127         for pat in pats {
128             check_legality_of_bindings_in_at_patterns(self, pat);
129         }
130     }
131
132     fn check_match(
133         &mut self,
134         scrut: &hir::Expr,
135         arms: &'tcx [hir::Arm],
136         source: hir::MatchSource
137     ) {
138         for arm in arms {
139             // First, check legality of move bindings.
140             self.check_patterns(arm.guard.is_some(), &arm.top_pats_hack());
141
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;
146             }
147
148             // Third, perform some lints.
149             for pat in arm.top_pats_hack() {
150                 check_for_bindings_named_same_as_variants(self, pat);
151             }
152         }
153
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;
157
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),
162                                                         self.tables);
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);
167                         have_errors = true;
168                     }
169                     (pattern, &**pat)
170                 }).collect(),
171                 arm.guard.as_ref().map(|g| match g {
172                     hir::Guard::If(ref e) => &**e,
173                 })
174             )).collect();
175
176             // Bail out early if inlining failed.
177             if have_errors {
178                 return;
179             }
180
181             // Fourth, check for unreachable arms.
182             check_arms(cx, &inlined_arms, source);
183
184             // Then, if the match has no arms, check whether the scrutinee
185             // is uninhabited.
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)
193                 } else {
194                     match pat_ty.sty {
195                         ty::Never => true,
196                         ty::Adt(def, _) => {
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)
202                                     .collect();
203                             }
204
205                             let is_non_exhaustive_and_non_local =
206                                 def.is_variant_list_non_exhaustive() && !def.did.is_local();
207
208                             !(is_non_exhaustive_and_non_local) && def.variants.is_empty()
209                         },
210                         _ => false
211                     }
212                 };
213                 if !scrutinee_is_uninhabited {
214                     // We know the type is inhabited, so this must be wrong
215                     let mut err = create_e0004(
216                         self.tcx.sess,
217                         scrut.span,
218                         format!("non-exhaustive patterns: {}", match missing_variants.len() {
219                             0 => format!("type `{}` is non-empty", pat_ty),
220                             1 => format!(
221                                 "pattern `{}` of type `{}` is not handled",
222                                 missing_variants[0].name,
223                                 pat_ty,
224                             ),
225                             _ => format!("multiple patterns of type `{}` are not handled", pat_ty),
226                         }),
227                     );
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));
232                     }
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");
236                     }
237                     err.emit();
238                 }
239                 // If the type *is* uninhabited, it's vacuously exhaustive
240                 return;
241             }
242
243             let matrix: Matrix<'_, '_> = inlined_arms
244                 .iter()
245                 .filter(|&&(_, guard)| guard.is_none())
246                 .flat_map(|arm| &arm.0)
247                 .map(|pat| smallvec![pat.0])
248                 .collect();
249             let scrut_ty = self.tables.node_type(scrut.hir_id);
250             check_exhaustive(cx, scrut_ty, scrut.span, &matrix);
251         })
252     }
253
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),
259                                                 self.tables);
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();
266
267             let witnesses = match check_not_useful(cx, pattern_ty, &pats) {
268                 Ok(_) => return,
269                 Err(err) => err,
270             };
271
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
277             );
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())
283                 }
284                 _ => pattern_not_convered_label(&witnesses, &joined_patterns),
285             });
286             adt_defined_here(cx, &mut err, pattern_ty, &witnesses);
287             err.emit();
288         });
289     }
290 }
291
292 fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pat) {
293     pat.walk(|p| {
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) {
297                     // Nothing to check.
298                     return true;
299                 }
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
304                     }) {
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 `{}`",
309                             ident, ty_path);
310                         err.span_suggestion(
311                             p.span,
312                             "to match on the variant, qualify the path",
313                             format!("{}::{}", ty_path, ident),
314                             Applicability::MachineApplicable
315                         );
316                         err.emit();
317                     }
318                 }
319             } else {
320                 cx.tcx.sess.delay_span_bug(p.span, "missing binding mode");
321             }
322         }
323         true
324     });
325 }
326
327 /// Checks for common cases of "catchall" patterns that may not be intended as such.
328 fn pat_is_catchall(pat: &Pat) -> bool {
329     match pat.node {
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| {
334             pat_is_catchall(&p)
335         }),
336         _ => false
337     }
338 }
339
340 // Check for unreachable patterns
341 fn check_arms<'tcx>(
342     cx: &mut MatchCheckCtxt<'_, 'tcx>,
343     arms: &[(Vec<(&Pattern<'tcx>, &hir::Pat)>, Option<&hir::Expr>)],
344     source: hir::MatchSource,
345 ) {
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];
351
352             match is_useful(cx, &seen, &v, LeaveOutWitness) {
353                 NotUseful => {
354                     match source {
355                         hir::MatchSource::IfDesugar { .. } |
356                         hir::MatchSource::WhileDesugar => bug!(),
357                         hir::MatchSource::IfLetDesugar { .. } => {
358                             cx.tcx.lint_hir(
359                                 lint::builtin::IRREFUTABLE_LET_PATTERNS,
360                                 hir_pat.hir_id,
361                                 pat.span,
362                                 "irrefutable if-let pattern",
363                             );
364                         }
365
366                         hir::MatchSource::WhileLetDesugar => {
367                             // check which arm we're on.
368                             match arm_index {
369                                 // The arm with the user-specified pattern.
370                                 0 => {
371                                     cx.tcx.lint_hir(
372                                         lint::builtin::UNREACHABLE_PATTERNS,
373                                         hir_pat.hir_id, pat.span,
374                                         "unreachable pattern");
375                                 },
376                                 // The arm with the wildcard pattern.
377                                 1 => {
378                                     cx.tcx.lint_hir(
379                                         lint::builtin::IRREFUTABLE_LET_PATTERNS,
380                                         hir_pat.hir_id,
381                                         pat.span,
382                                         "irrefutable while-let pattern",
383                                     );
384                                 },
385                                 _ => bug!(),
386                             }
387                         }
388
389                         hir::MatchSource::ForLoopDesugar |
390                         hir::MatchSource::Normal => {
391                             let mut err = cx.tcx.struct_span_lint_hir(
392                                 lint::builtin::UNREACHABLE_PATTERNS,
393                                 hir_pat.hir_id,
394                                 pat.span,
395                                 "unreachable pattern",
396                             );
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");
401                             }
402                             err.emit();
403                         }
404
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 => {}
409                     }
410                 }
411                 Useful => (),
412                 UsefulWithWitness(_) => bug!()
413             }
414             if guard.is_none() {
415                 seen.push(v);
416                 if catchall.is_none() && pat_is_catchall(hir_pat) {
417                     catchall = Some(pat.span);
418                 }
419             }
420         }
421     }
422 }
423
424 fn check_not_useful(
425     cx: &mut MatchCheckCtxt<'_, 'tcx>,
426     ty: Ty<'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() {
433             vec![wild_pattern]
434         } else {
435             pats.into_iter().map(|w| w.single_pattern()).collect()
436         }),
437         Useful => bug!(),
438     }
439 }
440
441 fn check_exhaustive<'tcx>(
442     cx: &mut MatchCheckCtxt<'_, 'tcx>,
443     scrut_ty: Ty<'tcx>,
444     sp: Span,
445     matrix: &Matrix<'_, 'tcx>,
446 ) {
447     let witnesses = match check_not_useful(cx, scrut_ty, matrix) {
448         Ok(_) => return,
449         Err(err) => err,
450     };
451
452     let joined_patterns = joined_uncovered_patterns(&witnesses);
453     let mut err = create_e0004(
454         cx.tcx.sess, sp,
455         format!("non-exhaustive patterns: {} not covered", joined_patterns),
456     );
457     err.span_label(sp, pattern_not_convered_label(&witnesses, &joined_patterns));
458     adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
459     err.help(
460         "ensure that all possible cases are being handled, \
461         possibly by adding wildcards or more match arms"
462     )
463     .emit();
464 }
465
466 fn joined_uncovered_patterns(witnesses: &[Pattern<'_>]) -> String {
467     const LIMIT: usize = 3;
468     match witnesses {
469         [] => bug!(),
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)
474         }
475         _ => {
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())
479         }
480     }
481 }
482
483 fn pattern_not_convered_label(witnesses: &[Pattern<'_>], joined_patterns: &str) -> String {
484     format!("pattern{} {} not covered", rustc_errors::pluralise!(witnesses.len()), joined_patterns)
485 }
486
487 /// Point at the definition of non-covered `enum` variants.
488 fn adt_defined_here(
489     cx: &MatchCheckCtxt<'_, '_>,
490     err: &mut DiagnosticBuilder<'_>,
491     ty: Ty<'_>,
492     witnesses: &[Pattern<'_>],
493 ) {
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));
498         }
499
500         if witnesses.len() < 4 {
501             for sp in maybe_point_at_variant(ty, &witnesses) {
502                 err.span_label(sp, "not covered");
503             }
504         }
505     }
506 }
507
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
512         // visual clutter.
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)));
518                 }
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) {
522                         continue;
523                     }
524                     covered.push(sp);
525
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));
530                 }
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));
536                 }
537                 Or { pats } => {
538                     let pats = pats.iter().cloned().collect::<Box<[_]>>();
539                     covered.extend(maybe_point_at_variant(ty, &pats));
540                 }
541                 _ => {}
542             }
543         }
544     }
545     covered
546 }
547
548 // Legality of move bindings checking
549 fn check_legality_of_move_bindings(
550     cx: &mut MatchVisitor<'_, '_>,
551     has_guard: bool,
552     pats: &[P<Pat>],
553 ) {
554     let mut by_ref_span = None;
555     for pat in pats {
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);
560                 }
561             } else {
562                 cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode");
563             }
564         })
565     }
566     let span_vec = &mut Vec::new();
567     let check_move = |
568         cx: &mut MatchVisitor<'_, '_>,
569         p: &Pat,
570         sub: Option<&Pat>,
571         span_vec: &mut Vec<Span>,
572     | {
573         // check legality of moving out of the enum
574
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")
580                 .emit();
581         } else if !has_guard {
582             if let Some(_by_ref_span) = by_ref_span {
583                 span_vec.push(p.span);
584             }
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(cx, 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 /// Forbids bindings in `@` patterns. This is necessary for memory safety,
627 /// because of the way rvalues are handled in the borrow check. (See issue
628 /// #14587.)
629 fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat) {
630     AtBindingPatternVisitor { cx: cx, bindings_allowed: true }.visit_pat(pat);
631 }
632
633 struct AtBindingPatternVisitor<'a, 'b, 'tcx> {
634     cx: &'a MatchVisitor<'b, 'tcx>,
635     bindings_allowed: bool
636 }
637
638 impl<'v> Visitor<'v> for AtBindingPatternVisitor<'_, '_, '_> {
639     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
640         NestedVisitorMap::None
641     }
642
643     fn visit_pat(&mut self, pat: &Pat) {
644         match pat.node {
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 `@`")
650                         .emit();
651                 }
652
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;
658                 }
659             }
660             _ => intravisit::walk_pat(self, pat),
661         }
662     }
663 }