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