rustc_borrowck/diagnostics/
conflict_errors.rs

1// ignore-tidy-filelength
2
3#![allow(rustc::diagnostic_outside_of_impl)]
4#![allow(rustc::untranslatable_diagnostic)]
5
6use std::iter;
7use std::ops::ControlFlow;
8
9use either::Either;
10use hir::{ClosureKind, Path};
11use rustc_data_structures::captures::Captures;
12use rustc_data_structures::fx::FxIndexSet;
13use rustc_errors::codes::*;
14use rustc_errors::{Applicability, Diag, MultiSpan, struct_span_code_err};
15use rustc_hir as hir;
16use rustc_hir::def::{DefKind, Res};
17use rustc_hir::intravisit::{Map, Visitor, walk_block, walk_expr};
18use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, LangItem, PatField};
19use rustc_middle::bug;
20use rustc_middle::hir::nested_filter::OnlyBodies;
21use rustc_middle::mir::tcx::PlaceTy;
22use rustc_middle::mir::{
23    self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
24    FakeBorrowKind, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind,
25    Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
26    TerminatorKind, VarBindingForm, VarDebugInfoContents,
27};
28use rustc_middle::ty::print::PrintTraitRefExt as _;
29use rustc_middle::ty::{
30    self, PredicateKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, Upcast,
31    suggest_constraining_type_params,
32};
33use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
34use rustc_span::def_id::{DefId, LocalDefId};
35use rustc_span::hygiene::DesugaringKind;
36use rustc_span::{BytePos, Ident, Span, Symbol, kw, sym};
37use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
38use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
39use rustc_trait_selection::error_reporting::traits::call_kind::CallKind;
40use rustc_trait_selection::infer::InferCtxtExt;
41use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
42use rustc_trait_selection::traits::{
43    Obligation, ObligationCause, ObligationCtxt, supertrait_def_ids,
44};
45use tracing::{debug, instrument};
46
47use super::explain_borrow::{BorrowExplanation, LaterUseKind};
48use super::{DescribePlaceOpt, RegionName, RegionNameSource, UseSpans};
49use crate::borrow_set::{BorrowData, TwoPhaseActivation};
50use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;
51use crate::diagnostics::{CapturedMessageOpt, call_kind, find_all_local_uses};
52use crate::prefixes::IsPrefixOf;
53use crate::{InitializationRequiringAction, MirBorrowckCtxt, WriteKind, borrowck_errors};
54
55#[derive(Debug)]
56struct MoveSite {
57    /// Index of the "move out" that we found. The `MoveData` can
58    /// then tell us where the move occurred.
59    moi: MoveOutIndex,
60
61    /// `true` if we traversed a back edge while walking from the point
62    /// of error to the move site.
63    traversed_back_edge: bool,
64}
65
66/// Which case a StorageDeadOrDrop is for.
67#[derive(Copy, Clone, PartialEq, Eq, Debug)]
68enum StorageDeadOrDrop<'tcx> {
69    LocalStorageDead,
70    BoxedStorageDead,
71    Destructor(Ty<'tcx>),
72}
73
74impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
75    pub(crate) fn report_use_of_moved_or_uninitialized(
76        &mut self,
77        location: Location,
78        desired_action: InitializationRequiringAction,
79        (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),
80        mpi: MovePathIndex,
81    ) {
82        debug!(
83            "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
84             moved_place={:?} used_place={:?} span={:?} mpi={:?}",
85            location, desired_action, moved_place, used_place, span, mpi
86        );
87
88        let use_spans =
89            self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
90        let span = use_spans.args_or_use();
91
92        let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
93        debug!(
94            "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
95            move_site_vec, use_spans
96        );
97        let move_out_indices: Vec<_> =
98            move_site_vec.iter().map(|move_site| move_site.moi).collect();
99
100        if move_out_indices.is_empty() {
101            let root_local = used_place.local;
102
103            if !self.uninitialized_error_reported.insert(root_local) {
104                debug!(
105                    "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
106                    root_local
107                );
108                return;
109            }
110
111            let err = self.report_use_of_uninitialized(
112                mpi,
113                used_place,
114                moved_place,
115                desired_action,
116                span,
117                use_spans,
118            );
119            self.buffer_error(err);
120        } else {
121            if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) {
122                if used_place.is_prefix_of(*reported_place) {
123                    debug!(
124                        "report_use_of_moved_or_uninitialized place: error suppressed mois={:?}",
125                        move_out_indices
126                    );
127                    return;
128                }
129            }
130
131            let is_partial_move = move_site_vec.iter().any(|move_site| {
132                let move_out = self.move_data.moves[(*move_site).moi];
133                let moved_place = &self.move_data.move_paths[move_out.path].place;
134                // `*(_1)` where `_1` is a `Box` is actually a move out.
135                let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]
136                    && self.body.local_decls[moved_place.local].ty.is_box();
137
138                !is_box_move
139                    && used_place != moved_place.as_ref()
140                    && used_place.is_prefix_of(moved_place.as_ref())
141            });
142
143            let partial_str = if is_partial_move { "partial " } else { "" };
144            let partially_str = if is_partial_move { "partially " } else { "" };
145
146            let mut err = self.cannot_act_on_moved_value(
147                span,
148                desired_action.as_noun(),
149                partially_str,
150                self.describe_place_with_options(
151                    moved_place,
152                    DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
153                ),
154            );
155
156            let reinit_spans = maybe_reinitialized_locations
157                .iter()
158                .take(3)
159                .map(|loc| {
160                    self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
161                        .args_or_use()
162                })
163                .collect::<Vec<Span>>();
164
165            let reinits = maybe_reinitialized_locations.len();
166            if reinits == 1 {
167                err.span_label(reinit_spans[0], "this reinitialization might get skipped");
168            } else if reinits > 1 {
169                err.span_note(
170                    MultiSpan::from_spans(reinit_spans),
171                    if reinits <= 3 {
172                        format!("these {reinits} reinitializations might get skipped")
173                    } else {
174                        format!(
175                            "these 3 reinitializations and {} other{} might get skipped",
176                            reinits - 3,
177                            if reinits == 4 { "" } else { "s" }
178                        )
179                    },
180                );
181            }
182
183            let closure = self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
184
185            let mut is_loop_move = false;
186            let mut in_pattern = false;
187            let mut seen_spans = FxIndexSet::default();
188
189            for move_site in &move_site_vec {
190                let move_out = self.move_data.moves[(*move_site).moi];
191                let moved_place = &self.move_data.move_paths[move_out.path].place;
192
193                let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
194                let move_span = move_spans.args_or_use();
195
196                let is_move_msg = move_spans.for_closure();
197
198                let is_loop_message = location == move_out.source || move_site.traversed_back_edge;
199
200                if location == move_out.source {
201                    is_loop_move = true;
202                }
203
204                let mut has_suggest_reborrow = false;
205                if !seen_spans.contains(&move_span) {
206                    self.suggest_ref_or_clone(
207                        mpi,
208                        &mut err,
209                        &mut in_pattern,
210                        move_spans,
211                        moved_place.as_ref(),
212                        &mut has_suggest_reborrow,
213                        closure,
214                    );
215
216                    let msg_opt = CapturedMessageOpt {
217                        is_partial_move,
218                        is_loop_message,
219                        is_move_msg,
220                        is_loop_move,
221                        has_suggest_reborrow,
222                        maybe_reinitialized_locations_is_empty: maybe_reinitialized_locations
223                            .is_empty(),
224                    };
225                    self.explain_captures(
226                        &mut err,
227                        span,
228                        move_span,
229                        move_spans,
230                        *moved_place,
231                        msg_opt,
232                    );
233                }
234                seen_spans.insert(move_span);
235            }
236
237            use_spans.var_path_only_subdiag(&mut err, desired_action);
238
239            if !is_loop_move {
240                err.span_label(
241                    span,
242                    format!(
243                        "value {} here after {partial_str}move",
244                        desired_action.as_verb_in_past_tense(),
245                    ),
246                );
247            }
248
249            let ty = used_place.ty(self.body, self.infcx.tcx).ty;
250            let needs_note = match ty.kind() {
251                ty::Closure(id, _) => {
252                    self.infcx.tcx.closure_kind_origin(id.expect_local()).is_none()
253                }
254                _ => true,
255            };
256
257            let mpi = self.move_data.moves[move_out_indices[0]].path;
258            let place = &self.move_data.move_paths[mpi].place;
259            let ty = place.ty(self.body, self.infcx.tcx).ty;
260
261            // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
262            // Same for if we're in a loop, see #101119.
263            if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
264                if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
265                    // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
266                    self.suggest_reborrow(&mut err, span, moved_place);
267                }
268            }
269
270            if self.infcx.param_env.caller_bounds().iter().any(|c| {
271                c.as_trait_clause().is_some_and(|pred| {
272                    pred.skip_binder().self_ty() == ty && self.infcx.tcx.is_fn_trait(pred.def_id())
273                })
274            }) {
275                // Suppress the next suggestion since we don't want to put more bounds onto
276                // something that already has `Fn`-like bounds (or is a closure), so we can't
277                // restrict anyways.
278            } else {
279                let copy_did = self.infcx.tcx.require_lang_item(LangItem::Copy, Some(span));
280                self.suggest_adding_bounds(&mut err, ty, copy_did, span);
281            }
282
283            let opt_name = self.describe_place_with_options(
284                place.as_ref(),
285                DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
286            );
287            let note_msg = match opt_name {
288                Some(name) => format!("`{name}`"),
289                None => "value".to_owned(),
290            };
291            if needs_note {
292                let ty = self.infcx.tcx.short_string(ty, err.long_ty_path());
293                if let Some(local) = place.as_local() {
294                    let span = self.body.local_decls[local].source_info.span;
295                    err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
296                        is_partial_move,
297                        ty,
298                        place: &note_msg,
299                        span,
300                    });
301                } else {
302                    err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note {
303                        is_partial_move,
304                        ty,
305                        place: &note_msg,
306                    });
307                };
308            }
309
310            if let UseSpans::FnSelfUse {
311                kind: CallKind::DerefCoercion { deref_target_span, deref_target_ty, .. },
312                ..
313            } = use_spans
314            {
315                err.note(format!(
316                    "{} occurs due to deref coercion to `{deref_target_ty}`",
317                    desired_action.as_noun(),
318                ));
319
320                // Check first whether the source is accessible (issue #87060)
321                if let Some(deref_target_span) = deref_target_span
322                    && self.infcx.tcx.sess.source_map().is_span_accessible(deref_target_span)
323                {
324                    err.span_note(deref_target_span, "deref defined here");
325                }
326            }
327
328            self.buffer_move_error(move_out_indices, (used_place, err));
329        }
330    }
331
332    fn suggest_ref_or_clone(
333        &self,
334        mpi: MovePathIndex,
335        err: &mut Diag<'infcx>,
336        in_pattern: &mut bool,
337        move_spans: UseSpans<'tcx>,
338        moved_place: PlaceRef<'tcx>,
339        has_suggest_reborrow: &mut bool,
340        moved_or_invoked_closure: bool,
341    ) {
342        let move_span = match move_spans {
343            UseSpans::ClosureUse { capture_kind_span, .. } => capture_kind_span,
344            _ => move_spans.args_or_use(),
345        };
346        struct ExpressionFinder<'hir> {
347            expr_span: Span,
348            expr: Option<&'hir hir::Expr<'hir>>,
349            pat: Option<&'hir hir::Pat<'hir>>,
350            parent_pat: Option<&'hir hir::Pat<'hir>>,
351            hir: rustc_middle::hir::map::Map<'hir>,
352        }
353        impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
354            type NestedFilter = OnlyBodies;
355
356            fn nested_visit_map(&mut self) -> Self::Map {
357                self.hir
358            }
359
360            fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
361                if e.span == self.expr_span {
362                    self.expr = Some(e);
363                }
364                hir::intravisit::walk_expr(self, e);
365            }
366            fn visit_pat(&mut self, p: &'hir hir::Pat<'hir>) {
367                if p.span == self.expr_span {
368                    self.pat = Some(p);
369                }
370                if let hir::PatKind::Binding(hir::BindingMode::NONE, _, i, sub) = p.kind {
371                    if i.span == self.expr_span || p.span == self.expr_span {
372                        self.pat = Some(p);
373                    }
374                    // Check if we are in a situation of `ident @ ident` where we want to suggest
375                    // `ref ident @ ref ident` or `ref ident @ Struct { ref ident }`.
376                    if let Some(subpat) = sub
377                        && self.pat.is_none()
378                    {
379                        self.visit_pat(subpat);
380                        if self.pat.is_some() {
381                            self.parent_pat = Some(p);
382                        }
383                        return;
384                    }
385                }
386                hir::intravisit::walk_pat(self, p);
387            }
388        }
389        let hir = self.infcx.tcx.hir();
390        if let Some(body) = hir.maybe_body_owned_by(self.mir_def_id()) {
391            let expr = body.value;
392            let place = &self.move_data.move_paths[mpi].place;
393            let span = place.as_local().map(|local| self.body.local_decls[local].source_info.span);
394            let mut finder = ExpressionFinder {
395                expr_span: move_span,
396                expr: None,
397                pat: None,
398                parent_pat: None,
399                hir,
400            };
401            finder.visit_expr(expr);
402            if let Some(span) = span
403                && let Some(expr) = finder.expr
404            {
405                for (_, expr) in hir.parent_iter(expr.hir_id) {
406                    if let hir::Node::Expr(expr) = expr {
407                        if expr.span.contains(span) {
408                            // If the let binding occurs within the same loop, then that
409                            // loop isn't relevant, like in the following, the outermost `loop`
410                            // doesn't play into `x` being moved.
411                            // ```
412                            // loop {
413                            //     let x = String::new();
414                            //     loop {
415                            //         foo(x);
416                            //     }
417                            // }
418                            // ```
419                            break;
420                        }
421                        if let hir::ExprKind::Loop(.., loop_span) = expr.kind {
422                            err.span_label(loop_span, "inside of this loop");
423                        }
424                    }
425                }
426                let typeck = self.infcx.tcx.typeck(self.mir_def_id());
427                let parent = self.infcx.tcx.parent_hir_node(expr.hir_id);
428                let (def_id, call_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
429                    && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
430                {
431                    let def_id = typeck.type_dependent_def_id(parent_expr.hir_id);
432                    (def_id, Some(parent_expr.hir_id), args, 1)
433                } else if let hir::Node::Expr(parent_expr) = parent
434                    && let hir::ExprKind::Call(call, args) = parent_expr.kind
435                    && let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind()
436                {
437                    (Some(*def_id), Some(call.hir_id), args, 0)
438                } else {
439                    (None, None, &[][..], 0)
440                };
441                let ty = place.ty(self.body, self.infcx.tcx).ty;
442
443                let mut can_suggest_clone = true;
444                if let Some(def_id) = def_id
445                    && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
446                {
447                    // The move occurred as one of the arguments to a function call. Is that
448                    // argument generic? `def_id` can't be a closure here, so using `fn_sig` is fine
449                    let arg_param = if self.infcx.tcx.def_kind(def_id).is_fn_like()
450                        && let sig =
451                            self.infcx.tcx.fn_sig(def_id).instantiate_identity().skip_binder()
452                        && let Some(arg_ty) = sig.inputs().get(pos + offset)
453                        && let ty::Param(arg_param) = arg_ty.kind()
454                    {
455                        Some(arg_param)
456                    } else {
457                        None
458                    };
459
460                    // If the moved value is a mut reference, it is used in a
461                    // generic function and it's type is a generic param, it can be
462                    // reborrowed to avoid moving.
463                    // for example:
464                    // struct Y(u32);
465                    // x's type is '& mut Y' and it is used in `fn generic<T>(x: T) {}`.
466                    if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind()
467                        && arg_param.is_some()
468                    {
469                        *has_suggest_reborrow = true;
470                        self.suggest_reborrow(err, expr.span, moved_place);
471                        return;
472                    }
473
474                    // If the moved place is used generically by the callee and a reference to it
475                    // would still satisfy any bounds on its type, suggest borrowing.
476                    if let Some(&param) = arg_param
477                        && let Some(generic_args) = call_id.and_then(|id| typeck.node_args_opt(id))
478                        && let Some(ref_mutability) = self.suggest_borrow_generic_arg(
479                            err,
480                            def_id,
481                            generic_args,
482                            param,
483                            moved_place,
484                            pos + offset,
485                            ty,
486                            expr.span,
487                        )
488                    {
489                        can_suggest_clone = ref_mutability.is_mut();
490                    } else if let Some(local_def_id) = def_id.as_local()
491                        && let node = self.infcx.tcx.hir_node_by_def_id(local_def_id)
492                        && let Some(fn_decl) = node.fn_decl()
493                        && let Some(ident) = node.ident()
494                        && let Some(arg) = fn_decl.inputs.get(pos + offset)
495                    {
496                        // If we can't suggest borrowing in the call, but the function definition
497                        // is local, instead offer changing the function to borrow that argument.
498                        let mut span: MultiSpan = arg.span.into();
499                        span.push_span_label(
500                            arg.span,
501                            "this parameter takes ownership of the value".to_string(),
502                        );
503                        let descr = match node.fn_kind() {
504                            Some(hir::intravisit::FnKind::ItemFn(..)) | None => "function",
505                            Some(hir::intravisit::FnKind::Method(..)) => "method",
506                            Some(hir::intravisit::FnKind::Closure) => "closure",
507                        };
508                        span.push_span_label(ident.span, format!("in this {descr}"));
509                        err.span_note(
510                            span,
511                            format!(
512                                "consider changing this parameter type in {descr} `{ident}` to \
513                                 borrow instead if owning the value isn't necessary",
514                            ),
515                        );
516                    }
517                }
518                if let hir::Node::Expr(parent_expr) = parent
519                    && let hir::ExprKind::Call(call_expr, _) = parent_expr.kind
520                    && let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IntoIterIntoIter, _)) =
521                        call_expr.kind
522                {
523                    // Do not suggest `.clone()` in a `for` loop, we already suggest borrowing.
524                } else if let UseSpans::FnSelfUse { kind: CallKind::Normal { .. }, .. } = move_spans
525                {
526                    // We already suggest cloning for these cases in `explain_captures`.
527                } else if moved_or_invoked_closure {
528                    // Do not suggest `closure.clone()()`.
529                } else if let UseSpans::ClosureUse {
530                    closure_kind:
531                        ClosureKind::Coroutine(CoroutineKind::Desugared(_, CoroutineSource::Block)),
532                    ..
533                } = move_spans
534                    && can_suggest_clone
535                {
536                    self.suggest_cloning(err, ty, expr, Some(move_spans));
537                } else if self.suggest_hoisting_call_outside_loop(err, expr) && can_suggest_clone {
538                    // The place where the type moves would be misleading to suggest clone.
539                    // #121466
540                    self.suggest_cloning(err, ty, expr, Some(move_spans));
541                }
542            }
543
544            self.suggest_ref_for_dbg_args(expr, place, move_span, err);
545
546            // it's useless to suggest inserting `ref` when the span don't comes from local code
547            if let Some(pat) = finder.pat
548                && !move_span.is_dummy()
549                && !self.infcx.tcx.sess.source_map().is_imported(move_span)
550            {
551                *in_pattern = true;
552                let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())];
553                if let Some(pat) = finder.parent_pat {
554                    sugg.insert(0, (pat.span.shrink_to_lo(), "ref ".to_string()));
555                }
556                err.multipart_suggestion_verbose(
557                    "borrow this binding in the pattern to avoid moving the value",
558                    sugg,
559                    Applicability::MachineApplicable,
560                );
561            }
562        }
563    }
564
565    // for dbg!(x) which may take ownership, suggest dbg!(&x) instead
566    // but here we actually do not check whether the macro name is `dbg!`
567    // so that we may extend the scope a bit larger to cover more cases
568    fn suggest_ref_for_dbg_args(
569        &self,
570        body: &hir::Expr<'_>,
571        place: &Place<'tcx>,
572        move_span: Span,
573        err: &mut Diag<'infcx>,
574    ) {
575        let var_info = self.body.var_debug_info.iter().find(|info| match info.value {
576            VarDebugInfoContents::Place(ref p) => p == place,
577            _ => false,
578        });
579        let arg_name = if let Some(var_info) = var_info {
580            var_info.name
581        } else {
582            return;
583        };
584        struct MatchArgFinder {
585            expr_span: Span,
586            match_arg_span: Option<Span>,
587            arg_name: Symbol,
588        }
589        impl Visitor<'_> for MatchArgFinder {
590            fn visit_expr(&mut self, e: &hir::Expr<'_>) {
591                // dbg! is expanded into a match pattern, we need to find the right argument span
592                if let hir::ExprKind::Match(expr, ..) = &e.kind
593                    && let hir::ExprKind::Path(hir::QPath::Resolved(
594                        _,
595                        path @ Path { segments: [seg], .. },
596                    )) = &expr.kind
597                    && seg.ident.name == self.arg_name
598                    && self.expr_span.source_callsite().contains(expr.span)
599                {
600                    self.match_arg_span = Some(path.span);
601                }
602                hir::intravisit::walk_expr(self, e);
603            }
604        }
605
606        let mut finder = MatchArgFinder { expr_span: move_span, match_arg_span: None, arg_name };
607        finder.visit_expr(body);
608        if let Some(macro_arg_span) = finder.match_arg_span {
609            err.span_suggestion_verbose(
610                macro_arg_span.shrink_to_lo(),
611                "consider borrowing instead of transferring ownership",
612                "&",
613                Applicability::MachineApplicable,
614            );
615        }
616    }
617
618    pub(crate) fn suggest_reborrow(
619        &self,
620        err: &mut Diag<'infcx>,
621        span: Span,
622        moved_place: PlaceRef<'tcx>,
623    ) {
624        err.span_suggestion_verbose(
625            span.shrink_to_lo(),
626            format!(
627                "consider creating a fresh reborrow of {} here",
628                self.describe_place(moved_place)
629                    .map(|n| format!("`{n}`"))
630                    .unwrap_or_else(|| "the mutable reference".to_string()),
631            ),
632            "&mut *",
633            Applicability::MachineApplicable,
634        );
635    }
636
637    /// If a place is used after being moved as an argument to a function, the function is generic
638    /// in that argument, and a reference to the argument's type would still satisfy the function's
639    /// bounds, suggest borrowing. This covers, e.g., borrowing an `impl Fn()` argument being passed
640    /// in an `impl FnOnce()` position.
641    /// Returns `Some(mutability)` when suggesting to borrow with mutability `mutability`, or `None`
642    /// if no suggestion is made.
643    fn suggest_borrow_generic_arg(
644        &self,
645        err: &mut Diag<'_>,
646        callee_did: DefId,
647        generic_args: ty::GenericArgsRef<'tcx>,
648        param: ty::ParamTy,
649        moved_place: PlaceRef<'tcx>,
650        moved_arg_pos: usize,
651        moved_arg_ty: Ty<'tcx>,
652        place_span: Span,
653    ) -> Option<ty::Mutability> {
654        let tcx = self.infcx.tcx;
655        let sig = tcx.fn_sig(callee_did).instantiate_identity().skip_binder();
656        let clauses = tcx.predicates_of(callee_did);
657
658        // First, is there at least one method on one of `param`'s trait bounds?
659        // This keeps us from suggesting borrowing the argument to `mem::drop`, e.g.
660        if !clauses.instantiate_identity(tcx).predicates.iter().any(|clause| {
661            clause.as_trait_clause().is_some_and(|tc| {
662                tc.self_ty().skip_binder().is_param(param.index)
663                    && tc.polarity() == ty::PredicatePolarity::Positive
664                    && supertrait_def_ids(tcx, tc.def_id())
665                        .flat_map(|trait_did| tcx.associated_items(trait_did).in_definition_order())
666                        .any(|item| item.fn_has_self_parameter)
667            })
668        }) {
669            return None;
670        }
671
672        // Try borrowing a shared reference first, then mutably.
673        if let Some(mutbl) = [ty::Mutability::Not, ty::Mutability::Mut].into_iter().find(|&mutbl| {
674            let re = self.infcx.tcx.lifetimes.re_erased;
675            let ref_ty = Ty::new_ref(self.infcx.tcx, re, moved_arg_ty, mutbl);
676
677            // Ensure that substituting `ref_ty` in the callee's signature doesn't break
678            // other inputs or the return type.
679            let new_args = tcx.mk_args_from_iter(generic_args.iter().enumerate().map(
680                |(i, arg)| {
681                    if i == param.index as usize { ref_ty.into() } else { arg }
682                },
683            ));
684            let can_subst = |ty: Ty<'tcx>| {
685                // Normalize before comparing to see through type aliases and projections.
686                let old_ty = ty::EarlyBinder::bind(ty).instantiate(tcx, generic_args);
687                let new_ty = ty::EarlyBinder::bind(ty).instantiate(tcx, new_args);
688                if let Ok(old_ty) = tcx.try_normalize_erasing_regions(
689                    self.infcx.typing_env(self.infcx.param_env),
690                    old_ty,
691                ) && let Ok(new_ty) = tcx.try_normalize_erasing_regions(
692                    self.infcx.typing_env(self.infcx.param_env),
693                    new_ty,
694                ) {
695                    old_ty == new_ty
696                } else {
697                    false
698                }
699            };
700            if !can_subst(sig.output())
701                || sig
702                    .inputs()
703                    .iter()
704                    .enumerate()
705                    .any(|(i, &input_ty)| i != moved_arg_pos && !can_subst(input_ty))
706            {
707                return false;
708            }
709
710            // Test the callee's predicates, substituting in `ref_ty` for the moved argument type.
711            clauses.instantiate(tcx, new_args).predicates.iter().all(|&(mut clause)| {
712                // Normalize before testing to see through type aliases and projections.
713                if let Ok(normalized) = tcx.try_normalize_erasing_regions(
714                    self.infcx.typing_env(self.infcx.param_env),
715                    clause,
716                ) {
717                    clause = normalized;
718                }
719                self.infcx.predicate_must_hold_modulo_regions(&Obligation::new(
720                    tcx,
721                    ObligationCause::dummy(),
722                    self.infcx.param_env,
723                    clause,
724                ))
725            })
726        }) {
727            let place_desc = if let Some(desc) = self.describe_place(moved_place) {
728                format!("`{desc}`")
729            } else {
730                "here".to_owned()
731            };
732            err.span_suggestion_verbose(
733                place_span.shrink_to_lo(),
734                format!("consider {}borrowing {place_desc}", mutbl.mutably_str()),
735                mutbl.ref_prefix_str(),
736                Applicability::MaybeIncorrect,
737            );
738            Some(mutbl)
739        } else {
740            None
741        }
742    }
743
744    fn report_use_of_uninitialized(
745        &self,
746        mpi: MovePathIndex,
747        used_place: PlaceRef<'tcx>,
748        moved_place: PlaceRef<'tcx>,
749        desired_action: InitializationRequiringAction,
750        span: Span,
751        use_spans: UseSpans<'tcx>,
752    ) -> Diag<'infcx> {
753        // We need all statements in the body where the binding was assigned to later find all
754        // the branching code paths where the binding *wasn't* assigned to.
755        let inits = &self.move_data.init_path_map[mpi];
756        let move_path = &self.move_data.move_paths[mpi];
757        let decl_span = self.body.local_decls[move_path.place.local].source_info.span;
758        let mut spans_set = FxIndexSet::default();
759        for init_idx in inits {
760            let init = &self.move_data.inits[*init_idx];
761            let span = init.span(self.body);
762            if !span.is_dummy() {
763                spans_set.insert(span);
764            }
765        }
766        let spans: Vec<_> = spans_set.into_iter().collect();
767
768        let (name, desc) = match self.describe_place_with_options(
769            moved_place,
770            DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
771        ) {
772            Some(name) => (format!("`{name}`"), format!("`{name}` ")),
773            None => ("the variable".to_string(), String::new()),
774        };
775        let path = match self.describe_place_with_options(
776            used_place,
777            DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
778        ) {
779            Some(name) => format!("`{name}`"),
780            None => "value".to_string(),
781        };
782
783        // We use the statements were the binding was initialized, and inspect the HIR to look
784        // for the branching codepaths that aren't covered, to point at them.
785        let map = self.infcx.tcx.hir();
786        let body = map.body_owned_by(self.mir_def_id());
787        let mut visitor = ConditionVisitor { tcx: self.infcx.tcx, spans, name, errors: vec![] };
788        visitor.visit_body(&body);
789        let spans = visitor.spans;
790
791        let mut show_assign_sugg = false;
792        let isnt_initialized = if let InitializationRequiringAction::PartialAssignment
793        | InitializationRequiringAction::Assignment = desired_action
794        {
795            // The same error is emitted for bindings that are *sometimes* initialized and the ones
796            // that are *partially* initialized by assigning to a field of an uninitialized
797            // binding. We differentiate between them for more accurate wording here.
798            "isn't fully initialized"
799        } else if !spans.iter().any(|i| {
800            // We filter these to avoid misleading wording in cases like the following,
801            // where `x` has an `init`, but it is in the same place we're looking at:
802            // ```
803            // let x;
804            // x += 1;
805            // ```
806            !i.contains(span)
807            // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs`
808            && !visitor
809                .errors
810                .iter()
811                .map(|(sp, _)| *sp)
812                .any(|sp| span < sp && !sp.contains(span))
813        }) {
814            show_assign_sugg = true;
815            "isn't initialized"
816        } else {
817            "is possibly-uninitialized"
818        };
819
820        let used = desired_action.as_general_verb_in_past_tense();
821        let mut err = struct_span_code_err!(
822            self.dcx(),
823            span,
824            E0381,
825            "{used} binding {desc}{isnt_initialized}"
826        );
827        use_spans.var_path_only_subdiag(&mut err, desired_action);
828
829        if let InitializationRequiringAction::PartialAssignment
830        | InitializationRequiringAction::Assignment = desired_action
831        {
832            err.help(
833                "partial initialization isn't supported, fully initialize the binding with a \
834                 default value and mutate it, or use `std::mem::MaybeUninit`",
835            );
836        }
837        err.span_label(span, format!("{path} {used} here but it {isnt_initialized}"));
838
839        let mut shown = false;
840        for (sp, label) in visitor.errors {
841            if sp < span && !sp.overlaps(span) {
842                // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention
843                // match arms coming after the primary span because they aren't relevant:
844                // ```
845                // let x;
846                // match y {
847                //     _ if { x = 2; true } => {}
848                //     _ if {
849                //         x; //~ ERROR
850                //         false
851                //     } => {}
852                //     _ => {} // We don't want to point to this.
853                // };
854                // ```
855                err.span_label(sp, label);
856                shown = true;
857            }
858        }
859        if !shown {
860            for sp in &spans {
861                if *sp < span && !sp.overlaps(span) {
862                    err.span_label(*sp, "binding initialized here in some conditions");
863                }
864            }
865        }
866
867        err.span_label(decl_span, "binding declared here but left uninitialized");
868        if show_assign_sugg {
869            struct LetVisitor {
870                decl_span: Span,
871                sugg_span: Option<Span>,
872            }
873
874            impl<'v> Visitor<'v> for LetVisitor {
875                fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
876                    if self.sugg_span.is_some() {
877                        return;
878                    }
879
880                    // FIXME: We make sure that this is a normal top-level binding,
881                    // but we could suggest `todo!()` for all uninitialized bindings in the pattern
882                    if let hir::StmtKind::Let(hir::LetStmt { span, ty, init: None, pat, .. }) =
883                        &ex.kind
884                        && let hir::PatKind::Binding(..) = pat.kind
885                        && span.contains(self.decl_span)
886                    {
887                        self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span));
888                    }
889                    hir::intravisit::walk_stmt(self, ex);
890                }
891            }
892
893            let mut visitor = LetVisitor { decl_span, sugg_span: None };
894            visitor.visit_body(&body);
895            if let Some(span) = visitor.sugg_span {
896                self.suggest_assign_value(&mut err, moved_place, span);
897            }
898        }
899        err
900    }
901
902    fn suggest_assign_value(
903        &self,
904        err: &mut Diag<'_>,
905        moved_place: PlaceRef<'tcx>,
906        sugg_span: Span,
907    ) {
908        let ty = moved_place.ty(self.body, self.infcx.tcx).ty;
909        debug!("ty: {:?}, kind: {:?}", ty, ty.kind());
910
911        let Some(assign_value) = self.infcx.err_ctxt().ty_kind_suggestion(self.infcx.param_env, ty)
912        else {
913            return;
914        };
915
916        err.span_suggestion_verbose(
917            sugg_span.shrink_to_hi(),
918            "consider assigning a value",
919            format!(" = {assign_value}"),
920            Applicability::MaybeIncorrect,
921        );
922    }
923
924    /// In a move error that occurs on a call within a loop, we try to identify cases where cloning
925    /// the value would lead to a logic error. We infer these cases by seeing if the moved value is
926    /// part of the logic to break the loop, either through an explicit `break` or if the expression
927    /// is part of a `while let`.
928    fn suggest_hoisting_call_outside_loop(&self, err: &mut Diag<'_>, expr: &hir::Expr<'_>) -> bool {
929        let tcx = self.infcx.tcx;
930        let mut can_suggest_clone = true;
931
932        // If the moved value is a locally declared binding, we'll look upwards on the expression
933        // tree until the scope where it is defined, and no further, as suggesting to move the
934        // expression beyond that point would be illogical.
935        let local_hir_id = if let hir::ExprKind::Path(hir::QPath::Resolved(
936            _,
937            hir::Path { res: hir::def::Res::Local(local_hir_id), .. },
938        )) = expr.kind
939        {
940            Some(local_hir_id)
941        } else {
942            // This case would be if the moved value comes from an argument binding, we'll just
943            // look within the entire item, that's fine.
944            None
945        };
946
947        /// This will allow us to look for a specific `HirId`, in our case `local_hir_id` where the
948        /// binding was declared, within any other expression. We'll use it to search for the
949        /// binding declaration within every scope we inspect.
950        struct Finder {
951            hir_id: hir::HirId,
952        }
953        impl<'hir> Visitor<'hir> for Finder {
954            type Result = ControlFlow<()>;
955            fn visit_pat(&mut self, pat: &'hir hir::Pat<'hir>) -> Self::Result {
956                if pat.hir_id == self.hir_id {
957                    return ControlFlow::Break(());
958                }
959                hir::intravisit::walk_pat(self, pat)
960            }
961            fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) -> Self::Result {
962                if ex.hir_id == self.hir_id {
963                    return ControlFlow::Break(());
964                }
965                hir::intravisit::walk_expr(self, ex)
966            }
967        }
968        // The immediate HIR parent of the moved expression. We'll look for it to be a call.
969        let mut parent = None;
970        // The top-most loop where the moved expression could be moved to a new binding.
971        let mut outer_most_loop: Option<&hir::Expr<'_>> = None;
972        for (_, node) in tcx.hir().parent_iter(expr.hir_id) {
973            let e = match node {
974                hir::Node::Expr(e) => e,
975                hir::Node::LetStmt(hir::LetStmt { els: Some(els), .. }) => {
976                    let mut finder = BreakFinder { found_breaks: vec![], found_continues: vec![] };
977                    finder.visit_block(els);
978                    if !finder.found_breaks.is_empty() {
979                        // Don't suggest clone as it could be will likely end in an infinite
980                        // loop.
981                        // let Some(_) = foo(non_copy.clone()) else { break; }
982                        // ---                       ^^^^^^^^         -----
983                        can_suggest_clone = false;
984                    }
985                    continue;
986                }
987                _ => continue,
988            };
989            if let Some(&hir_id) = local_hir_id {
990                if (Finder { hir_id }).visit_expr(e).is_break() {
991                    // The current scope includes the declaration of the binding we're accessing, we
992                    // can't look up any further for loops.
993                    break;
994                }
995            }
996            if parent.is_none() {
997                parent = Some(e);
998            }
999            match e.kind {
1000                hir::ExprKind::Let(_) => {
1001                    match tcx.parent_hir_node(e.hir_id) {
1002                        hir::Node::Expr(hir::Expr {
1003                            kind: hir::ExprKind::If(cond, ..), ..
1004                        }) => {
1005                            if (Finder { hir_id: expr.hir_id }).visit_expr(cond).is_break() {
1006                                // The expression where the move error happened is in a `while let`
1007                                // condition Don't suggest clone as it will likely end in an
1008                                // infinite loop.
1009                                // while let Some(_) = foo(non_copy.clone()) { }
1010                                // ---------                       ^^^^^^^^
1011                                can_suggest_clone = false;
1012                            }
1013                        }
1014                        _ => {}
1015                    }
1016                }
1017                hir::ExprKind::Loop(..) => {
1018                    outer_most_loop = Some(e);
1019                }
1020                _ => {}
1021            }
1022        }
1023        let loop_count: usize = tcx
1024            .hir()
1025            .parent_iter(expr.hir_id)
1026            .map(|(_, node)| match node {
1027                hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Loop(..), .. }) => 1,
1028                _ => 0,
1029            })
1030            .sum();
1031
1032        let sm = tcx.sess.source_map();
1033        if let Some(in_loop) = outer_most_loop {
1034            let mut finder = BreakFinder { found_breaks: vec![], found_continues: vec![] };
1035            finder.visit_expr(in_loop);
1036            // All of the spans for `break` and `continue` expressions.
1037            let spans = finder
1038                .found_breaks
1039                .iter()
1040                .chain(finder.found_continues.iter())
1041                .map(|(_, span)| *span)
1042                .filter(|span| {
1043                    !matches!(
1044                        span.desugaring_kind(),
1045                        Some(DesugaringKind::ForLoop | DesugaringKind::WhileLoop)
1046                    )
1047                })
1048                .collect::<Vec<Span>>();
1049            // All of the spans for the loops above the expression with the move error.
1050            let loop_spans: Vec<_> = tcx
1051                .hir()
1052                .parent_iter(expr.hir_id)
1053                .filter_map(|(_, node)| match node {
1054                    hir::Node::Expr(hir::Expr { span, kind: hir::ExprKind::Loop(..), .. }) => {
1055                        Some(*span)
1056                    }
1057                    _ => None,
1058                })
1059                .collect();
1060            // It is possible that a user written `break` or `continue` is in the wrong place. We
1061            // point them out at the user for them to make a determination. (#92531)
1062            if !spans.is_empty() && loop_count > 1 {
1063                // Getting fancy: if the spans of the loops *do not* overlap, we only use the line
1064                // number when referring to them. If there *are* overlaps (multiple loops on the
1065                // same line) then we use the more verbose span output (`file.rs:col:ll`).
1066                let mut lines: Vec<_> =
1067                    loop_spans.iter().map(|sp| sm.lookup_char_pos(sp.lo()).line).collect();
1068                lines.sort();
1069                lines.dedup();
1070                let fmt_span = |span: Span| {
1071                    if lines.len() == loop_spans.len() {
1072                        format!("line {}", sm.lookup_char_pos(span.lo()).line)
1073                    } else {
1074                        sm.span_to_diagnostic_string(span)
1075                    }
1076                };
1077                let mut spans: MultiSpan = spans.into();
1078                // Point at all the `continue`s and explicit `break`s in the relevant loops.
1079                for (desc, elements) in [
1080                    ("`break` exits", &finder.found_breaks),
1081                    ("`continue` advances", &finder.found_continues),
1082                ] {
1083                    for (destination, sp) in elements {
1084                        if let Ok(hir_id) = destination.target_id
1085                            && let hir::Node::Expr(expr) = tcx.hir().hir_node(hir_id)
1086                            && !matches!(
1087                                sp.desugaring_kind(),
1088                                Some(DesugaringKind::ForLoop | DesugaringKind::WhileLoop)
1089                            )
1090                        {
1091                            spans.push_span_label(
1092                                *sp,
1093                                format!("this {desc} the loop at {}", fmt_span(expr.span)),
1094                            );
1095                        }
1096                    }
1097                }
1098                // Point at all the loops that are between this move and the parent item.
1099                for span in loop_spans {
1100                    spans.push_span_label(sm.guess_head_span(span), "");
1101                }
1102
1103                // note: verify that your loop breaking logic is correct
1104                //   --> $DIR/nested-loop-moved-value-wrong-continue.rs:41:17
1105                //    |
1106                // 28 |     for foo in foos {
1107                //    |     ---------------
1108                // ...
1109                // 33 |         for bar in &bars {
1110                //    |         ----------------
1111                // ...
1112                // 41 |                 continue;
1113                //    |                 ^^^^^^^^ this `continue` advances the loop at line 33
1114                err.span_note(spans, "verify that your loop breaking logic is correct");
1115            }
1116            if let Some(parent) = parent
1117                && let hir::ExprKind::MethodCall(..) | hir::ExprKind::Call(..) = parent.kind
1118            {
1119                // FIXME: We could check that the call's *parent* takes `&mut val` to make the
1120                // suggestion more targeted to the `mk_iter(val).next()` case. Maybe do that only to
1121                // check for whether to suggest `let value` or `let mut value`.
1122
1123                let span = in_loop.span;
1124                if !finder.found_breaks.is_empty()
1125                    && let Ok(value) = sm.span_to_snippet(parent.span)
1126                {
1127                    // We know with high certainty that this move would affect the early return of a
1128                    // loop, so we suggest moving the expression with the move out of the loop.
1129                    let indent = if let Some(indent) = sm.indentation_before(span) {
1130                        format!("\n{indent}")
1131                    } else {
1132                        " ".to_string()
1133                    };
1134                    err.multipart_suggestion(
1135                        "consider moving the expression out of the loop so it is only moved once",
1136                        vec![
1137                            (span.shrink_to_lo(), format!("let mut value = {value};{indent}")),
1138                            (parent.span, "value".to_string()),
1139                        ],
1140                        Applicability::MaybeIncorrect,
1141                    );
1142                }
1143            }
1144        }
1145        can_suggest_clone
1146    }
1147
1148    /// We have `S { foo: val, ..base }`, and we suggest instead writing
1149    /// `S { foo: val, bar: base.bar.clone(), .. }` when valid.
1150    fn suggest_cloning_on_functional_record_update(
1151        &self,
1152        err: &mut Diag<'_>,
1153        ty: Ty<'tcx>,
1154        expr: &hir::Expr<'_>,
1155    ) {
1156        let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
1157        let hir::ExprKind::Struct(struct_qpath, fields, hir::StructTailExpr::Base(base)) =
1158            expr.kind
1159        else {
1160            return;
1161        };
1162        let hir::QPath::Resolved(_, path) = struct_qpath else { return };
1163        let hir::def::Res::Def(_, def_id) = path.res else { return };
1164        let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) else { return };
1165        let ty::Adt(def, args) = expr_ty.kind() else { return };
1166        let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = base.kind else { return };
1167        let (hir::def::Res::Local(_)
1168        | hir::def::Res::Def(
1169            DefKind::Const | DefKind::ConstParam | DefKind::Static { .. } | DefKind::AssocConst,
1170            _,
1171        )) = path.res
1172        else {
1173            return;
1174        };
1175        let Ok(base_str) = self.infcx.tcx.sess.source_map().span_to_snippet(base.span) else {
1176            return;
1177        };
1178
1179        // 1. look for the fields of type `ty`.
1180        // 2. check if they are clone and add them to suggestion
1181        // 3. check if there are any values left to `..` and remove it if not
1182        // 4. emit suggestion to clone the field directly as `bar: base.bar.clone()`
1183
1184        let mut final_field_count = fields.len();
1185        let Some(variant) = def.variants().iter().find(|variant| variant.def_id == def_id) else {
1186            // When we have an enum, look for the variant that corresponds to the variant the user
1187            // wrote.
1188            return;
1189        };
1190        let mut sugg = vec![];
1191        for field in &variant.fields {
1192            // In practice unless there are more than one field with the same type, we'll be
1193            // suggesting a single field at a type, because we don't aggregate multiple borrow
1194            // checker errors involving the functional record update syntax into a single one.
1195            let field_ty = field.ty(self.infcx.tcx, args);
1196            let ident = field.ident(self.infcx.tcx);
1197            if field_ty == ty && fields.iter().all(|field| field.ident.name != ident.name) {
1198                // Suggest adding field and cloning it.
1199                sugg.push(format!("{ident}: {base_str}.{ident}.clone()"));
1200                final_field_count += 1;
1201            }
1202        }
1203        let (span, sugg) = match fields {
1204            [.., last] => (
1205                if final_field_count == variant.fields.len() {
1206                    // We'll remove the `..base` as there aren't any fields left.
1207                    last.span.shrink_to_hi().with_hi(base.span.hi())
1208                } else {
1209                    last.span.shrink_to_hi()
1210                },
1211                format!(", {}", sugg.join(", ")),
1212            ),
1213            // Account for no fields in suggestion span.
1214            [] => (
1215                expr.span.with_lo(struct_qpath.span().hi()),
1216                if final_field_count == variant.fields.len() {
1217                    // We'll remove the `..base` as there aren't any fields left.
1218                    format!(" {{ {} }}", sugg.join(", "))
1219                } else {
1220                    format!(" {{ {}, ..{base_str} }}", sugg.join(", "))
1221                },
1222            ),
1223        };
1224        let prefix = if !self.implements_clone(ty) {
1225            let msg = format!("`{ty}` doesn't implement `Copy` or `Clone`");
1226            if let ty::Adt(def, _) = ty.kind() {
1227                err.span_note(self.infcx.tcx.def_span(def.did()), msg);
1228            } else {
1229                err.note(msg);
1230            }
1231            format!("if `{ty}` implemented `Clone`, you could ")
1232        } else {
1233            String::new()
1234        };
1235        let msg = format!(
1236            "{prefix}clone the value from the field instead of using the functional record update \
1237             syntax",
1238        );
1239        err.span_suggestion_verbose(span, msg, sugg, Applicability::MachineApplicable);
1240    }
1241
1242    pub(crate) fn suggest_cloning(
1243        &self,
1244        err: &mut Diag<'_>,
1245        ty: Ty<'tcx>,
1246        expr: &'tcx hir::Expr<'tcx>,
1247        use_spans: Option<UseSpans<'tcx>>,
1248    ) {
1249        if let hir::ExprKind::Struct(_, _, hir::StructTailExpr::Base(_)) = expr.kind {
1250            // We have `S { foo: val, ..base }`. In `check_aggregate_rvalue` we have a single
1251            // `Location` that covers both the `S { ... }` literal, all of its fields and the
1252            // `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr`
1253            //  will already be correct. Instead, we see if we can suggest writing.
1254            self.suggest_cloning_on_functional_record_update(err, ty, expr);
1255            return;
1256        }
1257
1258        if self.implements_clone(ty) {
1259            self.suggest_cloning_inner(err, ty, expr);
1260        } else if let ty::Adt(def, args) = ty.kind()
1261            && def.did().as_local().is_some()
1262            && def.variants().iter().all(|variant| {
1263                variant
1264                    .fields
1265                    .iter()
1266                    .all(|field| self.implements_clone(field.ty(self.infcx.tcx, args)))
1267            })
1268        {
1269            let ty_span = self.infcx.tcx.def_span(def.did());
1270            let mut span: MultiSpan = ty_span.into();
1271            span.push_span_label(ty_span, "consider implementing `Clone` for this type");
1272            span.push_span_label(expr.span, "you could clone this value");
1273            err.span_note(
1274                span,
1275                format!("if `{ty}` implemented `Clone`, you could clone the value"),
1276            );
1277        } else if let ty::Param(param) = ty.kind()
1278            && let Some(_clone_trait_def) = self.infcx.tcx.lang_items().clone_trait()
1279            && let generics = self.infcx.tcx.generics_of(self.mir_def_id())
1280            && let generic_param = generics.type_param(*param, self.infcx.tcx)
1281            && let param_span = self.infcx.tcx.def_span(generic_param.def_id)
1282            && if let Some(UseSpans::FnSelfUse { kind, .. }) = use_spans
1283                && let CallKind::FnCall { fn_trait_id, self_ty } = kind
1284                && let ty::Param(_) = self_ty.kind()
1285                && ty == self_ty
1286                && [
1287                    self.infcx.tcx.lang_items().fn_once_trait(),
1288                    self.infcx.tcx.lang_items().fn_mut_trait(),
1289                    self.infcx.tcx.lang_items().fn_trait(),
1290                ]
1291                .contains(&Some(fn_trait_id))
1292            {
1293                // Do not suggest `F: FnOnce() + Clone`.
1294                false
1295            } else {
1296                true
1297            }
1298        {
1299            let mut span: MultiSpan = param_span.into();
1300            span.push_span_label(
1301                param_span,
1302                "consider constraining this type parameter with `Clone`",
1303            );
1304            span.push_span_label(expr.span, "you could clone this value");
1305            err.span_help(
1306                span,
1307                format!("if `{ty}` implemented `Clone`, you could clone the value"),
1308            );
1309        }
1310    }
1311
1312    pub(crate) fn implements_clone(&self, ty: Ty<'tcx>) -> bool {
1313        let Some(clone_trait_def) = self.infcx.tcx.lang_items().clone_trait() else { return false };
1314        self.infcx
1315            .type_implements_trait(clone_trait_def, [ty], self.infcx.param_env)
1316            .must_apply_modulo_regions()
1317    }
1318
1319    /// Given an expression, check if it is a method call `foo.clone()`, where `foo` and
1320    /// `foo.clone()` both have the same type, returning the span for `.clone()` if so.
1321    pub(crate) fn clone_on_reference(&self, expr: &hir::Expr<'_>) -> Option<Span> {
1322        let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
1323        if let hir::ExprKind::MethodCall(segment, rcvr, args, span) = expr.kind
1324            && let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id)
1325            && let Some(rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id)
1326            && rcvr_ty == expr_ty
1327            && segment.ident.name == sym::clone
1328            && args.is_empty()
1329        {
1330            Some(span)
1331        } else {
1332            None
1333        }
1334    }
1335
1336    fn in_move_closure(&self, expr: &hir::Expr<'_>) -> bool {
1337        for (_, node) in self.infcx.tcx.hir().parent_iter(expr.hir_id) {
1338            if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) = node
1339                && let hir::CaptureBy::Value { .. } = closure.capture_clause
1340            {
1341                // `move || x.clone()` will not work. FIXME: suggest `let y = x.clone(); move || y`
1342                return true;
1343            }
1344        }
1345        false
1346    }
1347
1348    fn suggest_cloning_inner(
1349        &self,
1350        err: &mut Diag<'_>,
1351        ty: Ty<'tcx>,
1352        expr: &hir::Expr<'_>,
1353    ) -> bool {
1354        let tcx = self.infcx.tcx;
1355        if let Some(_) = self.clone_on_reference(expr) {
1356            // Avoid redundant clone suggestion already suggested in `explain_captures`.
1357            // See `tests/ui/moves/needs-clone-through-deref.rs`
1358            return false;
1359        }
1360        // We don't want to suggest `.clone()` in a move closure, since the value has already been
1361        // captured.
1362        if self.in_move_closure(expr) {
1363            return false;
1364        }
1365        // We also don't want to suggest cloning a closure itself, since the value has already been
1366        // captured.
1367        if let hir::ExprKind::Closure(_) = expr.kind {
1368            return false;
1369        }
1370        // Try to find predicates on *generic params* that would allow copying `ty`
1371        let mut suggestion =
1372            if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
1373                format!(": {symbol}.clone()")
1374            } else {
1375                ".clone()".to_owned()
1376            };
1377        let mut sugg = Vec::with_capacity(2);
1378        let mut inner_expr = expr;
1379        let mut is_raw_ptr = false;
1380        let typeck_result = self.infcx.tcx.typeck(self.mir_def_id());
1381        // Remove uses of `&` and `*` when suggesting `.clone()`.
1382        while let hir::ExprKind::AddrOf(.., inner) | hir::ExprKind::Unary(hir::UnOp::Deref, inner) =
1383            &inner_expr.kind
1384        {
1385            if let hir::ExprKind::AddrOf(_, hir::Mutability::Mut, _) = inner_expr.kind {
1386                // We assume that `&mut` refs are desired for their side-effects, so cloning the
1387                // value wouldn't do what the user wanted.
1388                return false;
1389            }
1390            inner_expr = inner;
1391            if let Some(inner_type) = typeck_result.node_type_opt(inner.hir_id) {
1392                if matches!(inner_type.kind(), ty::RawPtr(..)) {
1393                    is_raw_ptr = true;
1394                    break;
1395                }
1396            }
1397        }
1398        // Cloning the raw pointer doesn't make sense in some cases and would cause a type mismatch
1399        // error. (see #126863)
1400        if inner_expr.span.lo() != expr.span.lo() && !is_raw_ptr {
1401            // Remove "(*" or "(&"
1402            sugg.push((expr.span.with_hi(inner_expr.span.lo()), String::new()));
1403        }
1404        // Check whether `expr` is surrounded by parentheses or not.
1405        let span = if inner_expr.span.hi() != expr.span.hi() {
1406            // Account for `(*x)` to suggest `x.clone()`.
1407            if is_raw_ptr {
1408                expr.span.shrink_to_hi()
1409            } else {
1410                // Remove the close parenthesis ")"
1411                expr.span.with_lo(inner_expr.span.hi())
1412            }
1413        } else {
1414            if is_raw_ptr {
1415                sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
1416                suggestion = ").clone()".to_string();
1417            }
1418            expr.span.shrink_to_hi()
1419        };
1420        sugg.push((span, suggestion));
1421        let msg = if let ty::Adt(def, _) = ty.kind()
1422            && [tcx.get_diagnostic_item(sym::Arc), tcx.get_diagnostic_item(sym::Rc)]
1423                .contains(&Some(def.did()))
1424        {
1425            "clone the value to increment its reference count"
1426        } else {
1427            "consider cloning the value if the performance cost is acceptable"
1428        };
1429        err.multipart_suggestion_verbose(msg, sugg, Applicability::MachineApplicable);
1430        true
1431    }
1432
1433    fn suggest_adding_bounds(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, def_id: DefId, span: Span) {
1434        let tcx = self.infcx.tcx;
1435        let generics = tcx.generics_of(self.mir_def_id());
1436
1437        let Some(hir_generics) = tcx
1438            .typeck_root_def_id(self.mir_def_id().to_def_id())
1439            .as_local()
1440            .and_then(|def_id| tcx.hir().get_generics(def_id))
1441        else {
1442            return;
1443        };
1444        // Try to find predicates on *generic params* that would allow copying `ty`
1445        let ocx = ObligationCtxt::new_with_diagnostics(self.infcx);
1446        let cause = ObligationCause::misc(span, self.mir_def_id());
1447
1448        ocx.register_bound(cause, self.infcx.param_env, ty, def_id);
1449        let errors = ocx.select_all_or_error();
1450
1451        // Only emit suggestion if all required predicates are on generic
1452        let predicates: Result<Vec<_>, _> = errors
1453            .into_iter()
1454            .map(|err| match err.obligation.predicate.kind().skip_binder() {
1455                PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
1456                    match *predicate.self_ty().kind() {
1457                        ty::Param(param_ty) => Ok((
1458                            generics.type_param(param_ty, tcx),
1459                            predicate.trait_ref.print_trait_sugared().to_string(),
1460                            Some(predicate.trait_ref.def_id),
1461                        )),
1462                        _ => Err(()),
1463                    }
1464                }
1465                _ => Err(()),
1466            })
1467            .collect();
1468
1469        if let Ok(predicates) = predicates {
1470            suggest_constraining_type_params(
1471                tcx,
1472                hir_generics,
1473                err,
1474                predicates.iter().map(|(param, constraint, def_id)| {
1475                    (param.name.as_str(), &**constraint, *def_id)
1476                }),
1477                None,
1478            );
1479        }
1480    }
1481
1482    pub(crate) fn report_move_out_while_borrowed(
1483        &mut self,
1484        location: Location,
1485        (place, span): (Place<'tcx>, Span),
1486        borrow: &BorrowData<'tcx>,
1487    ) {
1488        debug!(
1489            "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
1490            location, place, span, borrow
1491        );
1492        let value_msg = self.describe_any_place(place.as_ref());
1493        let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
1494
1495        let borrow_spans = self.retrieve_borrow_spans(borrow);
1496        let borrow_span = borrow_spans.args_or_use();
1497
1498        let move_spans = self.move_spans(place.as_ref(), location);
1499        let span = move_spans.args_or_use();
1500
1501        let mut err = self.cannot_move_when_borrowed(
1502            span,
1503            borrow_span,
1504            &self.describe_any_place(place.as_ref()),
1505            &borrow_msg,
1506            &value_msg,
1507        );
1508        self.note_due_to_edition_2024_opaque_capture_rules(borrow, &mut err);
1509
1510        borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow);
1511
1512        move_spans.var_subdiag(&mut err, None, |kind, var_span| {
1513            use crate::session_diagnostics::CaptureVarCause::*;
1514            match kind {
1515                hir::ClosureKind::Coroutine(_) => MoveUseInCoroutine { var_span },
1516                hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
1517                    MoveUseInClosure { var_span }
1518                }
1519            }
1520        });
1521
1522        self.explain_why_borrow_contains_point(location, borrow, None)
1523            .add_explanation_to_diagnostic(&self, &mut err, "", Some(borrow_span), None);
1524        self.suggest_copy_for_type_in_cloned_ref(&mut err, place);
1525        let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
1526        if let Some(expr) = self.find_expr(borrow_span) {
1527            // This is a borrow span, so we want to suggest cloning the referent.
1528            if let hir::ExprKind::AddrOf(_, _, borrowed_expr) = expr.kind
1529                && let Some(ty) = typeck_results.expr_ty_opt(borrowed_expr)
1530            {
1531                self.suggest_cloning(&mut err, ty, borrowed_expr, Some(move_spans));
1532            } else if typeck_results.expr_adjustments(expr).first().is_some_and(|adj| {
1533                matches!(
1534                    adj.kind,
1535                    ty::adjustment::Adjust::Borrow(ty::adjustment::AutoBorrow::Ref(
1536                        ty::adjustment::AutoBorrowMutability::Not
1537                            | ty::adjustment::AutoBorrowMutability::Mut {
1538                                allow_two_phase_borrow: ty::adjustment::AllowTwoPhase::No
1539                            }
1540                    ))
1541                )
1542            }) && let Some(ty) = typeck_results.expr_ty_opt(expr)
1543            {
1544                self.suggest_cloning(&mut err, ty, expr, Some(move_spans));
1545            }
1546        }
1547        self.buffer_error(err);
1548    }
1549
1550    pub(crate) fn report_use_while_mutably_borrowed(
1551        &self,
1552        location: Location,
1553        (place, _span): (Place<'tcx>, Span),
1554        borrow: &BorrowData<'tcx>,
1555    ) -> Diag<'infcx> {
1556        let borrow_spans = self.retrieve_borrow_spans(borrow);
1557        let borrow_span = borrow_spans.args_or_use();
1558
1559        // Conflicting borrows are reported separately, so only check for move
1560        // captures.
1561        let use_spans = self.move_spans(place.as_ref(), location);
1562        let span = use_spans.var_or_use();
1563
1564        // If the attempted use is in a closure then we do not care about the path span of the
1565        // place we are currently trying to use we call `var_span_label` on `borrow_spans` to
1566        // annotate if the existing borrow was in a closure.
1567        let mut err = self.cannot_use_when_mutably_borrowed(
1568            span,
1569            &self.describe_any_place(place.as_ref()),
1570            borrow_span,
1571            &self.describe_any_place(borrow.borrowed_place.as_ref()),
1572        );
1573        self.note_due_to_edition_2024_opaque_capture_rules(borrow, &mut err);
1574
1575        borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| {
1576            use crate::session_diagnostics::CaptureVarCause::*;
1577            let place = &borrow.borrowed_place;
1578            let desc_place = self.describe_any_place(place.as_ref());
1579            match kind {
1580                hir::ClosureKind::Coroutine(_) => {
1581                    BorrowUsePlaceCoroutine { place: desc_place, var_span, is_single_var: true }
1582                }
1583                hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
1584                    BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: true }
1585                }
1586            }
1587        });
1588
1589        self.explain_why_borrow_contains_point(location, borrow, None)
1590            .add_explanation_to_diagnostic(&self, &mut err, "", None, None);
1591        err
1592    }
1593
1594    pub(crate) fn report_conflicting_borrow(
1595        &self,
1596        location: Location,
1597        (place, span): (Place<'tcx>, Span),
1598        gen_borrow_kind: BorrowKind,
1599        issued_borrow: &BorrowData<'tcx>,
1600    ) -> Diag<'infcx> {
1601        let issued_spans = self.retrieve_borrow_spans(issued_borrow);
1602        let issued_span = issued_spans.args_or_use();
1603
1604        let borrow_spans = self.borrow_spans(span, location);
1605        let span = borrow_spans.args_or_use();
1606
1607        let container_name = if issued_spans.for_coroutine() || borrow_spans.for_coroutine() {
1608            "coroutine"
1609        } else {
1610            "closure"
1611        };
1612
1613        let (desc_place, msg_place, msg_borrow, union_type_name) =
1614            self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
1615
1616        let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
1617        let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
1618
1619        // FIXME: supply non-"" `opt_via` when appropriate
1620        let first_borrow_desc;
1621        let mut err = match (gen_borrow_kind, issued_borrow.kind) {
1622            (
1623                BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),
1624                BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },
1625            ) => {
1626                first_borrow_desc = "mutable ";
1627                let mut err = self.cannot_reborrow_already_borrowed(
1628                    span,
1629                    &desc_place,
1630                    &msg_place,
1631                    "immutable",
1632                    issued_span,
1633                    "it",
1634                    "mutable",
1635                    &msg_borrow,
1636                    None,
1637                );
1638                self.suggest_slice_method_if_applicable(
1639                    &mut err,
1640                    place,
1641                    issued_borrow.borrowed_place,
1642                    span,
1643                    issued_span,
1644                );
1645                err
1646            }
1647            (
1648                BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },
1649                BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),
1650            ) => {
1651                first_borrow_desc = "immutable ";
1652                let mut err = self.cannot_reborrow_already_borrowed(
1653                    span,
1654                    &desc_place,
1655                    &msg_place,
1656                    "mutable",
1657                    issued_span,
1658                    "it",
1659                    "immutable",
1660                    &msg_borrow,
1661                    None,
1662                );
1663                self.suggest_slice_method_if_applicable(
1664                    &mut err,
1665                    place,
1666                    issued_borrow.borrowed_place,
1667                    span,
1668                    issued_span,
1669                );
1670                self.suggest_binding_for_closure_capture_self(&mut err, &issued_spans);
1671                self.suggest_using_closure_argument_instead_of_capture(
1672                    &mut err,
1673                    issued_borrow.borrowed_place,
1674                    &issued_spans,
1675                );
1676                err
1677            }
1678
1679            (
1680                BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },
1681                BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },
1682            ) => {
1683                first_borrow_desc = "first ";
1684                let mut err = self.cannot_mutably_borrow_multiply(
1685                    span,
1686                    &desc_place,
1687                    &msg_place,
1688                    issued_span,
1689                    &msg_borrow,
1690                    None,
1691                );
1692                self.suggest_slice_method_if_applicable(
1693                    &mut err,
1694                    place,
1695                    issued_borrow.borrowed_place,
1696                    span,
1697                    issued_span,
1698                );
1699                self.suggest_using_closure_argument_instead_of_capture(
1700                    &mut err,
1701                    issued_borrow.borrowed_place,
1702                    &issued_spans,
1703                );
1704                self.explain_iterator_advancement_in_for_loop_if_applicable(
1705                    &mut err,
1706                    span,
1707                    &issued_spans,
1708                );
1709                err
1710            }
1711
1712            (
1713                BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture },
1714                BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture },
1715            ) => {
1716                first_borrow_desc = "first ";
1717                self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
1718            }
1719
1720            (BorrowKind::Mut { .. }, BorrowKind::Fake(FakeBorrowKind::Shallow)) => {
1721                if let Some(immutable_section_description) =
1722                    self.classify_immutable_section(issued_borrow.assigned_place)
1723                {
1724                    let mut err = self.cannot_mutate_in_immutable_section(
1725                        span,
1726                        issued_span,
1727                        &desc_place,
1728                        immutable_section_description,
1729                        "mutably borrow",
1730                    );
1731                    borrow_spans.var_subdiag(
1732                        &mut err,
1733                        Some(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }),
1734                        |kind, var_span| {
1735                            use crate::session_diagnostics::CaptureVarCause::*;
1736                            match kind {
1737                                hir::ClosureKind::Coroutine(_) => BorrowUsePlaceCoroutine {
1738                                    place: desc_place,
1739                                    var_span,
1740                                    is_single_var: true,
1741                                },
1742                                hir::ClosureKind::Closure
1743                                | hir::ClosureKind::CoroutineClosure(_) => BorrowUsePlaceClosure {
1744                                    place: desc_place,
1745                                    var_span,
1746                                    is_single_var: true,
1747                                },
1748                            }
1749                        },
1750                    );
1751                    return err;
1752                } else {
1753                    first_borrow_desc = "immutable ";
1754                    self.cannot_reborrow_already_borrowed(
1755                        span,
1756                        &desc_place,
1757                        &msg_place,
1758                        "mutable",
1759                        issued_span,
1760                        "it",
1761                        "immutable",
1762                        &msg_borrow,
1763                        None,
1764                    )
1765                }
1766            }
1767
1768            (BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }, _) => {
1769                first_borrow_desc = "first ";
1770                self.cannot_uniquely_borrow_by_one_closure(
1771                    span,
1772                    container_name,
1773                    &desc_place,
1774                    "",
1775                    issued_span,
1776                    "it",
1777                    "",
1778                    None,
1779                )
1780            }
1781
1782            (
1783                BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),
1784                BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture },
1785            ) => {
1786                first_borrow_desc = "first ";
1787                self.cannot_reborrow_already_uniquely_borrowed(
1788                    span,
1789                    container_name,
1790                    &desc_place,
1791                    "",
1792                    "immutable",
1793                    issued_span,
1794                    "",
1795                    None,
1796                    second_borrow_desc,
1797                )
1798            }
1799
1800            (BorrowKind::Mut { .. }, BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }) => {
1801                first_borrow_desc = "first ";
1802                self.cannot_reborrow_already_uniquely_borrowed(
1803                    span,
1804                    container_name,
1805                    &desc_place,
1806                    "",
1807                    "mutable",
1808                    issued_span,
1809                    "",
1810                    None,
1811                    second_borrow_desc,
1812                )
1813            }
1814
1815            (
1816                BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),
1817                BorrowKind::Shared | BorrowKind::Fake(_),
1818            )
1819            | (
1820                BorrowKind::Fake(FakeBorrowKind::Shallow),
1821                BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake(_),
1822            ) => {
1823                unreachable!()
1824            }
1825        };
1826        self.note_due_to_edition_2024_opaque_capture_rules(issued_borrow, &mut err);
1827
1828        if issued_spans == borrow_spans {
1829            borrow_spans.var_subdiag(&mut err, Some(gen_borrow_kind), |kind, var_span| {
1830                use crate::session_diagnostics::CaptureVarCause::*;
1831                match kind {
1832                    hir::ClosureKind::Coroutine(_) => BorrowUsePlaceCoroutine {
1833                        place: desc_place,
1834                        var_span,
1835                        is_single_var: false,
1836                    },
1837                    hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
1838                        BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: false }
1839                    }
1840                }
1841            });
1842        } else {
1843            issued_spans.var_subdiag(&mut err, Some(issued_borrow.kind), |kind, var_span| {
1844                use crate::session_diagnostics::CaptureVarCause::*;
1845                let borrow_place = &issued_borrow.borrowed_place;
1846                let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
1847                match kind {
1848                    hir::ClosureKind::Coroutine(_) => {
1849                        FirstBorrowUsePlaceCoroutine { place: borrow_place_desc, var_span }
1850                    }
1851                    hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
1852                        FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span }
1853                    }
1854                }
1855            });
1856
1857            borrow_spans.var_subdiag(&mut err, Some(gen_borrow_kind), |kind, var_span| {
1858                use crate::session_diagnostics::CaptureVarCause::*;
1859                match kind {
1860                    hir::ClosureKind::Coroutine(_) => {
1861                        SecondBorrowUsePlaceCoroutine { place: desc_place, var_span }
1862                    }
1863                    hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
1864                        SecondBorrowUsePlaceClosure { place: desc_place, var_span }
1865                    }
1866                }
1867            });
1868        }
1869
1870        if union_type_name != "" {
1871            err.note(format!(
1872                "{msg_place} is a field of the union `{union_type_name}`, so it overlaps the field {msg_borrow}",
1873            ));
1874        }
1875
1876        explanation.add_explanation_to_diagnostic(
1877            &self,
1878            &mut err,
1879            first_borrow_desc,
1880            None,
1881            Some((issued_span, span)),
1882        );
1883
1884        self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation);
1885        self.suggest_copy_for_type_in_cloned_ref(&mut err, place);
1886
1887        err
1888    }
1889
1890    fn suggest_copy_for_type_in_cloned_ref(&self, err: &mut Diag<'infcx>, place: Place<'tcx>) {
1891        let tcx = self.infcx.tcx;
1892        let hir = tcx.hir();
1893        let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return };
1894
1895        struct FindUselessClone<'tcx> {
1896            tcx: TyCtxt<'tcx>,
1897            typeck_results: &'tcx ty::TypeckResults<'tcx>,
1898            clones: Vec<&'tcx hir::Expr<'tcx>>,
1899        }
1900        impl<'tcx> FindUselessClone<'tcx> {
1901            fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
1902                Self { tcx, typeck_results: tcx.typeck(def_id), clones: vec![] }
1903            }
1904        }
1905        impl<'tcx> Visitor<'tcx> for FindUselessClone<'tcx> {
1906            fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
1907                if let hir::ExprKind::MethodCall(..) = ex.kind
1908                    && let Some(method_def_id) =
1909                        self.typeck_results.type_dependent_def_id(ex.hir_id)
1910                    && self.tcx.is_lang_item(self.tcx.parent(method_def_id), LangItem::Clone)
1911                {
1912                    self.clones.push(ex);
1913                }
1914                hir::intravisit::walk_expr(self, ex);
1915            }
1916        }
1917
1918        let mut expr_finder = FindUselessClone::new(tcx, self.mir_def_id());
1919
1920        let body = hir.body(body_id).value;
1921        expr_finder.visit_expr(body);
1922
1923        struct Holds<'tcx> {
1924            ty: Ty<'tcx>,
1925        }
1926
1927        impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Holds<'tcx> {
1928            type Result = std::ops::ControlFlow<()>;
1929
1930            fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
1931                if t == self.ty {
1932                    return ControlFlow::Break(());
1933                }
1934                t.super_visit_with(self)
1935            }
1936        }
1937
1938        let mut types_to_constrain = FxIndexSet::default();
1939
1940        let local_ty = self.body.local_decls[place.local].ty;
1941        let typeck_results = tcx.typeck(self.mir_def_id());
1942        let clone = tcx.require_lang_item(LangItem::Clone, Some(body.span));
1943        for expr in expr_finder.clones {
1944            if let hir::ExprKind::MethodCall(_, rcvr, _, span) = expr.kind
1945                && let Some(rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id)
1946                && let Some(ty) = typeck_results.node_type_opt(expr.hir_id)
1947                && rcvr_ty == ty
1948                && let ty::Ref(_, inner, _) = rcvr_ty.kind()
1949                && let inner = inner.peel_refs()
1950                && (Holds { ty: inner }).visit_ty(local_ty).is_break()
1951                && let None =
1952                    self.infcx.type_implements_trait_shallow(clone, inner, self.infcx.param_env)
1953            {
1954                err.span_label(
1955                    span,
1956                    format!(
1957                        "this call doesn't do anything, the result is still `{rcvr_ty}` \
1958                             because `{inner}` doesn't implement `Clone`",
1959                    ),
1960                );
1961                types_to_constrain.insert(inner);
1962            }
1963        }
1964        for ty in types_to_constrain {
1965            self.suggest_adding_bounds_or_derive(err, ty, clone, body.span);
1966        }
1967    }
1968
1969    pub(crate) fn suggest_adding_bounds_or_derive(
1970        &self,
1971        err: &mut Diag<'_>,
1972        ty: Ty<'tcx>,
1973        trait_def_id: DefId,
1974        span: Span,
1975    ) {
1976        self.suggest_adding_bounds(err, ty, trait_def_id, span);
1977        if let ty::Adt(..) = ty.kind() {
1978            // The type doesn't implement the trait.
1979            let trait_ref =
1980                ty::Binder::dummy(ty::TraitRef::new(self.infcx.tcx, trait_def_id, [ty]));
1981            let obligation = Obligation::new(
1982                self.infcx.tcx,
1983                ObligationCause::dummy(),
1984                self.infcx.param_env,
1985                trait_ref,
1986            );
1987            self.infcx.err_ctxt().suggest_derive(
1988                &obligation,
1989                err,
1990                trait_ref.upcast(self.infcx.tcx),
1991            );
1992        }
1993    }
1994
1995    #[instrument(level = "debug", skip(self, err))]
1996    fn suggest_using_local_if_applicable(
1997        &self,
1998        err: &mut Diag<'_>,
1999        location: Location,
2000        issued_borrow: &BorrowData<'tcx>,
2001        explanation: BorrowExplanation<'tcx>,
2002    ) {
2003        let used_in_call = matches!(
2004            explanation,
2005            BorrowExplanation::UsedLater(
2006                _,
2007                LaterUseKind::Call | LaterUseKind::Other,
2008                _call_span,
2009                _
2010            )
2011        );
2012        if !used_in_call {
2013            debug!("not later used in call");
2014            return;
2015        }
2016        if matches!(
2017            self.body.local_decls[issued_borrow.borrowed_place.local].local_info(),
2018            LocalInfo::IfThenRescopeTemp { .. }
2019        ) {
2020            // A better suggestion will be issued by the `if_let_rescope` lint
2021            return;
2022        }
2023
2024        let use_span = if let BorrowExplanation::UsedLater(_, LaterUseKind::Other, use_span, _) =
2025            explanation
2026        {
2027            Some(use_span)
2028        } else {
2029            None
2030        };
2031
2032        let outer_call_loc =
2033            if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
2034                loc
2035            } else {
2036                issued_borrow.reserve_location
2037            };
2038        let outer_call_stmt = self.body.stmt_at(outer_call_loc);
2039
2040        let inner_param_location = location;
2041        let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
2042            debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
2043            return;
2044        };
2045        let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
2046            debug!(
2047                "`inner_param_location` {:?} is not for an assignment: {:?}",
2048                inner_param_location, inner_param_stmt
2049            );
2050            return;
2051        };
2052        let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
2053        let Some((inner_call_loc, inner_call_term)) =
2054            inner_param_uses.into_iter().find_map(|loc| {
2055                let Either::Right(term) = self.body.stmt_at(loc) else {
2056                    debug!("{:?} is a statement, so it can't be a call", loc);
2057                    return None;
2058                };
2059                let TerminatorKind::Call { args, .. } = &term.kind else {
2060                    debug!("not a call: {:?}", term);
2061                    return None;
2062                };
2063                debug!("checking call args for uses of inner_param: {:?}", args);
2064                args.iter()
2065                    .map(|a| &a.node)
2066                    .any(|a| a == &Operand::Move(inner_param))
2067                    .then_some((loc, term))
2068            })
2069        else {
2070            debug!("no uses of inner_param found as a by-move call arg");
2071            return;
2072        };
2073        debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
2074
2075        let inner_call_span = inner_call_term.source_info.span;
2076        let outer_call_span = match use_span {
2077            Some(span) => span,
2078            None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span,
2079        };
2080        if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
2081            // FIXME: This stops the suggestion in some cases where it should be emitted.
2082            //        Fix the spans for those cases so it's emitted correctly.
2083            debug!(
2084                "outer span {:?} does not strictly contain inner span {:?}",
2085                outer_call_span, inner_call_span
2086            );
2087            return;
2088        }
2089        err.span_help(
2090            inner_call_span,
2091            format!(
2092                "try adding a local storing this{}...",
2093                if use_span.is_some() { "" } else { " argument" }
2094            ),
2095        );
2096        err.span_help(
2097            outer_call_span,
2098            format!(
2099                "...and then using that local {}",
2100                if use_span.is_some() { "here" } else { "as the argument to this call" }
2101            ),
2102        );
2103    }
2104
2105    pub(crate) fn find_expr(&self, span: Span) -> Option<&'tcx hir::Expr<'tcx>> {
2106        let tcx = self.infcx.tcx;
2107        let body_id = tcx.hir_node(self.mir_hir_id()).body_id()?;
2108        let mut expr_finder = FindExprBySpan::new(span, tcx);
2109        expr_finder.visit_expr(tcx.hir().body(body_id).value);
2110        expr_finder.result
2111    }
2112
2113    fn suggest_slice_method_if_applicable(
2114        &self,
2115        err: &mut Diag<'_>,
2116        place: Place<'tcx>,
2117        borrowed_place: Place<'tcx>,
2118        span: Span,
2119        issued_span: Span,
2120    ) {
2121        let tcx = self.infcx.tcx;
2122        let hir = tcx.hir();
2123
2124        let has_split_at_mut = |ty: Ty<'tcx>| {
2125            let ty = ty.peel_refs();
2126            match ty.kind() {
2127                ty::Array(..) | ty::Slice(..) => true,
2128                ty::Adt(def, _) if tcx.get_diagnostic_item(sym::Vec) == Some(def.did()) => true,
2129                _ if ty == tcx.types.str_ => true,
2130                _ => false,
2131            }
2132        };
2133        if let ([ProjectionElem::Index(index1)], [ProjectionElem::Index(index2)])
2134        | (
2135            [ProjectionElem::Deref, ProjectionElem::Index(index1)],
2136            [ProjectionElem::Deref, ProjectionElem::Index(index2)],
2137        ) = (&place.projection[..], &borrowed_place.projection[..])
2138        {
2139            let decl1 = &self.body.local_decls[*index1];
2140            let decl2 = &self.body.local_decls[*index2];
2141
2142            let mut note_default_suggestion = || {
2143                err.help(
2144                    "consider using `.split_at_mut(position)` or similar method to obtain two \
2145                     mutable non-overlapping sub-slices",
2146                )
2147                .help(
2148                    "consider using `.swap(index_1, index_2)` to swap elements at the specified \
2149                     indices",
2150                );
2151            };
2152
2153            let Some(index1) = self.find_expr(decl1.source_info.span) else {
2154                note_default_suggestion();
2155                return;
2156            };
2157
2158            let Some(index2) = self.find_expr(decl2.source_info.span) else {
2159                note_default_suggestion();
2160                return;
2161            };
2162
2163            let sm = tcx.sess.source_map();
2164
2165            let Ok(index1_str) = sm.span_to_snippet(index1.span) else {
2166                note_default_suggestion();
2167                return;
2168            };
2169
2170            let Ok(index2_str) = sm.span_to_snippet(index2.span) else {
2171                note_default_suggestion();
2172                return;
2173            };
2174
2175            let Some(object) = hir.parent_id_iter(index1.hir_id).find_map(|id| {
2176                if let hir::Node::Expr(expr) = tcx.hir_node(id)
2177                    && let hir::ExprKind::Index(obj, ..) = expr.kind
2178                {
2179                    Some(obj)
2180                } else {
2181                    None
2182                }
2183            }) else {
2184                note_default_suggestion();
2185                return;
2186            };
2187
2188            let Ok(obj_str) = sm.span_to_snippet(object.span) else {
2189                note_default_suggestion();
2190                return;
2191            };
2192
2193            let Some(swap_call) = hir.parent_id_iter(object.hir_id).find_map(|id| {
2194                if let hir::Node::Expr(call) = tcx.hir_node(id)
2195                    && let hir::ExprKind::Call(callee, ..) = call.kind
2196                    && let hir::ExprKind::Path(qpath) = callee.kind
2197                    && let hir::QPath::Resolved(None, res) = qpath
2198                    && let hir::def::Res::Def(_, did) = res.res
2199                    && tcx.is_diagnostic_item(sym::mem_swap, did)
2200                {
2201                    Some(call)
2202                } else {
2203                    None
2204                }
2205            }) else {
2206                let hir::Node::Expr(parent) = tcx.parent_hir_node(index1.hir_id) else { return };
2207                let hir::ExprKind::Index(_, idx1, _) = parent.kind else { return };
2208                let hir::Node::Expr(parent) = tcx.parent_hir_node(index2.hir_id) else { return };
2209                let hir::ExprKind::Index(_, idx2, _) = parent.kind else { return };
2210                if !idx1.equivalent_for_indexing(idx2) {
2211                    err.help("use `.split_at_mut(position)` to obtain two mutable non-overlapping sub-slices");
2212                }
2213                return;
2214            };
2215
2216            err.span_suggestion(
2217                swap_call.span,
2218                "use `.swap()` to swap elements at the specified indices instead",
2219                format!("{obj_str}.swap({index1_str}, {index2_str})"),
2220                Applicability::MachineApplicable,
2221            );
2222            return;
2223        }
2224        let place_ty = PlaceRef::ty(&place.as_ref(), self.body, tcx).ty;
2225        let borrowed_place_ty = PlaceRef::ty(&borrowed_place.as_ref(), self.body, tcx).ty;
2226        if !has_split_at_mut(place_ty) && !has_split_at_mut(borrowed_place_ty) {
2227            // Only mention `split_at_mut` on `Vec`, array and slices.
2228            return;
2229        }
2230        let Some(index1) = self.find_expr(span) else { return };
2231        let hir::Node::Expr(parent) = tcx.parent_hir_node(index1.hir_id) else { return };
2232        let hir::ExprKind::Index(_, idx1, _) = parent.kind else { return };
2233        let Some(index2) = self.find_expr(issued_span) else { return };
2234        let hir::Node::Expr(parent) = tcx.parent_hir_node(index2.hir_id) else { return };
2235        let hir::ExprKind::Index(_, idx2, _) = parent.kind else { return };
2236        if idx1.equivalent_for_indexing(idx2) {
2237            // `let a = &mut foo[0]` and `let b = &mut foo[0]`? Don't mention `split_at_mut`
2238            return;
2239        }
2240        err.help("use `.split_at_mut(position)` to obtain two mutable non-overlapping sub-slices");
2241    }
2242
2243    /// Suggest using `while let` for call `next` on an iterator in a for loop.
2244    ///
2245    /// For example:
2246    /// ```ignore (illustrative)
2247    ///
2248    /// for x in iter {
2249    ///     ...
2250    ///     iter.next()
2251    /// }
2252    /// ```
2253    pub(crate) fn explain_iterator_advancement_in_for_loop_if_applicable(
2254        &self,
2255        err: &mut Diag<'_>,
2256        span: Span,
2257        issued_spans: &UseSpans<'tcx>,
2258    ) {
2259        let issue_span = issued_spans.args_or_use();
2260        let tcx = self.infcx.tcx;
2261        let hir = tcx.hir();
2262
2263        let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return };
2264        let typeck_results = tcx.typeck(self.mir_def_id());
2265
2266        struct ExprFinder<'hir> {
2267            issue_span: Span,
2268            expr_span: Span,
2269            body_expr: Option<&'hir hir::Expr<'hir>>,
2270            loop_bind: Option<&'hir Ident>,
2271            loop_span: Option<Span>,
2272            head_span: Option<Span>,
2273            pat_span: Option<Span>,
2274            head: Option<&'hir hir::Expr<'hir>>,
2275        }
2276        impl<'hir> Visitor<'hir> for ExprFinder<'hir> {
2277            fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
2278                // Try to find
2279                // let result = match IntoIterator::into_iter(<head>) {
2280                //     mut iter => {
2281                //         [opt_ident]: loop {
2282                //             match Iterator::next(&mut iter) {
2283                //                 None => break,
2284                //                 Some(<pat>) => <body>,
2285                //             };
2286                //         }
2287                //     }
2288                // };
2289                // corresponding to the desugaring of a for loop `for <pat> in <head> { <body> }`.
2290                if let hir::ExprKind::Call(path, [arg]) = ex.kind
2291                    && let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IntoIterIntoIter, _)) =
2292                        path.kind
2293                    && arg.span.contains(self.issue_span)
2294                {
2295                    // Find `IntoIterator::into_iter(<head>)`
2296                    self.head = Some(arg);
2297                }
2298                if let hir::ExprKind::Loop(
2299                    hir::Block { stmts: [stmt, ..], .. },
2300                    _,
2301                    hir::LoopSource::ForLoop,
2302                    _,
2303                ) = ex.kind
2304                    && let hir::StmtKind::Expr(hir::Expr {
2305                        kind: hir::ExprKind::Match(call, [_, bind, ..], _),
2306                        span: head_span,
2307                        ..
2308                    }) = stmt.kind
2309                    && let hir::ExprKind::Call(path, _args) = call.kind
2310                    && let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _)) =
2311                        path.kind
2312                    && let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind
2313                    && let hir::QPath::LangItem(LangItem::OptionSome, pat_span) = path
2314                    && call.span.contains(self.issue_span)
2315                {
2316                    // Find `<pat>` and the span for the whole `for` loop.
2317                    if let PatField {
2318                        pat: hir::Pat { kind: hir::PatKind::Binding(_, _, ident, ..), .. },
2319                        ..
2320                    } = field
2321                    {
2322                        self.loop_bind = Some(ident);
2323                    }
2324                    self.head_span = Some(*head_span);
2325                    self.pat_span = Some(pat_span);
2326                    self.loop_span = Some(stmt.span);
2327                }
2328
2329                if let hir::ExprKind::MethodCall(body_call, recv, ..) = ex.kind
2330                    && body_call.ident.name == sym::next
2331                    && recv.span.source_equal(self.expr_span)
2332                {
2333                    self.body_expr = Some(ex);
2334                }
2335
2336                hir::intravisit::walk_expr(self, ex);
2337            }
2338        }
2339        let mut finder = ExprFinder {
2340            expr_span: span,
2341            issue_span,
2342            loop_bind: None,
2343            body_expr: None,
2344            head_span: None,
2345            loop_span: None,
2346            pat_span: None,
2347            head: None,
2348        };
2349        finder.visit_expr(hir.body(body_id).value);
2350
2351        if let Some(body_expr) = finder.body_expr
2352            && let Some(loop_span) = finder.loop_span
2353            && let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id)
2354            && let Some(trait_did) = tcx.trait_of_item(def_id)
2355            && tcx.is_diagnostic_item(sym::Iterator, trait_did)
2356        {
2357            if let Some(loop_bind) = finder.loop_bind {
2358                err.note(format!(
2359                    "a for loop advances the iterator for you, the result is stored in `{}`",
2360                    loop_bind.name,
2361                ));
2362            } else {
2363                err.note(
2364                    "a for loop advances the iterator for you, the result is stored in its pattern",
2365                );
2366            }
2367            let msg = "if you want to call `next` on a iterator within the loop, consider using \
2368                       `while let`";
2369            if let Some(head) = finder.head
2370                && let Some(pat_span) = finder.pat_span
2371                && loop_span.contains(body_expr.span)
2372                && loop_span.contains(head.span)
2373            {
2374                let sm = self.infcx.tcx.sess.source_map();
2375
2376                let mut sugg = vec![];
2377                if let hir::ExprKind::Path(hir::QPath::Resolved(None, _)) = head.kind {
2378                    // A bare path doesn't need a `let` assignment, it's already a simple
2379                    // binding access.
2380                    // As a new binding wasn't added, we don't need to modify the advancing call.
2381                    sugg.push((loop_span.with_hi(pat_span.lo()), "while let Some(".to_string()));
2382                    sugg.push((
2383                        pat_span.shrink_to_hi().with_hi(head.span.lo()),
2384                        ") = ".to_string(),
2385                    ));
2386                    sugg.push((head.span.shrink_to_hi(), ".next()".to_string()));
2387                } else {
2388                    // Needs a new a `let` binding.
2389                    let indent = if let Some(indent) = sm.indentation_before(loop_span) {
2390                        format!("\n{indent}")
2391                    } else {
2392                        " ".to_string()
2393                    };
2394                    let Ok(head_str) = sm.span_to_snippet(head.span) else {
2395                        err.help(msg);
2396                        return;
2397                    };
2398                    sugg.push((
2399                        loop_span.with_hi(pat_span.lo()),
2400                        format!("let iter = {head_str};{indent}while let Some("),
2401                    ));
2402                    sugg.push((
2403                        pat_span.shrink_to_hi().with_hi(head.span.hi()),
2404                        ") = iter.next()".to_string(),
2405                    ));
2406                    // As a new binding was added, we should change how the iterator is advanced to
2407                    // use the newly introduced binding.
2408                    if let hir::ExprKind::MethodCall(_, recv, ..) = body_expr.kind
2409                        && let hir::ExprKind::Path(hir::QPath::Resolved(None, ..)) = recv.kind
2410                    {
2411                        // As we introduced a `let iter = <head>;`, we need to change where the
2412                        // already borrowed value was accessed from `<recv>.next()` to
2413                        // `iter.next()`.
2414                        sugg.push((recv.span, "iter".to_string()));
2415                    }
2416                }
2417                err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect);
2418            } else {
2419                err.help(msg);
2420            }
2421        }
2422    }
2423
2424    /// Suggest using closure argument instead of capture.
2425    ///
2426    /// For example:
2427    /// ```ignore (illustrative)
2428    /// struct S;
2429    ///
2430    /// impl S {
2431    ///     fn call(&mut self, f: impl Fn(&mut Self)) { /* ... */ }
2432    ///     fn x(&self) {}
2433    /// }
2434    ///
2435    ///     let mut v = S;
2436    ///     v.call(|this: &mut S| v.x());
2437    /// //  ^\                    ^-- help: try using the closure argument: `this`
2438    /// //    *-- error: cannot borrow `v` as mutable because it is also borrowed as immutable
2439    /// ```
2440    fn suggest_using_closure_argument_instead_of_capture(
2441        &self,
2442        err: &mut Diag<'_>,
2443        borrowed_place: Place<'tcx>,
2444        issued_spans: &UseSpans<'tcx>,
2445    ) {
2446        let &UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return };
2447        let tcx = self.infcx.tcx;
2448        let hir = tcx.hir();
2449
2450        // Get the type of the local that we are trying to borrow
2451        let local = borrowed_place.local;
2452        let local_ty = self.body.local_decls[local].ty;
2453
2454        // Get the body the error happens in
2455        let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return };
2456
2457        let body_expr = hir.body(body_id).value;
2458
2459        struct ClosureFinder<'hir> {
2460            hir: rustc_middle::hir::map::Map<'hir>,
2461            borrow_span: Span,
2462            res: Option<(&'hir hir::Expr<'hir>, &'hir hir::Closure<'hir>)>,
2463            /// The path expression with the `borrow_span` span
2464            error_path: Option<(&'hir hir::Expr<'hir>, &'hir hir::QPath<'hir>)>,
2465        }
2466        impl<'hir> Visitor<'hir> for ClosureFinder<'hir> {
2467            type NestedFilter = OnlyBodies;
2468
2469            fn nested_visit_map(&mut self) -> Self::Map {
2470                self.hir
2471            }
2472
2473            fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
2474                if let hir::ExprKind::Path(qpath) = &ex.kind
2475                    && ex.span == self.borrow_span
2476                {
2477                    self.error_path = Some((ex, qpath));
2478                }
2479
2480                if let hir::ExprKind::Closure(closure) = ex.kind
2481                    && ex.span.contains(self.borrow_span)
2482                    // To support cases like `|| { v.call(|this| v.get()) }`
2483                    // FIXME: actually support such cases (need to figure out how to move from the
2484                    // capture place to original local).
2485                    && self.res.as_ref().is_none_or(|(prev_res, _)| prev_res.span.contains(ex.span))
2486                {
2487                    self.res = Some((ex, closure));
2488                }
2489
2490                hir::intravisit::walk_expr(self, ex);
2491            }
2492        }
2493
2494        // Find the closure that most tightly wraps `capture_kind_span`
2495        let mut finder =
2496            ClosureFinder { hir, borrow_span: capture_kind_span, res: None, error_path: None };
2497        finder.visit_expr(body_expr);
2498        let Some((closure_expr, closure)) = finder.res else { return };
2499
2500        let typeck_results = tcx.typeck(self.mir_def_id());
2501
2502        // Check that the parent of the closure is a method call,
2503        // with receiver matching with local's type (modulo refs)
2504        if let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_expr.hir_id) {
2505            if let hir::ExprKind::MethodCall(_, recv, ..) = parent.kind {
2506                let recv_ty = typeck_results.expr_ty(recv);
2507
2508                if recv_ty.peel_refs() != local_ty {
2509                    return;
2510                }
2511            }
2512        }
2513
2514        // Get closure's arguments
2515        let ty::Closure(_, args) = typeck_results.expr_ty(closure_expr).kind() else {
2516            /* hir::Closure can be a coroutine too */
2517            return;
2518        };
2519        let sig = args.as_closure().sig();
2520        let tupled_params = tcx.instantiate_bound_regions_with_erased(
2521            sig.inputs().iter().next().unwrap().map_bound(|&b| b),
2522        );
2523        let ty::Tuple(params) = tupled_params.kind() else { return };
2524
2525        // Find the first argument with a matching type, get its name
2526        let Some((_, this_name)) =
2527            params.iter().zip(hir.body_param_names(closure.body)).find(|(param_ty, name)| {
2528                // FIXME: also support deref for stuff like `Rc` arguments
2529                param_ty.peel_refs() == local_ty && name != &Ident::empty()
2530            })
2531        else {
2532            return;
2533        };
2534
2535        let spans;
2536        if let Some((_path_expr, qpath)) = finder.error_path
2537            && let hir::QPath::Resolved(_, path) = qpath
2538            && let hir::def::Res::Local(local_id) = path.res
2539        {
2540            // Find all references to the problematic variable in this closure body
2541
2542            struct VariableUseFinder {
2543                local_id: hir::HirId,
2544                spans: Vec<Span>,
2545            }
2546            impl<'hir> Visitor<'hir> for VariableUseFinder {
2547                fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
2548                    if let hir::ExprKind::Path(qpath) = &ex.kind
2549                        && let hir::QPath::Resolved(_, path) = qpath
2550                        && let hir::def::Res::Local(local_id) = path.res
2551                        && local_id == self.local_id
2552                    {
2553                        self.spans.push(ex.span);
2554                    }
2555
2556                    hir::intravisit::walk_expr(self, ex);
2557                }
2558            }
2559
2560            let mut finder = VariableUseFinder { local_id, spans: Vec::new() };
2561            finder.visit_expr(hir.body(closure.body).value);
2562
2563            spans = finder.spans;
2564        } else {
2565            spans = vec![capture_kind_span];
2566        }
2567
2568        err.multipart_suggestion(
2569            "try using the closure argument",
2570            iter::zip(spans, iter::repeat(this_name.to_string())).collect(),
2571            Applicability::MaybeIncorrect,
2572        );
2573    }
2574
2575    fn suggest_binding_for_closure_capture_self(
2576        &self,
2577        err: &mut Diag<'_>,
2578        issued_spans: &UseSpans<'tcx>,
2579    ) {
2580        let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return };
2581
2582        struct ExpressionFinder<'tcx> {
2583            capture_span: Span,
2584            closure_change_spans: Vec<Span>,
2585            closure_arg_span: Option<Span>,
2586            in_closure: bool,
2587            suggest_arg: String,
2588            tcx: TyCtxt<'tcx>,
2589            closure_local_id: Option<hir::HirId>,
2590            closure_call_changes: Vec<(Span, String)>,
2591        }
2592        impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
2593            fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
2594                if e.span.contains(self.capture_span)
2595                    && let hir::ExprKind::Closure(&hir::Closure {
2596                        kind: hir::ClosureKind::Closure,
2597                        body,
2598                        fn_arg_span,
2599                        fn_decl: hir::FnDecl { inputs, .. },
2600                        ..
2601                    }) = e.kind
2602                    && let hir::Node::Expr(body) = self.tcx.hir_node(body.hir_id)
2603                {
2604                    self.suggest_arg = "this: &Self".to_string();
2605                    if inputs.len() > 0 {
2606                        self.suggest_arg.push_str(", ");
2607                    }
2608                    self.in_closure = true;
2609                    self.closure_arg_span = fn_arg_span;
2610                    self.visit_expr(body);
2611                    self.in_closure = false;
2612                }
2613                if let hir::Expr { kind: hir::ExprKind::Path(path), .. } = e
2614                    && let hir::QPath::Resolved(_, hir::Path { segments: [seg], .. }) = path
2615                    && seg.ident.name == kw::SelfLower
2616                    && self.in_closure
2617                {
2618                    self.closure_change_spans.push(e.span);
2619                }
2620                hir::intravisit::walk_expr(self, e);
2621            }
2622
2623            fn visit_local(&mut self, local: &'hir hir::LetStmt<'hir>) {
2624                if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } =
2625                    local.pat
2626                    && let Some(init) = local.init
2627                    && let hir::Expr {
2628                        kind:
2629                            hir::ExprKind::Closure(&hir::Closure {
2630                                kind: hir::ClosureKind::Closure,
2631                                ..
2632                            }),
2633                        ..
2634                    } = init
2635                    && init.span.contains(self.capture_span)
2636                {
2637                    self.closure_local_id = Some(*hir_id);
2638                }
2639
2640                hir::intravisit::walk_local(self, local);
2641            }
2642
2643            fn visit_stmt(&mut self, s: &'hir hir::Stmt<'hir>) {
2644                if let hir::StmtKind::Semi(e) = s.kind
2645                    && let hir::ExprKind::Call(
2646                        hir::Expr { kind: hir::ExprKind::Path(path), .. },
2647                        args,
2648                    ) = e.kind
2649                    && let hir::QPath::Resolved(_, hir::Path { segments: [seg], .. }) = path
2650                    && let Res::Local(hir_id) = seg.res
2651                    && Some(hir_id) == self.closure_local_id
2652                {
2653                    let (span, arg_str) = if args.len() > 0 {
2654                        (args[0].span.shrink_to_lo(), "self, ".to_string())
2655                    } else {
2656                        let span = e.span.trim_start(seg.ident.span).unwrap_or(e.span);
2657                        (span, "(self)".to_string())
2658                    };
2659                    self.closure_call_changes.push((span, arg_str));
2660                }
2661                hir::intravisit::walk_stmt(self, s);
2662            }
2663        }
2664
2665        if let hir::Node::ImplItem(hir::ImplItem {
2666            kind: hir::ImplItemKind::Fn(_fn_sig, body_id),
2667            ..
2668        }) = self.infcx.tcx.hir_node(self.mir_hir_id())
2669            && let hir::Node::Expr(expr) = self.infcx.tcx.hir_node(body_id.hir_id)
2670        {
2671            let mut finder = ExpressionFinder {
2672                capture_span: *capture_kind_span,
2673                closure_change_spans: vec![],
2674                closure_arg_span: None,
2675                in_closure: false,
2676                suggest_arg: String::new(),
2677                closure_local_id: None,
2678                closure_call_changes: vec![],
2679                tcx: self.infcx.tcx,
2680            };
2681            finder.visit_expr(expr);
2682
2683            if finder.closure_change_spans.is_empty() || finder.closure_call_changes.is_empty() {
2684                return;
2685            }
2686
2687            let sm = self.infcx.tcx.sess.source_map();
2688            let sugg = finder
2689                .closure_arg_span
2690                .map(|span| (sm.next_point(span.shrink_to_lo()).shrink_to_hi(), finder.suggest_arg))
2691                .into_iter()
2692                .chain(
2693                    finder.closure_change_spans.into_iter().map(|span| (span, "this".to_string())),
2694                )
2695                .chain(finder.closure_call_changes)
2696                .collect();
2697
2698            err.multipart_suggestion_verbose(
2699                "try explicitly passing `&Self` into the closure as an argument",
2700                sugg,
2701                Applicability::MachineApplicable,
2702            );
2703        }
2704    }
2705
2706    /// Returns the description of the root place for a conflicting borrow and the full
2707    /// descriptions of the places that caused the conflict.
2708    ///
2709    /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
2710    /// attempted while a shared borrow is live, then this function will return:
2711    /// ```
2712    /// ("x", "", "")
2713    /// # ;
2714    /// ```
2715    /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
2716    /// a shared borrow of another field `x.y`, then this function will return:
2717    /// ```
2718    /// ("x", "x.z", "x.y")
2719    /// # ;
2720    /// ```
2721    /// In the more complex union case, where the union is a field of a struct, then if a mutable
2722    /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
2723    /// another field `x.u.y`, then this function will return:
2724    /// ```
2725    /// ("x.u", "x.u.z", "x.u.y")
2726    /// # ;
2727    /// ```
2728    /// This is used when creating error messages like below:
2729    ///
2730    /// ```text
2731    /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
2732    /// mutable (via `a.u.s.b`) [E0502]
2733    /// ```
2734    fn describe_place_for_conflicting_borrow(
2735        &self,
2736        first_borrowed_place: Place<'tcx>,
2737        second_borrowed_place: Place<'tcx>,
2738    ) -> (String, String, String, String) {
2739        // Define a small closure that we can use to check if the type of a place
2740        // is a union.
2741        let union_ty = |place_base| {
2742            // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
2743            // using a type annotation in the closure argument instead leads to a lifetime error.
2744            let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
2745            ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
2746        };
2747
2748        // Start with an empty tuple, so we can use the functions on `Option` to reduce some
2749        // code duplication (particularly around returning an empty description in the failure
2750        // case).
2751        Some(())
2752            .filter(|_| {
2753                // If we have a conflicting borrow of the same place, then we don't want to add
2754                // an extraneous "via x.y" to our diagnostics, so filter out this case.
2755                first_borrowed_place != second_borrowed_place
2756            })
2757            .and_then(|_| {
2758                // We're going to want to traverse the first borrowed place to see if we can find
2759                // field access to a union. If we find that, then we will keep the place of the
2760                // union being accessed and the field that was being accessed so we can check the
2761                // second borrowed place for the same union and an access to a different field.
2762                for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
2763                    match elem {
2764                        ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
2765                            return Some((place_base, field));
2766                        }
2767                        _ => {}
2768                    }
2769                }
2770                None
2771            })
2772            .and_then(|(target_base, target_field)| {
2773                // With the place of a union and a field access into it, we traverse the second
2774                // borrowed place and look for an access to a different field of the same union.
2775                for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
2776                    if let ProjectionElem::Field(field, _) = elem {
2777                        if let Some(union_ty) = union_ty(place_base) {
2778                            if field != target_field && place_base == target_base {
2779                                return Some((
2780                                    self.describe_any_place(place_base),
2781                                    self.describe_any_place(first_borrowed_place.as_ref()),
2782                                    self.describe_any_place(second_borrowed_place.as_ref()),
2783                                    union_ty.to_string(),
2784                                ));
2785                            }
2786                        }
2787                    }
2788                }
2789                None
2790            })
2791            .unwrap_or_else(|| {
2792                // If we didn't find a field access into a union, or both places match, then
2793                // only return the description of the first place.
2794                (
2795                    self.describe_any_place(first_borrowed_place.as_ref()),
2796                    "".to_string(),
2797                    "".to_string(),
2798                    "".to_string(),
2799                )
2800            })
2801    }
2802
2803    /// This means that some data referenced by `borrow` needs to live
2804    /// past the point where the StorageDeadOrDrop of `place` occurs.
2805    /// This is usually interpreted as meaning that `place` has too
2806    /// short a lifetime. (But sometimes it is more useful to report
2807    /// it as a more direct conflict between the execution of a
2808    /// `Drop::drop` with an aliasing borrow.)
2809    #[instrument(level = "debug", skip(self))]
2810    pub(crate) fn report_borrowed_value_does_not_live_long_enough(
2811        &mut self,
2812        location: Location,
2813        borrow: &BorrowData<'tcx>,
2814        place_span: (Place<'tcx>, Span),
2815        kind: Option<WriteKind>,
2816    ) {
2817        let drop_span = place_span.1;
2818        let borrowed_local = borrow.borrowed_place.local;
2819
2820        let borrow_spans = self.retrieve_borrow_spans(borrow);
2821        let borrow_span = borrow_spans.var_or_use_path_span();
2822
2823        let proper_span = self.body.local_decls[borrowed_local].source_info.span;
2824
2825        if self.access_place_error_reported.contains(&(Place::from(borrowed_local), borrow_span)) {
2826            debug!(
2827                "suppressing access_place error when borrow doesn't live long enough for {:?}",
2828                borrow_span
2829            );
2830            return;
2831        }
2832
2833        self.access_place_error_reported.insert((Place::from(borrowed_local), borrow_span));
2834
2835        if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
2836            let err =
2837                self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
2838            self.buffer_error(err);
2839            return;
2840        }
2841
2842        if let StorageDeadOrDrop::Destructor(dropped_ty) =
2843            self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
2844        {
2845            // If a borrow of path `B` conflicts with drop of `D` (and
2846            // we're not in the uninteresting case where `B` is a
2847            // prefix of `D`), then report this as a more interesting
2848            // destructor conflict.
2849            if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
2850                self.report_borrow_conflicts_with_destructor(
2851                    location, borrow, place_span, kind, dropped_ty,
2852                );
2853                return;
2854            }
2855        }
2856
2857        let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
2858
2859        let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
2860        let explanation = self.explain_why_borrow_contains_point(location, borrow, kind_place);
2861
2862        debug!(?place_desc, ?explanation);
2863
2864        let mut err = match (place_desc, explanation) {
2865            // If the outlives constraint comes from inside the closure,
2866            // for example:
2867            //
2868            // let x = 0;
2869            // let y = &x;
2870            // Box::new(|| y) as Box<Fn() -> &'static i32>
2871            //
2872            // then just use the normal error. The closure isn't escaping
2873            // and `move` will not help here.
2874            (
2875                Some(name),
2876                BorrowExplanation::UsedLater(_, LaterUseKind::ClosureCapture, var_or_use_span, _),
2877            ) if borrow_spans.for_coroutine() || borrow_spans.for_closure() => self
2878                .report_escaping_closure_capture(
2879                    borrow_spans,
2880                    borrow_span,
2881                    &RegionName {
2882                        name: self.synthesize_region_name(),
2883                        source: RegionNameSource::Static,
2884                    },
2885                    ConstraintCategory::CallArgument(None),
2886                    var_or_use_span,
2887                    &format!("`{name}`"),
2888                    "block",
2889                ),
2890            (
2891                Some(name),
2892                BorrowExplanation::MustBeValidFor {
2893                    category:
2894                        category @ (ConstraintCategory::Return(_)
2895                        | ConstraintCategory::CallArgument(_)
2896                        | ConstraintCategory::OpaqueType),
2897                    from_closure: false,
2898                    ref region_name,
2899                    span,
2900                    ..
2901                },
2902            ) if borrow_spans.for_coroutine() || borrow_spans.for_closure() => self
2903                .report_escaping_closure_capture(
2904                    borrow_spans,
2905                    borrow_span,
2906                    region_name,
2907                    category,
2908                    span,
2909                    &format!("`{name}`"),
2910                    "function",
2911                ),
2912            (
2913                name,
2914                BorrowExplanation::MustBeValidFor {
2915                    category: ConstraintCategory::Assignment,
2916                    from_closure: false,
2917                    region_name:
2918                        RegionName {
2919                            source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
2920                            ..
2921                        },
2922                    span,
2923                    ..
2924                },
2925            ) => self.report_escaping_data(borrow_span, &name, upvar_span, upvar_name, span),
2926            (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
2927                location,
2928                &name,
2929                borrow,
2930                drop_span,
2931                borrow_spans,
2932                explanation,
2933            ),
2934            (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
2935                location,
2936                borrow,
2937                drop_span,
2938                borrow_spans,
2939                proper_span,
2940                explanation,
2941            ),
2942        };
2943        self.note_due_to_edition_2024_opaque_capture_rules(borrow, &mut err);
2944
2945        self.buffer_error(err);
2946    }
2947
2948    fn report_local_value_does_not_live_long_enough(
2949        &self,
2950        location: Location,
2951        name: &str,
2952        borrow: &BorrowData<'tcx>,
2953        drop_span: Span,
2954        borrow_spans: UseSpans<'tcx>,
2955        explanation: BorrowExplanation<'tcx>,
2956    ) -> Diag<'infcx> {
2957        debug!(
2958            "report_local_value_does_not_live_long_enough(\
2959             {:?}, {:?}, {:?}, {:?}, {:?}\
2960             )",
2961            location, name, borrow, drop_span, borrow_spans
2962        );
2963
2964        let borrow_span = borrow_spans.var_or_use_path_span();
2965        if let BorrowExplanation::MustBeValidFor {
2966            category,
2967            span,
2968            ref opt_place_desc,
2969            from_closure: false,
2970            ..
2971        } = explanation
2972        {
2973            if let Err(diag) = self.try_report_cannot_return_reference_to_local(
2974                borrow,
2975                borrow_span,
2976                span,
2977                category,
2978                opt_place_desc.as_ref(),
2979            ) {
2980                return diag;
2981            }
2982        }
2983
2984        let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{name}`"));
2985
2986        if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
2987            let region_name = annotation.emit(self, &mut err);
2988
2989            err.span_label(
2990                borrow_span,
2991                format!("`{name}` would have to be valid for `{region_name}`..."),
2992            );
2993
2994            err.span_label(
2995                drop_span,
2996                format!(
2997                    "...but `{}` will be dropped here, when the {} returns",
2998                    name,
2999                    self.infcx
3000                        .tcx
3001                        .opt_item_name(self.mir_def_id().to_def_id())
3002                        .map(|name| format!("function `{name}`"))
3003                        .unwrap_or_else(|| {
3004                            match &self.infcx.tcx.def_kind(self.mir_def_id()) {
3005                                DefKind::Closure
3006                                    if self
3007                                        .infcx
3008                                        .tcx
3009                                        .is_coroutine(self.mir_def_id().to_def_id()) =>
3010                                {
3011                                    "enclosing coroutine"
3012                                }
3013                                DefKind::Closure => "enclosing closure",
3014                                kind => bug!("expected closure or coroutine, found {:?}", kind),
3015                            }
3016                            .to_string()
3017                        })
3018                ),
3019            );
3020
3021            err.note(
3022                "functions cannot return a borrow to data owned within the function's scope, \
3023                    functions can only return borrows to data passed as arguments",
3024            );
3025            err.note(
3026                "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
3027                    references-and-borrowing.html#dangling-references>",
3028            );
3029
3030            if let BorrowExplanation::MustBeValidFor { .. } = explanation {
3031            } else {
3032                explanation.add_explanation_to_diagnostic(&self, &mut err, "", None, None);
3033            }
3034        } else {
3035            err.span_label(borrow_span, "borrowed value does not live long enough");
3036            err.span_label(drop_span, format!("`{name}` dropped here while still borrowed"));
3037
3038            borrow_spans.args_subdiag(&mut err, |args_span| {
3039                crate::session_diagnostics::CaptureArgLabel::Capture {
3040                    is_within: borrow_spans.for_coroutine(),
3041                    args_span,
3042                }
3043            });
3044
3045            explanation.add_explanation_to_diagnostic(&self, &mut err, "", Some(borrow_span), None);
3046        }
3047
3048        err
3049    }
3050
3051    fn report_borrow_conflicts_with_destructor(
3052        &mut self,
3053        location: Location,
3054        borrow: &BorrowData<'tcx>,
3055        (place, drop_span): (Place<'tcx>, Span),
3056        kind: Option<WriteKind>,
3057        dropped_ty: Ty<'tcx>,
3058    ) {
3059        debug!(
3060            "report_borrow_conflicts_with_destructor(\
3061             {:?}, {:?}, ({:?}, {:?}), {:?}\
3062             )",
3063            location, borrow, place, drop_span, kind,
3064        );
3065
3066        let borrow_spans = self.retrieve_borrow_spans(borrow);
3067        let borrow_span = borrow_spans.var_or_use();
3068
3069        let mut err = self.cannot_borrow_across_destructor(borrow_span);
3070
3071        let what_was_dropped = match self.describe_place(place.as_ref()) {
3072            Some(name) => format!("`{name}`"),
3073            None => String::from("temporary value"),
3074        };
3075
3076        let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
3077            Some(borrowed) => format!(
3078                "here, drop of {what_was_dropped} needs exclusive access to `{borrowed}`, \
3079                 because the type `{dropped_ty}` implements the `Drop` trait"
3080            ),
3081            None => format!(
3082                "here is drop of {what_was_dropped}; whose type `{dropped_ty}` implements the `Drop` trait"
3083            ),
3084        };
3085        err.span_label(drop_span, label);
3086
3087        // Only give this note and suggestion if they could be relevant.
3088        let explanation =
3089            self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
3090        match explanation {
3091            BorrowExplanation::UsedLater { .. }
3092            | BorrowExplanation::UsedLaterWhenDropped { .. } => {
3093                err.note("consider using a `let` binding to create a longer lived value");
3094            }
3095            _ => {}
3096        }
3097
3098        explanation.add_explanation_to_diagnostic(&self, &mut err, "", None, None);
3099
3100        self.buffer_error(err);
3101    }
3102
3103    fn report_thread_local_value_does_not_live_long_enough(
3104        &self,
3105        drop_span: Span,
3106        borrow_span: Span,
3107    ) -> Diag<'infcx> {
3108        debug!(
3109            "report_thread_local_value_does_not_live_long_enough(\
3110             {:?}, {:?}\
3111             )",
3112            drop_span, borrow_span
3113        );
3114
3115        // `TerminatorKind::Return`'s span (the `drop_span` here) `lo` can be subtly wrong and point
3116        // at a single character after the end of the function. This is somehow relied upon in
3117        // existing diagnostics, and changing this in `rustc_mir_build` makes diagnostics worse in
3118        // general. We fix these here.
3119        let sm = self.infcx.tcx.sess.source_map();
3120        let end_of_function = if drop_span.is_empty()
3121            && let Ok(adjusted_span) = sm.span_extend_prev_while(drop_span, |c| c == '}')
3122        {
3123            adjusted_span
3124        } else {
3125            drop_span
3126        };
3127        self.thread_local_value_does_not_live_long_enough(borrow_span)
3128            .with_span_label(
3129                borrow_span,
3130                "thread-local variables cannot be borrowed beyond the end of the function",
3131            )
3132            .with_span_label(end_of_function, "end of enclosing function is here")
3133    }
3134
3135    #[instrument(level = "debug", skip(self))]
3136    fn report_temporary_value_does_not_live_long_enough(
3137        &self,
3138        location: Location,
3139        borrow: &BorrowData<'tcx>,
3140        drop_span: Span,
3141        borrow_spans: UseSpans<'tcx>,
3142        proper_span: Span,
3143        explanation: BorrowExplanation<'tcx>,
3144    ) -> Diag<'infcx> {
3145        if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
3146            explanation
3147        {
3148            if let Err(diag) = self.try_report_cannot_return_reference_to_local(
3149                borrow,
3150                proper_span,
3151                span,
3152                category,
3153                None,
3154            ) {
3155                return diag;
3156            }
3157        }
3158
3159        let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
3160        err.span_label(proper_span, "creates a temporary value which is freed while still in use");
3161        err.span_label(drop_span, "temporary value is freed at the end of this statement");
3162
3163        match explanation {
3164            BorrowExplanation::UsedLater(..)
3165            | BorrowExplanation::UsedLaterInLoop(..)
3166            | BorrowExplanation::UsedLaterWhenDropped { .. } => {
3167                // Only give this note and suggestion if it could be relevant.
3168                let sm = self.infcx.tcx.sess.source_map();
3169                let mut suggested = false;
3170                let msg = "consider using a `let` binding to create a longer lived value";
3171
3172                /// We check that there's a single level of block nesting to ensure always correct
3173                /// suggestions. If we don't, then we only provide a free-form message to avoid
3174                /// misleading users in cases like `tests/ui/nll/borrowed-temporary-error.rs`.
3175                /// We could expand the analysis to suggest hoising all of the relevant parts of
3176                /// the users' code to make the code compile, but that could be too much.
3177                /// We found the `prop_expr` by the way to check whether the expression is a
3178                /// `FormatArguments`, which is a special case since it's generated by the
3179                /// compiler.
3180                struct NestedStatementVisitor<'tcx> {
3181                    span: Span,
3182                    current: usize,
3183                    found: usize,
3184                    prop_expr: Option<&'tcx hir::Expr<'tcx>>,
3185                    call: Option<&'tcx hir::Expr<'tcx>>,
3186                }
3187
3188                impl<'tcx> Visitor<'tcx> for NestedStatementVisitor<'tcx> {
3189                    fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
3190                        self.current += 1;
3191                        walk_block(self, block);
3192                        self.current -= 1;
3193                    }
3194                    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
3195                        if let hir::ExprKind::MethodCall(_, rcvr, _, _) = expr.kind {
3196                            if self.span == rcvr.span.source_callsite() {
3197                                self.call = Some(expr);
3198                            }
3199                        }
3200                        if self.span == expr.span.source_callsite() {
3201                            self.found = self.current;
3202                            if self.prop_expr.is_none() {
3203                                self.prop_expr = Some(expr);
3204                            }
3205                        }
3206                        walk_expr(self, expr);
3207                    }
3208                }
3209                let source_info = self.body.source_info(location);
3210                let proper_span = proper_span.source_callsite();
3211                if let Some(scope) = self.body.source_scopes.get(source_info.scope)
3212                    && let ClearCrossCrate::Set(scope_data) = &scope.local_data
3213                    && let Some(id) = self.infcx.tcx.hir_node(scope_data.lint_root).body_id()
3214                    && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind
3215                {
3216                    for stmt in block.stmts {
3217                        let mut visitor = NestedStatementVisitor {
3218                            span: proper_span,
3219                            current: 0,
3220                            found: 0,
3221                            prop_expr: None,
3222                            call: None,
3223                        };
3224                        visitor.visit_stmt(stmt);
3225
3226                        let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
3227                        let expr_ty: Option<Ty<'_>> =
3228                            visitor.prop_expr.map(|expr| typeck_results.expr_ty(expr).peel_refs());
3229
3230                        let is_format_arguments_item = if let Some(expr_ty) = expr_ty
3231                            && let ty::Adt(adt, _) = expr_ty.kind()
3232                        {
3233                            self.infcx.tcx.is_lang_item(adt.did(), LangItem::FormatArguments)
3234                        } else {
3235                            false
3236                        };
3237
3238                        if visitor.found == 0
3239                            && stmt.span.contains(proper_span)
3240                            && let Some(p) = sm.span_to_margin(stmt.span)
3241                            && let Ok(s) = sm.span_to_snippet(proper_span)
3242                        {
3243                            if let Some(call) = visitor.call
3244                                && let hir::ExprKind::MethodCall(path, _, [], _) = call.kind
3245                                && path.ident.name == sym::iter
3246                                && let Some(ty) = expr_ty
3247                            {
3248                                err.span_suggestion_verbose(
3249                                    path.ident.span,
3250                                    format!(
3251                                        "consider consuming the `{ty}` when turning it into an \
3252                                         `Iterator`",
3253                                    ),
3254                                    "into_iter",
3255                                    Applicability::MaybeIncorrect,
3256                                );
3257                            }
3258                            if !is_format_arguments_item {
3259                                let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
3260                                err.multipart_suggestion_verbose(
3261                                    msg,
3262                                    vec![
3263                                        (stmt.span.shrink_to_lo(), addition),
3264                                        (proper_span, "binding".to_string()),
3265                                    ],
3266                                    Applicability::MaybeIncorrect,
3267                                );
3268                            } else {
3269                                err.note("the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used");
3270                                err.note("to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>");
3271                            }
3272                            suggested = true;
3273                            break;
3274                        }
3275                    }
3276                }
3277                if !suggested {
3278                    err.note(msg);
3279                }
3280            }
3281            _ => {}
3282        }
3283        explanation.add_explanation_to_diagnostic(&self, &mut err, "", None, None);
3284
3285        borrow_spans.args_subdiag(&mut err, |args_span| {
3286            crate::session_diagnostics::CaptureArgLabel::Capture {
3287                is_within: borrow_spans.for_coroutine(),
3288                args_span,
3289            }
3290        });
3291
3292        err
3293    }
3294
3295    fn try_report_cannot_return_reference_to_local(
3296        &self,
3297        borrow: &BorrowData<'tcx>,
3298        borrow_span: Span,
3299        return_span: Span,
3300        category: ConstraintCategory<'tcx>,
3301        opt_place_desc: Option<&String>,
3302    ) -> Result<(), Diag<'infcx>> {
3303        let return_kind = match category {
3304            ConstraintCategory::Return(_) => "return",
3305            ConstraintCategory::Yield => "yield",
3306            _ => return Ok(()),
3307        };
3308
3309        // FIXME use a better heuristic than Spans
3310        let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
3311            "reference to"
3312        } else {
3313            "value referencing"
3314        };
3315
3316        let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
3317            let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
3318                match self.body.local_kind(local) {
3319                    LocalKind::Temp if self.body.local_decls[local].is_user_variable() => {
3320                        "local variable "
3321                    }
3322                    LocalKind::Arg
3323                        if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
3324                    {
3325                        "variable captured by `move` "
3326                    }
3327                    LocalKind::Arg => "function parameter ",
3328                    LocalKind::ReturnPointer | LocalKind::Temp => {
3329                        bug!("temporary or return pointer with a name")
3330                    }
3331                }
3332            } else {
3333                "local data "
3334            };
3335            (format!("{local_kind}`{place_desc}`"), format!("`{place_desc}` is borrowed here"))
3336        } else {
3337            let local = borrow.borrowed_place.local;
3338            match self.body.local_kind(local) {
3339                LocalKind::Arg => (
3340                    "function parameter".to_string(),
3341                    "function parameter borrowed here".to_string(),
3342                ),
3343                LocalKind::Temp if self.body.local_decls[local].is_user_variable() => {
3344                    ("local binding".to_string(), "local binding introduced here".to_string())
3345                }
3346                LocalKind::ReturnPointer | LocalKind::Temp => {
3347                    ("temporary value".to_string(), "temporary value created here".to_string())
3348                }
3349            }
3350        };
3351
3352        let mut err = self.cannot_return_reference_to_local(
3353            return_span,
3354            return_kind,
3355            reference_desc,
3356            &place_desc,
3357        );
3358
3359        if return_span != borrow_span {
3360            err.span_label(borrow_span, note);
3361
3362            let tcx = self.infcx.tcx;
3363
3364            let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
3365
3366            // to avoid panics
3367            if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator)
3368                && self
3369                    .infcx
3370                    .type_implements_trait(iter_trait, [return_ty], self.infcx.param_env)
3371                    .must_apply_modulo_regions()
3372            {
3373                err.span_suggestion_hidden(
3374                    return_span.shrink_to_hi(),
3375                    "use `.collect()` to allocate the iterator",
3376                    ".collect::<Vec<_>>()",
3377                    Applicability::MaybeIncorrect,
3378                );
3379            }
3380        }
3381
3382        Err(err)
3383    }
3384
3385    #[instrument(level = "debug", skip(self))]
3386    fn report_escaping_closure_capture(
3387        &self,
3388        use_span: UseSpans<'tcx>,
3389        var_span: Span,
3390        fr_name: &RegionName,
3391        category: ConstraintCategory<'tcx>,
3392        constraint_span: Span,
3393        captured_var: &str,
3394        scope: &str,
3395    ) -> Diag<'infcx> {
3396        let tcx = self.infcx.tcx;
3397        let args_span = use_span.args_or_use();
3398
3399        let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
3400            Ok(string) => {
3401                let coro_prefix = if string.starts_with("async") {
3402                    // `async` is 5 chars long. Not using `.len()` to avoid the cast from `usize`
3403                    // to `u32`.
3404                    Some(5)
3405                } else if string.starts_with("gen") {
3406                    // `gen` is 3 chars long
3407                    Some(3)
3408                } else if string.starts_with("static") {
3409                    // `static` is 6 chars long
3410                    // This is used for `!Unpin` coroutines
3411                    Some(6)
3412                } else {
3413                    None
3414                };
3415                if let Some(n) = coro_prefix {
3416                    let pos = args_span.lo() + BytePos(n);
3417                    (args_span.with_lo(pos).with_hi(pos), " move")
3418                } else {
3419                    (args_span.shrink_to_lo(), "move ")
3420                }
3421            }
3422            Err(_) => (args_span, "move |<args>| <body>"),
3423        };
3424        let kind = match use_span.coroutine_kind() {
3425            Some(coroutine_kind) => match coroutine_kind {
3426                CoroutineKind::Desugared(CoroutineDesugaring::Gen, kind) => match kind {
3427                    CoroutineSource::Block => "gen block",
3428                    CoroutineSource::Closure => "gen closure",
3429                    CoroutineSource::Fn => {
3430                        bug!("gen block/closure expected, but gen function found.")
3431                    }
3432                },
3433                CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, kind) => match kind {
3434                    CoroutineSource::Block => "async gen block",
3435                    CoroutineSource::Closure => "async gen closure",
3436                    CoroutineSource::Fn => {
3437                        bug!("gen block/closure expected, but gen function found.")
3438                    }
3439                },
3440                CoroutineKind::Desugared(CoroutineDesugaring::Async, async_kind) => {
3441                    match async_kind {
3442                        CoroutineSource::Block => "async block",
3443                        CoroutineSource::Closure => "async closure",
3444                        CoroutineSource::Fn => {
3445                            bug!("async block/closure expected, but async function found.")
3446                        }
3447                    }
3448                }
3449                CoroutineKind::Coroutine(_) => "coroutine",
3450            },
3451            None => "closure",
3452        };
3453
3454        let mut err = self.cannot_capture_in_long_lived_closure(
3455            args_span,
3456            kind,
3457            captured_var,
3458            var_span,
3459            scope,
3460        );
3461        err.span_suggestion_verbose(
3462            sugg_span,
3463            format!(
3464                "to force the {kind} to take ownership of {captured_var} (and any \
3465                 other referenced variables), use the `move` keyword"
3466            ),
3467            suggestion,
3468            Applicability::MachineApplicable,
3469        );
3470
3471        match category {
3472            ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
3473                let msg = format!("{kind} is returned here");
3474                err.span_note(constraint_span, msg);
3475            }
3476            ConstraintCategory::CallArgument(_) => {
3477                fr_name.highlight_region_name(&mut err);
3478                if matches!(
3479                    use_span.coroutine_kind(),
3480                    Some(CoroutineKind::Desugared(CoroutineDesugaring::Async, _))
3481                ) {
3482                    err.note(
3483                        "async blocks are not executed immediately and must either take a \
3484                         reference or ownership of outside variables they use",
3485                    );
3486                } else {
3487                    let msg = format!("{scope} requires argument type to outlive `{fr_name}`");
3488                    err.span_note(constraint_span, msg);
3489                }
3490            }
3491            _ => bug!(
3492                "report_escaping_closure_capture called with unexpected constraint \
3493                 category: `{:?}`",
3494                category
3495            ),
3496        }
3497
3498        err
3499    }
3500
3501    fn report_escaping_data(
3502        &self,
3503        borrow_span: Span,
3504        name: &Option<String>,
3505        upvar_span: Span,
3506        upvar_name: Symbol,
3507        escape_span: Span,
3508    ) -> Diag<'infcx> {
3509        let tcx = self.infcx.tcx;
3510
3511        let escapes_from = tcx.def_descr(self.mir_def_id().to_def_id());
3512
3513        let mut err =
3514            borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
3515
3516        err.span_label(
3517            upvar_span,
3518            format!("`{upvar_name}` declared here, outside of the {escapes_from} body"),
3519        );
3520
3521        err.span_label(borrow_span, format!("borrow is only valid in the {escapes_from} body"));
3522
3523        if let Some(name) = name {
3524            err.span_label(
3525                escape_span,
3526                format!("reference to `{name}` escapes the {escapes_from} body here"),
3527            );
3528        } else {
3529            err.span_label(escape_span, format!("reference escapes the {escapes_from} body here"));
3530        }
3531
3532        err
3533    }
3534
3535    fn get_moved_indexes(
3536        &self,
3537        location: Location,
3538        mpi: MovePathIndex,
3539    ) -> (Vec<MoveSite>, Vec<Location>) {
3540        fn predecessor_locations<'a, 'tcx>(
3541            body: &'a mir::Body<'tcx>,
3542            location: Location,
3543        ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
3544            if location.statement_index == 0 {
3545                let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
3546                Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
3547            } else {
3548                Either::Right(std::iter::once(Location {
3549                    statement_index: location.statement_index - 1,
3550                    ..location
3551                }))
3552            }
3553        }
3554
3555        let mut mpis = vec![mpi];
3556        let move_paths = &self.move_data.move_paths;
3557        mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
3558
3559        let mut stack = Vec::new();
3560        let mut back_edge_stack = Vec::new();
3561
3562        predecessor_locations(self.body, location).for_each(|predecessor| {
3563            if location.dominates(predecessor, self.dominators()) {
3564                back_edge_stack.push(predecessor)
3565            } else {
3566                stack.push(predecessor);
3567            }
3568        });
3569
3570        let mut reached_start = false;
3571
3572        /* Check if the mpi is initialized as an argument */
3573        let mut is_argument = false;
3574        for arg in self.body.args_iter() {
3575            if let Some(path) = self.move_data.rev_lookup.find_local(arg) {
3576                if mpis.contains(&path) {
3577                    is_argument = true;
3578                }
3579            }
3580        }
3581
3582        let mut visited = FxIndexSet::default();
3583        let mut move_locations = FxIndexSet::default();
3584        let mut reinits = vec![];
3585        let mut result = vec![];
3586
3587        let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
3588            debug!(
3589                "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
3590                location, is_back_edge
3591            );
3592
3593            if !visited.insert(location) {
3594                return true;
3595            }
3596
3597            // check for moves
3598            let stmt_kind =
3599                self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
3600            if let Some(StatementKind::StorageDead(..)) = stmt_kind {
3601                // This analysis only tries to find moves explicitly written by the user, so we
3602                // ignore the move-outs created by `StorageDead` and at the beginning of a
3603                // function.
3604            } else {
3605                // If we are found a use of a.b.c which was in error, then we want to look for
3606                // moves not only of a.b.c but also a.b and a.
3607                //
3608                // Note that the moves data already includes "parent" paths, so we don't have to
3609                // worry about the other case: that is, if there is a move of a.b.c, it is already
3610                // marked as a move of a.b and a as well, so we will generate the correct errors
3611                // there.
3612                for moi in &self.move_data.loc_map[location] {
3613                    debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
3614                    let path = self.move_data.moves[*moi].path;
3615                    if mpis.contains(&path) {
3616                        debug!(
3617                            "report_use_of_moved_or_uninitialized: found {:?}",
3618                            move_paths[path].place
3619                        );
3620                        result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
3621                        move_locations.insert(location);
3622
3623                        // Strictly speaking, we could continue our DFS here. There may be
3624                        // other moves that can reach the point of error. But it is kind of
3625                        // confusing to highlight them.
3626                        //
3627                        // Example:
3628                        //
3629                        // ```
3630                        // let a = vec![];
3631                        // let b = a;
3632                        // let c = a;
3633                        // drop(a); // <-- current point of error
3634                        // ```
3635                        //
3636                        // Because we stop the DFS here, we only highlight `let c = a`,
3637                        // and not `let b = a`. We will of course also report an error at
3638                        // `let c = a` which highlights `let b = a` as the move.
3639                        return true;
3640                    }
3641                }
3642            }
3643
3644            // check for inits
3645            let mut any_match = false;
3646            for ii in &self.move_data.init_loc_map[location] {
3647                let init = self.move_data.inits[*ii];
3648                match init.kind {
3649                    InitKind::Deep | InitKind::NonPanicPathOnly => {
3650                        if mpis.contains(&init.path) {
3651                            any_match = true;
3652                        }
3653                    }
3654                    InitKind::Shallow => {
3655                        if mpi == init.path {
3656                            any_match = true;
3657                        }
3658                    }
3659                }
3660            }
3661            if any_match {
3662                reinits.push(location);
3663                return true;
3664            }
3665            false
3666        };
3667
3668        while let Some(location) = stack.pop() {
3669            if dfs_iter(&mut result, location, false) {
3670                continue;
3671            }
3672
3673            let mut has_predecessor = false;
3674            predecessor_locations(self.body, location).for_each(|predecessor| {
3675                if location.dominates(predecessor, self.dominators()) {
3676                    back_edge_stack.push(predecessor)
3677                } else {
3678                    stack.push(predecessor);
3679                }
3680                has_predecessor = true;
3681            });
3682
3683            if !has_predecessor {
3684                reached_start = true;
3685            }
3686        }
3687        if (is_argument || !reached_start) && result.is_empty() {
3688            // Process back edges (moves in future loop iterations) only if
3689            // the move path is definitely initialized upon loop entry,
3690            // to avoid spurious "in previous iteration" errors.
3691            // During DFS, if there's a path from the error back to the start
3692            // of the function with no intervening init or move, then the
3693            // move path may be uninitialized at loop entry.
3694            while let Some(location) = back_edge_stack.pop() {
3695                if dfs_iter(&mut result, location, true) {
3696                    continue;
3697                }
3698
3699                predecessor_locations(self.body, location)
3700                    .for_each(|predecessor| back_edge_stack.push(predecessor));
3701            }
3702        }
3703
3704        // Check if we can reach these reinits from a move location.
3705        let reinits_reachable = reinits
3706            .into_iter()
3707            .filter(|reinit| {
3708                let mut visited = FxIndexSet::default();
3709                let mut stack = vec![*reinit];
3710                while let Some(location) = stack.pop() {
3711                    if !visited.insert(location) {
3712                        continue;
3713                    }
3714                    if move_locations.contains(&location) {
3715                        return true;
3716                    }
3717                    stack.extend(predecessor_locations(self.body, location));
3718                }
3719                false
3720            })
3721            .collect::<Vec<Location>>();
3722        (result, reinits_reachable)
3723    }
3724
3725    pub(crate) fn report_illegal_mutation_of_borrowed(
3726        &mut self,
3727        location: Location,
3728        (place, span): (Place<'tcx>, Span),
3729        loan: &BorrowData<'tcx>,
3730    ) {
3731        let loan_spans = self.retrieve_borrow_spans(loan);
3732        let loan_span = loan_spans.args_or_use();
3733
3734        let descr_place = self.describe_any_place(place.as_ref());
3735        if let BorrowKind::Fake(_) = loan.kind {
3736            if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
3737                let mut err = self.cannot_mutate_in_immutable_section(
3738                    span,
3739                    loan_span,
3740                    &descr_place,
3741                    section,
3742                    "assign",
3743                );
3744
3745                loan_spans.var_subdiag(&mut err, Some(loan.kind), |kind, var_span| {
3746                    use crate::session_diagnostics::CaptureVarCause::*;
3747                    match kind {
3748                        hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
3749                        hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
3750                            BorrowUseInClosure { var_span }
3751                        }
3752                    }
3753                });
3754
3755                self.buffer_error(err);
3756
3757                return;
3758            }
3759        }
3760
3761        let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
3762        self.note_due_to_edition_2024_opaque_capture_rules(loan, &mut err);
3763
3764        loan_spans.var_subdiag(&mut err, Some(loan.kind), |kind, var_span| {
3765            use crate::session_diagnostics::CaptureVarCause::*;
3766            match kind {
3767                hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
3768                hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
3769                    BorrowUseInClosure { var_span }
3770                }
3771            }
3772        });
3773
3774        self.explain_why_borrow_contains_point(location, loan, None)
3775            .add_explanation_to_diagnostic(&self, &mut err, "", None, None);
3776
3777        self.explain_deref_coercion(loan, &mut err);
3778
3779        self.buffer_error(err);
3780    }
3781
3782    fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diag<'_>) {
3783        let tcx = self.infcx.tcx;
3784        if let Some(Terminator { kind: TerminatorKind::Call { call_source, fn_span, .. }, .. }) =
3785            &self.body[loan.reserve_location.block].terminator
3786            && let Some((method_did, method_args)) = mir::find_self_call(
3787                tcx,
3788                self.body,
3789                loan.assigned_place.local,
3790                loan.reserve_location.block,
3791            )
3792            && let CallKind::DerefCoercion { deref_target_span, deref_target_ty, .. } = call_kind(
3793                self.infcx.tcx,
3794                self.infcx.typing_env(self.infcx.param_env),
3795                method_did,
3796                method_args,
3797                *fn_span,
3798                call_source.from_hir_call(),
3799                Some(self.infcx.tcx.fn_arg_names(method_did)[0]),
3800            )
3801        {
3802            err.note(format!("borrow occurs due to deref coercion to `{deref_target_ty}`"));
3803            if let Some(deref_target_span) = deref_target_span {
3804                err.span_note(deref_target_span, "deref defined here");
3805            }
3806        }
3807    }
3808
3809    /// Reports an illegal reassignment; for example, an assignment to
3810    /// (part of) a non-`mut` local that occurs potentially after that
3811    /// local has already been initialized. `place` is the path being
3812    /// assigned; `err_place` is a place providing a reason why
3813    /// `place` is not mutable (e.g., the non-`mut` local `x` in an
3814    /// assignment to `x.f`).
3815    pub(crate) fn report_illegal_reassignment(
3816        &mut self,
3817        (place, span): (Place<'tcx>, Span),
3818        assigned_span: Span,
3819        err_place: Place<'tcx>,
3820    ) {
3821        let (from_arg, local_decl) = match err_place.as_local() {
3822            Some(local) => {
3823                (self.body.local_kind(local) == LocalKind::Arg, Some(&self.body.local_decls[local]))
3824            }
3825            None => (false, None),
3826        };
3827
3828        // If root local is initialized immediately (everything apart from let
3829        // PATTERN;) then make the error refer to that local, rather than the
3830        // place being assigned later.
3831        let (place_description, assigned_span) = match local_decl {
3832            Some(LocalDecl {
3833                local_info:
3834                    ClearCrossCrate::Set(
3835                        box LocalInfo::User(BindingForm::Var(VarBindingForm {
3836                            opt_match_place: None,
3837                            ..
3838                        }))
3839                        | box LocalInfo::StaticRef { .. }
3840                        | box LocalInfo::Boring,
3841                    ),
3842                ..
3843            })
3844            | None => (self.describe_any_place(place.as_ref()), assigned_span),
3845            Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
3846        };
3847        let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
3848        let msg = if from_arg {
3849            "cannot assign to immutable argument"
3850        } else {
3851            "cannot assign twice to immutable variable"
3852        };
3853        if span != assigned_span && !from_arg {
3854            err.span_label(assigned_span, format!("first assignment to {place_description}"));
3855        }
3856        if let Some(decl) = local_decl
3857            && decl.can_be_made_mutable()
3858        {
3859            err.span_suggestion_verbose(
3860                decl.source_info.span.shrink_to_lo(),
3861                "consider making this binding mutable",
3862                "mut ".to_string(),
3863                Applicability::MachineApplicable,
3864            );
3865            if !from_arg
3866                && matches!(
3867                    decl.local_info(),
3868                    LocalInfo::User(BindingForm::Var(VarBindingForm {
3869                        opt_match_place: Some((Some(_), _)),
3870                        ..
3871                    }))
3872                )
3873            {
3874                err.span_suggestion_verbose(
3875                    decl.source_info.span.shrink_to_lo(),
3876                    "to modify the original value, take a borrow instead",
3877                    "ref mut ".to_string(),
3878                    Applicability::MaybeIncorrect,
3879                );
3880            }
3881        }
3882        err.span_label(span, msg);
3883        self.buffer_error(err);
3884    }
3885
3886    fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
3887        let tcx = self.infcx.tcx;
3888        let (kind, _place_ty) = place.projection.iter().fold(
3889            (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)),
3890            |(kind, place_ty), &elem| {
3891                (
3892                    match elem {
3893                        ProjectionElem::Deref => match kind {
3894                            StorageDeadOrDrop::LocalStorageDead
3895                            | StorageDeadOrDrop::BoxedStorageDead => {
3896                                assert!(
3897                                    place_ty.ty.is_box(),
3898                                    "Drop of value behind a reference or raw pointer"
3899                                );
3900                                StorageDeadOrDrop::BoxedStorageDead
3901                            }
3902                            StorageDeadOrDrop::Destructor(_) => kind,
3903                        },
3904                        ProjectionElem::OpaqueCast { .. }
3905                        | ProjectionElem::Field(..)
3906                        | ProjectionElem::Downcast(..) => {
3907                            match place_ty.ty.kind() {
3908                                ty::Adt(def, _) if def.has_dtor(tcx) => {
3909                                    // Report the outermost adt with a destructor
3910                                    match kind {
3911                                        StorageDeadOrDrop::Destructor(_) => kind,
3912                                        StorageDeadOrDrop::LocalStorageDead
3913                                        | StorageDeadOrDrop::BoxedStorageDead => {
3914                                            StorageDeadOrDrop::Destructor(place_ty.ty)
3915                                        }
3916                                    }
3917                                }
3918                                _ => kind,
3919                            }
3920                        }
3921                        ProjectionElem::ConstantIndex { .. }
3922                        | ProjectionElem::Subslice { .. }
3923                        | ProjectionElem::Subtype(_)
3924                        | ProjectionElem::Index(_)
3925                        | ProjectionElem::UnwrapUnsafeBinder(_) => kind,
3926                    },
3927                    place_ty.projection_ty(tcx, elem),
3928                )
3929            },
3930        );
3931        kind
3932    }
3933
3934    /// Describe the reason for the fake borrow that was assigned to `place`.
3935    fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
3936        use rustc_middle::mir::visit::Visitor;
3937        struct FakeReadCauseFinder<'tcx> {
3938            place: Place<'tcx>,
3939            cause: Option<FakeReadCause>,
3940        }
3941        impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
3942            fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
3943                match statement {
3944                    Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
3945                        if *place == self.place =>
3946                    {
3947                        self.cause = Some(*cause);
3948                    }
3949                    _ => (),
3950                }
3951            }
3952        }
3953        let mut visitor = FakeReadCauseFinder { place, cause: None };
3954        visitor.visit_body(self.body);
3955        match visitor.cause {
3956            Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
3957            Some(FakeReadCause::ForIndex) => Some("indexing expression"),
3958            _ => None,
3959        }
3960    }
3961
3962    /// Annotate argument and return type of function and closure with (synthesized) lifetime for
3963    /// borrow of local value that does not live long enough.
3964    fn annotate_argument_and_return_for_borrow(
3965        &self,
3966        borrow: &BorrowData<'tcx>,
3967    ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
3968        // Define a fallback for when we can't match a closure.
3969        let fallback = || {
3970            let is_closure = self.infcx.tcx.is_closure_like(self.mir_def_id().to_def_id());
3971            if is_closure {
3972                None
3973            } else {
3974                let ty = self.infcx.tcx.type_of(self.mir_def_id()).instantiate_identity();
3975                match ty.kind() {
3976                    ty::FnDef(_, _) | ty::FnPtr(..) => self.annotate_fn_sig(
3977                        self.mir_def_id(),
3978                        self.infcx.tcx.fn_sig(self.mir_def_id()).instantiate_identity(),
3979                    ),
3980                    _ => None,
3981                }
3982            }
3983        };
3984
3985        // In order to determine whether we need to annotate, we need to check whether the reserve
3986        // place was an assignment into a temporary.
3987        //
3988        // If it was, we check whether or not that temporary is eventually assigned into the return
3989        // place. If it was, we can add annotations about the function's return type and arguments
3990        // and it'll make sense.
3991        let location = borrow.reserve_location;
3992        debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
3993        if let Some(Statement { kind: StatementKind::Assign(box (reservation, _)), .. }) =
3994            &self.body[location.block].statements.get(location.statement_index)
3995        {
3996            debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
3997            // Check that the initial assignment of the reserve location is into a temporary.
3998            let mut target = match reservation.as_local() {
3999                Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
4000                _ => return None,
4001            };
4002
4003            // Next, look through the rest of the block, checking if we are assigning the
4004            // `target` (that is, the place that contains our borrow) to anything.
4005            let mut annotated_closure = None;
4006            for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
4007                debug!(
4008                    "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
4009                    target, stmt
4010                );
4011                if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
4012                    if let Some(assigned_to) = place.as_local() {
4013                        debug!(
4014                            "annotate_argument_and_return_for_borrow: assigned_to={:?} \
4015                             rvalue={:?}",
4016                            assigned_to, rvalue
4017                        );
4018                        // Check if our `target` was captured by a closure.
4019                        if let Rvalue::Aggregate(
4020                            box AggregateKind::Closure(def_id, args),
4021                            operands,
4022                        ) = rvalue
4023                        {
4024                            let def_id = def_id.expect_local();
4025                            for operand in operands {
4026                                let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) =
4027                                    operand
4028                                else {
4029                                    continue;
4030                                };
4031                                debug!(
4032                                    "annotate_argument_and_return_for_borrow: assigned_from={:?}",
4033                                    assigned_from
4034                                );
4035
4036                                // Find the local from the operand.
4037                                let Some(assigned_from_local) =
4038                                    assigned_from.local_or_deref_local()
4039                                else {
4040                                    continue;
4041                                };
4042
4043                                if assigned_from_local != target {
4044                                    continue;
4045                                }
4046
4047                                // If a closure captured our `target` and then assigned
4048                                // into a place then we should annotate the closure in
4049                                // case it ends up being assigned into the return place.
4050                                annotated_closure =
4051                                    self.annotate_fn_sig(def_id, args.as_closure().sig());
4052                                debug!(
4053                                    "annotate_argument_and_return_for_borrow: \
4054                                     annotated_closure={:?} assigned_from_local={:?} \
4055                                     assigned_to={:?}",
4056                                    annotated_closure, assigned_from_local, assigned_to
4057                                );
4058
4059                                if assigned_to == mir::RETURN_PLACE {
4060                                    // If it was assigned directly into the return place, then
4061                                    // return now.
4062                                    return annotated_closure;
4063                                } else {
4064                                    // Otherwise, update the target.
4065                                    target = assigned_to;
4066                                }
4067                            }
4068
4069                            // If none of our closure's operands matched, then skip to the next
4070                            // statement.
4071                            continue;
4072                        }
4073
4074                        // Otherwise, look at other types of assignment.
4075                        let assigned_from = match rvalue {
4076                            Rvalue::Ref(_, _, assigned_from) => assigned_from,
4077                            Rvalue::Use(operand) => match operand {
4078                                Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
4079                                    assigned_from
4080                                }
4081                                _ => continue,
4082                            },
4083                            _ => continue,
4084                        };
4085                        debug!(
4086                            "annotate_argument_and_return_for_borrow: \
4087                             assigned_from={:?}",
4088                            assigned_from,
4089                        );
4090
4091                        // Find the local from the rvalue.
4092                        let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
4093                            continue;
4094                        };
4095                        debug!(
4096                            "annotate_argument_and_return_for_borrow: \
4097                             assigned_from_local={:?}",
4098                            assigned_from_local,
4099                        );
4100
4101                        // Check if our local matches the target - if so, we've assigned our
4102                        // borrow to a new place.
4103                        if assigned_from_local != target {
4104                            continue;
4105                        }
4106
4107                        // If we assigned our `target` into a new place, then we should
4108                        // check if it was the return place.
4109                        debug!(
4110                            "annotate_argument_and_return_for_borrow: \
4111                             assigned_from_local={:?} assigned_to={:?}",
4112                            assigned_from_local, assigned_to
4113                        );
4114                        if assigned_to == mir::RETURN_PLACE {
4115                            // If it was then return the annotated closure if there was one,
4116                            // else, annotate this function.
4117                            return annotated_closure.or_else(fallback);
4118                        }
4119
4120                        // If we didn't assign into the return place, then we just update
4121                        // the target.
4122                        target = assigned_to;
4123                    }
4124                }
4125            }
4126
4127            // Check the terminator if we didn't find anything in the statements.
4128            let terminator = &self.body[location.block].terminator();
4129            debug!(
4130                "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
4131                target, terminator
4132            );
4133            if let TerminatorKind::Call { destination, target: Some(_), args, .. } =
4134                &terminator.kind
4135            {
4136                if let Some(assigned_to) = destination.as_local() {
4137                    debug!(
4138                        "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
4139                        assigned_to, args
4140                    );
4141                    for operand in args {
4142                        let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) =
4143                            &operand.node
4144                        else {
4145                            continue;
4146                        };
4147                        debug!(
4148                            "annotate_argument_and_return_for_borrow: assigned_from={:?}",
4149                            assigned_from,
4150                        );
4151
4152                        if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
4153                            debug!(
4154                                "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
4155                                assigned_from_local,
4156                            );
4157
4158                            if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
4159                                return annotated_closure.or_else(fallback);
4160                            }
4161                        }
4162                    }
4163                }
4164            }
4165        }
4166
4167        // If we haven't found an assignment into the return place, then we need not add
4168        // any annotations.
4169        debug!("annotate_argument_and_return_for_borrow: none found");
4170        None
4171    }
4172
4173    /// Annotate the first argument and return type of a function signature if they are
4174    /// references.
4175    fn annotate_fn_sig(
4176        &self,
4177        did: LocalDefId,
4178        sig: ty::PolyFnSig<'tcx>,
4179    ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
4180        debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
4181        let is_closure = self.infcx.tcx.is_closure_like(did.to_def_id());
4182        let fn_hir_id = self.infcx.tcx.local_def_id_to_hir_id(did);
4183        let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
4184
4185        // We need to work out which arguments to highlight. We do this by looking
4186        // at the return type, where there are three cases:
4187        //
4188        // 1. If there are named arguments, then we should highlight the return type and
4189        //    highlight any of the arguments that are also references with that lifetime.
4190        //    If there are no arguments that have the same lifetime as the return type,
4191        //    then don't highlight anything.
4192        // 2. The return type is a reference with an anonymous lifetime. If this is
4193        //    the case, then we can take advantage of (and teach) the lifetime elision
4194        //    rules.
4195        //
4196        //    We know that an error is being reported. So the arguments and return type
4197        //    must satisfy the elision rules. Therefore, if there is a single argument
4198        //    then that means the return type and first (and only) argument have the same
4199        //    lifetime and the borrow isn't meeting that, we can highlight the argument
4200        //    and return type.
4201        //
4202        //    If there are multiple arguments then the first argument must be self (else
4203        //    it would not satisfy the elision rules), so we can highlight self and the
4204        //    return type.
4205        // 3. The return type is not a reference. In this case, we don't highlight
4206        //    anything.
4207        let return_ty = sig.output();
4208        match return_ty.skip_binder().kind() {
4209            ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
4210                // This is case 1 from above, return type is a named reference so we need to
4211                // search for relevant arguments.
4212                let mut arguments = Vec::new();
4213                for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
4214                    if let ty::Ref(argument_region, _, _) = argument.kind()
4215                        && argument_region == return_region
4216                    {
4217                        // Need to use the `rustc_middle::ty` types to compare against the
4218                        // `return_region`. Then use the `rustc_hir` type to get only
4219                        // the lifetime span.
4220                        match &fn_decl.inputs[index].kind {
4221                            hir::TyKind::Ref(lifetime, _) => {
4222                                // With access to the lifetime, we can get
4223                                // the span of it.
4224                                arguments.push((*argument, lifetime.ident.span));
4225                            }
4226                            // Resolve `self` whose self type is `&T`.
4227                            hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
4228                                if let Res::SelfTyAlias { alias_to, .. } = path.res
4229                                    && let Some(alias_to) = alias_to.as_local()
4230                                    && let hir::Impl { self_ty, .. } = self
4231                                        .infcx
4232                                        .tcx
4233                                        .hir_node_by_def_id(alias_to)
4234                                        .expect_item()
4235                                        .expect_impl()
4236                                    && let hir::TyKind::Ref(lifetime, _) = self_ty.kind
4237                                {
4238                                    arguments.push((*argument, lifetime.ident.span));
4239                                }
4240                            }
4241                            _ => {
4242                                // Don't ICE though. It might be a type alias.
4243                            }
4244                        }
4245                    }
4246                }
4247
4248                // We need to have arguments. This shouldn't happen, but it's worth checking.
4249                if arguments.is_empty() {
4250                    return None;
4251                }
4252
4253                // We use a mix of the HIR and the Ty types to get information
4254                // as the HIR doesn't have full types for closure arguments.
4255                let return_ty = sig.output().skip_binder();
4256                let mut return_span = fn_decl.output.span();
4257                if let hir::FnRetTy::Return(ty) = &fn_decl.output {
4258                    if let hir::TyKind::Ref(lifetime, _) = ty.kind {
4259                        return_span = lifetime.ident.span;
4260                    }
4261                }
4262
4263                Some(AnnotatedBorrowFnSignature::NamedFunction {
4264                    arguments,
4265                    return_ty,
4266                    return_span,
4267                })
4268            }
4269            ty::Ref(_, _, _) if is_closure => {
4270                // This is case 2 from above but only for closures, return type is anonymous
4271                // reference so we select
4272                // the first argument.
4273                let argument_span = fn_decl.inputs.first()?.span;
4274                let argument_ty = sig.inputs().skip_binder().first()?;
4275
4276                // Closure arguments are wrapped in a tuple, so we need to get the first
4277                // from that.
4278                if let ty::Tuple(elems) = argument_ty.kind() {
4279                    let &argument_ty = elems.first()?;
4280                    if let ty::Ref(_, _, _) = argument_ty.kind() {
4281                        return Some(AnnotatedBorrowFnSignature::Closure {
4282                            argument_ty,
4283                            argument_span,
4284                        });
4285                    }
4286                }
4287
4288                None
4289            }
4290            ty::Ref(_, _, _) => {
4291                // This is also case 2 from above but for functions, return type is still an
4292                // anonymous reference so we select the first argument.
4293                let argument_span = fn_decl.inputs.first()?.span;
4294                let argument_ty = *sig.inputs().skip_binder().first()?;
4295
4296                let return_span = fn_decl.output.span();
4297                let return_ty = sig.output().skip_binder();
4298
4299                // We expect the first argument to be a reference.
4300                match argument_ty.kind() {
4301                    ty::Ref(_, _, _) => {}
4302                    _ => return None,
4303                }
4304
4305                Some(AnnotatedBorrowFnSignature::AnonymousFunction {
4306                    argument_ty,
4307                    argument_span,
4308                    return_ty,
4309                    return_span,
4310                })
4311            }
4312            _ => {
4313                // This is case 3 from above, return type is not a reference so don't highlight
4314                // anything.
4315                None
4316            }
4317        }
4318    }
4319}
4320
4321#[derive(Debug)]
4322enum AnnotatedBorrowFnSignature<'tcx> {
4323    NamedFunction {
4324        arguments: Vec<(Ty<'tcx>, Span)>,
4325        return_ty: Ty<'tcx>,
4326        return_span: Span,
4327    },
4328    AnonymousFunction {
4329        argument_ty: Ty<'tcx>,
4330        argument_span: Span,
4331        return_ty: Ty<'tcx>,
4332        return_span: Span,
4333    },
4334    Closure {
4335        argument_ty: Ty<'tcx>,
4336        argument_span: Span,
4337    },
4338}
4339
4340impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
4341    /// Annotate the provided diagnostic with information about borrow from the fn signature that
4342    /// helps explain.
4343    pub(crate) fn emit(&self, cx: &MirBorrowckCtxt<'_, '_, 'tcx>, diag: &mut Diag<'_>) -> String {
4344        match self {
4345            &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
4346                diag.span_label(
4347                    argument_span,
4348                    format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
4349                );
4350
4351                cx.get_region_name_for_ty(argument_ty, 0)
4352            }
4353            &AnnotatedBorrowFnSignature::AnonymousFunction {
4354                argument_ty,
4355                argument_span,
4356                return_ty,
4357                return_span,
4358            } => {
4359                let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
4360                diag.span_label(argument_span, format!("has type `{argument_ty_name}`"));
4361
4362                let return_ty_name = cx.get_name_for_ty(return_ty, 0);
4363                let types_equal = return_ty_name == argument_ty_name;
4364                diag.span_label(
4365                    return_span,
4366                    format!(
4367                        "{}has type `{}`",
4368                        if types_equal { "also " } else { "" },
4369                        return_ty_name,
4370                    ),
4371                );
4372
4373                diag.note(
4374                    "argument and return type have the same lifetime due to lifetime elision rules",
4375                );
4376                diag.note(
4377                    "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
4378                     lifetime-syntax.html#lifetime-elision>",
4379                );
4380
4381                cx.get_region_name_for_ty(return_ty, 0)
4382            }
4383            AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
4384                // Region of return type and arguments checked to be the same earlier.
4385                let region_name = cx.get_region_name_for_ty(*return_ty, 0);
4386                for (_, argument_span) in arguments {
4387                    diag.span_label(*argument_span, format!("has lifetime `{region_name}`"));
4388                }
4389
4390                diag.span_label(*return_span, format!("also has lifetime `{region_name}`",));
4391
4392                diag.help(format!(
4393                    "use data from the highlighted arguments which match the `{region_name}` lifetime of \
4394                     the return type",
4395                ));
4396
4397                region_name
4398            }
4399        }
4400    }
4401}
4402
4403/// Detect whether one of the provided spans is a statement nested within the top-most visited expr
4404struct ReferencedStatementsVisitor<'a>(&'a [Span]);
4405
4406impl<'v> Visitor<'v> for ReferencedStatementsVisitor<'_> {
4407    type Result = ControlFlow<()>;
4408    fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) -> Self::Result {
4409        match s.kind {
4410            hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => ControlFlow::Break(()),
4411            _ => ControlFlow::Continue(()),
4412        }
4413    }
4414}
4415
4416/// Look for `break` expressions within any arbitrary expressions. We'll do this to infer
4417/// whether this is a case where the moved value would affect the exit of a loop, making it
4418/// unsuitable for a `.clone()` suggestion.
4419struct BreakFinder {
4420    found_breaks: Vec<(hir::Destination, Span)>,
4421    found_continues: Vec<(hir::Destination, Span)>,
4422}
4423impl<'hir> Visitor<'hir> for BreakFinder {
4424    fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
4425        match ex.kind {
4426            hir::ExprKind::Break(destination, _) => {
4427                self.found_breaks.push((destination, ex.span));
4428            }
4429            hir::ExprKind::Continue(destination) => {
4430                self.found_continues.push((destination, ex.span));
4431            }
4432            _ => {}
4433        }
4434        hir::intravisit::walk_expr(self, ex);
4435    }
4436}
4437
4438/// Given a set of spans representing statements initializing the relevant binding, visit all the
4439/// function expressions looking for branching code paths that *do not* initialize the binding.
4440struct ConditionVisitor<'tcx> {
4441    tcx: TyCtxt<'tcx>,
4442    spans: Vec<Span>,
4443    name: String,
4444    errors: Vec<(Span, String)>,
4445}
4446
4447impl<'v, 'tcx> Visitor<'v> for ConditionVisitor<'tcx> {
4448    fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
4449        match ex.kind {
4450            hir::ExprKind::If(cond, body, None) => {
4451                // `if` expressions with no `else` that initialize the binding might be missing an
4452                // `else` arm.
4453                if ReferencedStatementsVisitor(&self.spans).visit_expr(body).is_break() {
4454                    self.errors.push((
4455                        cond.span,
4456                        format!(
4457                            "if this `if` condition is `false`, {} is not initialized",
4458                            self.name,
4459                        ),
4460                    ));
4461                    self.errors.push((
4462                        ex.span.shrink_to_hi(),
4463                        format!("an `else` arm might be missing here, initializing {}", self.name),
4464                    ));
4465                }
4466            }
4467            hir::ExprKind::If(cond, body, Some(other)) => {
4468                // `if` expressions where the binding is only initialized in one of the two arms
4469                // might be missing a binding initialization.
4470                let a = ReferencedStatementsVisitor(&self.spans).visit_expr(body).is_break();
4471                let b = ReferencedStatementsVisitor(&self.spans).visit_expr(other).is_break();
4472                match (a, b) {
4473                    (true, true) | (false, false) => {}
4474                    (true, false) => {
4475                        if other.span.is_desugaring(DesugaringKind::WhileLoop) {
4476                            self.errors.push((
4477                                cond.span,
4478                                format!(
4479                                    "if this condition isn't met and the `while` loop runs 0 \
4480                                     times, {} is not initialized",
4481                                    self.name
4482                                ),
4483                            ));
4484                        } else {
4485                            self.errors.push((
4486                                body.span.shrink_to_hi().until(other.span),
4487                                format!(
4488                                    "if the `if` condition is `false` and this `else` arm is \
4489                                     executed, {} is not initialized",
4490                                    self.name
4491                                ),
4492                            ));
4493                        }
4494                    }
4495                    (false, true) => {
4496                        self.errors.push((
4497                            cond.span,
4498                            format!(
4499                                "if this condition is `true`, {} is not initialized",
4500                                self.name
4501                            ),
4502                        ));
4503                    }
4504                }
4505            }
4506            hir::ExprKind::Match(e, arms, loop_desugar) => {
4507                // If the binding is initialized in one of the match arms, then the other match
4508                // arms might be missing an initialization.
4509                let results: Vec<bool> = arms
4510                    .iter()
4511                    .map(|arm| ReferencedStatementsVisitor(&self.spans).visit_arm(arm).is_break())
4512                    .collect();
4513                if results.iter().any(|x| *x) && !results.iter().all(|x| *x) {
4514                    for (arm, seen) in arms.iter().zip(results) {
4515                        if !seen {
4516                            if loop_desugar == hir::MatchSource::ForLoopDesugar {
4517                                self.errors.push((
4518                                    e.span,
4519                                    format!(
4520                                        "if the `for` loop runs 0 times, {} is not initialized",
4521                                        self.name
4522                                    ),
4523                                ));
4524                            } else if let Some(guard) = &arm.guard {
4525                                if matches!(
4526                                    self.tcx.hir_node(arm.body.hir_id),
4527                                    hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. })
4528                                ) {
4529                                    continue;
4530                                }
4531                                self.errors.push((
4532                                    arm.pat.span.to(guard.span),
4533                                    format!(
4534                                        "if this pattern and condition are matched, {} is not \
4535                                         initialized",
4536                                        self.name
4537                                    ),
4538                                ));
4539                            } else {
4540                                if matches!(
4541                                    self.tcx.hir_node(arm.body.hir_id),
4542                                    hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. })
4543                                ) {
4544                                    continue;
4545                                }
4546                                self.errors.push((
4547                                    arm.pat.span,
4548                                    format!(
4549                                        "if this pattern is matched, {} is not initialized",
4550                                        self.name
4551                                    ),
4552                                ));
4553                            }
4554                        }
4555                    }
4556                }
4557            }
4558            // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should
4559            // also be accounted for. For now it is fine, as if we don't find *any* relevant
4560            // branching code paths, we point at the places where the binding *is* initialized for
4561            // *some* context.
4562            _ => {}
4563        }
4564        walk_expr(self, ex);
4565    }
4566}