1use rustc_abi::FieldIdx;
2use rustc_data_structures::fx::FxHashSet;
3use rustc_errors::{Applicability, Diag};
4use rustc_hir::intravisit::Visitor;
5use rustc_hir::{self as hir, CaptureBy, ExprKind, HirId, Node};
6use rustc_middle::bug;
7use rustc_middle::mir::*;
8use rustc_middle::ty::{self, Ty, TyCtxt};
9use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
10use rustc_span::def_id::DefId;
11use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
12use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
13use rustc_trait_selection::infer::InferCtxtExt;
14use tracing::debug;
15
16use crate::MirBorrowckCtxt;
17use crate::diagnostics::{CapturedMessageOpt, DescribePlaceOpt, UseSpans};
18use crate::prefixes::PrefixSet;
19
20#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for IllegalMoveOriginKind<'tcx> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
IllegalMoveOriginKind::BorrowedContent { target_place: __self_0 }
=>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"BorrowedContent", "target_place", &__self_0),
IllegalMoveOriginKind::InteriorOfTypeWithDestructor {
container_ty: __self_0 } =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"InteriorOfTypeWithDestructor", "container_ty", &__self_0),
IllegalMoveOriginKind::InteriorOfSliceOrArray {
ty: __self_0, is_index: __self_1 } =>
::core::fmt::Formatter::debug_struct_field2_finish(f,
"InteriorOfSliceOrArray", "ty", __self_0, "is_index",
&__self_1),
}
}
}Debug)]
21pub(crate) enum IllegalMoveOriginKind<'tcx> {
22 BorrowedContent {
24 target_place: Place<'tcx>,
27 },
28
29 InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> },
34
35 InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool },
37}
38
39#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for MoveError<'tcx> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f, "MoveError",
"place", &self.place, "location", &self.location, "kind",
&&self.kind)
}
}Debug)]
40pub(crate) struct MoveError<'tcx> {
41 place: Place<'tcx>,
42 location: Location,
43 kind: IllegalMoveOriginKind<'tcx>,
44}
45
46impl<'tcx> MoveError<'tcx> {
47 pub(crate) fn new(
48 place: Place<'tcx>,
49 location: Location,
50 kind: IllegalMoveOriginKind<'tcx>,
51 ) -> Self {
52 MoveError { place, location, kind }
53 }
54}
55
56#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for GroupedMoveError<'tcx> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
GroupedMoveError::MovesFromPlace {
original_path: __self_0,
span: __self_1,
move_from: __self_2,
kind: __self_3,
binds_to: __self_4 } =>
::core::fmt::Formatter::debug_struct_field5_finish(f,
"MovesFromPlace", "original_path", __self_0, "span",
__self_1, "move_from", __self_2, "kind", __self_3,
"binds_to", &__self_4),
GroupedMoveError::MovesFromValue {
original_path: __self_0,
span: __self_1,
move_from: __self_2,
kind: __self_3,
binds_to: __self_4 } =>
::core::fmt::Formatter::debug_struct_field5_finish(f,
"MovesFromValue", "original_path", __self_0, "span",
__self_1, "move_from", __self_2, "kind", __self_3,
"binds_to", &__self_4),
GroupedMoveError::OtherIllegalMove {
original_path: __self_0, use_spans: __self_1, kind: __self_2 }
=>
::core::fmt::Formatter::debug_struct_field3_finish(f,
"OtherIllegalMove", "original_path", __self_0, "use_spans",
__self_1, "kind", &__self_2),
}
}
}Debug)]
70enum GroupedMoveError<'tcx> {
71 MovesFromPlace {
74 original_path: Place<'tcx>,
75 span: Span,
76 move_from: Place<'tcx>,
77 kind: IllegalMoveOriginKind<'tcx>,
78 binds_to: Vec<Local>,
79 },
80 MovesFromValue {
83 original_path: Place<'tcx>,
84 span: Span,
85 move_from: MovePathIndex,
86 kind: IllegalMoveOriginKind<'tcx>,
87 binds_to: Vec<Local>,
88 },
89 OtherIllegalMove {
91 original_path: Place<'tcx>,
92 use_spans: UseSpans<'tcx>,
93 kind: IllegalMoveOriginKind<'tcx>,
94 },
95}
96
97impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
98 pub(crate) fn report_move_errors(&mut self) {
99 let grouped_errors = self.group_move_errors();
100 for error in grouped_errors {
101 self.report(error);
102 }
103 }
104
105 fn group_move_errors(&mut self) -> Vec<GroupedMoveError<'tcx>> {
106 let mut grouped_errors = Vec::new();
107 let errors = std::mem::take(&mut self.move_errors);
108 for error in errors {
109 self.append_to_grouped_errors(&mut grouped_errors, error);
110 }
111 grouped_errors
112 }
113
114 fn append_to_grouped_errors(
115 &self,
116 grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
117 MoveError { place: original_path, location, kind }: MoveError<'tcx>,
118 ) {
119 if let Some(StatementKind::Assign(box (place, Rvalue::Use(Operand::Move(move_from))))) =
124 self.body.basic_blocks[location.block]
125 .statements
126 .get(location.statement_index)
127 .map(|stmt| &stmt.kind)
128 && let Some(local) = place.as_local()
129 {
130 let local_decl = &self.body.local_decls[local];
131 if let LocalInfo::User(BindingForm::Var(VarBindingForm {
139 opt_match_place: Some((opt_match_place, match_span)),
140 ..
141 })) = *local_decl.local_info()
142 {
143 let stmt_source_info = self.body.source_info(location);
144 self.append_binding_error(
145 grouped_errors,
146 kind,
147 original_path,
148 *move_from,
149 local,
150 opt_match_place,
151 match_span,
152 stmt_source_info.span,
153 );
154 return;
155 }
156 }
157
158 let move_spans = self.move_spans(original_path.as_ref(), location);
159 grouped_errors.push(GroupedMoveError::OtherIllegalMove {
160 use_spans: move_spans,
161 original_path,
162 kind,
163 });
164 }
165
166 fn append_binding_error(
167 &self,
168 grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
169 kind: IllegalMoveOriginKind<'tcx>,
170 original_path: Place<'tcx>,
171 move_from: Place<'tcx>,
172 bind_to: Local,
173 match_place: Option<Place<'tcx>>,
174 match_span: Span,
175 statement_span: Span,
176 ) {
177 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_borrowck/src/diagnostics/move_errors.rs:177",
"rustc_borrowck::diagnostics::move_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
::tracing_core::__macro_support::Option::Some(177u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
::tracing_core::field::FieldSet::new(&["message",
"match_place", "match_span"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("append_binding_error")
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&match_place)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&match_span)
as &dyn Value))])
});
} else { ; }
};debug!(?match_place, ?match_span, "append_binding_error");
178
179 let from_simple_let = match_place.is_none();
180 let match_place = match_place.unwrap_or(move_from);
181
182 match self.move_data.rev_lookup.find(match_place.as_ref()) {
183 LookupResult::Parent(_) => {
185 for ge in &mut *grouped_errors {
186 if let GroupedMoveError::MovesFromPlace { span, binds_to, .. } = ge
187 && match_span == *span
188 {
189 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_borrowck/src/diagnostics/move_errors.rs:189",
"rustc_borrowck::diagnostics::move_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
::tracing_core::__macro_support::Option::Some(189u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("appending local({0:?}) to list",
bind_to) as &dyn Value))])
});
} else { ; }
};debug!("appending local({bind_to:?}) to list");
190 if !binds_to.is_empty() {
191 binds_to.push(bind_to);
192 }
193 return;
194 }
195 }
196 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_borrowck/src/diagnostics/move_errors.rs:196",
"rustc_borrowck::diagnostics::move_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
::tracing_core::__macro_support::Option::Some(196u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("found a new move error location")
as &dyn Value))])
});
} else { ; }
};debug!("found a new move error location");
197
198 let (binds_to, span) = if from_simple_let {
200 (::alloc::vec::Vec::new()vec![], statement_span)
201 } else {
202 (<[_]>::into_vec(::alloc::boxed::box_new([bind_to]))vec![bind_to], match_span)
203 };
204 grouped_errors.push(GroupedMoveError::MovesFromPlace {
205 span,
206 move_from,
207 original_path,
208 kind,
209 binds_to,
210 });
211 }
212 LookupResult::Exact(_) => {
214 let LookupResult::Parent(Some(mpi)) =
215 self.move_data.rev_lookup.find(move_from.as_ref())
216 else {
217 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Probably not unreachable...")));
};unreachable!("Probably not unreachable...");
219 };
220 for ge in &mut *grouped_errors {
221 if let GroupedMoveError::MovesFromValue {
222 span,
223 move_from: other_mpi,
224 binds_to,
225 ..
226 } = ge
227 {
228 if match_span == *span && mpi == *other_mpi {
229 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_borrowck/src/diagnostics/move_errors.rs:229",
"rustc_borrowck::diagnostics::move_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
::tracing_core::__macro_support::Option::Some(229u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("appending local({0:?}) to list",
bind_to) as &dyn Value))])
});
} else { ; }
};debug!("appending local({bind_to:?}) to list");
230 binds_to.push(bind_to);
231 return;
232 }
233 }
234 }
235 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_borrowck/src/diagnostics/move_errors.rs:235",
"rustc_borrowck::diagnostics::move_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
::tracing_core::__macro_support::Option::Some(235u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("found a new move error location")
as &dyn Value))])
});
} else { ; }
};debug!("found a new move error location");
236 grouped_errors.push(GroupedMoveError::MovesFromValue {
237 span: match_span,
238 move_from: mpi,
239 original_path,
240 kind,
241 binds_to: <[_]>::into_vec(::alloc::boxed::box_new([bind_to]))vec![bind_to],
242 });
243 }
244 };
245 }
246
247 fn report(&mut self, error: GroupedMoveError<'tcx>) {
248 let (span, use_spans, original_path, kind) = match error {
249 GroupedMoveError::MovesFromPlace { span, original_path, ref kind, .. }
250 | GroupedMoveError::MovesFromValue { span, original_path, ref kind, .. } => {
251 (span, None, original_path, kind)
252 }
253 GroupedMoveError::OtherIllegalMove { use_spans, original_path, ref kind } => {
254 (use_spans.args_or_use(), Some(use_spans), original_path, kind)
255 }
256 };
257 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_borrowck/src/diagnostics/move_errors.rs:257",
"rustc_borrowck::diagnostics::move_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
::tracing_core::__macro_support::Option::Some(257u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("report: original_path={0:?} span={1:?}, kind={2:?} original_path.is_upvar_field_projection={3:?}",
original_path, span, kind,
self.is_upvar_field_projection(original_path.as_ref())) as
&dyn Value))])
});
} else { ; }
};debug!(
258 "report: original_path={:?} span={:?}, kind={:?} \
259 original_path.is_upvar_field_projection={:?}",
260 original_path,
261 span,
262 kind,
263 self.is_upvar_field_projection(original_path.as_ref())
264 );
265 if self.has_ambiguous_copy(original_path.ty(self.body, self.infcx.tcx).ty) {
266 self.dcx()
269 .span_delayed_bug(span, "Type may implement copy, but there is no other error.");
270 return;
271 }
272 let mut err = match kind {
273 &IllegalMoveOriginKind::BorrowedContent { target_place } => self
274 .report_cannot_move_from_borrowed_content(
275 original_path,
276 target_place,
277 span,
278 use_spans,
279 ),
280 &IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
281 self.cannot_move_out_of_interior_of_drop(span, ty)
282 }
283 &IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
284 self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index))
285 }
286 };
287
288 self.add_move_hints(error, &mut err, span);
289 self.buffer_error(err);
290 }
291
292 fn has_ambiguous_copy(&mut self, ty: Ty<'tcx>) -> bool {
293 let Some(copy_def_id) = self.infcx.tcx.lang_items().copy_trait() else { return false };
294
295 self.infcx.type_implements_trait(copy_def_id, [ty], self.infcx.param_env).may_apply()
297 && self.infcx.tcx.coherent_trait(copy_def_id).is_err()
298 }
299
300 fn report_cannot_move_from_static(&mut self, place: Place<'tcx>, span: Span) -> Diag<'infcx> {
301 let description = if place.projection.len() == 1 {
302 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("static item {0}",
self.describe_any_place(place.as_ref())))
})format!("static item {}", self.describe_any_place(place.as_ref()))
303 } else {
304 let base_static = PlaceRef { local: place.local, projection: &[ProjectionElem::Deref] };
305
306 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} as {1} is a static item",
self.describe_any_place(place.as_ref()),
self.describe_any_place(base_static)))
})format!(
307 "{} as {} is a static item",
308 self.describe_any_place(place.as_ref()),
309 self.describe_any_place(base_static),
310 )
311 };
312
313 self.cannot_move_out_of(span, &description)
314 }
315
316 pub(in crate::diagnostics) fn suggest_clone_of_captured_var_in_move_closure(
317 &self,
318 err: &mut Diag<'_>,
319 upvar_name: &str,
320 use_spans: Option<UseSpans<'tcx>>,
321 ) {
322 let tcx = self.infcx.tcx;
323 let Some(use_spans) = use_spans else { return };
324 let UseSpans::ClosureUse { args_span, .. } = use_spans else { return };
326 let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return };
327 let mut expr_finder = FindExprBySpan::new(args_span, tcx);
329 expr_finder.include_closures = true;
330 expr_finder.visit_expr(tcx.hir_body(body_id).value);
331 let Some(closure_expr) = expr_finder.result else { return };
332 let ExprKind::Closure(closure) = closure_expr.kind else { return };
333 let CaptureBy::Value { .. } = closure.capture_clause else { return };
335 let mut suggested = false;
337 let use_span = use_spans.var_or_use();
338 let mut expr_finder = FindExprBySpan::new(use_span, tcx);
339 expr_finder.include_closures = true;
340 expr_finder.visit_expr(tcx.hir_body(body_id).value);
341 let Some(use_expr) = expr_finder.result else { return };
342 let parent = tcx.parent_hir_node(use_expr.hir_id);
343 if let Node::Expr(expr) = parent
344 && let ExprKind::Assign(lhs, ..) = expr.kind
345 && lhs.hir_id == use_expr.hir_id
346 {
347 return;
367 }
368
369 for (_, node) in tcx.hir_parent_iter(closure_expr.hir_id) {
372 if let Node::Stmt(stmt) = node {
373 let padding = tcx
374 .sess
375 .source_map()
376 .indentation_before(stmt.span)
377 .unwrap_or_else(|| " ".to_string());
378 err.multipart_suggestion_verbose(
379 "consider cloning the value before moving it into the closure",
380 <[_]>::into_vec(::alloc::boxed::box_new([(stmt.span.shrink_to_lo(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("let value = {0}.clone();\n{1}",
upvar_name, padding))
})), (use_span, "value".to_string())]))vec![
381 (
382 stmt.span.shrink_to_lo(),
383 format!("let value = {upvar_name}.clone();\n{padding}"),
384 ),
385 (use_span, "value".to_string()),
386 ],
387 Applicability::MachineApplicable,
388 );
389 suggested = true;
390 break;
391 } else if let Node::Expr(expr) = node
392 && let ExprKind::Closure(_) = expr.kind
393 {
394 break;
397 }
398 }
399 if !suggested {
400 let padding = tcx
404 .sess
405 .source_map()
406 .indentation_before(closure_expr.span)
407 .unwrap_or_else(|| " ".to_string());
408 err.multipart_suggestion_verbose(
409 "consider cloning the value before moving it into the closure",
410 <[_]>::into_vec(::alloc::boxed::box_new([(closure_expr.span.shrink_to_lo(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{{\n{0}let value = {1}.clone();\n{0}",
padding, upvar_name))
})), (use_spans.var_or_use(), "value".to_string()),
(closure_expr.span.shrink_to_hi(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\n{0}}}", padding))
}))]))vec![
411 (
412 closure_expr.span.shrink_to_lo(),
413 format!("{{\n{padding}let value = {upvar_name}.clone();\n{padding}"),
414 ),
415 (use_spans.var_or_use(), "value".to_string()),
416 (closure_expr.span.shrink_to_hi(), format!("\n{padding}}}")),
417 ],
418 Applicability::MachineApplicable,
419 );
420 }
421 }
422
423 fn report_cannot_move_from_borrowed_content(
424 &mut self,
425 move_place: Place<'tcx>,
426 deref_target_place: Place<'tcx>,
427 span: Span,
428 use_spans: Option<UseSpans<'tcx>>,
429 ) -> Diag<'infcx> {
430 let tcx = self.infcx.tcx;
431 let ty = deref_target_place.ty(self.body, tcx).ty;
435 let upvar_field = self
436 .prefixes(move_place.as_ref(), PrefixSet::All)
437 .find_map(|p| self.is_upvar_field_projection(p));
438
439 let deref_base = match deref_target_place.projection.as_ref() {
440 [proj_base @ .., ProjectionElem::Deref] => {
441 PlaceRef { local: deref_target_place.local, projection: proj_base }
442 }
443 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("deref_target_place is not a deref projection"))bug!("deref_target_place is not a deref projection"),
444 };
445
446 if let PlaceRef { local, projection: [] } = deref_base {
447 let decl = &self.body.local_decls[local];
448 let local_name = self.local_name(local).map(|sym| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}`", sym))
})format!("`{sym}`"));
449 if decl.is_ref_for_guard() {
450 return self
451 .cannot_move_out_of(
452 span,
453 &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} in pattern guard",
local_name.as_deref().unwrap_or("the place")))
})format!(
454 "{} in pattern guard",
455 local_name.as_deref().unwrap_or("the place")
456 ),
457 )
458 .with_note(
459 "variables bound in patterns cannot be moved from \
460 until after the end of the pattern guard",
461 );
462 } else if decl.is_ref_to_static() {
463 return self.report_cannot_move_from_static(move_place, span);
464 }
465 }
466
467 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_borrowck/src/diagnostics/move_errors.rs:467",
"rustc_borrowck::diagnostics::move_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
::tracing_core::__macro_support::Option::Some(467u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("report: ty={0:?}",
ty) as &dyn Value))])
});
} else { ; }
};debug!("report: ty={:?}", ty);
468 let mut err = match ty.kind() {
469 ty::Array(..) | ty::Slice(..) => {
470 self.cannot_move_out_of_interior_noncopy(span, ty, None)
471 }
472 ty::Closure(def_id, closure_args)
473 if def_id.as_local() == Some(self.mir_def_id())
474 && let Some(upvar_field) = upvar_field =>
475 {
476 self.report_closure_move_error(
477 span,
478 move_place,
479 *def_id,
480 closure_args.as_closure().kind_ty(),
481 upvar_field,
482 ty::Asyncness::No,
483 )
484 }
485 ty::CoroutineClosure(def_id, closure_args)
486 if def_id.as_local() == Some(self.mir_def_id())
487 && let Some(upvar_field) = upvar_field
488 && self
489 .get_closure_bound_clause_span(*def_id, ty::Asyncness::Yes)
490 .is_some() =>
491 {
492 self.report_closure_move_error(
493 span,
494 move_place,
495 *def_id,
496 closure_args.as_coroutine_closure().kind_ty(),
497 upvar_field,
498 ty::Asyncness::Yes,
499 )
500 }
501 _ => {
502 let source = self.borrowed_content_source(deref_base);
503 let move_place_ref = move_place.as_ref();
504 match (
505 self.describe_place_with_options(
506 move_place_ref,
507 DescribePlaceOpt {
508 including_downcast: false,
509 including_tuple_field: false,
510 },
511 ),
512 self.describe_name(move_place_ref),
513 source.describe_for_named_place(),
514 ) {
515 (Some(place_desc), Some(name), Some(source_desc)) => self.cannot_move_out_of(
516 span,
517 &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` as enum variant `{1}` which is behind a {2}",
place_desc, name, source_desc))
})format!("`{place_desc}` as enum variant `{name}` which is behind a {source_desc}"),
518 ),
519 (Some(place_desc), Some(name), None) => self.cannot_move_out_of(
520 span,
521 &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` as enum variant `{1}`",
place_desc, name))
})format!("`{place_desc}` as enum variant `{name}`"),
522 ),
523 (Some(place_desc), _, Some(source_desc)) => self.cannot_move_out_of(
524 span,
525 &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` which is behind a {1}",
place_desc, source_desc))
})format!("`{place_desc}` which is behind a {source_desc}"),
526 ),
527 (_, _, _) => self.cannot_move_out_of(
528 span,
529 &source.describe_for_unnamed_place(tcx),
530 ),
531 }
532 }
533 };
534 let msg_opt = CapturedMessageOpt {
535 is_partial_move: false,
536 is_loop_message: false,
537 is_move_msg: false,
538 is_loop_move: false,
539 has_suggest_reborrow: false,
540 maybe_reinitialized_locations_is_empty: true,
541 };
542 if let Some(use_spans) = use_spans {
543 self.explain_captures(&mut err, span, span, use_spans, move_place, msg_opt);
544 }
545 err
546 }
547
548 fn report_closure_move_error(
549 &self,
550 span: Span,
551 move_place: Place<'tcx>,
552 def_id: DefId,
553 closure_kind_ty: Ty<'tcx>,
554 upvar_field: FieldIdx,
555 asyncness: ty::Asyncness,
556 ) -> Diag<'infcx> {
557 let tcx = self.infcx.tcx;
558
559 let closure_kind = match closure_kind_ty.to_opt_closure_kind() {
560 Some(kind @ (ty::ClosureKind::Fn | ty::ClosureKind::FnMut)) => kind,
561 Some(ty::ClosureKind::FnOnce) => {
562 ::rustc_middle::util::bug::bug_fmt(format_args!("closure kind does not match first argument type"))bug!("closure kind does not match first argument type")
563 }
564 None => ::rustc_middle::util::bug::bug_fmt(format_args!("closure kind not inferred by borrowck"))bug!("closure kind not inferred by borrowck"),
565 };
566
567 let async_prefix = if asyncness.is_async() { "Async" } else { "" };
568 let capture_description =
569 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("captured variable in an `{0}{1}` closure",
async_prefix, closure_kind))
})format!("captured variable in an `{async_prefix}{closure_kind}` closure");
570
571 let upvar = &self.upvars[upvar_field.index()];
572 let upvar_hir_id = upvar.get_root_variable();
573 let upvar_name = upvar.to_string(tcx);
574 let upvar_span = tcx.hir_span(upvar_hir_id);
575
576 let place_name = self.describe_any_place(move_place.as_ref());
577
578 let place_description = if self.is_upvar_field_projection(move_place.as_ref()).is_some() {
579 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}, a {1}", place_name,
capture_description))
})format!("{place_name}, a {capture_description}")
580 } else {
581 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}, as `{1}` is a {2}",
place_name, upvar_name, capture_description))
})format!("{place_name}, as `{upvar_name}` is a {capture_description}")
582 };
583
584 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_borrowck/src/diagnostics/move_errors.rs:584",
"rustc_borrowck::diagnostics::move_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/diagnostics/move_errors.rs"),
::tracing_core::__macro_support::Option::Some(584u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::move_errors"),
::tracing_core::field::FieldSet::new(&["closure_kind_ty",
"closure_kind", "place_description"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&closure_kind_ty)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&closure_kind)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&place_description)
as &dyn Value))])
});
} else { ; }
};debug!(?closure_kind_ty, ?closure_kind, ?place_description);
585
586 let closure_span = tcx.def_span(def_id);
587
588 let help_msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}Fn` and `{0}FnMut` closures require captured values to be able to be consumed multiple times, but `{0}FnOnce` closures may consume them only once",
async_prefix))
})format!(
589 "`{async_prefix}Fn` and `{async_prefix}FnMut` closures require captured values to \
590 be able to be consumed multiple times, but `{async_prefix}FnOnce` closures may \
591 consume them only once"
592 );
593
594 let mut err = self
595 .cannot_move_out_of(span, &place_description)
596 .with_span_label(upvar_span, "captured outer variable")
597 .with_span_label(
598 closure_span,
599 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("captured by this `{0}{1}` closure",
async_prefix, closure_kind))
})format!("captured by this `{async_prefix}{closure_kind}` closure"),
600 );
601
602 if let Some(bound_span) = self.get_closure_bound_clause_span(def_id, asyncness) {
603 err.span_help(bound_span, help_msg);
604 } else if !asyncness.is_async() {
605 err.help(help_msg);
609 }
610
611 err
612 }
613
614 fn get_closure_bound_clause_span(
615 &self,
616 def_id: DefId,
617 asyncness: ty::Asyncness,
618 ) -> Option<Span> {
619 let tcx = self.infcx.tcx;
620 let typeck_result = tcx.typeck(self.mir_def_id());
621 let closure_hir_id = tcx.local_def_id_to_hir_id(def_id.expect_local());
624 let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return None };
625
626 let predicates = match parent.kind {
627 hir::ExprKind::Call(callee, _) => {
628 let ty = typeck_result.node_type_opt(callee.hir_id)?;
629 let ty::FnDef(fn_def_id, args) = ty.kind() else { return None };
630 tcx.predicates_of(fn_def_id).instantiate(tcx, args)
631 }
632 hir::ExprKind::MethodCall(..) => {
633 let (_, method) = typeck_result.type_dependent_def(parent.hir_id)?;
634 let args = typeck_result.node_args(parent.hir_id);
635 tcx.predicates_of(method).instantiate(tcx, args)
636 }
637 _ => return None,
638 };
639
640 for (pred, span) in predicates.predicates.iter().zip(predicates.spans.iter()) {
643 let dominated_by_fn_trait = self
644 .closure_clause_kind(*pred, def_id, asyncness)
645 .is_some_and(|kind| #[allow(non_exhaustive_omitted_patterns)] match kind {
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => true,
_ => false,
}matches!(kind, ty::ClosureKind::Fn | ty::ClosureKind::FnMut));
646 if dominated_by_fn_trait {
647 return Some(*span);
652 }
653 }
654 None
655 }
656
657 fn closure_clause_kind(
661 &self,
662 pred: ty::Clause<'tcx>,
663 def_id: DefId,
664 asyncness: ty::Asyncness,
665 ) -> Option<ty::ClosureKind> {
666 let tcx = self.infcx.tcx;
667 let clause = pred.as_trait_clause()?;
668 let kind = match asyncness {
669 ty::Asyncness::Yes => tcx.async_fn_trait_kind_from_def_id(clause.def_id()),
670 ty::Asyncness::No => tcx.fn_trait_kind_from_def_id(clause.def_id()),
671 }?;
672 match clause.self_ty().skip_binder().kind() {
673 ty::Closure(id, _) | ty::CoroutineClosure(id, _) if *id == def_id => Some(kind),
674 _ => None,
675 }
676 }
677
678 fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) {
679 match error {
680 GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => {
681 self.add_borrow_suggestions(err, span);
682 if binds_to.is_empty() {
683 let place_ty = move_from.ty(self.body, self.infcx.tcx).ty;
684 let place_desc = match self.describe_place(move_from.as_ref()) {
685 Some(desc) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}`", desc))
})format!("`{desc}`"),
686 None => "value".to_string(),
687 };
688
689 if let Some(expr) = self.find_expr(span) {
690 self.suggest_cloning(err, move_from.as_ref(), place_ty, expr, None);
691 }
692
693 err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
694 is_partial_move: false,
695 ty: place_ty,
696 place: &place_desc,
697 span,
698 });
699 } else {
700 binds_to.sort();
701 binds_to.dedup();
702
703 self.add_move_error_details(err, &binds_to);
704 }
705 }
706 GroupedMoveError::MovesFromValue { mut binds_to, .. } => {
707 binds_to.sort();
708 binds_to.dedup();
709 self.add_move_error_suggestions(err, &binds_to);
710 self.add_move_error_details(err, &binds_to);
711 }
712 GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => {
714 let mut use_span = use_spans.var_or_use();
715 let place_ty = original_path.ty(self.body, self.infcx.tcx).ty;
716 let place_desc = match self.describe_place(original_path.as_ref()) {
717 Some(desc) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}`", desc))
})format!("`{desc}`"),
718 None => "value".to_string(),
719 };
720
721 if let Some(expr) = self.find_expr(use_span) {
722 self.suggest_cloning(
723 err,
724 original_path.as_ref(),
725 place_ty,
726 expr,
727 Some(use_spans),
728 );
729 }
730
731 if let Some(upvar_field) = self
732 .prefixes(original_path.as_ref(), PrefixSet::All)
733 .find_map(|p| self.is_upvar_field_projection(p))
734 {
735 let upvar = &self.upvars[upvar_field.index()];
737 let upvar_hir_id = upvar.get_root_variable();
738 use_span = match self.infcx.tcx.parent_hir_node(upvar_hir_id) {
739 hir::Node::Param(param) => {
740 param.ty_span
743 }
744 hir::Node::LetStmt(stmt) => match (stmt.ty, stmt.init) {
745 (Some(ty), _) => ty.span,
747 (None, Some(init))
751 if !self.infcx.tcx.sess.source_map().is_multiline(init.span) =>
752 {
753 init.span
754 }
755 _ => use_span,
756 },
757 _ => use_span,
758 };
759 }
760
761 err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
762 is_partial_move: false,
763 ty: place_ty,
764 place: &place_desc,
765 span: use_span,
766 });
767
768 let mut pointed_at_span = false;
769 use_spans.args_subdiag(err, |args_span| {
770 if args_span == span || args_span == use_span {
771 pointed_at_span = true;
772 }
773 crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
774 place: place_desc.clone(),
775 args_span,
776 }
777 });
778 if !pointed_at_span && use_span != span {
779 err.subdiagnostic(crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
780 place: place_desc,
781 args_span: span,
782 });
783 }
784
785 self.add_note_for_packed_struct_derive(err, original_path.local);
786 }
787 }
788 }
789
790 fn add_borrow_suggestions(&self, err: &mut Diag<'_>, span: Span) {
791 match self.infcx.tcx.sess.source_map().span_to_snippet(span) {
792 Ok(snippet) if snippet.starts_with('*') => {
793 let sp = span.with_lo(span.lo() + BytePos(1));
794 let inner = self.find_expr(sp);
795 let mut is_raw_ptr = false;
796 if let Some(inner) = inner {
797 let typck_result = self.infcx.tcx.typeck(self.mir_def_id());
798 if let Some(inner_type) = typck_result.node_type_opt(inner.hir_id) {
799 if #[allow(non_exhaustive_omitted_patterns)] match inner_type.kind() {
ty::RawPtr(..) => true,
_ => false,
}matches!(inner_type.kind(), ty::RawPtr(..)) {
800 is_raw_ptr = true;
801 }
802 }
803 }
804 if !is_raw_ptr {
807 err.span_suggestion_verbose(
808 span.with_hi(span.lo() + BytePos(1)),
809 "consider removing the dereference here",
810 String::new(),
811 Applicability::MaybeIncorrect,
812 );
813 }
814 }
815 _ => {
816 err.span_suggestion_verbose(
817 span.shrink_to_lo(),
818 "consider borrowing here",
819 '&',
820 Applicability::MaybeIncorrect,
821 );
822 }
823 }
824 }
825
826 fn add_move_error_suggestions(&self, err: &mut Diag<'_>, binds_to: &[Local]) {
827 struct BindingFinder<'tcx> {
830 typeck_results: &'tcx ty::TypeckResults<'tcx>,
831 tcx: TyCtxt<'tcx>,
832 pat_span: Span,
834 binding_spans: Vec<Span>,
836 found_pat: bool,
838 ref_pat: Option<&'tcx hir::Pat<'tcx>>,
840 has_adjustments: bool,
842 ref_pat_for_binding: Vec<(Span, Option<&'tcx hir::Pat<'tcx>>)>,
844 cannot_remove: FxHashSet<HirId>,
846 }
847 impl<'tcx> Visitor<'tcx> for BindingFinder<'tcx> {
848 type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies;
849
850 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
851 self.tcx
852 }
853
854 fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) -> Self::Result {
855 if !self.found_pat {
857 hir::intravisit::walk_expr(self, ex)
858 }
859 }
860
861 fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
862 if p.span == self.pat_span {
863 self.found_pat = true;
864 }
865
866 let parent_has_adjustments = self.has_adjustments;
867 self.has_adjustments |=
868 self.typeck_results.pat_adjustments().contains_key(p.hir_id);
869
870 let parent_ref_pat = self.ref_pat;
872 if let hir::PatKind::Ref(..) = p.kind {
873 self.ref_pat = Some(p);
874 self.cannot_remove.extend(parent_ref_pat.map(|r| r.hir_id));
877 if self.has_adjustments {
878 self.cannot_remove.insert(p.hir_id);
880 self.has_adjustments = false;
882 }
883 }
884
885 if let hir::PatKind::Binding(_, _, ident, _) = p.kind {
886 if let Some(&bind_sp) =
888 self.binding_spans.iter().find(|bind_sp| bind_sp.contains(ident.span))
889 {
890 self.ref_pat_for_binding.push((bind_sp, self.ref_pat));
891 } else {
892 if let Some(ref_pat) = self.ref_pat {
895 self.cannot_remove.insert(ref_pat.hir_id);
896 }
897 }
898 }
899
900 hir::intravisit::walk_pat(self, p);
901 self.ref_pat = parent_ref_pat;
902 self.has_adjustments = parent_has_adjustments;
903 }
904 }
905 let mut pat_span = None;
906 let mut binding_spans = Vec::new();
907 for local in binds_to {
908 let bind_to = &self.body.local_decls[*local];
909 if let LocalInfo::User(BindingForm::Var(VarBindingForm { pat_span: pat_sp, .. })) =
910 *bind_to.local_info()
911 {
912 pat_span = Some(pat_sp);
913 binding_spans.push(bind_to.source_info.span);
914 }
915 }
916 let Some(pat_span) = pat_span else { return };
917
918 let tcx = self.infcx.tcx;
919 let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) else { return };
920 let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
921 let mut finder = BindingFinder {
922 typeck_results,
923 tcx,
924 pat_span,
925 binding_spans,
926 found_pat: false,
927 ref_pat: None,
928 has_adjustments: false,
929 ref_pat_for_binding: Vec::new(),
930 cannot_remove: FxHashSet::default(),
931 };
932 finder.visit_body(body);
933
934 let mut suggestions = Vec::new();
935 for (binding_span, opt_ref_pat) in finder.ref_pat_for_binding {
936 if let Some(ref_pat) = opt_ref_pat
937 && !finder.cannot_remove.contains(&ref_pat.hir_id)
938 && let hir::PatKind::Ref(subpat, pinned, mutbl) = ref_pat.kind
939 && let Some(ref_span) = ref_pat.span.trim_end(subpat.span)
940 {
941 let pinned_str = if pinned.is_pinned() { "pinned " } else { "" };
942 let mutable_str = if mutbl.is_mut() { "mutable " } else { "" };
943 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider removing the {0}{1}borrow",
pinned_str, mutable_str))
})format!("consider removing the {pinned_str}{mutable_str}borrow");
944 suggestions.push((ref_span, msg, "".to_string()));
945 } else {
946 let msg = "consider borrowing the pattern binding".to_string();
947 suggestions.push((binding_span.shrink_to_lo(), msg, "ref ".to_string()));
948 }
949 }
950 suggestions.sort_unstable_by_key(|&(span, _, _)| span);
951 suggestions.dedup_by_key(|&mut (span, _, _)| span);
952 for (span, msg, suggestion) in suggestions {
953 err.span_suggestion_verbose(span, msg, suggestion, Applicability::MachineApplicable);
954 }
955 }
956
957 fn add_move_error_details(&self, err: &mut Diag<'_>, binds_to: &[Local]) {
958 for (j, local) in binds_to.iter().enumerate() {
959 let bind_to = &self.body.local_decls[*local];
960 let binding_span = bind_to.source_info.span;
961
962 if j == 0 {
963 err.span_label(binding_span, "data moved here");
964 } else {
965 err.span_label(binding_span, "...and here");
966 }
967
968 if binds_to.len() == 1 {
969 let place_desc = self.local_name(*local).map(|sym| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}`", sym))
})format!("`{sym}`"));
970
971 if let Some(expr) = self.find_expr(binding_span) {
972 let local_place: PlaceRef<'tcx> = (*local).into();
973 self.suggest_cloning(err, local_place, bind_to.ty, expr, None);
974 }
975
976 err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
977 is_partial_move: false,
978 ty: bind_to.ty,
979 place: place_desc.as_deref().unwrap_or("the place"),
980 span: binding_span,
981 });
982 }
983 }
984
985 if binds_to.len() > 1 {
986 err.note(
987 "move occurs because these variables have types that don't implement the `Copy` \
988 trait",
989 );
990 }
991 }
992
993 fn add_note_for_packed_struct_derive(&self, err: &mut Diag<'_>, local: Local) {
998 let local_place: PlaceRef<'tcx> = local.into();
999 let local_ty = local_place.ty(self.body.local_decls(), self.infcx.tcx).ty.peel_refs();
1000
1001 if let Some(adt) = local_ty.ty_adt_def()
1002 && adt.repr().packed()
1003 && let ExpnKind::Macro(MacroKind::Derive, name) =
1004 self.body.span.ctxt().outer_expn_data().kind
1005 {
1006 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`#[derive({0})]` triggers a move because taking references to the fields of a packed struct is undefined behaviour",
name))
})format!("`#[derive({name})]` triggers a move because taking references to the fields of a packed struct is undefined behaviour"));
1007 }
1008 }
1009}