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