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