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