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