1 use super::usefulness
::Usefulness
::*;
2 use super::usefulness
::{
3 compute_match_usefulness
, expand_pattern
, MatchArm
, MatchCheckCtxt
, UsefulnessReport
,
5 use super::{PatCtxt, PatKind, PatternError}
;
7 use rustc_arena
::TypedArena
;
8 use rustc_ast
::Mutability
;
9 use rustc_errors
::{error_code, struct_span_err, Applicability, DiagnosticBuilder}
;
11 use rustc_hir
::def
::*;
12 use rustc_hir
::def_id
::DefId
;
13 use rustc_hir
::intravisit
::{self, NestedVisitorMap, Visitor}
;
14 use rustc_hir
::{HirId, Pat}
;
15 use rustc_middle
::ty
::{self, Ty, TyCtxt}
;
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}
;
23 crate fn check_match(tcx
: TyCtxt
<'_
>, def_id
: DefId
) {
24 let body_id
= match def_id
.as_local() {
26 Some(id
) => tcx
.hir().body_owned_by(tcx
.hir().local_def_id_to_hir_id(id
)),
29 let mut visitor
= MatchVisitor
{
31 typeck_results
: tcx
.typeck_body(body_id
),
32 param_env
: tcx
.param_env(def_id
),
33 pattern_arena
: TypedArena
::default(),
35 visitor
.visit_body(tcx
.hir().body(body_id
));
38 fn create_e0004(sess
: &Session
, sp
: Span
, error_message
: String
) -> DiagnosticBuilder
<'_
> {
39 struct_span_err
!(sess
, sp
, E0004
, "{}", &error_message
)
42 struct MatchVisitor
<'a
, 'tcx
> {
44 typeck_results
: &'a ty
::TypeckResults
<'tcx
>,
45 param_env
: ty
::ParamEnv
<'tcx
>,
46 pattern_arena
: TypedArena
<super::Pat
<'tcx
>>,
49 impl<'tcx
> Visitor
<'tcx
> for MatchVisitor
<'_
, 'tcx
> {
50 type Map
= intravisit
::ErasedMap
<'tcx
>;
52 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
53 NestedVisitorMap
::None
56 fn visit_expr(&mut self, ex
: &'tcx hir
::Expr
<'tcx
>) {
57 intravisit
::walk_expr(self, ex
);
59 if let hir
::ExprKind
::Match(ref scrut
, ref arms
, source
) = ex
.kind
{
60 self.check_match(scrut
, arms
, source
);
64 fn visit_local(&mut self, loc
: &'tcx hir
::Local
<'tcx
>) {
65 intravisit
::walk_local(self, loc
);
67 let (msg
, sp
) = match loc
.source
{
68 hir
::LocalSource
::Normal
=> ("local binding", Some(loc
.span
)),
69 hir
::LocalSource
::ForLoopDesugar
=> ("`for` loop binding", None
),
70 hir
::LocalSource
::AsyncFn
=> ("async fn binding", None
),
71 hir
::LocalSource
::AwaitDesugar
=> ("`await` future binding", None
),
72 hir
::LocalSource
::AssignDesugar(_
) => ("destructuring assignment binding", None
),
74 self.check_irrefutable(&loc
.pat
, msg
, sp
);
75 self.check_patterns(&loc
.pat
);
78 fn visit_param(&mut self, param
: &'tcx hir
::Param
<'tcx
>) {
79 intravisit
::walk_param(self, param
);
80 self.check_irrefutable(¶m
.pat
, "function argument", None
);
81 self.check_patterns(¶m
.pat
);
85 impl PatCtxt
<'_
, '_
> {
86 fn report_inlining_errors(&self, pat_span
: Span
) {
87 for error
in &self.errors
{
89 PatternError
::StaticInPattern(span
) => {
90 self.span_e0158(span
, "statics cannot be referenced in patterns")
92 PatternError
::AssocConstInPattern(span
) => {
93 self.span_e0158(span
, "associated consts cannot be referenced in patterns")
95 PatternError
::ConstParamInPattern(span
) => {
96 self.span_e0158(span
, "const parameters cannot be referenced in patterns")
98 PatternError
::FloatBug
=> {
99 // FIXME(#31407) this is only necessary because float parsing is buggy
100 rustc_middle
::mir
::interpret
::struct_error(
101 self.tcx
.at(pat_span
),
102 "could not evaluate float literal (see issue #31407)",
106 PatternError
::NonConstPath(span
) => {
107 rustc_middle
::mir
::interpret
::struct_error(
109 "runtime values cannot be referenced in patterns",
117 fn span_e0158(&self, span
: Span
, text
: &str) {
118 struct_span_err
!(self.tcx
.sess
, span
, E0158
, "{}", text
).emit();
122 impl<'tcx
> MatchVisitor
<'_
, 'tcx
> {
123 fn check_patterns(&mut self, pat
: &Pat
<'_
>) {
124 pat
.walk_always(|pat
| check_borrow_conflicts_in_at_patterns(self, pat
));
125 if !self.tcx
.features().bindings_after_at
{
126 check_legality_of_bindings_in_at_patterns(self, pat
);
128 check_for_bindings_named_same_as_variants(self, pat
);
131 fn lower_pattern
<'p
>(
133 cx
: &mut MatchCheckCtxt
<'p
, 'tcx
>,
134 pat
: &'tcx hir
::Pat
<'tcx
>,
135 have_errors
: &mut bool
,
136 ) -> (&'p
super::Pat
<'tcx
>, Ty
<'tcx
>) {
137 let mut patcx
= PatCtxt
::new(self.tcx
, self.param_env
, self.typeck_results
);
138 patcx
.include_lint_checks();
139 let pattern
= patcx
.lower_pattern(pat
);
140 let pattern_ty
= pattern
.ty
;
141 let pattern
: &_
= cx
.pattern_arena
.alloc(expand_pattern(pattern
));
142 if !patcx
.errors
.is_empty() {
144 patcx
.report_inlining_errors(pat
.span
);
146 (pattern
, pattern_ty
)
149 fn new_cx(&self, hir_id
: HirId
) -> MatchCheckCtxt
<'_
, 'tcx
> {
152 param_env
: self.param_env
,
153 module
: self.tcx
.parent_module(hir_id
).to_def_id(),
154 pattern_arena
: &self.pattern_arena
,
160 scrut
: &hir
::Expr
<'_
>,
161 arms
: &'tcx
[hir
::Arm
<'tcx
>],
162 source
: hir
::MatchSource
,
165 // Check the arm for some things unrelated to exhaustiveness.
166 self.check_patterns(&arm
.pat
);
167 if let Some(hir
::Guard
::IfLet(ref pat
, _
)) = arm
.guard
{
168 self.check_patterns(pat
);
172 let mut cx
= self.new_cx(scrut
.hir_id
);
175 if let Some(hir
::Guard
::IfLet(ref pat
, _
)) = arm
.guard
{
176 let tpat
= self.lower_pattern(&mut cx
, pat
, &mut false).0;
177 check_if_let_guard(&mut cx
, &tpat
, pat
.hir_id
);
181 let mut have_errors
= false;
183 let arms
: Vec
<_
> = arms
185 .map(|hir
::Arm { pat, guard, .. }
| MatchArm
{
186 pat
: self.lower_pattern(&mut cx
, pat
, &mut have_errors
).0,
188 has_guard
: guard
.is_some(),
192 // Bail out early if lowering failed.
197 let scrut_ty
= self.typeck_results
.expr_ty_adjusted(scrut
);
198 let report
= compute_match_usefulness(&cx
, &arms
, scrut
.hir_id
, scrut_ty
);
200 // Report unreachable arms.
201 report_arm_reachability(&cx
, &report
, source
);
203 // Check if the match is exhaustive.
204 // Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
205 // since an empty matrix can occur when there are arms, if those arms all have guards.
206 let is_empty_match
= arms
.is_empty();
207 let witnesses
= report
.non_exhaustiveness_witnesses
;
208 if !witnesses
.is_empty() {
209 non_exhaustive_match(&cx
, scrut_ty
, scrut
.span
, witnesses
, is_empty_match
);
213 fn check_irrefutable(&self, pat
: &'tcx Pat
<'tcx
>, origin
: &str, sp
: Option
<Span
>) {
214 let mut cx
= self.new_cx(pat
.hir_id
);
216 let (pattern
, pattern_ty
) = self.lower_pattern(&mut cx
, pat
, &mut false);
217 let arms
= vec
![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }
];
218 let report
= compute_match_usefulness(&cx
, &arms
, pat
.hir_id
, pattern_ty
);
220 // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
221 // only care about exhaustiveness here.
222 let witnesses
= report
.non_exhaustiveness_witnesses
;
223 if witnesses
.is_empty() {
224 // The pattern is irrefutable.
228 let joined_patterns
= joined_uncovered_patterns(&witnesses
);
229 let mut err
= struct_span_err
!(
233 "refutable pattern in {}: {} not covered",
237 let suggest_if_let
= match &pat
.kind
{
238 hir
::PatKind
::Path(hir
::QPath
::Resolved(None
, path
))
239 if path
.segments
.len() == 1 && path
.segments
[0].args
.is_none() =>
241 const_not_var(&mut err
, cx
.tcx
, pat
, path
);
245 err
.span_label(pat
.span
, pattern_not_covered_label(&witnesses
, &joined_patterns
));
250 if let (Some(span
), true) = (sp
, suggest_if_let
) {
252 "`let` bindings require an \"irrefutable pattern\", like a `struct` or \
253 an `enum` with only one variant",
255 if let Ok(snippet
) = self.tcx
.sess
.source_map().span_to_snippet(span
) {
258 "you might want to use `if let` to ignore the variant that isn't matched",
259 format
!("if {} {{ /* */ }}", &snippet
[..snippet
.len() - 1]),
260 Applicability
::HasPlaceholders
,
264 "for more information, visit \
265 https://doc.rust-lang.org/book/ch18-02-refutability.html",
269 adt_defined_here(&cx
, &mut err
, pattern_ty
, &witnesses
);
270 err
.note(&format
!("the matched value is of type `{}`", pattern_ty
));
275 /// A path pattern was interpreted as a constant, not a new variable.
276 /// This caused an irrefutable match failure in e.g. `let`.
278 err
: &mut DiagnosticBuilder
<'_
>,
281 path
: &hir
::Path
<'_
>,
283 let descr
= path
.res
.descr();
286 format
!("interpreted as {} {} pattern, not a new variable", path
.res
.article(), descr
,),
291 "introduce a variable instead",
292 format
!("{}_var", path
.segments
[0].ident
).to_lowercase(),
293 // Cannot use `MachineApplicable` as it's not really *always* correct
294 // because there may be such an identifier in scope or the user maybe
295 // really wanted to match against the constant. This is quite unlikely however.
296 Applicability
::MaybeIncorrect
,
299 if let Some(span
) = tcx
.hir().res_span(path
.res
) {
300 err
.span_label(span
, format
!("{} defined here", descr
));
304 fn check_for_bindings_named_same_as_variants(cx
: &MatchVisitor
<'_
, '_
>, pat
: &Pat
<'_
>) {
305 pat
.walk_always(|p
| {
306 if let hir
::PatKind
::Binding(_
, _
, ident
, None
) = p
.kind
{
307 if let Some(ty
::BindByValue(hir
::Mutability
::Not
)) =
308 cx
.typeck_results
.extract_binding_mode(cx
.tcx
.sess
, p
.hir_id
, p
.span
)
310 let pat_ty
= cx
.typeck_results
.pat_ty(p
).peel_refs();
311 if let ty
::Adt(edef
, _
) = pat_ty
.kind() {
313 && edef
.variants
.iter().any(|variant
| {
314 variant
.ident
== ident
&& variant
.ctor_kind
== CtorKind
::Const
317 cx
.tcx
.struct_span_lint_hir(
318 BINDINGS_WITH_VARIANT_NAME
,
322 let ty_path
= cx
.tcx
.def_path_str(edef
.did
);
324 "pattern binding `{}` is named the same as one \
325 of the variants of the type `{}`",
328 .code(error_code
!(E0170
))
331 "to match on the variant, qualify the path",
332 format
!("{}::{}", ty_path
, ident
),
333 Applicability
::MachineApplicable
,
345 /// Checks for common cases of "catchall" patterns that may not be intended as such.
346 fn pat_is_catchall(pat
: &super::Pat
<'_
>) -> bool
{
347 use super::PatKind
::*;
349 Binding { subpattern: None, .. }
=> true,
350 Binding { subpattern: Some(s), .. }
| Deref { subpattern: s }
=> pat_is_catchall(s
),
351 Leaf { subpatterns: s }
=> s
.iter().all(|p
| pat_is_catchall(&p
.pattern
)),
356 fn unreachable_pattern(tcx
: TyCtxt
<'_
>, span
: Span
, id
: HirId
, catchall
: Option
<Span
>) {
357 tcx
.struct_span_lint_hir(UNREACHABLE_PATTERNS
, id
, span
, |lint
| {
358 let mut err
= lint
.build("unreachable pattern");
359 if let Some(catchall
) = catchall
{
360 // We had a catchall pattern, hint at that.
361 err
.span_label(span
, "unreachable pattern");
362 err
.span_label(catchall
, "matches any value");
368 fn irrefutable_let_pattern(tcx
: TyCtxt
<'_
>, span
: Span
, id
: HirId
, source
: hir
::MatchSource
) {
369 tcx
.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS
, id
, span
, |lint
| {
370 let msg
= match source
{
371 hir
::MatchSource
::IfLetDesugar { .. }
=> "irrefutable if-let pattern",
372 hir
::MatchSource
::WhileLetDesugar
=> "irrefutable while-let pattern",
373 hir
::MatchSource
::IfLetGuardDesugar
=> "irrefutable if-let guard",
376 lint
.build(msg
).emit()
380 fn check_if_let_guard
<'p
, 'tcx
>(
381 cx
: &mut MatchCheckCtxt
<'p
, 'tcx
>,
382 pat
: &'p
super::Pat
<'tcx
>,
385 let arms
= [MatchArm { pat, hir_id: pat_id, has_guard: false }
];
386 let report
= compute_match_usefulness(&cx
, &arms
, pat_id
, pat
.ty
);
387 report_arm_reachability(&cx
, &report
, hir
::MatchSource
::IfLetGuardDesugar
);
389 if report
.non_exhaustiveness_witnesses
.is_empty() {
390 // The match is exhaustive, i.e. the if let pattern is irrefutable.
391 irrefutable_let_pattern(cx
.tcx
, pat
.span
, pat_id
, hir
::MatchSource
::IfLetGuardDesugar
)
395 /// Report unreachable arms, if any.
396 fn report_arm_reachability
<'p
, 'tcx
>(
397 cx
: &MatchCheckCtxt
<'p
, 'tcx
>,
398 report
: &UsefulnessReport
<'p
, 'tcx
>,
399 source
: hir
::MatchSource
,
401 let mut catchall
= None
;
402 for (arm_index
, (arm
, is_useful
)) in report
.arm_usefulness
.iter().enumerate() {
406 hir
::MatchSource
::IfDesugar { .. }
| hir
::MatchSource
::WhileDesugar
=> bug
!(),
408 hir
::MatchSource
::IfLetDesugar { .. }
| hir
::MatchSource
::WhileLetDesugar
=> {
409 // Check which arm we're on.
411 // The arm with the user-specified pattern.
412 0 => unreachable_pattern(cx
.tcx
, arm
.pat
.span
, arm
.hir_id
, None
),
413 // The arm with the wildcard pattern.
414 1 => irrefutable_let_pattern(cx
.tcx
, arm
.pat
.span
, arm
.hir_id
, source
),
419 hir
::MatchSource
::IfLetGuardDesugar
=> {
420 assert_eq
!(arm_index
, 0);
421 unreachable_pattern(cx
.tcx
, arm
.pat
.span
, arm
.hir_id
, None
);
424 hir
::MatchSource
::ForLoopDesugar
| hir
::MatchSource
::Normal
=> {
425 unreachable_pattern(cx
.tcx
, arm
.pat
.span
, arm
.hir_id
, catchall
);
428 // Unreachable patterns in try and await expressions occur when one of
429 // the arms are an uninhabited type. Which is OK.
430 hir
::MatchSource
::AwaitDesugar
| hir
::MatchSource
::TryDesugar
=> {}
433 Useful(unreachables
) if unreachables
.is_empty() => {}
434 // The arm is reachable, but contains unreachable subpatterns (from or-patterns).
435 Useful(unreachables
) => {
436 let mut unreachables
: Vec
<_
> = unreachables
.iter().collect();
437 // Emit lints in the order in which they occur in the file.
438 unreachables
.sort_unstable();
439 for span
in unreachables
{
440 unreachable_pattern(cx
.tcx
, span
, arm
.hir_id
, None
);
443 UsefulWithWitness(_
) => bug
!(),
445 if !arm
.has_guard
&& catchall
.is_none() && pat_is_catchall(arm
.pat
) {
446 catchall
= Some(arm
.pat
.span
);
451 /// Report that a match is not exhaustive.
452 fn non_exhaustive_match
<'p
, 'tcx
>(
453 cx
: &MatchCheckCtxt
<'p
, 'tcx
>,
456 witnesses
: Vec
<super::Pat
<'tcx
>>,
457 is_empty_match
: bool
,
459 let non_empty_enum
= match scrut_ty
.kind() {
460 ty
::Adt(def
, _
) => def
.is_enum() && !def
.variants
.is_empty(),
463 // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
466 if is_empty_match
&& !non_empty_enum
{
470 format
!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty
),
473 let joined_patterns
= joined_uncovered_patterns(&witnesses
);
477 format
!("non-exhaustive patterns: {} not covered", joined_patterns
),
479 err
.span_label(sp
, pattern_not_covered_label(&witnesses
, &joined_patterns
));
482 adt_defined_here(cx
, &mut err
, scrut_ty
, &witnesses
);
484 "ensure that all possible cases are being handled, \
485 possibly by adding wildcards or more match arms",
487 err
.note(&format
!("the matched value is of type `{}`", scrut_ty
));
488 if (scrut_ty
== cx
.tcx
.types
.usize || scrut_ty
== cx
.tcx
.types
.isize)
490 && witnesses
.len() == 1
491 && witnesses
[0].is_wildcard()
494 "`{}` does not have a fixed maximum value, \
495 so a wildcard `_` is necessary to match exhaustively",
498 if cx
.tcx
.sess
.is_nightly_build() {
500 "add `#![feature(precise_pointer_size_matching)]` \
501 to the crate attributes to enable precise `{}` matching",
509 fn joined_uncovered_patterns(witnesses
: &[super::Pat
<'_
>]) -> String
{
510 const LIMIT
: usize = 3;
513 [witness
] => format
!("`{}`", witness
),
514 [head @
.., tail
] if head
.len() < LIMIT
=> {
515 let head
: Vec
<_
> = head
.iter().map(<_
>::to_string
).collect();
516 format
!("`{}` and `{}`", head
.join("`, `"), tail
)
519 let (head
, tail
) = witnesses
.split_at(LIMIT
);
520 let head
: Vec
<_
> = head
.iter().map(<_
>::to_string
).collect();
521 format
!("`{}` and {} more", head
.join("`, `"), tail
.len())
526 fn pattern_not_covered_label(witnesses
: &[super::Pat
<'_
>], joined_patterns
: &str) -> String
{
527 format
!("pattern{} {} not covered", rustc_errors
::pluralize
!(witnesses
.len()), joined_patterns
)
530 /// Point at the definition of non-covered `enum` variants.
532 cx
: &MatchCheckCtxt
<'_
, '_
>,
533 err
: &mut DiagnosticBuilder
<'_
>,
535 witnesses
: &[super::Pat
<'_
>],
537 let ty
= ty
.peel_refs();
538 if let ty
::Adt(def
, _
) = ty
.kind() {
539 if let Some(sp
) = cx
.tcx
.hir().span_if_local(def
.did
) {
540 err
.span_label(sp
, format
!("`{}` defined here", ty
));
543 if witnesses
.len() < 4 {
544 for sp
in maybe_point_at_variant(ty
, &witnesses
) {
545 err
.span_label(sp
, "not covered");
551 fn maybe_point_at_variant(ty
: Ty
<'_
>, patterns
: &[super::Pat
<'_
>]) -> Vec
<Span
> {
552 let mut covered
= vec
![];
553 if let ty
::Adt(def
, _
) = ty
.kind() {
554 // Don't point at variants that have already been covered due to other patterns to avoid
556 for pattern
in patterns
{
557 use PatKind
::{AscribeUserType, Deref, Leaf, Or, Variant}
;
558 match &*pattern
.kind
{
559 AscribeUserType { subpattern, .. }
| Deref { subpattern }
=> {
560 covered
.extend(maybe_point_at_variant(ty
, slice
::from_ref(&subpattern
)));
562 Variant { adt_def, variant_index, subpatterns, .. }
if adt_def
.did
== def
.did
=> {
563 let sp
= def
.variants
[*variant_index
].ident
.span
;
564 if covered
.contains(&sp
) {
569 let pats
= subpatterns
571 .map(|field_pattern
| field_pattern
.pattern
.clone())
572 .collect
::<Box
<[_
]>>();
573 covered
.extend(maybe_point_at_variant(ty
, &pats
));
575 Leaf { subpatterns }
=> {
576 let pats
= subpatterns
578 .map(|field_pattern
| field_pattern
.pattern
.clone())
579 .collect
::<Box
<[_
]>>();
580 covered
.extend(maybe_point_at_variant(ty
, &pats
));
583 let pats
= pats
.iter().cloned().collect
::<Box
<[_
]>>();
584 covered
.extend(maybe_point_at_variant(ty
, &pats
));
593 /// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
594 fn is_binding_by_move(cx
: &MatchVisitor
<'_
, '_
>, hir_id
: HirId
, span
: Span
) -> bool
{
595 !cx
.typeck_results
.node_type(hir_id
).is_copy_modulo_regions(cx
.tcx
.at(span
), cx
.param_env
)
598 /// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
600 /// For example, this would reject:
601 /// - `ref x @ Some(ref mut y)`,
602 /// - `ref mut x @ Some(ref y)`,
603 /// - `ref mut x @ Some(ref mut y)`,
604 /// - `ref mut? x @ Some(y)`, and
605 /// - `x @ Some(ref mut? y)`.
607 /// This analysis is *not* subsumed by NLL.
608 fn check_borrow_conflicts_in_at_patterns(cx
: &MatchVisitor
<'_
, '_
>, pat
: &Pat
<'_
>) {
609 // Extract `sub` in `binding @ sub`.
610 let (name
, sub
) = match &pat
.kind
{
611 hir
::PatKind
::Binding(.., name
, Some(sub
)) => (*name
, sub
),
614 let binding_span
= pat
.span
.with_hi(name
.span
.hi());
616 let typeck_results
= cx
.typeck_results
;
617 let sess
= cx
.tcx
.sess
;
619 // Get the binding move, extract the mutability if by-ref.
620 let mut_outer
= match typeck_results
.extract_binding_mode(sess
, pat
.hir_id
, pat
.span
) {
621 Some(ty
::BindByValue(_
)) if is_binding_by_move(cx
, pat
.hir_id
, pat
.span
) => {
622 // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
623 let mut conflicts_ref
= Vec
::new();
624 sub
.each_binding(|_
, hir_id
, span
, _
| {
625 match typeck_results
.extract_binding_mode(sess
, hir_id
, span
) {
626 Some(ty
::BindByValue(_
)) | None
=> {}
627 Some(ty
::BindByReference(_
)) => conflicts_ref
.push(span
),
630 if !conflicts_ref
.is_empty() {
631 let occurs_because
= format
!(
632 "move occurs because `{}` has type `{}` which does not implement the `Copy` trait",
634 typeck_results
.node_type(pat
.hir_id
),
636 sess
.struct_span_err(pat
.span
, "borrow of moved value")
637 .span_label(binding_span
, format
!("value moved into `{}` here", name
))
638 .span_label(binding_span
, occurs_because
)
639 .span_labels(conflicts_ref
, "value borrowed here after move")
644 Some(ty
::BindByValue(_
)) | None
=> return,
645 Some(ty
::BindByReference(m
)) => m
,
648 // We now have `ref $mut_outer binding @ sub` (semantically).
649 // Recurse into each binding in `sub` and find mutability or move conflicts.
650 let mut conflicts_move
= Vec
::new();
651 let mut conflicts_mut_mut
= Vec
::new();
652 let mut conflicts_mut_ref
= Vec
::new();
653 sub
.each_binding(|_
, hir_id
, span
, name
| {
654 match typeck_results
.extract_binding_mode(sess
, hir_id
, span
) {
655 Some(ty
::BindByReference(mut_inner
)) => match (mut_outer
, mut_inner
) {
656 (Mutability
::Not
, Mutability
::Not
) => {}
// Both sides are `ref`.
657 (Mutability
::Mut
, Mutability
::Mut
) => conflicts_mut_mut
.push((span
, name
)), // 2x `ref mut`.
658 _
=> conflicts_mut_ref
.push((span
, name
)), // `ref` + `ref mut` in either direction.
660 Some(ty
::BindByValue(_
)) if is_binding_by_move(cx
, hir_id
, span
) => {
661 conflicts_move
.push((span
, name
)) // `ref mut?` + by-move conflict.
663 Some(ty
::BindByValue(_
)) | None
=> {}
// `ref mut?` + by-copy is fine.
667 // Report errors if any.
668 if !conflicts_mut_mut
.is_empty() {
669 // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
671 .struct_span_err(pat
.span
, "cannot borrow value as mutable more than once at a time");
672 err
.span_label(binding_span
, format
!("first mutable borrow, by `{}`, occurs here", name
));
673 for (span
, name
) in conflicts_mut_mut
{
674 err
.span_label(span
, format
!("another mutable borrow, by `{}`, occurs here", name
));
676 for (span
, name
) in conflicts_mut_ref
{
677 err
.span_label(span
, format
!("also borrowed as immutable, by `{}`, here", name
));
679 for (span
, name
) in conflicts_move
{
680 err
.span_label(span
, format
!("also moved into `{}` here", name
));
683 } else if !conflicts_mut_ref
.is_empty() {
684 // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
685 let (primary
, also
) = match mut_outer
{
686 Mutability
::Mut
=> ("mutable", "immutable"),
687 Mutability
::Not
=> ("immutable", "mutable"),
690 format
!("cannot borrow value as {} because it is also borrowed as {}", also
, primary
);
691 let mut err
= sess
.struct_span_err(pat
.span
, &msg
);
692 err
.span_label(binding_span
, format
!("{} borrow, by `{}`, occurs here", primary
, name
));
693 for (span
, name
) in conflicts_mut_ref
{
694 err
.span_label(span
, format
!("{} borrow, by `{}`, occurs here", also
, name
));
696 for (span
, name
) in conflicts_move
{
697 err
.span_label(span
, format
!("also moved into `{}` here", name
));
700 } else if !conflicts_move
.is_empty() {
701 // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
703 sess
.struct_span_err(pat
.span
, "cannot move out of value because it is borrowed");
704 err
.span_label(binding_span
, format
!("value borrowed, by `{}`, here", name
));
705 for (span
, name
) in conflicts_move
{
706 err
.span_label(span
, format
!("value moved into `{}` here", name
));
712 /// Forbids bindings in `@` patterns. This used to be is necessary for memory safety,
713 /// because of the way rvalues were handled in the borrow check. (See issue #14587.)
714 fn check_legality_of_bindings_in_at_patterns(cx
: &MatchVisitor
<'_
, '_
>, pat
: &Pat
<'_
>) {
715 AtBindingPatternVisitor { cx, bindings_allowed: true }
.visit_pat(pat
);
717 struct AtBindingPatternVisitor
<'a
, 'b
, 'tcx
> {
718 cx
: &'a MatchVisitor
<'b
, 'tcx
>,
719 bindings_allowed
: bool
,
722 impl<'v
> Visitor
<'v
> for AtBindingPatternVisitor
<'_
, '_
, '_
> {
723 type Map
= intravisit
::ErasedMap
<'v
>;
725 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
726 NestedVisitorMap
::None
729 fn visit_pat(&mut self, pat
: &Pat
<'_
>) {
731 hir
::PatKind
::Binding(.., ref subpat
) => {
732 if !self.bindings_allowed
{
734 &self.cx
.tcx
.sess
.parse_sess
,
735 sym
::bindings_after_at
,
737 "pattern bindings after an `@` are unstable",
742 if subpat
.is_some() {
743 let bindings_were_allowed
= self.bindings_allowed
;
744 self.bindings_allowed
= false;
745 intravisit
::walk_pat(self, pat
);
746 self.bindings_allowed
= bindings_were_allowed
;
749 _
=> intravisit
::walk_pat(self, pat
),