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