Skip to main content

rustc_borrowck/diagnostics/
move_errors.rs

1use rustc_abi::FieldIdx;
2use rustc_data_structures::fx::FxHashSet;
3use rustc_errors::{Applicability, Diag, MultiSpan};
4use rustc_hir::def::DefKind;
5use rustc_hir::intravisit::Visitor;
6use rustc_hir::{self as hir, CaptureBy, ExprKind, HirId, Node};
7use rustc_middle::mir::*;
8use rustc_middle::ty::{self, Ty, TyCtxt};
9use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
10use rustc_span::def_id::DefId;
11use rustc_span::{BytePos, ExpnKind, MacroKind, Span, bug, sym};
12use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
13use rustc_trait_selection::infer::InferCtxtExt;
14use tracing::debug;
15
16use crate::MirBorrowckCtxt;
17use crate::diagnostics::{
18    BorrowedContentSource, CapturedMessageOpt, CloneSuggestion, DescribePlaceOpt, UseSpans,
19};
20use crate::prefixes::PrefixSet;
21
22#[derive(#[automatically_derived]impl<'tcx> ::core::fmt::Debug for
    IllegalMoveOriginKind<'tcx> {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::BorrowedContent { target_place: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "BorrowedContent target_place", &__self_0),
            Self::InteriorOfTypeWithDestructor { container_ty: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "InteriorOfTypeWithDestructor container_ty", &__self_0),
            Self::InteriorOfSliceOrArray { ty: __self_0, is_index: __self_1 }
                =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "InteriorOfSliceOrArray ty is_index", __self_0, &__self_1),
        }
    }
}Debug)]
23pub(crate) enum IllegalMoveOriginKind<'tcx> {
24    /// Illegal move due to attempt to move from behind a reference.
25    BorrowedContent {
26        /// The place the reference refers to: if erroneous code was trying to
27        /// move from `(*x).f` this will be `*x`.
28        target_place: Place<'tcx>,
29    },
30
31    /// Illegal move due to attempt to move from field of an ADT that
32    /// implements `Drop`. Rust maintains invariant that all `Drop`
33    /// ADT's remain fully-initialized so that user-defined destructor
34    /// can safely read from all of the ADT's fields.
35    InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> },
36
37    /// Illegal move due to attempt to move out of a slice or array.
38    InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool },
39}
40
41#[derive(#[automatically_derived]impl<'tcx> ::core::fmt::Debug for MoveError<'tcx> {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f,
            "MoveError place location kind", &self.place, &self.location,
            &&self.kind)
    }
}Debug)]
42pub(crate) struct MoveError<'tcx> {
43    place: Place<'tcx>,
44    location: Location,
45    kind: IllegalMoveOriginKind<'tcx>,
46}
47
48impl<'tcx> MoveError<'tcx> {
49    pub(crate) fn new(
50        place: Place<'tcx>,
51        location: Location,
52        kind: IllegalMoveOriginKind<'tcx>,
53    ) -> Self {
54        MoveError { place, location, kind }
55    }
56}
57
58// Often when desugaring a pattern match we may have many individual moves in
59// MIR that are all part of one operation from the user's point-of-view. For
60// example:
61//
62// let (x, y) = foo()
63//
64// would move x from the 0 field of some temporary, and y from the 1 field. We
65// group such errors together for cleaner error reporting.
66//
67// Errors are kept separate if they are from places with different parent move
68// paths. For example, this generates two errors:
69//
70// let (&x, &y) = (&String::new(), &String::new());
71#[derive(#[automatically_derived]impl<'tcx> ::core::fmt::Debug for
    GroupedMoveError<'tcx> {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::MovesFromPlace {
                original_path: __self_0,
                span: __self_1,
                move_from: __self_2,
                kind: __self_3,
                binds_to: __self_4 } =>
                ::core::fmt::Formatter::debug_struct_field5_finish(f,
                    "MovesFromPlace original_path span move_from kind binds_to",
                    __self_0, __self_1, __self_2, __self_3, &__self_4),
            Self::MovesFromValue {
                original_path: __self_0,
                span: __self_1,
                move_from: __self_2,
                kind: __self_3,
                binds_to: __self_4 } =>
                ::core::fmt::Formatter::debug_struct_field5_finish(f,
                    "MovesFromValue original_path span move_from kind binds_to",
                    __self_0, __self_1, __self_2, __self_3, &__self_4),
            Self::OtherIllegalMove {
                original_path: __self_0, use_spans: __self_1, kind: __self_2 }
                =>
                ::core::fmt::Formatter::debug_struct_field3_finish(f,
                    "OtherIllegalMove original_path use_spans kind", __self_0,
                    __self_1, &__self_2),
        }
    }
}Debug)]
72enum GroupedMoveError<'tcx> {
73    // Place expression can't be moved from,
74    // e.g., match x[0] { s => (), } where x: &[String]
75    MovesFromPlace {
76        original_path: Place<'tcx>,
77        span: Span,
78        move_from: Place<'tcx>,
79        kind: IllegalMoveOriginKind<'tcx>,
80        binds_to: Vec<Local>,
81    },
82    // Part of a value expression can't be moved from,
83    // e.g., match &String::new() { &x => (), }
84    MovesFromValue {
85        original_path: Place<'tcx>,
86        span: Span,
87        move_from: MovePathIndex,
88        kind: IllegalMoveOriginKind<'tcx>,
89        binds_to: Vec<Local>,
90    },
91    // Everything that isn't from pattern matching.
92    OtherIllegalMove {
93        original_path: Place<'tcx>,
94        use_spans: UseSpans<'tcx>,
95        kind: IllegalMoveOriginKind<'tcx>,
96    },
97}
98
99struct PatternBindingInfo {
100    pat_span: Span,
101    binding_spans: Vec<Span>,
102    has_mutable_by_value_binding: bool,
103}
104
105impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> {
106    pub(crate) fn report_move_errors(&mut self) {
107        let grouped_errors = self.group_move_errors();
108        for error in grouped_errors {
109            self.report(error);
110        }
111    }
112
113    fn group_move_errors(&mut self) -> Vec<GroupedMoveError<'tcx>> {
114        let mut grouped_errors = Vec::new();
115        let errors = std::mem::take(&mut self.move_errors);
116        for error in errors {
117            self.append_to_grouped_errors(&mut grouped_errors, error);
118        }
119        grouped_errors
120    }
121
122    fn append_to_grouped_errors(
123        &self,
124        grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
125        MoveError { place: original_path, location, kind }: MoveError<'tcx>,
126    ) {
127        // Note: that the only time we assign a place isn't a temporary
128        // to a user variable is when initializing it.
129        // If that ever stops being the case, then the ever initialized
130        // flow could be used.
131        if let Some(StatementKind::Assign((place, Rvalue::Use(Operand::Move(move_from), _)))) =
132            self.body.basic_blocks[location.block]
133                .statements
134                .get(location.statement_index)
135                .map(|stmt| &stmt.kind)
136            && let Some(local) = place.as_local()
137        {
138            let local_decl = &self.body.local_decls[local];
139            // opt_match_place is the
140            // match_span is the span of the expression being matched on
141            // match *x.y { ... }        match_place is Some(*x.y)
142            //       ^^^^                match_span is the span of *x.y
143            //
144            // opt_match_place is None for let [mut] x = ... statements,
145            // whether or not the right-hand side is a place expression
146            if let LocalInfo::User(BindingForm::Var(VarBindingForm {
147                opt_match_place: Some((opt_match_place, match_span)),
148                ..
149            })) = *local_decl.local_info()
150            {
151                let stmt_source_info = self.body.source_info(location);
152                self.append_binding_error(
153                    grouped_errors,
154                    kind,
155                    original_path,
156                    *move_from,
157                    local,
158                    opt_match_place,
159                    match_span,
160                    stmt_source_info.span,
161                );
162                return;
163            }
164        }
165
166        let move_spans = self.move_spans(original_path.as_ref(), location);
167        grouped_errors.push(GroupedMoveError::OtherIllegalMove {
168            use_spans: move_spans,
169            original_path,
170            kind,
171        });
172    }
173
174    fn append_binding_error(
175        &self,
176        grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
177        kind: IllegalMoveOriginKind<'tcx>,
178        original_path: Place<'tcx>,
179        move_from: Place<'tcx>,
180        bind_to: Local,
181        match_place: Option<Place<'tcx>>,
182        match_span: Span,
183        statement_span: Span,
184    ) {
185        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs:185",
                        "rustc_borrowck::diagnostics::move_errors",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
                        ::tracing_core::__macro_support::Option::Some(185u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
                        ::tracing_core::field::FieldSet::new(&["message",
                                        {
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("match_place")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("match_place");
                                            NAME.as_str()
                                        },
                                        {
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("match_span")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("match_span");
                                            NAME.as_str()
                                        }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("append_binding_error")
                                            as &dyn ::tracing::field::Value)),
                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&match_place)
                                            as &dyn ::tracing::field::Value)),
                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&match_span)
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(?match_place, ?match_span, "append_binding_error");
186
187        let from_simple_let = match_place.is_none();
188        let match_place = match_place.unwrap_or(move_from);
189
190        match self.move_data.rev_lookup.find(match_place.as_ref()) {
191            // Error with the match place
192            LookupResult::Parent(_) => {
193                for ge in &mut *grouped_errors {
194                    if let GroupedMoveError::MovesFromPlace { span, binds_to, .. } = ge
195                        && match_span == *span
196                    {
197                        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs:197",
                        "rustc_borrowck::diagnostics::move_errors",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
                        ::tracing_core::__macro_support::Option::Some(197u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("appending local({0:?}) to list",
                                                    bind_to) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("appending local({bind_to:?}) to list");
198                        if !binds_to.is_empty() {
199                            binds_to.push(bind_to);
200                        }
201                        return;
202                    }
203                }
204                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs:204",
                        "rustc_borrowck::diagnostics::move_errors",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
                        ::tracing_core::__macro_support::Option::Some(204u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("found a new move error location")
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("found a new move error location");
205
206                // Don't need to point to x in let x = ... .
207                let (binds_to, span) = if from_simple_let {
208                    (::alloc::vec::Vec::new()vec![], statement_span)
209                } else {
210                    (::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [bind_to]))vec![bind_to], match_span)
211                };
212                grouped_errors.push(GroupedMoveError::MovesFromPlace {
213                    span,
214                    move_from,
215                    original_path,
216                    kind,
217                    binds_to,
218                });
219            }
220            // Error with the pattern
221            LookupResult::Exact(_) => {
222                let LookupResult::Parent(Some(mpi)) =
223                    self.move_data.rev_lookup.find(move_from.as_ref())
224                else {
225                    // move_from should be a projection from match_place.
226                    {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("Probably not unreachable...")));
};unreachable!("Probably not unreachable...");
227                };
228                for ge in &mut *grouped_errors {
229                    if let GroupedMoveError::MovesFromValue {
230                        span,
231                        move_from: other_mpi,
232                        binds_to,
233                        ..
234                    } = ge
235                    {
236                        if match_span == *span && mpi == *other_mpi {
237                            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs:237",
                        "rustc_borrowck::diagnostics::move_errors",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
                        ::tracing_core::__macro_support::Option::Some(237u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("appending local({0:?}) to list",
                                                    bind_to) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("appending local({bind_to:?}) to list");
238                            binds_to.push(bind_to);
239                            return;
240                        }
241                    }
242                }
243                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs:243",
                        "rustc_borrowck::diagnostics::move_errors",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
                        ::tracing_core::__macro_support::Option::Some(243u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("found a new move error location")
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("found a new move error location");
244                grouped_errors.push(GroupedMoveError::MovesFromValue {
245                    span: match_span,
246                    move_from: mpi,
247                    original_path,
248                    kind,
249                    binds_to: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [bind_to]))vec![bind_to],
250                });
251            }
252        };
253    }
254
255    fn report(&mut self, error: GroupedMoveError<'tcx>) {
256        let (span, use_spans, original_path, kind) = match error {
257            GroupedMoveError::MovesFromPlace { span, original_path, ref kind, .. }
258            | GroupedMoveError::MovesFromValue { span, original_path, ref kind, .. } => {
259                (span, None, original_path, kind)
260            }
261            GroupedMoveError::OtherIllegalMove { use_spans, original_path, ref kind } => {
262                (use_spans.args_or_use(), Some(use_spans), original_path, kind)
263            }
264        };
265        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs:265",
                        "rustc_borrowck::diagnostics::move_errors",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
                        ::tracing_core::__macro_support::Option::Some(265u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("report: original_path={0:?} span={1:?}, kind={2:?} original_path.is_upvar_field_projection={3:?}",
                                                    original_path, span, kind,
                                                    self.is_upvar_field_projection(original_path.as_ref())) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(
266            "report: original_path={:?} span={:?}, kind={:?} \
267             original_path.is_upvar_field_projection={:?}",
268            original_path,
269            span,
270            kind,
271            self.is_upvar_field_projection(original_path.as_ref())
272        );
273        if self.has_ambiguous_copy(original_path.ty(self.body, self.infcx.tcx).ty) {
274            // If the type may implement Copy, skip the error.
275            // It's an error with the Copy implementation (e.g. duplicate Copy) rather than borrow check
276            self.dcx()
277                .span_delayed_bug(span, "Type may implement copy, but there is no other error.");
278            return;
279        }
280
281        let mut has_clone_suggestion = CloneSuggestion::NotEmitted;
282        let mut err = match kind {
283            &IllegalMoveOriginKind::BorrowedContent { target_place } => {
284                let (diag, clone_sugg) = self.report_cannot_move_from_borrowed_content(
285                    original_path,
286                    target_place,
287                    span,
288                    use_spans,
289                );
290                has_clone_suggestion = clone_sugg;
291                diag
292            }
293            &IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
294                self.cannot_move_out_of_interior_of_drop(span, ty)
295            }
296            &IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
297                self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index))
298            }
299        };
300
301        self.add_move_hints(error, &mut err, span, has_clone_suggestion);
302        self.buffer_error(err);
303    }
304
305    fn has_ambiguous_copy(&mut self, ty: Ty<'tcx>) -> bool {
306        let Some(copy_def_id) = self.infcx.tcx.lang_items().copy_trait() else { return false };
307
308        // Avoid bogus move errors because of an incoherent `Copy` impl.
309        self.infcx.type_implements_trait(copy_def_id, [ty], self.infcx.param_env).may_apply()
310            && self.infcx.tcx.ensure_result().coherent_trait(copy_def_id).is_err()
311    }
312
313    fn report_cannot_move_from_static(&mut self, place: Place<'tcx>, span: Span) -> Diag<'diag> {
314        let description = if place.projection.len() == 1 {
315            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("static item {0}",
                self.describe_any_place(place.as_ref())))
    })format!("static item {}", self.describe_any_place(place.as_ref()))
316        } else {
317            let base_static = PlaceRef { local: place.local, projection: &[ProjectionElem::Deref] };
318
319            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} as {1} is a static item",
                self.describe_any_place(place.as_ref()),
                self.describe_any_place(base_static)))
    })format!(
320                "{} as {} is a static item",
321                self.describe_any_place(place.as_ref()),
322                self.describe_any_place(base_static),
323            )
324        };
325
326        self.cannot_move_out_of(span, &description)
327    }
328
329    pub(in crate::diagnostics) fn suggest_clone_of_captured_var_in_move_closure(
330        &self,
331        err: &mut Diag<'_>,
332        upvar_name: &str,
333        use_spans: Option<UseSpans<'tcx>>,
334    ) {
335        let tcx = self.infcx.tcx;
336        let Some(use_spans) = use_spans else { return };
337        // We only care about the case where a closure captured a binding.
338        let UseSpans::ClosureUse { args_span, .. } = use_spans else { return };
339        let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return };
340        // Find the closure that captured the binding.
341        let mut expr_finder = FindExprBySpan::new(args_span, tcx);
342        expr_finder.include_closures = true;
343        expr_finder.visit_expr(tcx.hir_body(body_id).value);
344        let Some(closure_expr) = expr_finder.result else { return };
345        let ExprKind::Closure(closure) = closure_expr.kind else { return };
346        // We'll only suggest cloning the binding if it's a `move` closure.
347        let CaptureBy::Value { .. } = closure.capture_clause else { return };
348        // Find the expression within the closure where the binding is consumed.
349        let mut suggested = false;
350        let use_span = use_spans.var_or_use();
351        let mut expr_finder = FindExprBySpan::new(use_span, tcx);
352        expr_finder.include_closures = true;
353        expr_finder.visit_expr(tcx.hir_body(body_id).value);
354        let Some(use_expr) = expr_finder.result else { return };
355        let parent = tcx.parent_hir_node(use_expr.hir_id);
356        if let Node::Expr(expr) = parent
357            && let ExprKind::Assign(lhs, ..) = expr.kind
358            && lhs.hir_id == use_expr.hir_id
359        {
360            // Cloning the value being assigned makes no sense:
361            //
362            // error[E0507]: cannot move out of `var`, a captured variable in an `FnMut` closure
363            //   --> $DIR/option-content-move2.rs:11:9
364            //    |
365            // LL |     let mut var = None;
366            //    |         ------- captured outer variable
367            // LL |     func(|| {
368            //    |          -- captured by this `FnMut` closure
369            // LL |         // Shouldn't suggest `move ||.as_ref()` here
370            // LL |         move || {
371            //    |         ^^^^^^^ `var` is moved here
372            // LL |
373            // LL |             var = Some(NotCopyable);
374            //    |             ---
375            //    |             |
376            //    |             variable moved due to use in closure
377            //    |             move occurs because `var` has type `Option<NotCopyable>`, which does not implement the `Copy` trait
378            //    |
379            return;
380        }
381
382        // Search for an appropriate place for the structured `.clone()` suggestion to be applied.
383        // If we encounter a statement before the borrow error, we insert a statement there.
384        for (_, node) in tcx.hir_parent_iter(closure_expr.hir_id) {
385            if let Node::Stmt(stmt) = node {
386                let padding = tcx
387                    .sess
388                    .source_map()
389                    .indentation_before(stmt.span)
390                    .unwrap_or_else(|| "    ".to_string());
391                err.multipart_suggestion(
392                    "consider cloning the value before moving it into the closure",
393                    ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [(stmt.span.shrink_to_lo(),
                    ::alloc::__export::must_use({
                            ::alloc::fmt::format(format_args!("let value = {0}.clone();\n{1}",
                                    upvar_name, padding))
                        })), (use_span, "value".to_string())]))vec![
394                        (
395                            stmt.span.shrink_to_lo(),
396                            format!("let value = {upvar_name}.clone();\n{padding}"),
397                        ),
398                        (use_span, "value".to_string()),
399                    ],
400                    Applicability::MachineApplicable,
401                );
402                suggested = true;
403                break;
404            } else if let Node::Expr(expr) = node
405                && let ExprKind::Closure(_) = expr.kind
406            {
407                // We want to suggest cloning only on the first closure, not
408                // subsequent ones (like `ui/suggestions/option-content-move2.rs`).
409                break;
410            }
411        }
412        if !suggested {
413            // If we couldn't find a statement for us to insert a new `.clone()` statement before,
414            // we have a bare expression, so we suggest the creation of a new block inline to go
415            // from `move || val` to `{ let value = val.clone(); move || value }`.
416            let padding = tcx
417                .sess
418                .source_map()
419                .indentation_before(closure_expr.span)
420                .unwrap_or_else(|| "    ".to_string());
421            err.multipart_suggestion(
422                "consider cloning the value before moving it into the closure",
423                ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [(closure_expr.span.shrink_to_lo(),
                    ::alloc::__export::must_use({
                            ::alloc::fmt::format(format_args!("{{\n{0}let value = {1}.clone();\n{0}",
                                    padding, upvar_name))
                        })), (use_spans.var_or_use(), "value".to_string()),
                (closure_expr.span.shrink_to_hi(),
                    ::alloc::__export::must_use({
                            ::alloc::fmt::format(format_args!("\n{0}}}", padding))
                        }))]))vec![
424                    (
425                        closure_expr.span.shrink_to_lo(),
426                        format!("{{\n{padding}let value = {upvar_name}.clone();\n{padding}"),
427                    ),
428                    (use_spans.var_or_use(), "value".to_string()),
429                    (closure_expr.span.shrink_to_hi(), format!("\n{padding}}}")),
430                ],
431                Applicability::MachineApplicable,
432            );
433        }
434    }
435
436    fn report_cannot_move_from_borrowed_content(
437        &mut self,
438        move_place: Place<'tcx>,
439        deref_target_place: Place<'tcx>,
440        span: Span,
441        use_spans: Option<UseSpans<'tcx>>,
442    ) -> (Diag<'diag>, CloneSuggestion) {
443        let tcx = self.infcx.tcx;
444        // Inspect the type of the content behind the
445        // borrow to provide feedback about why this
446        // was a move rather than a copy.
447        let ty = deref_target_place.ty(self.body, tcx).ty;
448        let upvar_field = self
449            .prefixes(move_place.as_ref(), PrefixSet::All)
450            .find_map(|p| self.is_upvar_field_projection(p));
451
452        let deref_base = match deref_target_place.projection.as_ref() {
453            [proj_base @ .., ProjectionElem::Deref] => {
454                PlaceRef { local: deref_target_place.local, projection: proj_base }
455            }
456            _ => ::rustc_span::macros::bug_impl(None,
    format_args!("deref_target_place is not a deref projection"),
    Location::caller())bug!("deref_target_place is not a deref projection"),
457        };
458
459        if let PlaceRef { local, projection: [] } = deref_base {
460            let decl = &self.body.local_decls[local];
461            let local_name = self.local_name(local).map(|sym| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}`", sym))
    })format!("`{sym}`"));
462            if decl.is_ref_for_guard() {
463                return (
464                    self.cannot_move_out_of(
465                        span,
466                        &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} in pattern guard",
                local_name.as_deref().unwrap_or("the place")))
    })format!(
467                            "{} in pattern guard",
468                            local_name.as_deref().unwrap_or("the place")
469                        ),
470                    )
471                    .with_note(
472                        "variables bound in patterns cannot be moved from \
473                         until after the end of the pattern guard",
474                    ),
475                    CloneSuggestion::NotEmitted,
476                );
477            } else if decl.is_ref_to_static() {
478                return (
479                    self.report_cannot_move_from_static(move_place, span),
480                    CloneSuggestion::NotEmitted,
481                );
482            }
483        }
484
485        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs:485",
                        "rustc_borrowck::diagnostics::move_errors",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
                        ::tracing_core::__macro_support::Option::Some(485u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("report: ty={0:?}",
                                                    ty) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("report: ty={:?}", ty);
486        let mut err = match ty.kind() {
487            ty::Array(..) | ty::Slice(..) => {
488                self.cannot_move_out_of_interior_noncopy(span, ty, None)
489            }
490            ty::Closure(def_id, closure_args)
491                if def_id.as_local() == Some(self.mir_def_id())
492                    && let Some(upvar_field) = upvar_field =>
493            {
494                self.report_closure_move_error(
495                    span,
496                    move_place,
497                    *def_id,
498                    closure_args.as_closure().kind_ty(),
499                    upvar_field,
500                    ty::Asyncness::No,
501                )
502            }
503            ty::CoroutineClosure(def_id, closure_args)
504                if def_id.as_local() == Some(self.mir_def_id())
505                    && let Some(upvar_field) = upvar_field
506                    && self
507                        .get_closure_bound_clause_span(*def_id, ty::Asyncness::Yes)
508                        .is_some() =>
509            {
510                self.report_closure_move_error(
511                    span,
512                    move_place,
513                    *def_id,
514                    closure_args.as_coroutine_closure().kind_ty(),
515                    upvar_field,
516                    ty::Asyncness::Yes,
517                )
518            }
519            _ => {
520                let source = self.borrowed_content_source(deref_base);
521                let move_place_ref = move_place.as_ref();
522                match (
523                    self.describe_place_with_options(
524                        move_place_ref,
525                        DescribePlaceOpt {
526                            including_downcast: false,
527                            including_tuple_field: false,
528                        },
529                    ),
530                    self.describe_name(move_place_ref),
531                    source.describe_for_named_place(),
532                ) {
533                    (Some(place_desc), Some(name), Some(source_desc)) => self.cannot_move_out_of(
534                        span,
535                        &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` as enum variant `{1}` which is behind a {2}",
                place_desc, name, source_desc))
    })format!("`{place_desc}` as enum variant `{name}` which is behind a {source_desc}"),
536                    ),
537                    (Some(place_desc), Some(name), None) => self.cannot_move_out_of(
538                        span,
539                        &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` as enum variant `{1}`",
                place_desc, name))
    })format!("`{place_desc}` as enum variant `{name}`"),
540                    ),
541                    (Some(place_desc), _, Some(source_desc)) => self.cannot_move_out_of(
542                        span,
543                        &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` which is behind a {1}",
                place_desc, source_desc))
    })format!("`{place_desc}` which is behind a {source_desc}"),
544                    ),
545                    (_, _, _) => self.cannot_move_out_of(
546                        span,
547                        &source.describe_for_unnamed_place(tcx),
548                    ),
549                }
550            }
551        };
552        let msg_opt = CapturedMessageOpt {
553            is_partial_move: false,
554            is_loop_message: false,
555            is_move_msg: false,
556            is_loop_move: false,
557            has_suggest_reborrow: false,
558            maybe_reinitialized_locations_is_empty: true,
559        };
560        let suggested_cloning = if let Some(use_spans) = use_spans {
561            self.explain_captures(&mut err, span, span, use_spans, move_place, msg_opt)
562        } else {
563            CloneSuggestion::NotEmitted
564        };
565        (err, suggested_cloning)
566    }
567
568    fn report_closure_move_error(
569        &self,
570        span: Span,
571        move_place: Place<'tcx>,
572        def_id: DefId,
573        closure_kind_ty: Ty<'tcx>,
574        upvar_field: FieldIdx,
575        asyncness: ty::Asyncness,
576    ) -> Diag<'diag> {
577        let tcx = self.infcx.tcx;
578
579        let closure_kind = match closure_kind_ty.to_opt_closure_kind() {
580            Some(kind @ (ty::ClosureKind::Fn | ty::ClosureKind::FnMut)) => kind,
581            Some(ty::ClosureKind::FnOnce) => {
582                ::rustc_span::macros::bug_impl(None,
    format_args!("closure kind does not match first argument type"),
    Location::caller())bug!("closure kind does not match first argument type")
583            }
584            None => ::rustc_span::macros::bug_impl(None,
    format_args!("closure kind not inferred by borrowck"), Location::caller())bug!("closure kind not inferred by borrowck"),
585        };
586
587        let async_prefix = if asyncness.is_async() { "Async" } else { "" };
588        let capture_description =
589            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("captured variable in an `{0}{1}` closure",
                async_prefix, closure_kind))
    })format!("captured variable in an `{async_prefix}{closure_kind}` closure");
590
591        let upvar = &self.upvars[upvar_field.index()];
592        let upvar_hir_id = upvar.get_root_variable();
593        let upvar_name = upvar.to_string(tcx);
594        let upvar_span = tcx.hir_span(upvar_hir_id);
595
596        let place_name = self.describe_any_place(move_place.as_ref());
597
598        let place_description = if self.is_upvar_field_projection(move_place.as_ref()).is_some() {
599            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}, a {1}", place_name,
                capture_description))
    })format!("{place_name}, a {capture_description}")
600        } else {
601            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}, as `{1}` is a {2}",
                place_name, upvar_name, capture_description))
    })format!("{place_name}, as `{upvar_name}` is a {capture_description}")
602        };
603
604        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs:604",
                        "rustc_borrowck::diagnostics::move_errors",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
                        ::tracing_core::__macro_support::Option::Some(604u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
                        ::tracing_core::field::FieldSet::new(&[{
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("closure_kind_ty")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("closure_kind_ty");
                                            NAME.as_str()
                                        },
                                        {
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("closure_kind")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("closure_kind");
                                            NAME.as_str()
                                        },
                                        {
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("place_description")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("place_description");
                                            NAME.as_str()
                                        }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&closure_kind_ty)
                                            as &dyn ::tracing::field::Value)),
                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&closure_kind)
                                            as &dyn ::tracing::field::Value)),
                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&place_description)
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(?closure_kind_ty, ?closure_kind, ?place_description);
605
606        let closure_span = tcx.def_span(def_id);
607
608        let help_msg = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}Fn` and `{0}FnMut` closures require captured values to be able to be consumed multiple times, but `{0}FnOnce` closures may consume them only once",
                async_prefix))
    })format!(
609            "`{async_prefix}Fn` and `{async_prefix}FnMut` closures require captured values to \
610             be able to be consumed multiple times, but `{async_prefix}FnOnce` closures may \
611             consume them only once"
612        );
613
614        let mut err = self
615            .cannot_move_out_of(span, &place_description)
616            .with_span_label(upvar_span, "captured outer variable")
617            .with_span_label(
618                closure_span,
619                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("captured by this `{0}{1}` closure",
                async_prefix, closure_kind))
    })format!("captured by this `{async_prefix}{closure_kind}` closure"),
620            );
621
622        if let Some(bound_span) = self.get_closure_bound_clause_span(def_id, asyncness) {
623            err.span_help(bound_span, help_msg);
624        } else if !asyncness.is_async() {
625            // For sync closures, always emit the help message even without a span.
626            // For async closures, we only enter this branch if we found a valid span
627            // (due to the match guard), so no fallback is needed.
628            err.help(help_msg);
629        }
630
631        err
632    }
633
634    fn get_closure_bound_clause_span(
635        &self,
636        def_id: DefId,
637        asyncness: ty::Asyncness,
638    ) -> Option<MultiSpan> {
639        let tcx = self.infcx.tcx;
640        let typeck_result = tcx.typeck(self.mir_def_id());
641        // Check whether the closure is an argument to a call, if so,
642        // get the instantiated where-bounds of that call.
643        let closure_hir_id = tcx.local_def_id_to_hir_id(def_id.expect_local());
644        let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return None };
645
646        let mut context = ::alloc::vec::Vec::new()vec![];
647        let gen_clauses = match parent.kind {
648            hir::ExprKind::Call(callee, _) => {
649                let ty = typeck_result.node_type_opt(callee.hir_id)?;
650                let ty::FnDef(fn_def_id, args) = *ty.kind() else { return None };
651                context.push(tcx.def_span(fn_def_id).shrink_to_lo());
652                if tcx.def_kind(fn_def_id) == DefKind::AssocFn {
653                    context.push(tcx.def_span(tcx.parent(fn_def_id)).shrink_to_lo());
654                }
655                tcx.clauses_of(fn_def_id).instantiate(tcx, args.no_bound_vars().unwrap())
656            }
657            hir::ExprKind::MethodCall(..) => {
658                let (_, method) = typeck_result.type_dependent_def(parent.hir_id)?;
659                context.push(tcx.def_span(method).shrink_to_lo());
660                context.push(tcx.def_span(tcx.parent(method)).shrink_to_lo());
661                let args = typeck_result.node_args(parent.hir_id);
662                tcx.clauses_of(method).instantiate(tcx, args)
663            }
664            _ => return None,
665        };
666
667        // Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`
668        // or `AsyncFn[Mut]`.
669        for (clause, &span) in gen_clauses.clauses.iter().zip(gen_clauses.spans.iter()) {
670            let clause = clause.skip_norm_wip();
671            let dominated_by_fn_trait = self
672                .closure_clause_kind(clause, def_id, asyncness)
673                .is_some_and(|kind| #[allow(non_exhaustive_omitted_patterns)] match kind {
    ty::ClosureKind::Fn | ty::ClosureKind::FnMut => true,
    _ => false,
}matches!(kind, ty::ClosureKind::Fn | ty::ClosureKind::FnMut));
674            if dominated_by_fn_trait {
675                // Found `<TyOfCapturingClosure as FnMut>` or
676                // `<TyOfCapturingClosure as AsyncFnMut>`.
677                // We point at the bound that coerced the closure, which could be changed
678                // to `FnOnce()` or `AsyncFnOnce()` to avoid the move error.
679                let mut span: MultiSpan = span.into();
680                for context in context {
681                    span.push_span_context(context);
682                }
683                return Some(span);
684            }
685        }
686        None
687    }
688
689    /// If `pred` is a trait clause binding the closure `def_id` to `Fn`/`FnMut`/`FnOnce`
690    /// (or their async equivalents based on `asyncness`), returns the corresponding
691    /// `ClosureKind`. Otherwise returns `None`.
692    fn closure_clause_kind(
693        &self,
694        pred: ty::Clause<'tcx>,
695        def_id: DefId,
696        asyncness: ty::Asyncness,
697    ) -> Option<ty::ClosureKind> {
698        let tcx = self.infcx.tcx;
699        let clause = pred.as_trait_clause()?;
700        let kind = match asyncness {
701            ty::Asyncness::Yes => tcx.async_fn_trait_kind_from_def_id(clause.def_id()),
702            ty::Asyncness::No => tcx.fn_trait_kind_from_def_id(clause.def_id()),
703        }?;
704        match clause.self_ty().skip_binder().kind() {
705            ty::Closure(id, _) | ty::CoroutineClosure(id, _) if *id == def_id => Some(kind),
706            _ => None,
707        }
708    }
709
710    /// Suggest cloning via UFCS when a move occurs through a custom `Deref` impl.
711    ///
712    /// A simple `.clone()` on a type like `MyBox<Vec<i32>>` would clone the wrapper,
713    /// not the inner `Vec<i32>`. Instead, we suggest `<Vec<i32> as Clone>::clone(&val)`.
714    fn suggest_cloning_through_overloaded_deref(
715        &self,
716        err: &mut Diag<'_>,
717        ty: Ty<'tcx>,
718        span: Span,
719    ) -> CloneSuggestion {
720        let tcx = self.infcx.tcx;
721        let Some(clone_trait) = tcx.lang_items().clone_trait() else {
722            return CloneSuggestion::NotEmitted;
723        };
724        let Some(errors) =
725            self.infcx.type_implements_trait_shallow(clone_trait, ty, self.infcx.param_env)
726        else {
727            return CloneSuggestion::NotEmitted;
728        };
729
730        if errors.has_errors() {
731            return CloneSuggestion::NotEmitted;
732        }
733        let sugg = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [(span.shrink_to_lo(),
                    ::alloc::__export::must_use({
                            ::alloc::fmt::format(format_args!("<{0} as Clone>::clone(&",
                                    ty))
                        })), (span.shrink_to_hi(), ")".to_string())]))vec![
734            (span.shrink_to_lo(), format!("<{ty} as Clone>::clone(&")),
735            (span.shrink_to_hi(), ")".to_string()),
736        ];
737        err.multipart_suggestion(
738            "you can `clone` the value and consume it, but this might not be \
739             your desired behavior",
740            sugg,
741            Applicability::MaybeIncorrect,
742        );
743        CloneSuggestion::Emitted
744    }
745
746    fn add_move_hints(
747        &self,
748        error: GroupedMoveError<'tcx>,
749        err: &mut Diag<'_>,
750        span: Span,
751        has_clone_suggestion: CloneSuggestion,
752    ) {
753        match error {
754            GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => {
755                binds_to.sort();
756                binds_to.dedup();
757
758                if binds_to.is_empty() {
759                    self.add_borrow_suggestions(err, span, false);
760                    let place_ty = move_from.ty(self.body, self.infcx.tcx).ty;
761                    let place_desc = match self.describe_place(move_from.as_ref()) {
762                        Some(desc) => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}`", desc))
    })format!("`{desc}`"),
763                        None => "value".to_string(),
764                    };
765
766                    if let Some(expr) = self.find_expr(span) {
767                        self.suggest_cloning(err, move_from.as_ref(), place_ty, expr, None);
768                    }
769
770                    err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
771                        is_partial_move: false,
772                        ty: place_ty,
773                        place: &place_desc,
774                        span,
775                    });
776                } else {
777                    let binding_info = self.pattern_binding_info(&binds_to);
778                    let suggest_pattern_binding = binding_info.as_ref().is_some_and(|info| {
779                        self.should_suggest_pattern_binding_instead(span, info)
780                    });
781                    let desugar_spans = if suggest_pattern_binding {
782                        self.add_move_error_suggestions(err, &binds_to)
783                    } else {
784                        if self.should_suggest_borrow_instead(span, binding_info.as_ref()) {
785                            self.add_borrow_suggestions(err, span, true);
786                        }
787                        None
788                    };
789                    self.add_move_error_details(
790                        err,
791                        &binds_to,
792                        desugar_spans.as_deref().unwrap_or_default(),
793                    );
794                }
795            }
796            GroupedMoveError::MovesFromValue { mut binds_to, .. } => {
797                binds_to.sort();
798                binds_to.dedup();
799                let desugar_spans =
800                    self.add_move_error_suggestions(err, &binds_to).unwrap_or_default();
801                self.add_move_error_details(err, &binds_to, &desugar_spans);
802            }
803            // No binding. Nothing to suggest.
804            GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => {
805                let mut use_span = use_spans.var_or_use();
806                let place_ty = original_path.ty(self.body, self.infcx.tcx).ty;
807                let place_desc = match self.describe_place(original_path.as_ref()) {
808                    Some(desc) => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}`", desc))
    })format!("`{desc}`"),
809                    None => "value".to_string(),
810                };
811
812                if has_clone_suggestion == CloneSuggestion::NotEmitted {
813                    // Check if the move is directly through a custom Deref impl
814                    // (e.g. `*my_box` where MyBox implements Deref).
815                    // A simple `.clone()` would clone the wrapper type rather than
816                    // the inner value, so we need a UFCS suggestion instead.
817                    //
818                    // However, if there are further projections after the deref
819                    // (e.g. `(*rc).field`), the value accessed is already the inner
820                    // type and a simple `.clone()` works correctly.
821                    let needs_ufcs = original_path.projection.last()
822                        == Some(&ProjectionElem::Deref)
823                        && original_path.iter_projections().any(|(place, elem)| {
824                            #[allow(non_exhaustive_omitted_patterns)] match elem {
    ProjectionElem::Deref => true,
    _ => false,
}matches!(elem, ProjectionElem::Deref)
825                                && #[allow(non_exhaustive_omitted_patterns)] match self.borrowed_content_source(place)
    {
    BorrowedContentSource::OverloadedDeref(_) |
        BorrowedContentSource::OverloadedIndex(_) => true,
    _ => false,
}matches!(
826                                    self.borrowed_content_source(place),
827                                    BorrowedContentSource::OverloadedDeref(_)
828                                        | BorrowedContentSource::OverloadedIndex(_)
829                                )
830                        });
831
832                    let emitted_ufcs = if needs_ufcs {
833                        self.suggest_cloning_through_overloaded_deref(err, place_ty, use_span)
834                    } else {
835                        CloneSuggestion::NotEmitted
836                    };
837
838                    if emitted_ufcs == CloneSuggestion::NotEmitted {
839                        if let Some(expr) = self.find_expr(use_span) {
840                            self.suggest_cloning(
841                                err,
842                                original_path.as_ref(),
843                                place_ty,
844                                expr,
845                                Some(use_spans),
846                            );
847                        }
848                    }
849                }
850
851                if let Some(upvar_field) = self
852                    .prefixes(original_path.as_ref(), PrefixSet::All)
853                    .find_map(|p| self.is_upvar_field_projection(p))
854                {
855                    // Look for the introduction of the original binding being moved.
856                    let upvar = &self.upvars[upvar_field.index()];
857                    let upvar_hir_id = upvar.get_root_variable();
858                    use_span = match self.infcx.tcx.parent_hir_node(upvar_hir_id) {
859                        hir::Node::Param(param) => {
860                            // Instead of pointing at the path where we access the value within a
861                            // closure, we point at the type of the outer `fn` argument.
862                            param.ty_span
863                        }
864                        hir::Node::LetStmt(stmt) => match (stmt.ty, stmt.init) {
865                            // We point at the type of the outer let-binding.
866                            (Some(ty), _) => ty.span,
867                            // We point at the initializer of the outer let-binding, but only if it
868                            // isn't something that spans multiple lines, like a closure, as the
869                            // ASCII art gets messy.
870                            (None, Some(init))
871                                if !self.infcx.tcx.sess.source_map().is_multiline(init.span) =>
872                            {
873                                init.span
874                            }
875                            _ => use_span,
876                        },
877                        _ => use_span,
878                    };
879                }
880
881                err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
882                    is_partial_move: false,
883                    ty: place_ty,
884                    place: &place_desc,
885                    span: use_span,
886                });
887
888                let mut pointed_at_span = false;
889                use_spans.args_subdiag(err, |args_span| {
890                    if args_span == span || args_span == use_span {
891                        pointed_at_span = true;
892                    }
893                    crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
894                        place: place_desc.clone(),
895                        args_span,
896                    }
897                });
898                if !pointed_at_span && use_span != span {
899                    err.subdiagnostic(crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
900                        place: place_desc,
901                        args_span: span,
902                    });
903                }
904
905                self.add_note_for_packed_struct_derive(err, original_path.local);
906            }
907        }
908    }
909
910    fn add_borrow_suggestions(
911        &self,
912        err: &mut Diag<'_>,
913        span: Span,
914        is_destructuring_pattern_move: bool,
915    ) {
916        match self.infcx.tcx.sess.source_map().span_to_snippet(span) {
917            Ok(snippet) if snippet.starts_with('*') => {
918                let sp = span.with_lo(span.lo() + BytePos(1));
919                let inner = self.find_expr(sp);
920                let mut is_raw_ptr = false;
921                let mut is_ref = false;
922                let mut is_destructuring_assignment = false;
923                let mut is_nested_deref = false;
924                if let Some(inner) = inner {
925                    is_nested_deref =
926                        #[allow(non_exhaustive_omitted_patterns)] match inner.kind {
    hir::ExprKind::Unary(hir::UnOp::Deref, _) => true,
    _ => false,
}matches!(inner.kind, hir::ExprKind::Unary(hir::UnOp::Deref, _));
927                    let typck_result = self.infcx.tcx.typeck(self.mir_def_id());
928                    if let Some(inner_type) = typck_result.node_type_opt(inner.hir_id) {
929                        if #[allow(non_exhaustive_omitted_patterns)] match inner_type.kind() {
    ty::RawPtr(..) => true,
    _ => false,
}matches!(inner_type.kind(), ty::RawPtr(..)) {
930                            is_raw_ptr = true;
931                        } else if #[allow(non_exhaustive_omitted_patterns)] match inner_type.kind() {
    ty::Ref(..) => true,
    _ => false,
}matches!(inner_type.kind(), ty::Ref(..)) {
932                            is_ref = true;
933                        }
934                    }
935                    is_destructuring_assignment =
936                        self.infcx.tcx.hir_parent_iter(inner.hir_id).any(|(_, node)| {
937                            #[allow(non_exhaustive_omitted_patterns)] match node {
    hir::Node::LetStmt(&hir::LetStmt {
        source: hir::LocalSource::AssignDesugar, .. }) => true,
    _ => false,
}matches!(
938                                node,
939                                hir::Node::LetStmt(&hir::LetStmt {
940                                    source: hir::LocalSource::AssignDesugar,
941                                    ..
942                                })
943                            )
944                        });
945                }
946                // If the `inner` is a raw pointer, do not suggest removing the "*", see #126863
947                // FIXME: need to check whether the assigned object can be a raw pointer, see `tests/ui/borrowck/issue-20801.rs`.
948                if is_raw_ptr {
949                    return;
950                }
951
952                if !is_destructuring_pattern_move || is_ref {
953                    err.span_suggestion_verbose(
954                        span.with_hi(span.lo() + BytePos(1)),
955                        "consider removing the dereference here",
956                        String::new(),
957                        Applicability::MaybeIncorrect,
958                    );
959                } else if !is_destructuring_assignment && !is_nested_deref {
960                    err.span_suggestion_verbose(
961                        span.shrink_to_lo(),
962                        "consider borrowing here",
963                        '&',
964                        Applicability::MaybeIncorrect,
965                    );
966                } else {
967                    err.span_help(
968                        span,
969                        "destructuring assignment cannot borrow from this expression; consider using a `let` binding instead",
970                    );
971                }
972            }
973            _ => {
974                err.span_suggestion_verbose(
975                    span.shrink_to_lo(),
976                    "consider borrowing here",
977                    '&',
978                    Applicability::MaybeIncorrect,
979                );
980            }
981        }
982    }
983
984    fn should_suggest_pattern_binding_instead(
985        &self,
986        span: Span,
987        binding_info: &PatternBindingInfo,
988    ) -> bool {
989        let Some(expr) = self.find_expr(span) else {
990            return false;
991        };
992
993        let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
994        let projection_qualifies = match expr.kind {
995            hir::ExprKind::Field(base, ..) => {
996                !typeck_results.node_type_opt(base.hir_id).is_some_and(|base_ty| {
997                    binding_info.has_mutable_by_value_binding
998                        && #[allow(non_exhaustive_omitted_patterns)] match base_ty.kind() {
    ty::Ref(_, _, hir::Mutability::Not) => true,
    _ => false,
}matches!(base_ty.kind(), ty::Ref(_, _, hir::Mutability::Not))
999                })
1000            }
1001            hir::ExprKind::Index(base, ..) => typeck_results
1002                .node_type_opt(base.hir_id)
1003                .is_some_and(|base_ty| match base_ty.kind() {
1004                    ty::Ref(_, _, hir::Mutability::Not) | ty::RawPtr(..) => false,
1005                    ty::Ref(_, _, hir::Mutability::Mut) => {
1006                        binding_info.has_mutable_by_value_binding
1007                    }
1008                    _ => true,
1009                }),
1010            _ => false,
1011        };
1012        if !projection_qualifies {
1013            return false;
1014        }
1015
1016        let is_single_binding = binding_info.binding_spans.len() == 1
1017            && binding_info.binding_spans[0] == binding_info.pat_span;
1018        !is_single_binding
1019    }
1020
1021    fn should_suggest_borrow_instead(
1022        &self,
1023        span: Span,
1024        binding_info: Option<&PatternBindingInfo>,
1025    ) -> bool {
1026        if !binding_info.is_some_and(|info| info.has_mutable_by_value_binding) {
1027            return true;
1028        }
1029
1030        let Some(expr) = self.find_expr(span) else {
1031            return true;
1032        };
1033
1034        let Some(base) = (match expr.kind {
1035            hir::ExprKind::Field(base, _) | hir::ExprKind::Index(base, ..) => Some(base),
1036            _ => None,
1037        }) else {
1038            return true;
1039        };
1040
1041        !self
1042            .infcx
1043            .tcx
1044            .typeck(self.mir_def_id())
1045            .node_type_opt(base.hir_id)
1046            .is_some_and(|base_ty| #[allow(non_exhaustive_omitted_patterns)] match base_ty.kind() {
    ty::Ref(_, _, hir::Mutability::Not) => true,
    _ => false,
}matches!(base_ty.kind(), ty::Ref(_, _, hir::Mutability::Not)))
1047    }
1048
1049    fn pattern_binding_info(&self, binds_to: &[Local]) -> Option<PatternBindingInfo> {
1050        let mut pat_span = None;
1051        let mut binding_spans = Vec::new();
1052        let mut has_mutable_by_value_binding = false;
1053        for local in binds_to {
1054            let bind_to = &self.body.local_decls[*local];
1055            if let LocalInfo::User(BindingForm::Var(VarBindingForm {
1056                pat_span: pat_sp,
1057                binding_mode,
1058                ..
1059            })) = *bind_to.local_info()
1060            {
1061                pat_span = Some(pat_sp);
1062                binding_spans.push(bind_to.source_info.span);
1063                has_mutable_by_value_binding |=
1064                    #[allow(non_exhaustive_omitted_patterns)] match binding_mode {
    hir::BindingMode(hir::ByRef::No, hir::Mutability::Mut) => true,
    _ => false,
}matches!(binding_mode, hir::BindingMode(hir::ByRef::No, hir::Mutability::Mut));
1065            }
1066        }
1067
1068        Some(PatternBindingInfo {
1069            pat_span: pat_span?,
1070            binding_spans,
1071            has_mutable_by_value_binding,
1072        })
1073    }
1074
1075    fn add_move_error_suggestions(
1076        &self,
1077        err: &mut Diag<'_>,
1078        binds_to: &[Local],
1079    ) -> Option<Vec<Span>> {
1080        /// A HIR visitor to associate each binding with a `&` or `&mut` that could be removed to
1081        /// make it bind by reference instead (if possible)
1082        struct BindingFinder<'tcx> {
1083            typeck_results: &'tcx ty::TypeckResults<'tcx>,
1084            tcx: TyCtxt<'tcx>,
1085            /// Input: the span of the pattern we're finding bindings in
1086            pat_span: Span,
1087            /// Input: the spans of the bindings we're providing suggestions for
1088            binding_spans: Vec<Span>,
1089            /// Internal state: have we reached the pattern we're finding bindings in?
1090            found_pat: bool,
1091            /// Internal state: the innermost `&` or `&mut` "above" the visitor
1092            ref_pat: Option<&'tcx hir::Pat<'tcx>>,
1093            /// Internal state: could removing a `&` give bindings unexpected types?
1094            has_adjustments: bool,
1095            /// Output: for each input binding, the `&` or `&mut` to remove to make it by-ref
1096            ref_pat_for_binding: Vec<(Span, Option<&'tcx hir::Pat<'tcx>>)>,
1097            /// Output: ref patterns that can't be removed straightforwardly
1098            cannot_remove: FxHashSet<HirId>,
1099            /// Output: binding spans from destructuring assignment desugaring
1100            desugar_binding_spans: Vec<Span>,
1101        }
1102        impl<'tcx> Visitor<'tcx> for BindingFinder<'tcx> {
1103            type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies;
1104
1105            fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1106                self.tcx
1107            }
1108
1109            fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) -> Self::Result {
1110                // Don't walk into const patterns or anything else that might confuse this
1111                if !self.found_pat {
1112                    hir::intravisit::walk_expr(self, ex)
1113                }
1114            }
1115
1116            fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
1117                if p.span == self.pat_span {
1118                    self.found_pat = true;
1119                }
1120
1121                let parent_has_adjustments = self.has_adjustments;
1122                self.has_adjustments |=
1123                    self.typeck_results.pat_adjustments().contains_key(p.hir_id);
1124
1125                // Track the innermost `&` or `&mut` enclosing bindings, to suggest removing it.
1126                let parent_ref_pat = self.ref_pat;
1127                if let hir::PatKind::Ref(..) = p.kind {
1128                    self.ref_pat = Some(p);
1129                    // To avoid edition-dependent logic to figure out how many refs this `&` can
1130                    // peel off, simply don't remove the "parent" `&`.
1131                    self.cannot_remove.extend(parent_ref_pat.map(|r| r.hir_id));
1132                    if self.has_adjustments {
1133                        // Removing this `&` could give child bindings unexpected types, so don't.
1134                        self.cannot_remove.insert(p.hir_id);
1135                        // As long the `&` stays, child patterns' types should be as expected.
1136                        self.has_adjustments = false;
1137                    }
1138                }
1139
1140                if let hir::PatKind::Binding(_, _, ident, _) = p.kind {
1141                    // Skip synthetic bindings from destructuring assignment desugaring
1142                    // These have name `lhs` and their parent is a `LetStmt` with
1143                    // `LocalSource::AssignDesugar`
1144                    let dominated_by_desugar_assign = ident.name == sym::lhs
1145                        && self.tcx.hir_parent_iter(p.hir_id).any(|(_, node)| {
1146                            #[allow(non_exhaustive_omitted_patterns)] match node {
    hir::Node::LetStmt(&hir::LetStmt {
        source: hir::LocalSource::AssignDesugar, .. }) => true,
    _ => false,
}matches!(
1147                                node,
1148                                hir::Node::LetStmt(&hir::LetStmt {
1149                                    source: hir::LocalSource::AssignDesugar,
1150                                    ..
1151                                })
1152                            )
1153                        });
1154
1155                    if dominated_by_desugar_assign {
1156                        if let Some(&bind_sp) =
1157                            self.binding_spans.iter().find(|bind_sp| bind_sp.contains(ident.span))
1158                        {
1159                            self.desugar_binding_spans.push(bind_sp);
1160                        }
1161                    } else {
1162                        // the spans in `binding_spans` encompass both the ident and binding mode
1163                        if let Some(&bind_sp) =
1164                            self.binding_spans.iter().find(|bind_sp| bind_sp.contains(ident.span))
1165                        {
1166                            self.ref_pat_for_binding.push((bind_sp, self.ref_pat));
1167                        } else {
1168                            // we've encountered a binding that we're not reporting a move error for.
1169                            // we don't want to change its type, so don't remove the surrounding `&`.
1170                            if let Some(ref_pat) = self.ref_pat {
1171                                self.cannot_remove.insert(ref_pat.hir_id);
1172                            }
1173                        }
1174                    }
1175                }
1176
1177                hir::intravisit::walk_pat(self, p);
1178                self.ref_pat = parent_ref_pat;
1179                self.has_adjustments = parent_has_adjustments;
1180            }
1181        }
1182        let Some(binding_info) = self.pattern_binding_info(binds_to) else {
1183            return None;
1184        };
1185
1186        let tcx = self.infcx.tcx;
1187        let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) else {
1188            return None;
1189        };
1190        let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
1191        let mut finder = BindingFinder {
1192            typeck_results,
1193            tcx,
1194            pat_span: binding_info.pat_span,
1195            binding_spans: binding_info.binding_spans,
1196            found_pat: false,
1197            ref_pat: None,
1198            has_adjustments: false,
1199            ref_pat_for_binding: Vec::new(),
1200            cannot_remove: FxHashSet::default(),
1201            desugar_binding_spans: Vec::new(),
1202        };
1203        finder.visit_body(body);
1204
1205        let mut suggestions = Vec::new();
1206        for (binding_span, opt_ref_pat) in finder.ref_pat_for_binding {
1207            if let Some(ref_pat) = opt_ref_pat
1208                && !finder.cannot_remove.contains(&ref_pat.hir_id)
1209                && let hir::PatKind::Ref(subpat, pinned, mutbl) = ref_pat.kind
1210                && let Some(ref_span) = ref_pat.span.trim_end(subpat.span)
1211            {
1212                let pinned_str = if pinned.is_pinned() { "pinned " } else { "" };
1213                let mutable_str = if mutbl.is_mut() { "mutable " } else { "" };
1214                let msg = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("consider removing the {0}{1}borrow",
                pinned_str, mutable_str))
    })format!("consider removing the {pinned_str}{mutable_str}borrow");
1215                suggestions.push((ref_span, msg, "".to_string()));
1216            } else {
1217                let msg = "consider borrowing the pattern binding".to_string();
1218                suggestions.push((binding_span.shrink_to_lo(), msg, "ref ".to_string()));
1219            }
1220        }
1221        suggestions.sort_unstable_by_key(|&(span, _, _)| span.lo_hi());
1222        suggestions.dedup_by_key(|&mut (span, _, _)| span.lo_hi());
1223        for (span, msg, suggestion) in suggestions {
1224            err.span_suggestion_verbose(span, msg, suggestion, Applicability::MachineApplicable);
1225        }
1226
1227        Some(finder.desugar_binding_spans)
1228    }
1229
1230    fn add_move_error_details(
1231        &self,
1232        err: &mut Diag<'_>,
1233        binds_to: &[Local],
1234        desugar_spans: &[Span],
1235    ) {
1236        for (j, local) in binds_to.iter().enumerate() {
1237            let bind_to = &self.body.local_decls[*local];
1238            let binding_span = bind_to.source_info.span;
1239
1240            if binds_to.len() == 1 {
1241                let place_desc = self.local_name(*local).map(|sym| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}`", sym))
    })format!("`{sym}`"));
1242
1243                err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::LabelMovedHere {
1244                    ty: bind_to.ty,
1245                    place: place_desc.as_deref().unwrap_or("the place"),
1246                    span: binding_span,
1247                });
1248
1249                if !desugar_spans.contains(&binding_span)
1250                    && let Some(expr) = self.find_expr(binding_span)
1251                {
1252                    let local_place: PlaceRef<'tcx> = (*local).into();
1253                    self.suggest_cloning(err, local_place, bind_to.ty, expr, None);
1254                }
1255            } else if j == 0 {
1256                err.span_label(binding_span, "data moved here");
1257            } else if j == 5 && binds_to.len() > 6 && !self.infcx.tcx.sess.opts.verbose {
1258                err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("...and {0} other places",
                binds_to.len() - 5))
    })format!("...and {} other places", binds_to.len() - 5));
1259                break;
1260            } else {
1261                err.span_label(binding_span, "...and here");
1262            }
1263        }
1264
1265        if binds_to.len() > 1 {
1266            err.note(
1267                "move occurs because these variables have types that don't implement the `Copy` \
1268                 trait",
1269            );
1270        }
1271    }
1272
1273    /// Adds an explanatory note if the move error occurs in a derive macro
1274    /// expansion of a packed struct.
1275    /// Such errors happen because derive macro expansions shy away from taking
1276    /// references to the struct's fields since doing so would be undefined behaviour
1277    fn add_note_for_packed_struct_derive(&self, err: &mut Diag<'_>, local: Local) {
1278        let local_place: PlaceRef<'tcx> = local.into();
1279        let local_ty = local_place.ty(self.body.local_decls(), self.infcx.tcx).ty.peel_refs();
1280
1281        if let Some(adt) = local_ty.ty_adt_def()
1282            && adt.repr().packed()
1283            && let ExpnKind::Macro(MacroKind::Derive, name) =
1284                self.body.span.ctxt().outer_expn_data().kind
1285        {
1286            err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`#[derive({0})]` triggers a move because taking references to the fields of a packed struct is undefined behaviour",
                name))
    })format!("`#[derive({name})]` triggers a move because taking references to the fields of a packed struct is undefined behaviour"));
1287        }
1288    }
1289}