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