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