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