rustc_borrowck/diagnostics/
mod.rs

1//! Borrow checker diagnostics.
2
3use std::collections::BTreeMap;
4
5use rustc_abi::{FieldIdx, VariantIdx};
6use rustc_data_structures::fx::FxIndexMap;
7use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify};
8use rustc_hir::def::{CtorKind, Namespace};
9use rustc_hir::{self as hir, CoroutineKind, LangItem};
10use rustc_index::{IndexSlice, IndexVec};
11use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin};
12use rustc_infer::traits::SelectionError;
13use rustc_middle::mir::{
14    AggregateKind, CallSource, ConstOperand, ConstraintCategory, FakeReadCause, Local, LocalInfo,
15    LocalKind, Location, Operand, Place, PlaceRef, PlaceTy, ProjectionElem, Rvalue, Statement,
16    StatementKind, Terminator, TerminatorKind, VarDebugInfoContents, find_self_call,
17};
18use rustc_middle::ty::print::Print;
19use rustc_middle::ty::{self, Ty, TyCtxt};
20use rustc_middle::{bug, span_bug};
21use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveOutIndex};
22use rustc_span::def_id::LocalDefId;
23use rustc_span::source_map::Spanned;
24use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
25use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
26use rustc_trait_selection::error_reporting::traits::call_kind::{CallDesugaringKind, call_kind};
27use rustc_trait_selection::infer::InferCtxtExt;
28use rustc_trait_selection::traits::{
29    FulfillmentError, FulfillmentErrorCode, type_known_to_meet_bound_modulo_regions,
30};
31use tracing::debug;
32
33use super::MirBorrowckCtxt;
34use super::borrow_set::BorrowData;
35use crate::constraints::OutlivesConstraint;
36use crate::fluent_generated as fluent;
37use crate::nll::ConstraintDescription;
38use crate::session_diagnostics::{
39    CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause,
40    CaptureVarKind, CaptureVarPathUseCause, OnClosureNote,
41};
42
43mod find_all_local_uses;
44mod find_use;
45mod outlives_suggestion;
46mod region_name;
47mod var_name;
48
49mod bound_region_errors;
50mod conflict_errors;
51mod explain_borrow;
52mod move_errors;
53mod mutability_errors;
54mod opaque_suggestions;
55mod region_errors;
56
57pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo};
58pub(crate) use move_errors::{IllegalMoveOriginKind, MoveError};
59pub(crate) use mutability_errors::AccessKind;
60pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder;
61pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
62pub(crate) use region_name::{RegionName, RegionNameSource};
63pub(crate) use rustc_trait_selection::error_reporting::traits::call_kind::CallKind;
64
65pub(super) struct DescribePlaceOpt {
66    including_downcast: bool,
67
68    /// Enable/Disable tuple fields.
69    /// For example `x` tuple. if it's `true` `x.0`. Otherwise `x`
70    including_tuple_field: bool,
71}
72
73pub(super) struct IncludingTupleField(pub(super) bool);
74
75enum BufferedDiag<'infcx> {
76    Error(Diag<'infcx>),
77    NonError(Diag<'infcx, ()>),
78}
79
80impl<'infcx> BufferedDiag<'infcx> {
81    fn sort_span(&self) -> Span {
82        match self {
83            BufferedDiag::Error(diag) => diag.sort_span,
84            BufferedDiag::NonError(diag) => diag.sort_span,
85        }
86    }
87}
88
89#[derive(Default)]
90pub(crate) struct BorrowckDiagnosticsBuffer<'infcx, 'tcx> {
91    /// This field keeps track of move errors that are to be reported for given move indices.
92    ///
93    /// There are situations where many errors can be reported for a single move out (see
94    /// #53807) and we want only the best of those errors.
95    ///
96    /// The `report_use_of_moved_or_uninitialized` function checks this map and replaces the
97    /// diagnostic (if there is one) if the `Place` of the error being reported is a prefix of
98    /// the `Place` of the previous most diagnostic. This happens instead of buffering the
99    /// error. Once all move errors have been reported, any diagnostics in this map are added
100    /// to the buffer to be emitted.
101    ///
102    /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary
103    /// when errors in the map are being re-added to the error buffer so that errors with the
104    /// same primary span come out in a consistent order.
105    buffered_move_errors: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, Diag<'infcx>)>,
106
107    buffered_mut_errors: FxIndexMap<Span, (Diag<'infcx>, usize)>,
108
109    /// Buffer of diagnostics to be reported. A mixture of error and non-error diagnostics.
110    buffered_diags: Vec<BufferedDiag<'infcx>>,
111}
112
113impl<'infcx, 'tcx> BorrowckDiagnosticsBuffer<'infcx, 'tcx> {
114    pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
115        self.buffered_diags.push(BufferedDiag::NonError(diag));
116    }
117}
118
119impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
120    pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) {
121        self.diags_buffer.buffered_diags.push(BufferedDiag::Error(diag));
122    }
123
124    pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
125        self.diags_buffer.buffer_non_error(diag);
126    }
127
128    pub(crate) fn buffer_move_error(
129        &mut self,
130        move_out_indices: Vec<MoveOutIndex>,
131        place_and_err: (PlaceRef<'tcx>, Diag<'infcx>),
132    ) -> bool {
133        if let Some((_, diag)) =
134            self.diags_buffer.buffered_move_errors.insert(move_out_indices, place_and_err)
135        {
136            // Cancel the old diagnostic so we don't ICE
137            diag.cancel();
138            false
139        } else {
140            true
141        }
142    }
143
144    pub(crate) fn get_buffered_mut_error(&mut self, span: Span) -> Option<(Diag<'infcx>, usize)> {
145        // FIXME(#120456) - is `swap_remove` correct?
146        self.diags_buffer.buffered_mut_errors.swap_remove(&span)
147    }
148
149    pub(crate) fn buffer_mut_error(&mut self, span: Span, diag: Diag<'infcx>, count: usize) {
150        self.diags_buffer.buffered_mut_errors.insert(span, (diag, count));
151    }
152
153    pub(crate) fn emit_errors(&mut self) -> Option<ErrorGuaranteed> {
154        let mut res = self.infcx.tainted_by_errors();
155
156        // Buffer any move errors that we collected and de-duplicated.
157        for (_, (_, diag)) in std::mem::take(&mut self.diags_buffer.buffered_move_errors) {
158            // We have already set tainted for this error, so just buffer it.
159            self.buffer_error(diag);
160        }
161        for (_, (mut diag, count)) in std::mem::take(&mut self.diags_buffer.buffered_mut_errors) {
162            if count > 10 {
163                #[allow(rustc::diagnostic_outside_of_impl)]
164                #[allow(rustc::untranslatable_diagnostic)]
165                diag.note(format!("...and {} other attempted mutable borrows", count - 10));
166            }
167            self.buffer_error(diag);
168        }
169
170        if !self.diags_buffer.buffered_diags.is_empty() {
171            self.diags_buffer.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span());
172            for buffered_diag in self.diags_buffer.buffered_diags.drain(..) {
173                match buffered_diag {
174                    BufferedDiag::Error(diag) => res = Some(diag.emit()),
175                    BufferedDiag::NonError(diag) => diag.emit(),
176                }
177            }
178        }
179
180        res
181    }
182
183    pub(crate) fn has_buffered_diags(&self) -> bool {
184        self.diags_buffer.buffered_diags.is_empty()
185    }
186
187    pub(crate) fn has_move_error(
188        &self,
189        move_out_indices: &[MoveOutIndex],
190    ) -> Option<&(PlaceRef<'tcx>, Diag<'infcx>)> {
191        self.diags_buffer.buffered_move_errors.get(move_out_indices)
192    }
193
194    /// Uses `body.var_debug_info` to find the symbol
195    fn local_name(&self, index: Local) -> Option<Symbol> {
196        *self.local_names().get(index)?
197    }
198
199    fn local_names(&self) -> &IndexSlice<Local, Option<Symbol>> {
200        self.local_names.get_or_init(|| {
201            let mut local_names = IndexVec::from_elem(None, &self.body.local_decls);
202            for var_debug_info in &self.body.var_debug_info {
203                if let VarDebugInfoContents::Place(place) = var_debug_info.value {
204                    if let Some(local) = place.as_local() {
205                        if let Some(prev_name) = local_names[local]
206                            && var_debug_info.name != prev_name
207                        {
208                            span_bug!(
209                                var_debug_info.source_info.span,
210                                "local {:?} has many names (`{}` vs `{}`)",
211                                local,
212                                prev_name,
213                                var_debug_info.name
214                            );
215                        }
216                        local_names[local] = Some(var_debug_info.name);
217                    }
218                }
219            }
220            local_names
221        })
222    }
223}
224
225impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
226    /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
227    /// is moved after being invoked.
228    ///
229    /// ```text
230    /// note: closure cannot be invoked more than once because it moves the variable `dict` out of
231    ///       its environment
232    ///   --> $DIR/issue-42065.rs:16:29
233    ///    |
234    /// LL |         for (key, value) in dict {
235    ///    |                             ^^^^
236    /// ```
237    #[allow(rustc::diagnostic_outside_of_impl)] // FIXME
238    pub(super) fn add_moved_or_invoked_closure_note(
239        &self,
240        location: Location,
241        place: PlaceRef<'tcx>,
242        diag: &mut Diag<'infcx>,
243    ) -> bool {
244        debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
245        let mut target = place.local_or_deref_local();
246        for stmt in &self.body[location.block].statements[location.statement_index..] {
247            debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
248            if let StatementKind::Assign(box (into, Rvalue::Use(from))) = &stmt.kind {
249                debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
250                match from {
251                    Operand::Copy(place) | Operand::Move(place)
252                        if target == place.local_or_deref_local() =>
253                    {
254                        target = into.local_or_deref_local()
255                    }
256                    _ => {}
257                }
258            }
259        }
260
261        // Check if we are attempting to call a closure after it has been invoked.
262        let terminator = self.body[location.block].terminator();
263        debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
264        if let TerminatorKind::Call {
265            func: Operand::Constant(box ConstOperand { const_, .. }),
266            args,
267            ..
268        } = &terminator.kind
269        {
270            if let ty::FnDef(id, _) = *const_.ty().kind() {
271                debug!("add_moved_or_invoked_closure_note: id={:?}", id);
272                if self.infcx.tcx.is_lang_item(self.infcx.tcx.parent(id), LangItem::FnOnce) {
273                    let closure = match args.first() {
274                        Some(Spanned {
275                            node: Operand::Copy(place) | Operand::Move(place), ..
276                        }) if target == place.local_or_deref_local() => {
277                            place.local_or_deref_local().unwrap()
278                        }
279                        _ => return false,
280                    };
281
282                    debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
283                    if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() {
284                        let did = did.expect_local();
285                        if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
286                            diag.subdiagnostic(OnClosureNote::InvokedTwice {
287                                place_name: &ty::place_to_string_for_capture(
288                                    self.infcx.tcx,
289                                    hir_place,
290                                ),
291                                span: *span,
292                            });
293                            return true;
294                        }
295                    }
296                }
297            }
298        }
299
300        // Check if we are just moving a closure after it has been invoked.
301        if let Some(target) = target {
302            if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() {
303                let did = did.expect_local();
304                if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
305                    diag.subdiagnostic(OnClosureNote::MovedTwice {
306                        place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
307                        span: *span,
308                    });
309                    return true;
310                }
311            }
312        }
313        false
314    }
315
316    /// End-user visible description of `place` if one can be found.
317    /// If the place is a temporary for instance, `"value"` will be returned.
318    pub(super) fn describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String {
319        match self.describe_place(place_ref) {
320            Some(mut descr) => {
321                // Surround descr with `backticks`.
322                descr.reserve(2);
323                descr.insert(0, '`');
324                descr.push('`');
325                descr
326            }
327            None => "value".to_string(),
328        }
329    }
330
331    /// End-user visible description of `place` if one can be found.
332    /// If the place is a temporary for instance, `None` will be returned.
333    pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String> {
334        self.describe_place_with_options(
335            place_ref,
336            DescribePlaceOpt { including_downcast: false, including_tuple_field: true },
337        )
338    }
339
340    /// End-user visible description of `place` if one can be found. If the place is a temporary
341    /// for instance, `None` will be returned.
342    /// `IncludingDowncast` parameter makes the function return `None` if `ProjectionElem` is
343    /// `Downcast` and `IncludingDowncast` is true
344    pub(super) fn describe_place_with_options(
345        &self,
346        place: PlaceRef<'tcx>,
347        opt: DescribePlaceOpt,
348    ) -> Option<String> {
349        let local = place.local;
350        if self.body.local_decls[local]
351            .source_info
352            .span
353            .in_external_macro(self.infcx.tcx.sess.source_map())
354        {
355            return None;
356        }
357
358        let mut autoderef_index = None;
359        let mut buf = String::new();
360        let mut ok = self.append_local_to_string(local, &mut buf);
361
362        for (index, elem) in place.projection.into_iter().enumerate() {
363            match elem {
364                ProjectionElem::Deref => {
365                    if index == 0 {
366                        if self.body.local_decls[local].is_ref_for_guard() {
367                            continue;
368                        }
369                        if let LocalInfo::StaticRef { def_id, .. } =
370                            *self.body.local_decls[local].local_info()
371                        {
372                            buf.push_str(self.infcx.tcx.item_name(def_id).as_str());
373                            ok = Ok(());
374                            continue;
375                        }
376                    }
377                    if let Some(field) = self.is_upvar_field_projection(PlaceRef {
378                        local,
379                        projection: place.projection.split_at(index + 1).0,
380                    }) {
381                        let var_index = field.index();
382                        buf = self.upvars[var_index].to_string(self.infcx.tcx);
383                        ok = Ok(());
384                        if !self.upvars[var_index].is_by_ref() {
385                            buf.insert(0, '*');
386                        }
387                    } else {
388                        if autoderef_index.is_none() {
389                            autoderef_index = match place.projection.iter().rposition(|elem| {
390                                !matches!(
391                                    elem,
392                                    ProjectionElem::Deref | ProjectionElem::Downcast(..)
393                                )
394                            }) {
395                                Some(index) => Some(index + 1),
396                                None => Some(0),
397                            };
398                        }
399                        if index >= autoderef_index.unwrap() {
400                            buf.insert(0, '*');
401                        }
402                    }
403                }
404                ProjectionElem::Downcast(..) if opt.including_downcast => return None,
405                ProjectionElem::Downcast(..) => (),
406                ProjectionElem::OpaqueCast(..) => (),
407                ProjectionElem::Subtype(..) => (),
408                ProjectionElem::UnwrapUnsafeBinder(_) => (),
409                ProjectionElem::Field(field, _ty) => {
410                    // FIXME(project-rfc_2229#36): print capture precisely here.
411                    if let Some(field) = self.is_upvar_field_projection(PlaceRef {
412                        local,
413                        projection: place.projection.split_at(index + 1).0,
414                    }) {
415                        buf = self.upvars[field.index()].to_string(self.infcx.tcx);
416                        ok = Ok(());
417                    } else {
418                        let field_name = self.describe_field(
419                            PlaceRef { local, projection: place.projection.split_at(index).0 },
420                            *field,
421                            IncludingTupleField(opt.including_tuple_field),
422                        );
423                        if let Some(field_name_str) = field_name {
424                            buf.push('.');
425                            buf.push_str(&field_name_str);
426                        }
427                    }
428                }
429                ProjectionElem::Index(index) => {
430                    buf.push('[');
431                    if self.append_local_to_string(*index, &mut buf).is_err() {
432                        buf.push('_');
433                    }
434                    buf.push(']');
435                }
436                ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
437                    // Since it isn't possible to borrow an element on a particular index and
438                    // then use another while the borrow is held, don't output indices details
439                    // to avoid confusing the end-user
440                    buf.push_str("[..]");
441                }
442            }
443        }
444        ok.ok().map(|_| buf)
445    }
446
447    fn describe_name(&self, place: PlaceRef<'tcx>) -> Option<Symbol> {
448        for elem in place.projection.into_iter() {
449            match elem {
450                ProjectionElem::Downcast(Some(name), _) => {
451                    return Some(*name);
452                }
453                _ => {}
454            }
455        }
456        None
457    }
458
459    /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
460    /// a name, or its name was generated by the compiler, then `Err` is returned
461    fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> {
462        let decl = &self.body.local_decls[local];
463        match self.local_name(local) {
464            Some(name) if !decl.from_compiler_desugaring() => {
465                buf.push_str(name.as_str());
466                Ok(())
467            }
468            _ => Err(()),
469        }
470    }
471
472    /// End-user visible description of the `field`nth field of `base`
473    fn describe_field(
474        &self,
475        place: PlaceRef<'tcx>,
476        field: FieldIdx,
477        including_tuple_field: IncludingTupleField,
478    ) -> Option<String> {
479        let place_ty = match place {
480            PlaceRef { local, projection: [] } => PlaceTy::from_ty(self.body.local_decls[local].ty),
481            PlaceRef { local, projection: [proj_base @ .., elem] } => match elem {
482                ProjectionElem::Deref
483                | ProjectionElem::Index(..)
484                | ProjectionElem::ConstantIndex { .. }
485                | ProjectionElem::Subslice { .. } => {
486                    PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
487                }
488                ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
489                ProjectionElem::Subtype(ty)
490                | ProjectionElem::OpaqueCast(ty)
491                | ProjectionElem::UnwrapUnsafeBinder(ty) => PlaceTy::from_ty(*ty),
492                ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
493            },
494        };
495        self.describe_field_from_ty(
496            place_ty.ty,
497            field,
498            place_ty.variant_index,
499            including_tuple_field,
500        )
501    }
502
503    /// End-user visible description of the `field_index`nth field of `ty`
504    fn describe_field_from_ty(
505        &self,
506        ty: Ty<'_>,
507        field: FieldIdx,
508        variant_index: Option<VariantIdx>,
509        including_tuple_field: IncludingTupleField,
510    ) -> Option<String> {
511        if let Some(boxed_ty) = ty.boxed_ty() {
512            // If the type is a box, the field is described from the boxed type
513            self.describe_field_from_ty(boxed_ty, field, variant_index, including_tuple_field)
514        } else {
515            match *ty.kind() {
516                ty::Adt(def, _) => {
517                    let variant = if let Some(idx) = variant_index {
518                        assert!(def.is_enum());
519                        def.variant(idx)
520                    } else {
521                        def.non_enum_variant()
522                    };
523                    if !including_tuple_field.0 && variant.ctor_kind() == Some(CtorKind::Fn) {
524                        return None;
525                    }
526                    Some(variant.fields[field].name.to_string())
527                }
528                ty::Tuple(_) => Some(field.index().to_string()),
529                ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => {
530                    self.describe_field_from_ty(ty, field, variant_index, including_tuple_field)
531                }
532                ty::Array(ty, _) | ty::Slice(ty) => {
533                    self.describe_field_from_ty(ty, field, variant_index, including_tuple_field)
534                }
535                ty::Closure(def_id, _) | ty::Coroutine(def_id, _) => {
536                    // We won't be borrowck'ing here if the closure came from another crate,
537                    // so it's safe to call `expect_local`.
538                    //
539                    // We know the field exists so it's safe to call operator[] and `unwrap` here.
540                    let def_id = def_id.expect_local();
541                    let var_id =
542                        self.infcx.tcx.closure_captures(def_id)[field.index()].get_root_variable();
543
544                    Some(self.infcx.tcx.hir_name(var_id).to_string())
545                }
546                _ => {
547                    // Might need a revision when the fields in trait RFC is implemented
548                    // (https://github.com/rust-lang/rfcs/pull/1546)
549                    bug!("End-user description not implemented for field access on `{:?}`", ty);
550                }
551            }
552        }
553    }
554
555    pub(super) fn borrowed_content_source(
556        &self,
557        deref_base: PlaceRef<'tcx>,
558    ) -> BorrowedContentSource<'tcx> {
559        let tcx = self.infcx.tcx;
560
561        // Look up the provided place and work out the move path index for it,
562        // we'll use this to check whether it was originally from an overloaded
563        // operator.
564        match self.move_data.rev_lookup.find(deref_base) {
565            LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => {
566                debug!("borrowed_content_source: mpi={:?}", mpi);
567
568                for i in &self.move_data.init_path_map[mpi] {
569                    let init = &self.move_data.inits[*i];
570                    debug!("borrowed_content_source: init={:?}", init);
571                    // We're only interested in statements that initialized a value, not the
572                    // initializations from arguments.
573                    let InitLocation::Statement(loc) = init.location else { continue };
574
575                    let bbd = &self.body[loc.block];
576                    let is_terminator = bbd.statements.len() == loc.statement_index;
577                    debug!(
578                        "borrowed_content_source: loc={:?} is_terminator={:?}",
579                        loc, is_terminator,
580                    );
581                    if !is_terminator {
582                        continue;
583                    } else if let Some(Terminator {
584                        kind:
585                            TerminatorKind::Call {
586                                func,
587                                call_source: CallSource::OverloadedOperator,
588                                ..
589                            },
590                        ..
591                    }) = &bbd.terminator
592                    {
593                        if let Some(source) =
594                            BorrowedContentSource::from_call(func.ty(self.body, tcx), tcx)
595                        {
596                            return source;
597                        }
598                    }
599                }
600            }
601            // Base is a `static` so won't be from an overloaded operator
602            _ => (),
603        };
604
605        // If we didn't find an overloaded deref or index, then assume it's a
606        // built in deref and check the type of the base.
607        let base_ty = deref_base.ty(self.body, tcx).ty;
608        if base_ty.is_raw_ptr() {
609            BorrowedContentSource::DerefRawPointer
610        } else if base_ty.is_mutable_ptr() {
611            BorrowedContentSource::DerefMutableRef
612        } else {
613            BorrowedContentSource::DerefSharedRef
614        }
615    }
616
617    /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
618    /// name where required.
619    pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
620        let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
621
622        // We need to add synthesized lifetimes where appropriate. We do
623        // this by hooking into the pretty printer and telling it to label the
624        // lifetimes without names with the value `'0`.
625        if let ty::Ref(region, ..) = ty.kind() {
626            match region.kind() {
627                ty::ReBound(_, ty::BoundRegion { kind: br, .. })
628                | ty::RePlaceholder(ty::PlaceholderRegion {
629                    bound: ty::BoundRegion { kind: br, .. },
630                    ..
631                }) => printer.region_highlight_mode.highlighting_bound_region(br, counter),
632                _ => {}
633            }
634        }
635
636        ty.print(&mut printer).unwrap();
637        printer.into_buffer()
638    }
639
640    /// Returns the name of the provided `Ty` (that must be a reference)'s region with a
641    /// synthesized lifetime name where required.
642    pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
643        let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
644
645        let region = if let ty::Ref(region, ..) = ty.kind() {
646            match region.kind() {
647                ty::ReBound(_, ty::BoundRegion { kind: br, .. })
648                | ty::RePlaceholder(ty::PlaceholderRegion {
649                    bound: ty::BoundRegion { kind: br, .. },
650                    ..
651                }) => printer.region_highlight_mode.highlighting_bound_region(br, counter),
652                _ => {}
653            }
654            region
655        } else {
656            bug!("ty for annotation of borrow region is not a reference");
657        };
658
659        region.print(&mut printer).unwrap();
660        printer.into_buffer()
661    }
662
663    /// Add a note to region errors and borrow explanations when higher-ranked regions in predicates
664    /// implicitly introduce an "outlives `'static`" constraint.
665    fn add_placeholder_from_predicate_note<G: EmissionGuarantee>(
666        &self,
667        err: &mut Diag<'_, G>,
668        path: &[OutlivesConstraint<'tcx>],
669    ) {
670        let predicate_span = path.iter().find_map(|constraint| {
671            let outlived = constraint.sub;
672            if let Some(origin) = self.regioncx.definitions.get(outlived)
673                && let NllRegionVariableOrigin::Placeholder(_) = origin.origin
674                && let ConstraintCategory::Predicate(span) = constraint.category
675            {
676                Some(span)
677            } else {
678                None
679            }
680        });
681
682        if let Some(span) = predicate_span {
683            err.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
684        }
685    }
686
687    /// Add a label to region errors and borrow explanations when outlives constraints arise from
688    /// proving a type implements `Sized` or `Copy`.
689    fn add_sized_or_copy_bound_info<G: EmissionGuarantee>(
690        &self,
691        err: &mut Diag<'_, G>,
692        blamed_category: ConstraintCategory<'tcx>,
693        path: &[OutlivesConstraint<'tcx>],
694    ) {
695        for sought_category in [ConstraintCategory::SizedBound, ConstraintCategory::CopyBound] {
696            if sought_category != blamed_category
697                && let Some(sought_constraint) = path.iter().find(|c| c.category == sought_category)
698            {
699                let label = format!(
700                    "requirement occurs due to {}",
701                    sought_category.description().trim_end()
702                );
703                err.span_label(sought_constraint.span, label);
704            }
705        }
706    }
707}
708
709/// The span(s) associated to a use of a place.
710#[derive(Copy, Clone, PartialEq, Eq, Debug)]
711pub(super) enum UseSpans<'tcx> {
712    /// The access is caused by capturing a variable for a closure.
713    ClosureUse {
714        /// This is true if the captured variable was from a coroutine.
715        closure_kind: hir::ClosureKind,
716        /// The span of the args of the closure, including the `move` keyword if
717        /// it's present.
718        args_span: Span,
719        /// The span of the use resulting in capture kind
720        /// Check `ty::CaptureInfo` for more details
721        capture_kind_span: Span,
722        /// The span of the use resulting in the captured path
723        /// Check `ty::CaptureInfo` for more details
724        path_span: Span,
725    },
726    /// The access is caused by using a variable as the receiver of a method
727    /// that takes 'self'
728    FnSelfUse {
729        /// The span of the variable being moved
730        var_span: Span,
731        /// The span of the method call on the variable
732        fn_call_span: Span,
733        /// The definition span of the method being called
734        fn_span: Span,
735        kind: CallKind<'tcx>,
736    },
737    /// This access is caused by a `match` or `if let` pattern.
738    PatUse(Span),
739    /// This access has a single span associated to it: common case.
740    OtherUse(Span),
741}
742
743impl UseSpans<'_> {
744    pub(super) fn args_or_use(self) -> Span {
745        match self {
746            UseSpans::ClosureUse { args_span: span, .. }
747            | UseSpans::PatUse(span)
748            | UseSpans::OtherUse(span) => span,
749            UseSpans::FnSelfUse { var_span, .. } => var_span,
750        }
751    }
752
753    /// Returns the span of `self`, in the case of a `ClosureUse` returns the `path_span`
754    pub(super) fn var_or_use_path_span(self) -> Span {
755        match self {
756            UseSpans::ClosureUse { path_span: span, .. }
757            | UseSpans::PatUse(span)
758            | UseSpans::OtherUse(span) => span,
759            UseSpans::FnSelfUse { var_span, .. } => var_span,
760        }
761    }
762
763    /// Returns the span of `self`, in the case of a `ClosureUse` returns the `capture_kind_span`
764    pub(super) fn var_or_use(self) -> Span {
765        match self {
766            UseSpans::ClosureUse { capture_kind_span: span, .. }
767            | UseSpans::PatUse(span)
768            | UseSpans::OtherUse(span) => span,
769            UseSpans::FnSelfUse { var_span, .. } => var_span,
770        }
771    }
772
773    // FIXME(coroutines): Make this just return the `ClosureKind` directly?
774    pub(super) fn coroutine_kind(self) -> Option<CoroutineKind> {
775        match self {
776            UseSpans::ClosureUse {
777                closure_kind: hir::ClosureKind::Coroutine(coroutine_kind),
778                ..
779            } => Some(coroutine_kind),
780            _ => None,
781        }
782    }
783
784    /// Add a span label to the arguments of the closure, if it exists.
785    #[allow(rustc::diagnostic_outside_of_impl)]
786    pub(super) fn args_subdiag(self, err: &mut Diag<'_>, f: impl FnOnce(Span) -> CaptureArgLabel) {
787        if let UseSpans::ClosureUse { args_span, .. } = self {
788            err.subdiagnostic(f(args_span));
789        }
790    }
791
792    /// Add a span label to the use of the captured variable, if it exists.
793    /// only adds label to the `path_span`
794    #[allow(rustc::diagnostic_outside_of_impl)]
795    pub(super) fn var_path_only_subdiag(
796        self,
797        err: &mut Diag<'_>,
798        action: crate::InitializationRequiringAction,
799    ) {
800        use CaptureVarPathUseCause::*;
801
802        use crate::InitializationRequiringAction::*;
803        if let UseSpans::ClosureUse { closure_kind, path_span, .. } = self {
804            match closure_kind {
805                hir::ClosureKind::Coroutine(_) => {
806                    err.subdiagnostic(match action {
807                        Borrow => BorrowInCoroutine { path_span },
808                        MatchOn | Use => UseInCoroutine { path_span },
809                        Assignment => AssignInCoroutine { path_span },
810                        PartialAssignment => AssignPartInCoroutine { path_span },
811                    });
812                }
813                hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
814                    err.subdiagnostic(match action {
815                        Borrow => BorrowInClosure { path_span },
816                        MatchOn | Use => UseInClosure { path_span },
817                        Assignment => AssignInClosure { path_span },
818                        PartialAssignment => AssignPartInClosure { path_span },
819                    });
820                }
821            }
822        }
823    }
824
825    /// Add a subdiagnostic to the use of the captured variable, if it exists.
826    #[allow(rustc::diagnostic_outside_of_impl)]
827    pub(super) fn var_subdiag(
828        self,
829        err: &mut Diag<'_>,
830        kind: Option<rustc_middle::mir::BorrowKind>,
831        f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause,
832    ) {
833        if let UseSpans::ClosureUse { closure_kind, capture_kind_span, path_span, .. } = self {
834            if capture_kind_span != path_span {
835                err.subdiagnostic(match kind {
836                    Some(kd) => match kd {
837                        rustc_middle::mir::BorrowKind::Shared
838                        | rustc_middle::mir::BorrowKind::Fake(_) => {
839                            CaptureVarKind::Immut { kind_span: capture_kind_span }
840                        }
841
842                        rustc_middle::mir::BorrowKind::Mut { .. } => {
843                            CaptureVarKind::Mut { kind_span: capture_kind_span }
844                        }
845                    },
846                    None => CaptureVarKind::Move { kind_span: capture_kind_span },
847                });
848            };
849            let diag = f(closure_kind, path_span);
850            err.subdiagnostic(diag);
851        }
852    }
853
854    /// Returns `false` if this place is not used in a closure.
855    pub(super) fn for_closure(&self) -> bool {
856        match *self {
857            UseSpans::ClosureUse { closure_kind, .. } => {
858                matches!(closure_kind, hir::ClosureKind::Closure)
859            }
860            _ => false,
861        }
862    }
863
864    /// Returns `false` if this place is not used in a coroutine.
865    pub(super) fn for_coroutine(&self) -> bool {
866        match *self {
867            // FIXME(coroutines): Do we want this to apply to synthetic coroutines?
868            UseSpans::ClosureUse { closure_kind, .. } => {
869                matches!(closure_kind, hir::ClosureKind::Coroutine(..))
870            }
871            _ => false,
872        }
873    }
874
875    pub(super) fn or_else<F>(self, if_other: F) -> Self
876    where
877        F: FnOnce() -> Self,
878    {
879        match self {
880            closure @ UseSpans::ClosureUse { .. } => closure,
881            UseSpans::PatUse(_) | UseSpans::OtherUse(_) => if_other(),
882            fn_self @ UseSpans::FnSelfUse { .. } => fn_self,
883        }
884    }
885}
886
887pub(super) enum BorrowedContentSource<'tcx> {
888    DerefRawPointer,
889    DerefMutableRef,
890    DerefSharedRef,
891    OverloadedDeref(Ty<'tcx>),
892    OverloadedIndex(Ty<'tcx>),
893}
894
895impl<'tcx> BorrowedContentSource<'tcx> {
896    pub(super) fn describe_for_unnamed_place(&self, tcx: TyCtxt<'_>) -> String {
897        match *self {
898            BorrowedContentSource::DerefRawPointer => "a raw pointer".to_string(),
899            BorrowedContentSource::DerefSharedRef => "a shared reference".to_string(),
900            BorrowedContentSource::DerefMutableRef => "a mutable reference".to_string(),
901            BorrowedContentSource::OverloadedDeref(ty) => ty
902                .ty_adt_def()
903                .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? {
904                    name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")),
905                    _ => None,
906                })
907                .unwrap_or_else(|| format!("dereference of `{ty}`")),
908            BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{ty}`"),
909        }
910    }
911
912    pub(super) fn describe_for_named_place(&self) -> Option<&'static str> {
913        match *self {
914            BorrowedContentSource::DerefRawPointer => Some("raw pointer"),
915            BorrowedContentSource::DerefSharedRef => Some("shared reference"),
916            BorrowedContentSource::DerefMutableRef => Some("mutable reference"),
917            // Overloaded deref and index operators should be evaluated into a
918            // temporary. So we don't need a description here.
919            BorrowedContentSource::OverloadedDeref(_)
920            | BorrowedContentSource::OverloadedIndex(_) => None,
921        }
922    }
923
924    pub(super) fn describe_for_immutable_place(&self, tcx: TyCtxt<'_>) -> String {
925        match *self {
926            BorrowedContentSource::DerefRawPointer => "a `*const` pointer".to_string(),
927            BorrowedContentSource::DerefSharedRef => "a `&` reference".to_string(),
928            BorrowedContentSource::DerefMutableRef => {
929                bug!("describe_for_immutable_place: DerefMutableRef isn't immutable")
930            }
931            BorrowedContentSource::OverloadedDeref(ty) => ty
932                .ty_adt_def()
933                .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? {
934                    name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")),
935                    _ => None,
936                })
937                .unwrap_or_else(|| format!("dereference of `{ty}`")),
938            BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{ty}`"),
939        }
940    }
941
942    fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> {
943        match *func.kind() {
944            ty::FnDef(def_id, args) => {
945                let trait_id = tcx.trait_of_item(def_id)?;
946
947                if tcx.is_lang_item(trait_id, LangItem::Deref)
948                    || tcx.is_lang_item(trait_id, LangItem::DerefMut)
949                {
950                    Some(BorrowedContentSource::OverloadedDeref(args.type_at(0)))
951                } else if tcx.is_lang_item(trait_id, LangItem::Index)
952                    || tcx.is_lang_item(trait_id, LangItem::IndexMut)
953                {
954                    Some(BorrowedContentSource::OverloadedIndex(args.type_at(0)))
955                } else {
956                    None
957                }
958            }
959            _ => None,
960        }
961    }
962}
963
964/// Helper struct for `explain_captures`.
965struct CapturedMessageOpt {
966    is_partial_move: bool,
967    is_loop_message: bool,
968    is_move_msg: bool,
969    is_loop_move: bool,
970    has_suggest_reborrow: bool,
971    maybe_reinitialized_locations_is_empty: bool,
972}
973
974impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
975    /// Finds the spans associated to a move or copy of move_place at location.
976    pub(super) fn move_spans(
977        &self,
978        moved_place: PlaceRef<'tcx>, // Could also be an upvar.
979        location: Location,
980    ) -> UseSpans<'tcx> {
981        use self::UseSpans::*;
982
983        let Some(stmt) = self.body[location.block].statements.get(location.statement_index) else {
984            return OtherUse(self.body.source_info(location).span);
985        };
986
987        debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
988        if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind
989            && let AggregateKind::Closure(def_id, _) | AggregateKind::Coroutine(def_id, _) = **kind
990        {
991            debug!("move_spans: def_id={:?} places={:?}", def_id, places);
992            let def_id = def_id.expect_local();
993            if let Some((args_span, closure_kind, capture_kind_span, path_span)) =
994                self.closure_span(def_id, moved_place, places)
995            {
996                return ClosureUse { closure_kind, args_span, capture_kind_span, path_span };
997            }
998        }
999
1000        // StatementKind::FakeRead only contains a def_id if they are introduced as a result
1001        // of pattern matching within a closure.
1002        if let StatementKind::FakeRead(box (cause, place)) = stmt.kind {
1003            match cause {
1004                FakeReadCause::ForMatchedPlace(Some(closure_def_id))
1005                | FakeReadCause::ForLet(Some(closure_def_id)) => {
1006                    debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place);
1007                    let places = &[Operand::Move(place)];
1008                    if let Some((args_span, closure_kind, capture_kind_span, path_span)) =
1009                        self.closure_span(closure_def_id, moved_place, IndexSlice::from_raw(places))
1010                    {
1011                        return ClosureUse {
1012                            closure_kind,
1013                            args_span,
1014                            capture_kind_span,
1015                            path_span,
1016                        };
1017                    }
1018                }
1019                _ => {}
1020            }
1021        }
1022
1023        let normal_ret =
1024            if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) {
1025                PatUse(stmt.source_info.span)
1026            } else {
1027                OtherUse(stmt.source_info.span)
1028            };
1029
1030        // We are trying to find MIR of the form:
1031        // ```
1032        // _temp = _moved_val;
1033        // ...
1034        // FnSelfCall(_temp, ...)
1035        // ```
1036        //
1037        // where `_moved_val` is the place we generated the move error for,
1038        // `_temp` is some other local, and `FnSelfCall` is a function
1039        // that has a `self` parameter.
1040
1041        let target_temp = match stmt.kind {
1042            StatementKind::Assign(box (temp, _)) if temp.as_local().is_some() => {
1043                temp.as_local().unwrap()
1044            }
1045            _ => return normal_ret,
1046        };
1047
1048        debug!("move_spans: target_temp = {:?}", target_temp);
1049
1050        if let Some(Terminator {
1051            kind: TerminatorKind::Call { fn_span, call_source, .. }, ..
1052        }) = &self.body[location.block].terminator
1053        {
1054            let Some((method_did, method_args)) =
1055                find_self_call(self.infcx.tcx, self.body, target_temp, location.block)
1056            else {
1057                return normal_ret;
1058            };
1059
1060            let kind = call_kind(
1061                self.infcx.tcx,
1062                self.infcx.typing_env(self.infcx.param_env),
1063                method_did,
1064                method_args,
1065                *fn_span,
1066                call_source.from_hir_call(),
1067                self.infcx.tcx.fn_arg_idents(method_did)[0],
1068            );
1069
1070            return FnSelfUse {
1071                var_span: stmt.source_info.span,
1072                fn_call_span: *fn_span,
1073                fn_span: self.infcx.tcx.def_span(method_did),
1074                kind,
1075            };
1076        }
1077
1078        normal_ret
1079    }
1080
1081    /// Finds the span of arguments of a closure (within `maybe_closure_span`)
1082    /// and its usage of the local assigned at `location`.
1083    /// This is done by searching in statements succeeding `location`
1084    /// and originating from `maybe_closure_span`.
1085    pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx> {
1086        use self::UseSpans::*;
1087        debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
1088
1089        let target = match self.body[location.block].statements.get(location.statement_index) {
1090            Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) => {
1091                if let Some(local) = place.as_local() {
1092                    local
1093                } else {
1094                    return OtherUse(use_span);
1095                }
1096            }
1097            _ => return OtherUse(use_span),
1098        };
1099
1100        if self.body.local_kind(target) != LocalKind::Temp {
1101            // operands are always temporaries.
1102            return OtherUse(use_span);
1103        }
1104
1105        // drop and replace might have moved the assignment to the next block
1106        let maybe_additional_statement =
1107            if let TerminatorKind::Drop { target: drop_target, .. } =
1108                self.body[location.block].terminator().kind
1109            {
1110                self.body[drop_target].statements.first()
1111            } else {
1112                None
1113            };
1114
1115        let statements =
1116            self.body[location.block].statements[location.statement_index + 1..].iter();
1117
1118        for stmt in statements.chain(maybe_additional_statement) {
1119            if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind {
1120                let (&def_id, is_coroutine) = match kind {
1121                    box AggregateKind::Closure(def_id, _) => (def_id, false),
1122                    box AggregateKind::Coroutine(def_id, _) => (def_id, true),
1123                    _ => continue,
1124                };
1125                let def_id = def_id.expect_local();
1126
1127                debug!(
1128                    "borrow_spans: def_id={:?} is_coroutine={:?} places={:?}",
1129                    def_id, is_coroutine, places
1130                );
1131                if let Some((args_span, closure_kind, capture_kind_span, path_span)) =
1132                    self.closure_span(def_id, Place::from(target).as_ref(), places)
1133                {
1134                    return ClosureUse { closure_kind, args_span, capture_kind_span, path_span };
1135                } else {
1136                    return OtherUse(use_span);
1137                }
1138            }
1139
1140            if use_span != stmt.source_info.span {
1141                break;
1142            }
1143        }
1144
1145        OtherUse(use_span)
1146    }
1147
1148    /// Finds the spans of a captured place within a closure or coroutine.
1149    /// The first span is the location of the use resulting in the capture kind of the capture
1150    /// The second span is the location the use resulting in the captured path of the capture
1151    fn closure_span(
1152        &self,
1153        def_id: LocalDefId,
1154        target_place: PlaceRef<'tcx>,
1155        places: &IndexSlice<FieldIdx, Operand<'tcx>>,
1156    ) -> Option<(Span, hir::ClosureKind, Span, Span)> {
1157        debug!(
1158            "closure_span: def_id={:?} target_place={:?} places={:?}",
1159            def_id, target_place, places
1160        );
1161        let hir_id = self.infcx.tcx.local_def_id_to_hir_id(def_id);
1162        let expr = &self.infcx.tcx.hir_expect_expr(hir_id).kind;
1163        debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
1164        if let &hir::ExprKind::Closure(&hir::Closure { kind, fn_decl_span, .. }) = expr {
1165            for (captured_place, place) in
1166                self.infcx.tcx.closure_captures(def_id).iter().zip(places)
1167            {
1168                match place {
1169                    Operand::Copy(place) | Operand::Move(place)
1170                        if target_place == place.as_ref() =>
1171                    {
1172                        debug!("closure_span: found captured local {:?}", place);
1173                        return Some((
1174                            fn_decl_span,
1175                            kind,
1176                            captured_place.get_capture_kind_span(self.infcx.tcx),
1177                            captured_place.get_path_span(self.infcx.tcx),
1178                        ));
1179                    }
1180                    _ => {}
1181                }
1182            }
1183        }
1184        None
1185    }
1186
1187    /// Helper to retrieve span(s) of given borrow from the current MIR
1188    /// representation
1189    pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx> {
1190        let span = self.body.source_info(borrow.reserve_location).span;
1191        self.borrow_spans(span, borrow.reserve_location)
1192    }
1193
1194    #[allow(rustc::diagnostic_outside_of_impl)]
1195    #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
1196    fn explain_captures(
1197        &mut self,
1198        err: &mut Diag<'infcx>,
1199        span: Span,
1200        move_span: Span,
1201        move_spans: UseSpans<'tcx>,
1202        moved_place: Place<'tcx>,
1203        msg_opt: CapturedMessageOpt,
1204    ) {
1205        let CapturedMessageOpt {
1206            is_partial_move: is_partial,
1207            is_loop_message,
1208            is_move_msg,
1209            is_loop_move,
1210            has_suggest_reborrow,
1211            maybe_reinitialized_locations_is_empty,
1212        } = msg_opt;
1213        if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
1214            let place_name = self
1215                .describe_place(moved_place.as_ref())
1216                .map(|n| format!("`{n}`"))
1217                .unwrap_or_else(|| "value".to_owned());
1218            match kind {
1219                CallKind::FnCall { fn_trait_id, self_ty }
1220                    if self.infcx.tcx.is_lang_item(fn_trait_id, LangItem::FnOnce) =>
1221                {
1222                    err.subdiagnostic(CaptureReasonLabel::Call {
1223                        fn_call_span,
1224                        place_name: &place_name,
1225                        is_partial,
1226                        is_loop_message,
1227                    });
1228                    // Check if the move occurs on a value because of a call on a closure that comes
1229                    // from a type parameter `F: FnOnce()`. If so, we provide a targeted `note`:
1230                    // ```
1231                    // error[E0382]: use of moved value: `blk`
1232                    //   --> $DIR/once-cant-call-twice-on-heap.rs:8:5
1233                    //    |
1234                    // LL | fn foo<F:FnOnce()>(blk: F) {
1235                    //    |                    --- move occurs because `blk` has type `F`, which does not implement the `Copy` trait
1236                    // LL | blk();
1237                    //    | ----- `blk` moved due to this call
1238                    // LL | blk();
1239                    //    | ^^^ value used here after move
1240                    //    |
1241                    // note: `FnOnce` closures can only be called once
1242                    //   --> $DIR/once-cant-call-twice-on-heap.rs:6:10
1243                    //    |
1244                    // LL | fn foo<F:FnOnce()>(blk: F) {
1245                    //    |        ^^^^^^^^ `F` is made to be an `FnOnce` closure here
1246                    // LL | blk();
1247                    //    | ----- this value implements `FnOnce`, which causes it to be moved when called
1248                    // ```
1249                    if let ty::Param(param_ty) = *self_ty.kind()
1250                        && let generics = self.infcx.tcx.generics_of(self.mir_def_id())
1251                        && let param = generics.type_param(param_ty, self.infcx.tcx)
1252                        && let Some(hir_generics) = self
1253                            .infcx
1254                            .tcx
1255                            .typeck_root_def_id(self.mir_def_id().to_def_id())
1256                            .as_local()
1257                            .and_then(|def_id| self.infcx.tcx.hir_get_generics(def_id))
1258                        && let spans = hir_generics
1259                            .predicates
1260                            .iter()
1261                            .filter_map(|pred| match pred.kind {
1262                                hir::WherePredicateKind::BoundPredicate(pred) => Some(pred),
1263                                _ => None,
1264                            })
1265                            .filter(|pred| {
1266                                if let Some((id, _)) = pred.bounded_ty.as_generic_param() {
1267                                    id == param.def_id
1268                                } else {
1269                                    false
1270                                }
1271                            })
1272                            .flat_map(|pred| pred.bounds)
1273                            .filter_map(|bound| {
1274                                if let Some(trait_ref) = bound.trait_ref()
1275                                    && let Some(trait_def_id) = trait_ref.trait_def_id()
1276                                    && trait_def_id == fn_trait_id
1277                                {
1278                                    Some(bound.span())
1279                                } else {
1280                                    None
1281                                }
1282                            })
1283                            .collect::<Vec<Span>>()
1284                        && !spans.is_empty()
1285                    {
1286                        let mut span: MultiSpan = spans.clone().into();
1287                        err.arg("ty", param_ty.to_string());
1288                        let msg = err.dcx.eagerly_translate_to_string(
1289                            fluent::borrowck_moved_a_fn_once_in_call_def,
1290                            err.args.iter(),
1291                        );
1292                        err.remove_arg("ty");
1293                        for sp in spans {
1294                            span.push_span_label(sp, msg.clone());
1295                        }
1296                        span.push_span_label(
1297                            fn_call_span,
1298                            fluent::borrowck_moved_a_fn_once_in_call,
1299                        );
1300                        err.span_note(span, fluent::borrowck_moved_a_fn_once_in_call_call);
1301                    } else {
1302                        err.subdiagnostic(CaptureReasonNote::FnOnceMoveInCall { var_span });
1303                    }
1304                }
1305                CallKind::Operator { self_arg, trait_id, .. } => {
1306                    let self_arg = self_arg.unwrap();
1307                    err.subdiagnostic(CaptureReasonLabel::OperatorUse {
1308                        fn_call_span,
1309                        place_name: &place_name,
1310                        is_partial,
1311                        is_loop_message,
1312                    });
1313                    if self.fn_self_span_reported.insert(fn_span) {
1314                        let lang = self.infcx.tcx.lang_items();
1315                        err.subdiagnostic(
1316                            if [lang.not_trait(), lang.deref_trait(), lang.neg_trait()]
1317                                .contains(&Some(trait_id))
1318                            {
1319                                CaptureReasonNote::UnOpMoveByOperator { span: self_arg.span }
1320                            } else {
1321                                CaptureReasonNote::LhsMoveByOperator { span: self_arg.span }
1322                            },
1323                        );
1324                    }
1325                }
1326                CallKind::Normal { self_arg, desugaring, method_did, method_args } => {
1327                    let self_arg = self_arg.unwrap();
1328                    let mut has_sugg = false;
1329                    let tcx = self.infcx.tcx;
1330                    // Avoid pointing to the same function in multiple different
1331                    // error messages.
1332                    if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
1333                        self.explain_iterator_advancement_in_for_loop_if_applicable(
1334                            err,
1335                            span,
1336                            &move_spans,
1337                        );
1338
1339                        let func = tcx.def_path_str(method_did);
1340                        err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
1341                            func,
1342                            place_name: place_name.clone(),
1343                            span: self_arg.span,
1344                        });
1345                    }
1346                    let parent_did = tcx.parent(method_did);
1347                    let parent_self_ty =
1348                        matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. })
1349                            .then_some(parent_did)
1350                            .and_then(|did| match tcx.type_of(did).instantiate_identity().kind() {
1351                                ty::Adt(def, ..) => Some(def.did()),
1352                                _ => None,
1353                            });
1354                    let is_option_or_result = parent_self_ty.is_some_and(|def_id| {
1355                        matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
1356                    });
1357                    if is_option_or_result && maybe_reinitialized_locations_is_empty {
1358                        err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
1359                    }
1360                    if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
1361                        let ty = moved_place.ty(self.body, tcx).ty;
1362                        let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) {
1363                            Some(def_id) => type_known_to_meet_bound_modulo_regions(
1364                                self.infcx,
1365                                self.infcx.param_env,
1366                                Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty),
1367                                def_id,
1368                            ),
1369                            _ => false,
1370                        };
1371                        if suggest {
1372                            err.subdiagnostic(CaptureReasonSuggest::IterateSlice {
1373                                ty,
1374                                span: move_span.shrink_to_lo(),
1375                            });
1376                        }
1377
1378                        err.subdiagnostic(CaptureReasonLabel::ImplicitCall {
1379                            fn_call_span,
1380                            place_name: &place_name,
1381                            is_partial,
1382                            is_loop_message,
1383                        });
1384                        // If the moved place was a `&mut` ref, then we can
1385                        // suggest to reborrow it where it was moved, so it
1386                        // will still be valid by the time we get to the usage.
1387                        if let ty::Ref(_, _, hir::Mutability::Mut) =
1388                            moved_place.ty(self.body, self.infcx.tcx).ty.kind()
1389                        {
1390                            // Suggest `reborrow` in other place for following situations:
1391                            // 1. If we are in a loop this will be suggested later.
1392                            // 2. If the moved value is a mut reference, it is used in a
1393                            // generic function and the corresponding arg's type is generic param.
1394                            if !is_loop_move && !has_suggest_reborrow {
1395                                self.suggest_reborrow(
1396                                    err,
1397                                    move_span.shrink_to_lo(),
1398                                    moved_place.as_ref(),
1399                                );
1400                            }
1401                        }
1402                    } else {
1403                        if let Some((CallDesugaringKind::Await, _)) = desugaring {
1404                            err.subdiagnostic(CaptureReasonLabel::Await {
1405                                fn_call_span,
1406                                place_name: &place_name,
1407                                is_partial,
1408                                is_loop_message,
1409                            });
1410                        } else {
1411                            err.subdiagnostic(CaptureReasonLabel::MethodCall {
1412                                fn_call_span,
1413                                place_name: &place_name,
1414                                is_partial,
1415                                is_loop_message,
1416                            });
1417                        }
1418                        // Erase and shadow everything that could be passed to the new infcx.
1419                        let ty = moved_place.ty(self.body, tcx).ty;
1420
1421                        if let ty::Adt(def, args) = ty.peel_refs().kind()
1422                            && tcx.is_lang_item(def.did(), LangItem::Pin)
1423                            && let ty::Ref(_, _, hir::Mutability::Mut) = args.type_at(0).kind()
1424                            && let self_ty = self.infcx.instantiate_binder_with_fresh_vars(
1425                                fn_call_span,
1426                                BoundRegionConversionTime::FnCall,
1427                                tcx.fn_sig(method_did).instantiate(tcx, method_args).input(0),
1428                            )
1429                            && self.infcx.can_eq(self.infcx.param_env, ty, self_ty)
1430                        {
1431                            err.subdiagnostic(CaptureReasonSuggest::FreshReborrow {
1432                                span: move_span.shrink_to_hi(),
1433                            });
1434                            has_sugg = true;
1435                        }
1436                        if let Some(clone_trait) = tcx.lang_items().clone_trait() {
1437                            let sugg = if moved_place
1438                                .iter_projections()
1439                                .any(|(_, elem)| matches!(elem, ProjectionElem::Deref))
1440                            {
1441                                let (start, end) = if let Some(expr) = self.find_expr(move_span)
1442                                    && let Some(_) = self.clone_on_reference(expr)
1443                                    && let hir::ExprKind::MethodCall(_, rcvr, _, _) = expr.kind
1444                                {
1445                                    (move_span.shrink_to_lo(), move_span.with_lo(rcvr.span.hi()))
1446                                } else {
1447                                    (move_span.shrink_to_lo(), move_span.shrink_to_hi())
1448                                };
1449                                vec![
1450                                    // We use the fully-qualified path because `.clone()` can
1451                                    // sometimes choose `<&T as Clone>` instead of `<T as Clone>`
1452                                    // when going through auto-deref, so this ensures that doesn't
1453                                    // happen, causing suggestions for `.clone().clone()`.
1454                                    (start, format!("<{ty} as Clone>::clone(&")),
1455                                    (end, ")".to_string()),
1456                                ]
1457                            } else {
1458                                vec![(move_span.shrink_to_hi(), ".clone()".to_string())]
1459                            };
1460                            if let Some(errors) = self.infcx.type_implements_trait_shallow(
1461                                clone_trait,
1462                                ty,
1463                                self.infcx.param_env,
1464                            ) && !has_sugg
1465                            {
1466                                let msg = match &errors[..] {
1467                                    [] => "you can `clone` the value and consume it, but this \
1468                                           might not be your desired behavior"
1469                                        .to_string(),
1470                                    [error] => {
1471                                        format!(
1472                                            "you could `clone` the value and consume it, if the \
1473                                             `{}` trait bound could be satisfied",
1474                                            error.obligation.predicate,
1475                                        )
1476                                    }
1477                                    _ => {
1478                                        format!(
1479                                            "you could `clone` the value and consume it, if the \
1480                                             following trait bounds could be satisfied: {}",
1481                                            listify(&errors, |e: &FulfillmentError<'tcx>| format!(
1482                                                "`{}`",
1483                                                e.obligation.predicate
1484                                            ))
1485                                            .unwrap(),
1486                                        )
1487                                    }
1488                                };
1489                                err.multipart_suggestion_verbose(
1490                                    msg,
1491                                    sugg,
1492                                    Applicability::MaybeIncorrect,
1493                                );
1494                                for error in errors {
1495                                    if let FulfillmentErrorCode::Select(
1496                                        SelectionError::Unimplemented,
1497                                    ) = error.code
1498                                        && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
1499                                            pred,
1500                                        )) = error.obligation.predicate.kind().skip_binder()
1501                                    {
1502                                        self.infcx.err_ctxt().suggest_derive(
1503                                            &error.obligation,
1504                                            err,
1505                                            error.obligation.predicate.kind().rebind(pred),
1506                                        );
1507                                    }
1508                                }
1509                            }
1510                        }
1511                    }
1512                }
1513                // Other desugarings takes &self, which cannot cause a move
1514                _ => {}
1515            }
1516        } else {
1517            if move_span != span || is_loop_message {
1518                err.subdiagnostic(CaptureReasonLabel::MovedHere {
1519                    move_span,
1520                    is_partial,
1521                    is_move_msg,
1522                    is_loop_message,
1523                });
1524            }
1525            // If the move error occurs due to a loop, don't show
1526            // another message for the same span
1527            if !is_loop_message {
1528                move_spans.var_subdiag(err, None, |kind, var_span| match kind {
1529                    hir::ClosureKind::Coroutine(_) => {
1530                        CaptureVarCause::PartialMoveUseInCoroutine { var_span, is_partial }
1531                    }
1532                    hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
1533                        CaptureVarCause::PartialMoveUseInClosure { var_span, is_partial }
1534                    }
1535                })
1536            }
1537        }
1538    }
1539
1540    /// Skip over locals that begin with an underscore or have no name
1541    pub(crate) fn local_excluded_from_unused_mut_lint(&self, index: Local) -> bool {
1542        self.local_name(index).is_none_or(|name| name.as_str().starts_with('_'))
1543    }
1544}