]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir/hair/pattern/check_match.rs
New upstream version 1.40.0+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, DUMMY_SP};
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::MutImmutable) {
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 { ty, span: DUMMY_SP, kind: box PatKind::Wild };
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::pluralise!(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 }