]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir/hair/pattern/check_match.rs
New upstream version 1.41.1+dfsg1
[rustc.git] / src / librustc_mir / hair / pattern / check_match.rs
1 use super::_match::Usefulness::*;
2 use super::_match::WitnessPreference::*;
3 use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack};
4
5 use super::{PatCtxt, PatKind, PatternError};
6
7 use rustc::lint;
8 use rustc::session::Session;
9 use rustc::ty::subst::{InternalSubsts, SubstsRef};
10 use rustc::ty::{self, Ty, TyCtxt};
11 use rustc_errors::{Applicability, DiagnosticBuilder};
12
13 use rustc::hir::def::*;
14 use rustc::hir::def_id::DefId;
15 use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
16 use rustc::hir::HirId;
17 use rustc::hir::{self, Pat};
18
19 use std::slice;
20
21 use syntax_pos::{MultiSpan, Span};
22
23 use rustc_error_codes::*;
24
25 crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
26 let body_id = match tcx.hir().as_local_hir_id(def_id) {
27 None => return,
28 Some(id) => tcx.hir().body_owned_by(id),
29 };
30
31 let mut visitor = MatchVisitor {
32 tcx,
33 tables: tcx.body_tables(body_id),
34 param_env: tcx.param_env(def_id),
35 identity_substs: InternalSubsts::identity_for_item(tcx, def_id),
36 };
37 visitor.visit_body(tcx.hir().body(body_id));
38 }
39
40 fn create_e0004(sess: &Session, sp: Span, error_message: String) -> DiagnosticBuilder<'_> {
41 struct_span_err!(sess, sp, E0004, "{}", &error_message)
42 }
43
44 struct MatchVisitor<'a, 'tcx> {
45 tcx: TyCtxt<'tcx>,
46 tables: &'a ty::TypeckTables<'tcx>,
47 param_env: ty::ParamEnv<'tcx>,
48 identity_substs: SubstsRef<'tcx>,
49 }
50
51 impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
52 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
53 NestedVisitorMap::None
54 }
55
56 fn visit_expr(&mut self, ex: &'tcx hir::Expr) {
57 intravisit::walk_expr(self, ex);
58
59 if let hir::ExprKind::Match(ref scrut, ref arms, source) = ex.kind {
60 self.check_match(scrut, arms, source);
61 }
62 }
63
64 fn visit_local(&mut self, loc: &'tcx hir::Local) {
65 intravisit::walk_local(self, loc);
66
67 let (msg, sp) = match loc.source {
68 hir::LocalSource::Normal => ("local binding", Some(loc.span)),
69 hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None),
70 hir::LocalSource::AsyncFn => ("async fn binding", None),
71 hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
72 };
73 self.check_irrefutable(&loc.pat, msg, sp);
74
75 // Check legality of move bindings and `@` patterns.
76 self.check_patterns(false, &loc.pat);
77 }
78
79 fn visit_body(&mut self, body: &'tcx hir::Body) {
80 intravisit::walk_body(self, body);
81
82 for param in &body.params {
83 self.check_irrefutable(&param.pat, "function argument", None);
84 self.check_patterns(false, &param.pat);
85 }
86 }
87 }
88
89 impl PatCtxt<'_, '_> {
90 fn report_inlining_errors(&self, pat_span: Span) {
91 for error in &self.errors {
92 match *error {
93 PatternError::StaticInPattern(span) => {
94 self.span_e0158(span, "statics cannot be referenced in patterns")
95 }
96 PatternError::AssocConstInPattern(span) => {
97 self.span_e0158(span, "associated consts cannot be referenced in patterns")
98 }
99 PatternError::FloatBug => {
100 // FIXME(#31407) this is only necessary because float parsing is buggy
101 ::rustc::mir::interpret::struct_error(
102 self.tcx.at(pat_span),
103 "could not evaluate float literal (see issue #31407)",
104 )
105 .emit();
106 }
107 PatternError::NonConstPath(span) => {
108 ::rustc::mir::interpret::struct_error(
109 self.tcx.at(span),
110 "runtime values cannot be referenced in patterns",
111 )
112 .emit();
113 }
114 }
115 }
116 }
117
118 fn span_e0158(&self, span: Span, text: &str) {
119 span_err!(self.tcx.sess, span, E0158, "{}", text)
120 }
121 }
122
123 impl<'tcx> MatchVisitor<'_, 'tcx> {
124 fn check_patterns(&mut self, has_guard: bool, pat: &Pat) {
125 check_legality_of_move_bindings(self, has_guard, pat);
126 check_legality_of_bindings_in_at_patterns(self, pat);
127 }
128
129 fn check_match(&mut self, scrut: &hir::Expr, arms: &'tcx [hir::Arm], source: hir::MatchSource) {
130 for arm in arms {
131 // First, check legality of move bindings.
132 self.check_patterns(arm.guard.is_some(), &arm.pat);
133
134 // Second, perform some lints.
135 check_for_bindings_named_same_as_variants(self, &arm.pat);
136 }
137
138 let module = self.tcx.hir().get_module_parent(scrut.hir_id);
139 MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
140 let mut have_errors = false;
141
142 let inlined_arms: Vec<_> = arms
143 .iter()
144 .map(|arm| {
145 let mut patcx = PatCtxt::new(
146 self.tcx,
147 self.param_env.and(self.identity_substs),
148 self.tables,
149 );
150 patcx.include_lint_checks();
151 let pattern = patcx.lower_pattern(&arm.pat);
152 let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
153 if !patcx.errors.is_empty() {
154 patcx.report_inlining_errors(arm.pat.span);
155 have_errors = true;
156 }
157 (pattern, &*arm.pat, arm.guard.is_some())
158 })
159 .collect();
160
161 // Bail out early if inlining failed.
162 if have_errors {
163 return;
164 }
165
166 // Fourth, check for unreachable arms.
167 let matrix = check_arms(cx, &inlined_arms, source);
168
169 // Fifth, check if the match is exhaustive.
170 let scrut_ty = self.tables.node_type(scrut.hir_id);
171 // Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
172 // since an empty matrix can occur when there are arms, if those arms all have guards.
173 let is_empty_match = inlined_arms.is_empty();
174 check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match);
175 })
176 }
177
178 fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str, sp: Option<Span>) {
179 let module = self.tcx.hir().get_module_parent(pat.hir_id);
180 MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
181 let mut patcx =
182 PatCtxt::new(self.tcx, self.param_env.and(self.identity_substs), self.tables);
183 patcx.include_lint_checks();
184 let pattern = patcx.lower_pattern(pat);
185 let pattern_ty = pattern.ty;
186 let pattern = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
187 let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect();
188
189 let witnesses = match check_not_useful(cx, pattern_ty, &pats, pat.hir_id) {
190 Ok(_) => return,
191 Err(err) => err,
192 };
193
194 let joined_patterns = joined_uncovered_patterns(&witnesses);
195 let mut err = struct_span_err!(
196 self.tcx.sess,
197 pat.span,
198 E0005,
199 "refutable pattern in {}: {} not covered",
200 origin,
201 joined_patterns
202 );
203 let suggest_if_let = match &pat.kind {
204 hir::PatKind::Path(hir::QPath::Resolved(None, path))
205 if path.segments.len() == 1 && path.segments[0].args.is_none() =>
206 {
207 const_not_var(&mut err, cx.tcx, pat, path);
208 false
209 }
210 _ => {
211 err.span_label(
212 pat.span,
213 pattern_not_covered_label(&witnesses, &joined_patterns),
214 );
215 true
216 }
217 };
218
219 if let (Some(span), true) = (sp, suggest_if_let) {
220 err.note(
221 "`let` bindings require an \"irrefutable pattern\", like a `struct` or \
222 an `enum` with only one variant",
223 );
224 if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
225 err.span_suggestion(
226 span,
227 "you might want to use `if let` to ignore the variant that isn't matched",
228 format!("if {} {{ /* */ }}", &snippet[..snippet.len() - 1]),
229 Applicability::HasPlaceholders,
230 );
231 }
232 err.note(
233 "for more information, visit \
234 https://doc.rust-lang.org/book/ch18-02-refutability.html",
235 );
236 }
237
238 adt_defined_here(cx, &mut err, pattern_ty, &witnesses);
239 err.emit();
240 });
241 }
242 }
243
244 /// A path pattern was interpreted as a constant, not a new variable.
245 /// This caused an irrefutable match failure in e.g. `let`.
246 fn const_not_var(err: &mut DiagnosticBuilder<'_>, tcx: TyCtxt<'_>, pat: &Pat, path: &hir::Path) {
247 let descr = path.res.descr();
248 err.span_label(
249 pat.span,
250 format!("interpreted as {} {} pattern, not a new variable", path.res.article(), descr,),
251 );
252
253 err.span_suggestion(
254 pat.span,
255 "introduce a variable instead",
256 format!("{}_var", path.segments[0].ident).to_lowercase(),
257 // Cannot use `MachineApplicable` as it's not really *always* correct
258 // because there may be such an identifier in scope or the user maybe
259 // really wanted to match against the constant. This is quite unlikely however.
260 Applicability::MaybeIncorrect,
261 );
262
263 if let Some(span) = tcx.hir().res_span(path.res) {
264 err.span_label(span, format!("{} defined here", descr));
265 }
266 }
267
268 fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pat) {
269 pat.walk(|p| {
270 if let hir::PatKind::Binding(_, _, ident, None) = p.kind {
271 if let Some(&bm) = cx.tables.pat_binding_modes().get(p.hir_id) {
272 if bm != ty::BindByValue(hir::Mutability::Immutable) {
273 // Nothing to check.
274 return true;
275 }
276 let pat_ty = cx.tables.pat_ty(p);
277 if let ty::Adt(edef, _) = pat_ty.kind {
278 if edef.is_enum()
279 && edef.variants.iter().any(|variant| {
280 variant.ident == ident && variant.ctor_kind == CtorKind::Const
281 })
282 {
283 let ty_path = cx.tcx.def_path_str(edef.did);
284 let mut err = struct_span_warn!(
285 cx.tcx.sess,
286 p.span,
287 E0170,
288 "pattern binding `{}` is named the same as one \
289 of the variants of the type `{}`",
290 ident,
291 ty_path
292 );
293 err.span_suggestion(
294 p.span,
295 "to match on the variant, qualify the path",
296 format!("{}::{}", ty_path, ident),
297 Applicability::MachineApplicable,
298 );
299 err.emit();
300 }
301 }
302 } else {
303 cx.tcx.sess.delay_span_bug(p.span, "missing binding mode");
304 }
305 }
306 true
307 });
308 }
309
310 /// Checks for common cases of "catchall" patterns that may not be intended as such.
311 fn pat_is_catchall(pat: &Pat) -> bool {
312 match pat.kind {
313 hir::PatKind::Binding(.., None) => true,
314 hir::PatKind::Binding(.., Some(ref s)) => pat_is_catchall(s),
315 hir::PatKind::Ref(ref s, _) => pat_is_catchall(s),
316 hir::PatKind::Tuple(ref v, _) => v.iter().all(|p| pat_is_catchall(&p)),
317 _ => false,
318 }
319 }
320
321 // Check for unreachable patterns
322 fn check_arms<'p, 'tcx>(
323 cx: &mut MatchCheckCtxt<'p, 'tcx>,
324 arms: &[(&'p super::Pat<'tcx>, &hir::Pat, bool)],
325 source: hir::MatchSource,
326 ) -> Matrix<'p, 'tcx> {
327 let mut seen = Matrix::empty();
328 let mut catchall = None;
329 for (arm_index, (pat, hir_pat, has_guard)) in arms.iter().enumerate() {
330 let v = PatStack::from_pattern(pat);
331
332 match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id, true) {
333 NotUseful => {
334 match source {
335 hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(),
336
337 hir::MatchSource::IfLetDesugar { .. } | hir::MatchSource::WhileLetDesugar => {
338 // check which arm we're on.
339 match arm_index {
340 // The arm with the user-specified pattern.
341 0 => {
342 cx.tcx.lint_hir(
343 lint::builtin::UNREACHABLE_PATTERNS,
344 hir_pat.hir_id,
345 pat.span,
346 "unreachable pattern",
347 );
348 }
349 // The arm with the wildcard pattern.
350 1 => {
351 let msg = match source {
352 hir::MatchSource::IfLetDesugar { .. } => {
353 "irrefutable if-let pattern"
354 }
355 hir::MatchSource::WhileLetDesugar => {
356 "irrefutable while-let pattern"
357 }
358 _ => bug!(),
359 };
360 cx.tcx.lint_hir(
361 lint::builtin::IRREFUTABLE_LET_PATTERNS,
362 hir_pat.hir_id,
363 pat.span,
364 msg,
365 );
366 }
367 _ => bug!(),
368 }
369 }
370
371 hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
372 let mut err = cx.tcx.struct_span_lint_hir(
373 lint::builtin::UNREACHABLE_PATTERNS,
374 hir_pat.hir_id,
375 pat.span,
376 "unreachable pattern",
377 );
378 // if we had a catchall pattern, hint at that
379 if let Some(catchall) = catchall {
380 err.span_label(pat.span, "unreachable pattern");
381 err.span_label(catchall, "matches any value");
382 }
383 err.emit();
384 }
385
386 // Unreachable patterns in try and await expressions occur when one of
387 // the arms are an uninhabited type. Which is OK.
388 hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
389 }
390 }
391 Useful(unreachable_subpatterns) => {
392 for pat in unreachable_subpatterns {
393 cx.tcx.lint_hir(
394 lint::builtin::UNREACHABLE_PATTERNS,
395 hir_pat.hir_id,
396 pat.span,
397 "unreachable pattern",
398 );
399 }
400 }
401 UsefulWithWitness(_) => bug!(),
402 }
403 if !has_guard {
404 seen.push(v);
405 if catchall.is_none() && pat_is_catchall(hir_pat) {
406 catchall = Some(pat.span);
407 }
408 }
409 }
410 seen
411 }
412
413 fn check_not_useful<'p, 'tcx>(
414 cx: &mut MatchCheckCtxt<'p, 'tcx>,
415 ty: Ty<'tcx>,
416 matrix: &Matrix<'p, 'tcx>,
417 hir_id: HirId,
418 ) -> Result<(), Vec<super::Pat<'tcx>>> {
419 let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty));
420 let v = PatStack::from_pattern(wild_pattern);
421 match is_useful(cx, matrix, &v, ConstructWitness, hir_id, true) {
422 NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
423 UsefulWithWitness(pats) => Err(if pats.is_empty() {
424 bug!("Exhaustiveness check returned no witnesses")
425 } else {
426 pats.into_iter().map(|w| w.single_pattern()).collect()
427 }),
428 Useful(_) => bug!(),
429 }
430 }
431
432 fn check_exhaustive<'p, 'tcx>(
433 cx: &mut MatchCheckCtxt<'p, 'tcx>,
434 scrut_ty: Ty<'tcx>,
435 sp: Span,
436 matrix: &Matrix<'p, 'tcx>,
437 hir_id: HirId,
438 is_empty_match: bool,
439 ) {
440 // In the absence of the `exhaustive_patterns` feature, empty matches are not detected by
441 // `is_useful` to exhaustively match uninhabited types, so we manually check here.
442 if is_empty_match && !cx.tcx.features().exhaustive_patterns {
443 let scrutinee_is_visibly_uninhabited = match scrut_ty.kind {
444 ty::Never => true,
445 ty::Adt(def, _) => {
446 def.is_enum()
447 && def.variants.is_empty()
448 && !cx.is_foreign_non_exhaustive_enum(scrut_ty)
449 }
450 _ => false,
451 };
452 if scrutinee_is_visibly_uninhabited {
453 // If the type *is* uninhabited, an empty match is vacuously exhaustive.
454 return;
455 }
456 }
457
458 let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) {
459 Ok(_) => return,
460 Err(err) => err,
461 };
462
463 let non_empty_enum = match scrut_ty.kind {
464 ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(),
465 _ => false,
466 };
467 // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
468 // informative.
469 let mut err;
470 if is_empty_match && !non_empty_enum {
471 err = create_e0004(
472 cx.tcx.sess,
473 sp,
474 format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
475 );
476 } else {
477 let joined_patterns = joined_uncovered_patterns(&witnesses);
478 err = create_e0004(
479 cx.tcx.sess,
480 sp,
481 format!("non-exhaustive patterns: {} not covered", joined_patterns),
482 );
483 err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
484 };
485
486 adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
487 err.help(
488 "ensure that all possible cases are being handled, \
489 possibly by adding wildcards or more match arms",
490 );
491 err.emit();
492 }
493
494 fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String {
495 const LIMIT: usize = 3;
496 match witnesses {
497 [] => bug!(),
498 [witness] => format!("`{}`", witness),
499 [head @ .., tail] if head.len() < LIMIT => {
500 let head: Vec<_> = head.iter().map(<_>::to_string).collect();
501 format!("`{}` and `{}`", head.join("`, `"), tail)
502 }
503 _ => {
504 let (head, tail) = witnesses.split_at(LIMIT);
505 let head: Vec<_> = head.iter().map(<_>::to_string).collect();
506 format!("`{}` and {} more", head.join("`, `"), tail.len())
507 }
508 }
509 }
510
511 fn pattern_not_covered_label(witnesses: &[super::Pat<'_>], joined_patterns: &str) -> String {
512 format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
513 }
514
515 /// Point at the definition of non-covered `enum` variants.
516 fn adt_defined_here(
517 cx: &MatchCheckCtxt<'_, '_>,
518 err: &mut DiagnosticBuilder<'_>,
519 ty: Ty<'_>,
520 witnesses: &[super::Pat<'_>],
521 ) {
522 let ty = ty.peel_refs();
523 if let ty::Adt(def, _) = ty.kind {
524 if let Some(sp) = cx.tcx.hir().span_if_local(def.did) {
525 err.span_label(sp, format!("`{}` defined here", ty));
526 }
527
528 if witnesses.len() < 4 {
529 for sp in maybe_point_at_variant(ty, &witnesses) {
530 err.span_label(sp, "not covered");
531 }
532 }
533 }
534 }
535
536 fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec<Span> {
537 let mut covered = vec![];
538 if let ty::Adt(def, _) = ty.kind {
539 // Don't point at variants that have already been covered due to other patterns to avoid
540 // visual clutter.
541 for pattern in patterns {
542 use PatKind::{AscribeUserType, Deref, Leaf, Or, Variant};
543 match &*pattern.kind {
544 AscribeUserType { subpattern, .. } | Deref { subpattern } => {
545 covered.extend(maybe_point_at_variant(ty, slice::from_ref(&subpattern)));
546 }
547 Variant { adt_def, variant_index, subpatterns, .. } if adt_def.did == def.did => {
548 let sp = def.variants[*variant_index].ident.span;
549 if covered.contains(&sp) {
550 continue;
551 }
552 covered.push(sp);
553
554 let pats = subpatterns
555 .iter()
556 .map(|field_pattern| field_pattern.pattern.clone())
557 .collect::<Box<[_]>>();
558 covered.extend(maybe_point_at_variant(ty, &pats));
559 }
560 Leaf { subpatterns } => {
561 let pats = subpatterns
562 .iter()
563 .map(|field_pattern| field_pattern.pattern.clone())
564 .collect::<Box<[_]>>();
565 covered.extend(maybe_point_at_variant(ty, &pats));
566 }
567 Or { pats } => {
568 let pats = pats.iter().cloned().collect::<Box<[_]>>();
569 covered.extend(maybe_point_at_variant(ty, &pats));
570 }
571 _ => {}
572 }
573 }
574 }
575 covered
576 }
577
578 // Check the legality of legality of by-move bindings.
579 fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: bool, pat: &Pat) {
580 let mut by_ref_span = None;
581 pat.each_binding(|_, hir_id, span, _| {
582 if let Some(&bm) = cx.tables.pat_binding_modes().get(hir_id) {
583 if let ty::BindByReference(..) = bm {
584 by_ref_span = Some(span);
585 }
586 } else {
587 cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode");
588 }
589 });
590
591 let span_vec = &mut Vec::new();
592 let mut check_move = |p: &Pat, sub: Option<&Pat>| {
593 // Check legality of moving out of the enum.
594 //
595 // `x @ Foo(..)` is legal, but `x @ Foo(y)` isn't.
596 if sub.map_or(false, |p| p.contains_bindings()) {
597 struct_span_err!(cx.tcx.sess, p.span, E0007, "cannot bind by-move with sub-bindings")
598 .span_label(p.span, "binds an already bound by-move value by moving it")
599 .emit();
600 } else if !has_guard && by_ref_span.is_some() {
601 span_vec.push(p.span);
602 }
603 };
604
605 pat.walk(|p| {
606 if let hir::PatKind::Binding(.., sub) = &p.kind {
607 if let Some(&bm) = cx.tables.pat_binding_modes().get(p.hir_id) {
608 if let ty::BindByValue(..) = bm {
609 let pat_ty = cx.tables.node_type(p.hir_id);
610 if !pat_ty.is_copy_modulo_regions(cx.tcx, cx.param_env, pat.span) {
611 check_move(p, sub.as_deref());
612 }
613 }
614 } else {
615 cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode");
616 }
617 }
618 true
619 });
620
621 if !span_vec.is_empty() {
622 let mut err = struct_span_err!(
623 cx.tcx.sess,
624 MultiSpan::from_spans(span_vec.clone()),
625 E0009,
626 "cannot bind by-move and by-ref in the same pattern",
627 );
628 if let Some(by_ref_span) = by_ref_span {
629 err.span_label(by_ref_span, "both by-ref and by-move used");
630 }
631 for span in span_vec.iter() {
632 err.span_label(*span, "by-move pattern here");
633 }
634 err.emit();
635 }
636 }
637
638 /// Forbids bindings in `@` patterns. This is necessary for memory safety,
639 /// because of the way rvalues are handled in the borrow check. (See issue
640 /// #14587.)
641 fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat) {
642 AtBindingPatternVisitor { cx, bindings_allowed: true }.visit_pat(pat);
643 }
644
645 struct AtBindingPatternVisitor<'a, 'b, 'tcx> {
646 cx: &'a MatchVisitor<'b, 'tcx>,
647 bindings_allowed: bool,
648 }
649
650 impl<'v> Visitor<'v> for AtBindingPatternVisitor<'_, '_, '_> {
651 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
652 NestedVisitorMap::None
653 }
654
655 fn visit_pat(&mut self, pat: &Pat) {
656 match pat.kind {
657 hir::PatKind::Binding(.., ref subpat) => {
658 if !self.bindings_allowed {
659 struct_span_err!(
660 self.cx.tcx.sess,
661 pat.span,
662 E0303,
663 "pattern bindings are not allowed after an `@`"
664 )
665 .span_label(pat.span, "not allowed after `@`")
666 .emit();
667 }
668
669 if subpat.is_some() {
670 let bindings_were_allowed = self.bindings_allowed;
671 self.bindings_allowed = false;
672 intravisit::walk_pat(self, pat);
673 self.bindings_allowed = bindings_were_allowed;
674 }
675 }
676 _ => intravisit::walk_pat(self, pat),
677 }
678 }
679 }