1 use super::_match
::Usefulness
::*;
2 use super::_match
::WitnessPreference
::*;
3 use super::_match
::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack}
;
5 use super::{PatCtxt, PatKind, PatternError}
;
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}
;
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}
;
24 crate fn check_match(tcx
: TyCtxt
<'_
>, def_id
: DefId
) {
25 let body_id
= match tcx
.hir().as_local_hir_id(def_id
) {
27 Some(id
) => tcx
.hir().body_owned_by(id
),
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
));
35 fn create_e0004(sess
: &Session
, sp
: Span
, error_message
: String
) -> DiagnosticBuilder
<'_
> {
36 struct_span_err
!(sess
, sp
, E0004
, "{}", &error_message
)
39 struct MatchVisitor
<'a
, 'tcx
> {
41 tables
: &'a ty
::TypeckTables
<'tcx
>,
42 param_env
: ty
::ParamEnv
<'tcx
>,
45 impl<'tcx
> Visitor
<'tcx
> for MatchVisitor
<'_
, 'tcx
> {
48 fn nested_visit_map(&mut self) -> NestedVisitorMap
<'_
, Self::Map
> {
49 NestedVisitorMap
::None
52 fn visit_expr(&mut self, ex
: &'tcx hir
::Expr
<'tcx
>) {
53 intravisit
::walk_expr(self, ex
);
55 if let hir
::ExprKind
::Match(ref scrut
, ref arms
, source
) = ex
.kind
{
56 self.check_match(scrut
, arms
, source
);
60 fn visit_local(&mut self, loc
: &'tcx hir
::Local
<'tcx
>) {
61 intravisit
::walk_local(self, loc
);
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
),
69 self.check_irrefutable(&loc
.pat
, msg
, sp
);
70 self.check_patterns(false, &loc
.pat
);
73 fn visit_param(&mut self, param
: &'tcx hir
::Param
<'tcx
>) {
74 intravisit
::walk_param(self, param
);
75 self.check_irrefutable(¶m
.pat
, "function argument", None
);
76 self.check_patterns(false, ¶m
.pat
);
80 impl PatCtxt
<'_
, '_
> {
81 fn report_inlining_errors(&self, pat_span
: Span
) {
82 for error
in &self.errors
{
84 PatternError
::StaticInPattern(span
) => {
85 self.span_e0158(span
, "statics cannot be referenced in patterns")
87 PatternError
::AssocConstInPattern(span
) => {
88 self.span_e0158(span
, "associated consts cannot be referenced in patterns")
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)",
98 PatternError
::NonConstPath(span
) => {
99 ::rustc
::mir
::interpret
::struct_error(
101 "runtime values cannot be referenced in patterns",
109 fn span_e0158(&self, span
: Span
, text
: &str) {
110 struct_span_err
!(self.tcx
.sess
, span
, E0158
, "{}", text
).emit();
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
);
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
);
123 check_for_bindings_named_same_as_variants(self, pat
);
126 fn lower_pattern
<'p
>(
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() {
139 patcx
.report_inlining_errors(pat
.span
);
141 (pattern
, pattern_ty
)
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
));
151 scrut
: &hir
::Expr
<'_
>,
152 arms
: &'tcx
[hir
::Arm
<'tcx
>],
153 source
: hir
::MatchSource
,
156 // Check the arm for some things unrelated to exhaustiveness.
157 self.check_patterns(arm
.guard
.is_some(), &arm
.pat
);
160 self.check_in_cx(scrut
.hir_id
, |ref mut cx
| {
161 let mut have_errors
= false;
163 let inlined_arms
: Vec
<_
> = arms
165 .map(|hir
::Arm { pat, guard, .. }
| {
166 (self.lower_pattern(cx
, pat
, &mut have_errors
).0, pat
.hir_id
, guard
.is_some())
170 // Bail out early if inlining failed.
175 // Fourth, check for unreachable arms.
176 let matrix
= check_arms(cx
, &inlined_arms
, source
);
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
);
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();
192 let witnesses
= match check_not_useful(cx
, pattern_ty
, &pats
, pat
.hir_id
) {
197 let joined_patterns
= joined_uncovered_patterns(&witnesses
);
198 let mut err
= struct_span_err
!(
202 "refutable pattern in {}: {} not covered",
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() =>
210 const_not_var(&mut err
, cx
.tcx
, pat
, path
);
216 pattern_not_covered_label(&witnesses
, &joined_patterns
),
222 if let (Some(span
), true) = (sp
, suggest_if_let
) {
224 "`let` bindings require an \"irrefutable pattern\", like a `struct` or \
225 an `enum` with only one variant",
227 if let Ok(snippet
) = self.tcx
.sess
.source_map().span_to_snippet(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
,
236 "for more information, visit \
237 https://doc.rust-lang.org/book/ch18-02-refutability.html",
241 adt_defined_here(cx
, &mut err
, pattern_ty
, &witnesses
);
247 /// A path pattern was interpreted as a constant, not a new variable.
248 /// This caused an irrefutable match failure in e.g. `let`.
250 err
: &mut DiagnosticBuilder
<'_
>,
253 path
: &hir
::Path
<'_
>,
255 let descr
= path
.res
.descr();
258 format
!("interpreted as {} {} pattern, not a new variable", path
.res
.article(), descr
,),
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
,
271 if let Some(span
) = tcx
.hir().res_span(path
.res
) {
272 err
.span_label(span
, format
!("{} defined here", descr
));
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
)
282 let pat_ty
= cx
.tables
.pat_ty(p
).peel_refs();
283 if let ty
::Adt(edef
, _
) = pat_ty
.kind
{
285 && edef
.variants
.iter().any(|variant
| {
286 variant
.ident
== ident
&& variant
.ctor_kind
== CtorKind
::Const
289 cx
.tcx
.struct_span_lint_hir(
290 BINDINGS_WITH_VARIANT_NAME
,
294 let ty_path
= cx
.tcx
.def_path_str(edef
.did
);
296 "pattern binding `{}` is named the same as one \
297 of the variants of the type `{}`",
300 .code(error_code
!(E0170
))
303 "to match on the variant, qualify the path",
304 format
!("{}::{}", ty_path
, ident
),
305 Applicability
::MachineApplicable
,
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
::*;
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
)),
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");
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",
347 lint
.build(msg
).emit()
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) {
364 hir
::MatchSource
::IfDesugar { .. }
| hir
::MatchSource
::WhileDesugar
=> bug
!(),
366 hir
::MatchSource
::IfLetDesugar { .. }
| hir
::MatchSource
::WhileLetDesugar
=> {
367 // Check which arm we're on.
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
),
377 hir
::MatchSource
::ForLoopDesugar
| hir
::MatchSource
::Normal
=> {
378 unreachable_pattern(cx
.tcx
, pat
.span
, id
, catchall
);
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
=> {}
386 Useful(unreachable_subpatterns
) => {
387 for pat
in unreachable_subpatterns
{
388 unreachable_pattern(cx
.tcx
, pat
.span
, id
, None
);
391 UsefulWithWitness(_
) => bug
!(),
395 if catchall
.is_none() && pat_is_catchall(pat
) {
396 catchall
= Some(pat
.span
);
403 fn check_not_useful
<'p
, 'tcx
>(
404 cx
: &mut MatchCheckCtxt
<'p
, 'tcx
>,
406 matrix
: &Matrix
<'p
, 'tcx
>,
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")
416 pats
.into_iter().map(|w
| w
.single_pattern()).collect()
422 fn check_exhaustive
<'p
, 'tcx
>(
423 cx
: &mut MatchCheckCtxt
<'p
, 'tcx
>,
426 matrix
: &Matrix
<'p
, 'tcx
>,
428 is_empty_match
: bool
,
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
{
437 && def
.variants
.is_empty()
438 && !cx
.is_foreign_non_exhaustive_enum(scrut_ty
)
442 if scrutinee_is_visibly_uninhabited
{
443 // If the type *is* uninhabited, an empty match is vacuously exhaustive.
448 let witnesses
= match check_not_useful(cx
, scrut_ty
, matrix
, hir_id
) {
453 let non_empty_enum
= match scrut_ty
.kind
{
454 ty
::Adt(def
, _
) => def
.is_enum() && !def
.variants
.is_empty(),
457 // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
460 if is_empty_match
&& !non_empty_enum
{
464 format
!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty
),
467 let joined_patterns
= joined_uncovered_patterns(&witnesses
);
471 format
!("non-exhaustive patterns: {} not covered", joined_patterns
),
473 err
.span_label(sp
, pattern_not_covered_label(&witnesses
, &joined_patterns
));
476 adt_defined_here(cx
, &mut err
, scrut_ty
, &witnesses
);
478 "ensure that all possible cases are being handled, \
479 possibly by adding wildcards or more match arms",
484 fn joined_uncovered_patterns(witnesses
: &[super::Pat
<'_
>]) -> String
{
485 const LIMIT
: usize = 3;
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
)
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())
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
)
505 /// Point at the definition of non-covered `enum` variants.
507 cx
: &MatchCheckCtxt
<'_
, '_
>,
508 err
: &mut DiagnosticBuilder
<'_
>,
510 witnesses
: &[super::Pat
<'_
>],
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
));
518 if witnesses
.len() < 4 {
519 for sp
in maybe_point_at_variant(ty
, &witnesses
) {
520 err
.span_label(sp
, "not covered");
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
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
)));
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
) {
544 let pats
= subpatterns
546 .map(|field_pattern
| field_pattern
.pattern
.clone())
547 .collect
::<Box
<[_
]>>();
548 covered
.extend(maybe_point_at_variant(ty
, &pats
));
550 Leaf { subpatterns }
=> {
551 let pats
= subpatterns
553 .map(|field_pattern
| field_pattern
.pattern
.clone())
554 .collect
::<Box
<[_
]>>();
555 covered
.extend(maybe_point_at_variant(ty
, &pats
));
558 let pats
= pats
.iter().cloned().collect
::<Box
<[_
]>>();
559 covered
.extend(maybe_point_at_variant(ty
, &pats
));
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
)
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
;
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
);
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.
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")
596 } else if !has_guard
&& !by_ref_spans
.is_empty() {
597 by_move_spans
.push(p
.span
);
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());
610 // Found some bad by-move spans, error!
611 if !by_move_spans
.is_empty() {
612 let mut err
= feature_err(
614 sym
::move_ref_pattern
,
615 by_move_spans
.clone(),
616 "binding by-move and by-ref in the same pattern is unstable",
618 for span
in by_ref_spans
.iter() {
619 err
.span_label(*span
, "by-ref pattern here");
621 for span
in by_move_spans
.iter() {
622 err
.span_label(*span
, "by-move pattern here");
628 /// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
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)`.
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
),
644 let binding_span
= pat
.span
.with_hi(name
.span
.hi());
646 let tables
= cx
.tables
;
647 let sess
= cx
.tcx
.sess
;
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
),
660 if !conflicts_ref
.is_empty() {
661 let occurs_because
= format
!(
662 "move occurs because `{}` has type `{}` which does not implement the `Copy` trait",
664 tables
.node_type(pat
.hir_id
),
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")
674 Some(ty
::BindByValue(_
)) | None
=> return,
675 Some(ty
::BindByReference(m
)) => m
,
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.
690 Some(ty
::BindByValue(_
)) if is_binding_by_move(cx
, hir_id
, span
) => {
691 conflicts_move
.push((span
, name
)) // `ref mut?` + by-move conflict.
693 Some(ty
::BindByValue(_
)) | None
=> {}
// `ref mut?` + by-copy is fine.
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)`.
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
));
706 for (span
, name
) in conflicts_mut_ref
{
707 err
.span_label(span
, format
!("also borrowed as immutable, by `{}`, here", name
));
709 for (span
, name
) in conflicts_move
{
710 err
.span_label(span
, format
!("also moved into `{}` here", name
));
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"),
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
));
726 for (span
, name
) in conflicts_move
{
727 err
.span_label(span
, format
!("also moved into `{}` here", name
));
730 } else if !conflicts_move
.is_empty() {
731 // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
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
));
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
);
747 struct AtBindingPatternVisitor
<'a
, 'b
, 'tcx
> {
748 cx
: &'a MatchVisitor
<'b
, 'tcx
>,
749 bindings_allowed
: bool
,
752 impl<'v
> Visitor
<'v
> for AtBindingPatternVisitor
<'_
, '_
, '_
> {
755 fn nested_visit_map(&mut self) -> NestedVisitorMap
<'_
, Self::Map
> {
756 NestedVisitorMap
::None
759 fn visit_pat(&mut self, pat
: &Pat
<'_
>) {
761 hir
::PatKind
::Binding(.., ref subpat
) => {
762 if !self.bindings_allowed
{
764 &self.cx
.tcx
.sess
.parse_sess
,
765 sym
::bindings_after_at
,
767 "pattern bindings after an `@` are unstable",
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
;
779 _
=> intravisit
::walk_pat(self, pat
),