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