1#![allow(rustc::diagnostic_outside_of_impl)]
2#![allow(rustc::untranslatable_diagnostic)]
3
4use rustc_data_structures::fx::FxHashSet;
5use rustc_errors::{Applicability, Diag};
6use rustc_hir::intravisit::Visitor;
7use rustc_hir::{self as hir, CaptureBy, ExprKind, HirId, Node};
8use rustc_middle::bug;
9use rustc_middle::mir::*;
10use rustc_middle::ty::{self, Ty, TyCtxt};
11use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
12use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
13use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
14use rustc_trait_selection::infer::InferCtxtExt;
15use tracing::debug;
16
17use crate::MirBorrowckCtxt;
18use crate::diagnostics::{CapturedMessageOpt, DescribePlaceOpt, UseSpans};
19use crate::prefixes::PrefixSet;
20
21#[derive(Debug)]
22pub(crate) enum IllegalMoveOriginKind<'tcx> {
23 BorrowedContent {
25 target_place: Place<'tcx>,
28 },
29
30 InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> },
35
36 InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool },
38}
39
40#[derive(Debug)]
41pub(crate) struct MoveError<'tcx> {
42 place: Place<'tcx>,
43 location: Location,
44 kind: IllegalMoveOriginKind<'tcx>,
45}
46
47impl<'tcx> MoveError<'tcx> {
48 pub(crate) fn new(
49 place: Place<'tcx>,
50 location: Location,
51 kind: IllegalMoveOriginKind<'tcx>,
52 ) -> Self {
53 MoveError { place, location, kind }
54 }
55}
56
57#[derive(Debug)]
71enum GroupedMoveError<'tcx> {
72 MovesFromPlace {
75 original_path: Place<'tcx>,
76 span: Span,
77 move_from: Place<'tcx>,
78 kind: IllegalMoveOriginKind<'tcx>,
79 binds_to: Vec<Local>,
80 },
81 MovesFromValue {
84 original_path: Place<'tcx>,
85 span: Span,
86 move_from: MovePathIndex,
87 kind: IllegalMoveOriginKind<'tcx>,
88 binds_to: Vec<Local>,
89 },
90 OtherIllegalMove {
92 original_path: Place<'tcx>,
93 use_spans: UseSpans<'tcx>,
94 kind: IllegalMoveOriginKind<'tcx>,
95 },
96}
97
98impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
99 pub(crate) fn report_move_errors(&mut self) {
100 let grouped_errors = self.group_move_errors();
101 for error in grouped_errors {
102 self.report(error);
103 }
104 }
105
106 fn group_move_errors(&mut self) -> Vec<GroupedMoveError<'tcx>> {
107 let mut grouped_errors = Vec::new();
108 let errors = std::mem::take(&mut self.move_errors);
109 for error in errors {
110 self.append_to_grouped_errors(&mut grouped_errors, error);
111 }
112 grouped_errors
113 }
114
115 fn append_to_grouped_errors(
116 &self,
117 grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
118 MoveError { place: original_path, location, kind }: MoveError<'tcx>,
119 ) {
120 if let Some(StatementKind::Assign(box (place, Rvalue::Use(Operand::Move(move_from))))) =
125 self.body.basic_blocks[location.block]
126 .statements
127 .get(location.statement_index)
128 .map(|stmt| &stmt.kind)
129 && let Some(local) = place.as_local()
130 {
131 let local_decl = &self.body.local_decls[local];
132 if let LocalInfo::User(BindingForm::Var(VarBindingForm {
140 opt_match_place: Some((opt_match_place, match_span)),
141 binding_mode: _,
142 opt_ty_info: _,
143 pat_span: _,
144 })) = *local_decl.local_info()
145 {
146 let stmt_source_info = self.body.source_info(location);
147 self.append_binding_error(
148 grouped_errors,
149 kind,
150 original_path,
151 *move_from,
152 local,
153 opt_match_place,
154 match_span,
155 stmt_source_info.span,
156 );
157 return;
158 }
159 }
160
161 let move_spans = self.move_spans(original_path.as_ref(), location);
162 grouped_errors.push(GroupedMoveError::OtherIllegalMove {
163 use_spans: move_spans,
164 original_path,
165 kind,
166 });
167 }
168
169 fn append_binding_error(
170 &self,
171 grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
172 kind: IllegalMoveOriginKind<'tcx>,
173 original_path: Place<'tcx>,
174 move_from: Place<'tcx>,
175 bind_to: Local,
176 match_place: Option<Place<'tcx>>,
177 match_span: Span,
178 statement_span: Span,
179 ) {
180 debug!(?match_place, ?match_span, "append_binding_error");
181
182 let from_simple_let = match_place.is_none();
183 let match_place = match_place.unwrap_or(move_from);
184
185 match self.move_data.rev_lookup.find(match_place.as_ref()) {
186 LookupResult::Parent(_) => {
188 for ge in &mut *grouped_errors {
189 if let GroupedMoveError::MovesFromPlace { span, binds_to, .. } = ge
190 && match_span == *span
191 {
192 debug!("appending local({bind_to:?}) to list");
193 if !binds_to.is_empty() {
194 binds_to.push(bind_to);
195 }
196 return;
197 }
198 }
199 debug!("found a new move error location");
200
201 let (binds_to, span) = if from_simple_let {
203 (vec![], statement_span)
204 } else {
205 (vec![bind_to], match_span)
206 };
207 grouped_errors.push(GroupedMoveError::MovesFromPlace {
208 span,
209 move_from,
210 original_path,
211 kind,
212 binds_to,
213 });
214 }
215 LookupResult::Exact(_) => {
217 let LookupResult::Parent(Some(mpi)) =
218 self.move_data.rev_lookup.find(move_from.as_ref())
219 else {
220 unreachable!("Probably not unreachable...");
222 };
223 for ge in &mut *grouped_errors {
224 if let GroupedMoveError::MovesFromValue {
225 span,
226 move_from: other_mpi,
227 binds_to,
228 ..
229 } = ge
230 {
231 if match_span == *span && mpi == *other_mpi {
232 debug!("appending local({bind_to:?}) to list");
233 binds_to.push(bind_to);
234 return;
235 }
236 }
237 }
238 debug!("found a new move error location");
239 grouped_errors.push(GroupedMoveError::MovesFromValue {
240 span: match_span,
241 move_from: mpi,
242 original_path,
243 kind,
244 binds_to: vec![bind_to],
245 });
246 }
247 };
248 }
249
250 fn report(&mut self, error: GroupedMoveError<'tcx>) {
251 let (span, use_spans, original_path, kind) = match error {
252 GroupedMoveError::MovesFromPlace { span, original_path, ref kind, .. }
253 | GroupedMoveError::MovesFromValue { span, original_path, ref kind, .. } => {
254 (span, None, original_path, kind)
255 }
256 GroupedMoveError::OtherIllegalMove { use_spans, original_path, ref kind } => {
257 (use_spans.args_or_use(), Some(use_spans), original_path, kind)
258 }
259 };
260 debug!(
261 "report: original_path={:?} span={:?}, kind={:?} \
262 original_path.is_upvar_field_projection={:?}",
263 original_path,
264 span,
265 kind,
266 self.is_upvar_field_projection(original_path.as_ref())
267 );
268 if self.has_ambiguous_copy(original_path.ty(self.body, self.infcx.tcx).ty) {
269 self.dcx()
272 .span_delayed_bug(span, "Type may implement copy, but there is no other error.");
273 return;
274 }
275 let mut err = match kind {
276 &IllegalMoveOriginKind::BorrowedContent { target_place } => self
277 .report_cannot_move_from_borrowed_content(
278 original_path,
279 target_place,
280 span,
281 use_spans,
282 ),
283 &IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
284 self.cannot_move_out_of_interior_of_drop(span, ty)
285 }
286 &IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
287 self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index))
288 }
289 };
290
291 self.add_move_hints(error, &mut err, span);
292 self.buffer_error(err);
293 }
294
295 fn has_ambiguous_copy(&mut self, ty: Ty<'tcx>) -> bool {
296 let Some(copy_def_id) = self.infcx.tcx.lang_items().copy_trait() else { return false };
297
298 self.infcx.type_implements_trait(copy_def_id, [ty], self.infcx.param_env).may_apply()
300 && self.infcx.tcx.coherent_trait(copy_def_id).is_err()
301 }
302
303 fn report_cannot_move_from_static(&mut self, place: Place<'tcx>, span: Span) -> Diag<'infcx> {
304 let description = if place.projection.len() == 1 {
305 format!("static item {}", self.describe_any_place(place.as_ref()))
306 } else {
307 let base_static = PlaceRef { local: place.local, projection: &[ProjectionElem::Deref] };
308
309 format!(
310 "{} as {} is a static item",
311 self.describe_any_place(place.as_ref()),
312 self.describe_any_place(base_static),
313 )
314 };
315
316 self.cannot_move_out_of(span, &description)
317 }
318
319 pub(in crate::diagnostics) fn suggest_clone_of_captured_var_in_move_closure(
320 &self,
321 err: &mut Diag<'_>,
322 upvar_name: &str,
323 use_spans: Option<UseSpans<'tcx>>,
324 ) {
325 let tcx = self.infcx.tcx;
326 let Some(use_spans) = use_spans else { return };
327 let UseSpans::ClosureUse { args_span, .. } = use_spans else { return };
329 let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return };
330 let mut expr_finder = FindExprBySpan::new(args_span, tcx);
332 expr_finder.include_closures = true;
333 expr_finder.visit_expr(tcx.hir_body(body_id).value);
334 let Some(closure_expr) = expr_finder.result else { return };
335 let ExprKind::Closure(closure) = closure_expr.kind else { return };
336 let CaptureBy::Value { .. } = closure.capture_clause else { return };
338 let mut suggested = false;
340 let use_span = use_spans.var_or_use();
341 let mut expr_finder = FindExprBySpan::new(use_span, tcx);
342 expr_finder.include_closures = true;
343 expr_finder.visit_expr(tcx.hir_body(body_id).value);
344 let Some(use_expr) = expr_finder.result else { return };
345 let parent = tcx.parent_hir_node(use_expr.hir_id);
346 if let Node::Expr(expr) = parent
347 && let ExprKind::Assign(lhs, ..) = expr.kind
348 && lhs.hir_id == use_expr.hir_id
349 {
350 return;
370 }
371
372 for (_, node) in tcx.hir_parent_iter(closure_expr.hir_id) {
375 if let Node::Stmt(stmt) = node {
376 let padding = tcx
377 .sess
378 .source_map()
379 .indentation_before(stmt.span)
380 .unwrap_or_else(|| " ".to_string());
381 err.multipart_suggestion_verbose(
382 "consider cloning the value before moving it into the closure",
383 vec![
384 (
385 stmt.span.shrink_to_lo(),
386 format!("let value = {upvar_name}.clone();\n{padding}"),
387 ),
388 (use_span, "value".to_string()),
389 ],
390 Applicability::MachineApplicable,
391 );
392 suggested = true;
393 break;
394 } else if let Node::Expr(expr) = node
395 && let ExprKind::Closure(_) = expr.kind
396 {
397 break;
400 }
401 }
402 if !suggested {
403 let padding = tcx
407 .sess
408 .source_map()
409 .indentation_before(closure_expr.span)
410 .unwrap_or_else(|| " ".to_string());
411 err.multipart_suggestion_verbose(
412 "consider cloning the value before moving it into the closure",
413 vec![
414 (
415 closure_expr.span.shrink_to_lo(),
416 format!("{{\n{padding}let value = {upvar_name}.clone();\n{padding}"),
417 ),
418 (use_spans.var_or_use(), "value".to_string()),
419 (closure_expr.span.shrink_to_hi(), format!("\n{padding}}}")),
420 ],
421 Applicability::MachineApplicable,
422 );
423 }
424 }
425
426 fn report_cannot_move_from_borrowed_content(
427 &mut self,
428 move_place: Place<'tcx>,
429 deref_target_place: Place<'tcx>,
430 span: Span,
431 use_spans: Option<UseSpans<'tcx>>,
432 ) -> Diag<'infcx> {
433 let tcx = self.infcx.tcx;
434 let ty = deref_target_place.ty(self.body, tcx).ty;
438 let upvar_field = self
439 .prefixes(move_place.as_ref(), PrefixSet::All)
440 .find_map(|p| self.is_upvar_field_projection(p));
441
442 let deref_base = match deref_target_place.projection.as_ref() {
443 [proj_base @ .., ProjectionElem::Deref] => {
444 PlaceRef { local: deref_target_place.local, projection: proj_base }
445 }
446 _ => bug!("deref_target_place is not a deref projection"),
447 };
448
449 if let PlaceRef { local, projection: [] } = deref_base {
450 let decl = &self.body.local_decls[local];
451 let local_name = self.local_name(local).map(|sym| format!("`{sym}`"));
452 if decl.is_ref_for_guard() {
453 return self
454 .cannot_move_out_of(
455 span,
456 &format!(
457 "{} in pattern guard",
458 local_name.as_deref().unwrap_or("the place")
459 ),
460 )
461 .with_note(
462 "variables bound in patterns cannot be moved from \
463 until after the end of the pattern guard",
464 );
465 } else if decl.is_ref_to_static() {
466 return self.report_cannot_move_from_static(move_place, span);
467 }
468 }
469
470 debug!("report: ty={:?}", ty);
471 let mut err = match ty.kind() {
472 ty::Array(..) | ty::Slice(..) => {
473 self.cannot_move_out_of_interior_noncopy(span, ty, None)
474 }
475 ty::Closure(def_id, closure_args)
476 if def_id.as_local() == Some(self.mir_def_id())
477 && let Some(upvar_field) = upvar_field =>
478 {
479 let closure_kind_ty = closure_args.as_closure().kind_ty();
480 let closure_kind = match closure_kind_ty.to_opt_closure_kind() {
481 Some(kind @ (ty::ClosureKind::Fn | ty::ClosureKind::FnMut)) => kind,
482 Some(ty::ClosureKind::FnOnce) => {
483 bug!("closure kind does not match first argument type")
484 }
485 None => bug!("closure kind not inferred by borrowck"),
486 };
487 let capture_description =
488 format!("captured variable in an `{closure_kind}` closure");
489
490 let upvar = &self.upvars[upvar_field.index()];
491 let upvar_hir_id = upvar.get_root_variable();
492 let upvar_name = upvar.to_string(tcx);
493 let upvar_span = tcx.hir_span(upvar_hir_id);
494
495 let place_name = self.describe_any_place(move_place.as_ref());
496
497 let place_description =
498 if self.is_upvar_field_projection(move_place.as_ref()).is_some() {
499 format!("{place_name}, a {capture_description}")
500 } else {
501 format!("{place_name}, as `{upvar_name}` is a {capture_description}")
502 };
503
504 debug!(
505 "report: closure_kind_ty={:?} closure_kind={:?} place_description={:?}",
506 closure_kind_ty, closure_kind, place_description,
507 );
508
509 let closure_span = tcx.def_span(def_id);
510 self.cannot_move_out_of(span, &place_description)
511 .with_span_label(upvar_span, "captured outer variable")
512 .with_span_label(
513 closure_span,
514 format!("captured by this `{closure_kind}` closure"),
515 )
516 }
517 _ => {
518 let source = self.borrowed_content_source(deref_base);
519 let move_place_ref = move_place.as_ref();
520 match (
521 self.describe_place_with_options(
522 move_place_ref,
523 DescribePlaceOpt {
524 including_downcast: false,
525 including_tuple_field: false,
526 },
527 ),
528 self.describe_name(move_place_ref),
529 source.describe_for_named_place(),
530 ) {
531 (Some(place_desc), Some(name), Some(source_desc)) => self.cannot_move_out_of(
532 span,
533 &format!("`{place_desc}` as enum variant `{name}` which is behind a {source_desc}"),
534 ),
535 (Some(place_desc), Some(name), None) => self.cannot_move_out_of(
536 span,
537 &format!("`{place_desc}` as enum variant `{name}`"),
538 ),
539 (Some(place_desc), _, Some(source_desc)) => self.cannot_move_out_of(
540 span,
541 &format!("`{place_desc}` which is behind a {source_desc}"),
542 ),
543 (_, _, _) => self.cannot_move_out_of(
544 span,
545 &source.describe_for_unnamed_place(tcx),
546 ),
547 }
548 }
549 };
550 let msg_opt = CapturedMessageOpt {
551 is_partial_move: false,
552 is_loop_message: false,
553 is_move_msg: false,
554 is_loop_move: false,
555 has_suggest_reborrow: false,
556 maybe_reinitialized_locations_is_empty: true,
557 };
558 if let Some(use_spans) = use_spans {
559 self.explain_captures(&mut err, span, span, use_spans, move_place, msg_opt);
560 }
561 err
562 }
563
564 fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) {
565 match error {
566 GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => {
567 self.add_borrow_suggestions(err, span);
568 if binds_to.is_empty() {
569 let place_ty = move_from.ty(self.body, self.infcx.tcx).ty;
570 let place_desc = match self.describe_place(move_from.as_ref()) {
571 Some(desc) => format!("`{desc}`"),
572 None => "value".to_string(),
573 };
574
575 if let Some(expr) = self.find_expr(span) {
576 self.suggest_cloning(err, move_from.as_ref(), place_ty, expr, None);
577 }
578
579 err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
580 is_partial_move: false,
581 ty: place_ty,
582 place: &place_desc,
583 span,
584 });
585 } else {
586 binds_to.sort();
587 binds_to.dedup();
588
589 self.add_move_error_details(err, &binds_to);
590 }
591 }
592 GroupedMoveError::MovesFromValue { mut binds_to, .. } => {
593 binds_to.sort();
594 binds_to.dedup();
595 self.add_move_error_suggestions(err, &binds_to);
596 self.add_move_error_details(err, &binds_to);
597 }
598 GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => {
600 let mut use_span = use_spans.var_or_use();
601 let place_ty = original_path.ty(self.body, self.infcx.tcx).ty;
602 let place_desc = match self.describe_place(original_path.as_ref()) {
603 Some(desc) => format!("`{desc}`"),
604 None => "value".to_string(),
605 };
606
607 if let Some(expr) = self.find_expr(use_span) {
608 self.suggest_cloning(
609 err,
610 original_path.as_ref(),
611 place_ty,
612 expr,
613 Some(use_spans),
614 );
615 }
616
617 if let Some(upvar_field) = self
618 .prefixes(original_path.as_ref(), PrefixSet::All)
619 .find_map(|p| self.is_upvar_field_projection(p))
620 {
621 let upvar = &self.upvars[upvar_field.index()];
623 let upvar_hir_id = upvar.get_root_variable();
624 use_span = match self.infcx.tcx.parent_hir_node(upvar_hir_id) {
625 hir::Node::Param(param) => {
626 param.ty_span
629 }
630 hir::Node::LetStmt(stmt) => match (stmt.ty, stmt.init) {
631 (Some(ty), _) => ty.span,
633 (None, Some(init))
637 if !self.infcx.tcx.sess.source_map().is_multiline(init.span) =>
638 {
639 init.span
640 }
641 _ => use_span,
642 },
643 _ => use_span,
644 };
645 }
646
647 err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
648 is_partial_move: false,
649 ty: place_ty,
650 place: &place_desc,
651 span: use_span,
652 });
653
654 let mut pointed_at_span = false;
655 use_spans.args_subdiag(err, |args_span| {
656 if args_span == span || args_span == use_span {
657 pointed_at_span = true;
658 }
659 crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
660 place: place_desc.clone(),
661 args_span,
662 }
663 });
664 if !pointed_at_span && use_span != span {
665 err.subdiagnostic(crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
666 place: place_desc,
667 args_span: span,
668 });
669 }
670
671 self.add_note_for_packed_struct_derive(err, original_path.local);
672 }
673 }
674 }
675
676 fn add_borrow_suggestions(&self, err: &mut Diag<'_>, span: Span) {
677 match self.infcx.tcx.sess.source_map().span_to_snippet(span) {
678 Ok(snippet) if snippet.starts_with('*') => {
679 let sp = span.with_lo(span.lo() + BytePos(1));
680 let inner = self.find_expr(sp);
681 let mut is_raw_ptr = false;
682 if let Some(inner) = inner {
683 let typck_result = self.infcx.tcx.typeck(self.mir_def_id());
684 if let Some(inner_type) = typck_result.node_type_opt(inner.hir_id) {
685 if matches!(inner_type.kind(), ty::RawPtr(..)) {
686 is_raw_ptr = true;
687 }
688 }
689 }
690 if !is_raw_ptr {
693 err.span_suggestion_verbose(
694 span.with_hi(span.lo() + BytePos(1)),
695 "consider removing the dereference here",
696 String::new(),
697 Applicability::MaybeIncorrect,
698 );
699 }
700 }
701 _ => {
702 err.span_suggestion_verbose(
703 span.shrink_to_lo(),
704 "consider borrowing here",
705 '&',
706 Applicability::MaybeIncorrect,
707 );
708 }
709 }
710 }
711
712 fn add_move_error_suggestions(&self, err: &mut Diag<'_>, binds_to: &[Local]) {
713 struct BindingFinder<'tcx> {
716 typeck_results: &'tcx ty::TypeckResults<'tcx>,
717 tcx: TyCtxt<'tcx>,
718 pat_span: Span,
720 binding_spans: Vec<Span>,
722 found_pat: bool,
724 ref_pat: Option<&'tcx hir::Pat<'tcx>>,
726 has_adjustments: bool,
728 ref_pat_for_binding: Vec<(Span, Option<&'tcx hir::Pat<'tcx>>)>,
730 cannot_remove: FxHashSet<HirId>,
732 }
733 impl<'tcx> Visitor<'tcx> for BindingFinder<'tcx> {
734 type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies;
735
736 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
737 self.tcx
738 }
739
740 fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) -> Self::Result {
741 if !self.found_pat {
743 hir::intravisit::walk_expr(self, ex)
744 }
745 }
746
747 fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
748 if p.span == self.pat_span {
749 self.found_pat = true;
750 }
751
752 let parent_has_adjustments = self.has_adjustments;
753 self.has_adjustments |=
754 self.typeck_results.pat_adjustments().contains_key(p.hir_id);
755
756 let parent_ref_pat = self.ref_pat;
758 if let hir::PatKind::Ref(..) = p.kind {
759 self.ref_pat = Some(p);
760 self.cannot_remove.extend(parent_ref_pat.map(|r| r.hir_id));
763 if self.has_adjustments {
764 self.cannot_remove.insert(p.hir_id);
766 self.has_adjustments = false;
768 }
769 }
770
771 if let hir::PatKind::Binding(_, _, ident, _) = p.kind {
772 if let Some(&bind_sp) =
774 self.binding_spans.iter().find(|bind_sp| bind_sp.contains(ident.span))
775 {
776 self.ref_pat_for_binding.push((bind_sp, self.ref_pat));
777 } else {
778 if let Some(ref_pat) = self.ref_pat {
781 self.cannot_remove.insert(ref_pat.hir_id);
782 }
783 }
784 }
785
786 hir::intravisit::walk_pat(self, p);
787 self.ref_pat = parent_ref_pat;
788 self.has_adjustments = parent_has_adjustments;
789 }
790 }
791 let mut pat_span = None;
792 let mut binding_spans = Vec::new();
793 for local in binds_to {
794 let bind_to = &self.body.local_decls[*local];
795 if let LocalInfo::User(BindingForm::Var(VarBindingForm { pat_span: pat_sp, .. })) =
796 *bind_to.local_info()
797 {
798 pat_span = Some(pat_sp);
799 binding_spans.push(bind_to.source_info.span);
800 }
801 }
802 let Some(pat_span) = pat_span else { return };
803
804 let tcx = self.infcx.tcx;
805 let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) else { return };
806 let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
807 let mut finder = BindingFinder {
808 typeck_results,
809 tcx,
810 pat_span,
811 binding_spans,
812 found_pat: false,
813 ref_pat: None,
814 has_adjustments: false,
815 ref_pat_for_binding: Vec::new(),
816 cannot_remove: FxHashSet::default(),
817 };
818 finder.visit_body(body);
819
820 let mut suggestions = Vec::new();
821 for (binding_span, opt_ref_pat) in finder.ref_pat_for_binding {
822 if let Some(ref_pat) = opt_ref_pat
823 && !finder.cannot_remove.contains(&ref_pat.hir_id)
824 && let hir::PatKind::Ref(subpat, mutbl) = ref_pat.kind
825 && let Some(ref_span) = ref_pat.span.trim_end(subpat.span)
826 {
827 let mutable_str = if mutbl.is_mut() { "mutable " } else { "" };
828 let msg = format!("consider removing the {mutable_str}borrow");
829 suggestions.push((ref_span, msg, "".to_string()));
830 } else {
831 let msg = "consider borrowing the pattern binding".to_string();
832 suggestions.push((binding_span.shrink_to_lo(), msg, "ref ".to_string()));
833 }
834 }
835 suggestions.sort_unstable_by_key(|&(span, _, _)| span);
836 suggestions.dedup_by_key(|&mut (span, _, _)| span);
837 for (span, msg, suggestion) in suggestions {
838 err.span_suggestion_verbose(span, msg, suggestion, Applicability::MachineApplicable);
839 }
840 }
841
842 fn add_move_error_details(&self, err: &mut Diag<'_>, binds_to: &[Local]) {
843 for (j, local) in binds_to.iter().enumerate() {
844 let bind_to = &self.body.local_decls[*local];
845 let binding_span = bind_to.source_info.span;
846
847 if j == 0 {
848 err.span_label(binding_span, "data moved here");
849 } else {
850 err.span_label(binding_span, "...and here");
851 }
852
853 if binds_to.len() == 1 {
854 let place_desc = self.local_name(*local).map(|sym| format!("`{sym}`"));
855
856 if let Some(expr) = self.find_expr(binding_span) {
857 let local_place: PlaceRef<'tcx> = (*local).into();
858 self.suggest_cloning(err, local_place, bind_to.ty, expr, None);
859 }
860
861 err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
862 is_partial_move: false,
863 ty: bind_to.ty,
864 place: place_desc.as_deref().unwrap_or("the place"),
865 span: binding_span,
866 });
867 }
868 }
869
870 if binds_to.len() > 1 {
871 err.note(
872 "move occurs because these variables have types that don't implement the `Copy` \
873 trait",
874 );
875 }
876 }
877
878 fn add_note_for_packed_struct_derive(&self, err: &mut Diag<'_>, local: Local) {
883 let local_place: PlaceRef<'tcx> = local.into();
884 let local_ty = local_place.ty(self.body.local_decls(), self.infcx.tcx).ty.peel_refs();
885
886 if let Some(adt) = local_ty.ty_adt_def()
887 && adt.repr().packed()
888 && let ExpnKind::Macro(MacroKind::Derive, name) =
889 self.body.span.ctxt().outer_expn_data().kind
890 {
891 err.note(format!("`#[derive({name})]` triggers a move because taking references to the fields of a packed struct is undefined behaviour"));
892 }
893 }
894}