1use core::ops::ControlFlow;
2
3use either::Either;
4use hir::{ExprKind, Param};
5use rustc_abi::FieldIdx;
6use rustc_errors::{Applicability, Diag};
7use rustc_hir::def_id::DefId;
8use rustc_hir::intravisit::Visitor;
9use rustc_hir::{self as hir, BindingMode, ByRef, Expr, Node};
10use rustc_middle::bug;
11use rustc_middle::hir::place::PlaceBase;
12use rustc_middle::mir::visit::PlaceContext;
13use rustc_middle::mir::{
14 self, BindingForm, Body, BorrowKind, Local, LocalDecl, LocalInfo, LocalKind, Location,
15 Mutability, Operand, Place, PlaceRef, ProjectionElem, RawPtrKind, Rvalue, Statement,
16 StatementKind, TerminatorKind,
17};
18use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, Upcast};
19use rustc_span::{BytePos, DesugaringKind, Span, Symbol, kw, sym};
20use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
21use rustc_trait_selection::infer::InferCtxtExt;
22use rustc_trait_selection::traits;
23use tracing::{debug, trace};
24
25use crate::diagnostics::BorrowedContentSource;
26use crate::{MirBorrowckCtxt, session_diagnostics};
27
28#[derive(#[automatically_derived]
impl ::core::marker::Copy for AccessKind { }Copy, #[automatically_derived]
impl ::core::clone::Clone for AccessKind {
#[inline]
fn clone(&self) -> AccessKind { *self }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for AccessKind {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
AccessKind::MutableBorrow => "MutableBorrow",
AccessKind::Mutate => "Mutate",
})
}
}Debug, #[automatically_derived]
impl ::core::cmp::Eq for AccessKind {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq, #[automatically_derived]
impl ::core::cmp::PartialEq for AccessKind {
#[inline]
fn eq(&self, other: &AccessKind) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq)]
29pub(crate) enum AccessKind {
30 MutableBorrow,
31 Mutate,
32}
33
34fn find_assignments(body: &Body<'_>, local: Local) -> Vec<Location> {
37 use rustc_middle::mir::visit::Visitor;
38
39 struct FindLocalAssignmentVisitor {
40 needle: Local,
41 locations: Vec<Location>,
42 }
43
44 impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
45 fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) {
46 if self.needle != local {
47 return;
48 }
49
50 if place_context.is_place_assignment() {
51 self.locations.push(location);
52 }
53 }
54 }
55
56 let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: ::alloc::vec::Vec::new()vec![] };
57 visitor.visit_body(body);
58 visitor.locations
59}
60
61impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
62 pub(crate) fn report_mutability_error(
63 &mut self,
64 access_place: Place<'tcx>,
65 span: Span,
66 the_place_err: PlaceRef<'tcx>,
67 error_access: AccessKind,
68 location: Location,
69 ) {
70 {
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/mutability_errors.rs:70",
"rustc_borrowck::diagnostics::mutability_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs"),
::tracing_core::__macro_support::Option::Some(70u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::mutability_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_mutability_error(access_place={0:?}, span={1:?}, the_place_err={2:?}, error_access={3:?}, location={4:?},)",
access_place, span, the_place_err, error_access, location)
as &dyn Value))])
});
} else { ; }
};debug!(
71 "report_mutability_error(\
72 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
73 )",
74 access_place, span, the_place_err, error_access, location,
75 );
76
77 let mut err;
78 let item_msg;
79 let reason;
80 let mut opt_source = None;
81 let access_place_desc = self.describe_any_place(access_place.as_ref());
82 {
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/mutability_errors.rs:82",
"rustc_borrowck::diagnostics::mutability_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs"),
::tracing_core::__macro_support::Option::Some(82u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::mutability_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_mutability_error: access_place_desc={0:?}",
access_place_desc) as &dyn Value))])
});
} else { ; }
};debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
83
84 match the_place_err {
85 PlaceRef { local, projection: [] } => {
86 item_msg = access_place_desc;
87 if access_place.as_local().is_some() {
88 reason = ", as it is not declared as mutable".to_string();
89 } else {
90 let name = self.local_name(local).expect("immutable unnamed local");
91 reason = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(", as `{0}` is not declared as mutable",
name))
})format!(", as `{name}` is not declared as mutable");
92 }
93 }
94
95 PlaceRef {
96 local,
97 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
98 } => {
99 if true {
if !is_closure_like(Place::ty_from(local, proj_base, self.body,
self.infcx.tcx).ty) {
::core::panicking::panic("assertion failed: is_closure_like(Place::ty_from(local, proj_base, self.body,\n self.infcx.tcx).ty)")
};
};debug_assert!(is_closure_like(
100 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
101 ));
102
103 let imm_borrow_derefed = self.upvars[upvar_index.index()]
104 .place
105 .deref_tys()
106 .any(|ty| #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Ref(.., hir::Mutability::Not) => true,
_ => false,
}matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)));
107
108 if imm_borrow_derefed {
115 return;
117 } else {
118 item_msg = access_place_desc;
119 if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
120 reason = ", as it is not declared as mutable".to_string();
121 } else {
122 let name = self.upvars[upvar_index.index()].to_string(self.infcx.tcx);
123 reason = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(", as `{0}` is not declared as mutable",
name))
})format!(", as `{name}` is not declared as mutable");
124 }
125 }
126 }
127
128 PlaceRef { local, projection: [ProjectionElem::Deref] }
129 if self.body.local_decls[local].is_ref_for_guard() =>
130 {
131 item_msg = access_place_desc;
132 reason = ", as it is immutable for the pattern guard".to_string();
133 }
134 PlaceRef { local, projection: [ProjectionElem::Deref] }
135 if self.body.local_decls[local].is_ref_to_static() =>
136 {
137 if access_place.projection.len() == 1 {
138 item_msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("immutable static item {0}",
access_place_desc))
})format!("immutable static item {access_place_desc}");
139 reason = String::new();
140 } else {
141 item_msg = access_place_desc;
142 let local_info = self.body.local_decls[local].local_info();
143 let LocalInfo::StaticRef { def_id, .. } = *local_info else {
144 ::rustc_middle::util::bug::bug_fmt(format_args!("is_ref_to_static return true, but not ref to static?"));bug!("is_ref_to_static return true, but not ref to static?");
145 };
146 let static_name = &self.infcx.tcx.item_name(def_id);
147 reason = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(", as `{0}` is an immutable static item",
static_name))
})format!(", as `{static_name}` is an immutable static item");
148 }
149 }
150 PlaceRef { local, projection: [proj_base @ .., ProjectionElem::Deref] } => {
151 if local == ty::CAPTURE_STRUCT_LOCAL
152 && proj_base.is_empty()
153 && !self.upvars.is_empty()
154 {
155 item_msg = access_place_desc;
156 if true {
if !self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_ref() {
::core::panicking::panic("assertion failed: self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_ref()")
};
};debug_assert!(self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_ref());
157 if true {
if !is_closure_like(the_place_err.ty(self.body, self.infcx.tcx).ty) {
::core::panicking::panic("assertion failed: is_closure_like(the_place_err.ty(self.body, self.infcx.tcx).ty)")
};
};debug_assert!(is_closure_like(the_place_err.ty(self.body, self.infcx.tcx).ty));
158
159 reason = if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
160 ", as it is a captured variable in a `Fn` closure".to_string()
161 } else {
162 ", as `Fn` closures cannot mutate their captured variables".to_string()
163 }
164 } else {
165 let source =
166 self.borrowed_content_source(PlaceRef { local, projection: proj_base });
167 let pointer_type = source.describe_for_immutable_place(self.infcx.tcx);
168 opt_source = Some(source);
169 if let Some(desc) = self.describe_place(access_place.as_ref()) {
170 item_msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}`", desc))
})format!("`{desc}`");
171 reason = match error_access {
172 AccessKind::Mutate => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(", which is behind {0}",
pointer_type))
})format!(", which is behind {pointer_type}"),
173 AccessKind::MutableBorrow => {
174 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(", as it is behind {0}",
pointer_type))
})format!(", as it is behind {pointer_type}")
175 }
176 }
177 } else {
178 item_msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("data in {0}", pointer_type))
})format!("data in {pointer_type}");
179 reason = String::new();
180 }
181 }
182 }
183
184 PlaceRef {
185 local: _,
186 projection:
187 [
188 ..,
189 ProjectionElem::Index(_)
190 | ProjectionElem::ConstantIndex { .. }
191 | ProjectionElem::OpaqueCast { .. }
192 | ProjectionElem::Subslice { .. }
193 | ProjectionElem::Downcast(..)
194 | ProjectionElem::UnwrapUnsafeBinder(_),
195 ],
196 } => ::rustc_middle::util::bug::bug_fmt(format_args!("Unexpected immutable place."))bug!("Unexpected immutable place."),
197 }
198
199 {
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/mutability_errors.rs:199",
"rustc_borrowck::diagnostics::mutability_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs"),
::tracing_core::__macro_support::Option::Some(199u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::mutability_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_mutability_error: item_msg={0:?}, reason={1:?}",
item_msg, reason) as &dyn Value))])
});
} else { ; }
};debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
200
201 let act;
204 let acted_on;
205 let mut suggest = true;
206 let mut mut_error = None;
207 let mut count = 1;
208
209 let span = match error_access {
210 AccessKind::Mutate => {
211 err = self.cannot_assign(span, &(item_msg + &reason));
212 act = "assign";
213 acted_on = "written to";
214 span
215 }
216 AccessKind::MutableBorrow => {
217 act = "borrow as mutable";
218 acted_on = "borrowed as mutable";
219
220 let borrow_spans = self.borrow_spans(span, location);
221 let borrow_span = borrow_spans.args_or_use();
222 match the_place_err {
223 PlaceRef { local, projection: [] }
224 if self.body.local_decls[local].can_be_made_mutable() =>
225 {
226 let span = self.body.local_decls[local].source_info.span;
227 mut_error = Some(span);
228 if let Some((buffered_err, c)) = self.get_buffered_mut_error(span) {
229 err = buffered_err;
234 count = c + 1;
235 if count == 2 {
236 err.replace_span_with(span, false);
237 err.span_label(span, "not mutable");
238 }
239 suggest = false;
240 } else {
241 err = self.cannot_borrow_path_as_mutable_because(
242 borrow_span,
243 &item_msg,
244 &reason,
245 );
246 }
247 }
248 _ => {
249 err = self.cannot_borrow_path_as_mutable_because(
250 borrow_span,
251 &item_msg,
252 &reason,
253 );
254 }
255 }
256 if suggest {
257 borrow_spans.var_subdiag(
258 &mut err,
259 Some(mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }),
260 |_kind, var_span| {
261 let place = self.describe_any_place(access_place.as_ref());
262 session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure {
263 place,
264 var_span,
265 }
266 },
267 );
268 }
269 borrow_span
270 }
271 };
272
273 {
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/mutability_errors.rs:273",
"rustc_borrowck::diagnostics::mutability_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs"),
::tracing_core::__macro_support::Option::Some(273u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::mutability_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_mutability_error: act={0:?}, acted_on={1:?}",
act, acted_on) as &dyn Value))])
});
} else { ; }
};debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
274
275 match the_place_err {
276 PlaceRef {
283 local,
284 projection:
285 [
286 proj_base @ ..,
287 ProjectionElem::Deref,
288 ProjectionElem::Field(field, _),
289 ProjectionElem::Deref,
290 ],
291 } => {
292 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
293
294 let place = Place::ty_from(local, proj_base, self.body, self.infcx.tcx);
295 if let Some(span) = get_mut_span_in_struct_field(self.infcx.tcx, place.ty, *field) {
296 err.span_suggestion_verbose(
297 span,
298 "consider changing this to be mutable",
299 " mut ",
300 Applicability::MaybeIncorrect,
301 );
302 }
303 }
304
305 PlaceRef { local, projection: [] }
307 if self
308 .body
309 .local_decls
310 .get(local)
311 .is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_name(local))) =>
312 {
313 let decl = &self.body.local_decls[local];
314 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
315 if let Some(mir::Statement {
316 source_info,
317 kind:
318 mir::StatementKind::Assign((
319 _,
320 mir::Rvalue::Ref(
321 _,
322 mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
323 _,
324 ),
325 )),
326 ..
327 }) = &self.body[location.block].statements.get(location.statement_index)
328 {
329 match *decl.local_info() {
330 LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
331 binding_mode: BindingMode(ByRef::No, Mutability::Not),
332 opt_ty_info: Some(sp),
333 pat_span,
334 ..
335 })) => {
336 if suggest {
337 err.span_note(sp, "the binding is already a mutable borrow");
338 err.span_suggestion_verbose(
339 pat_span.shrink_to_lo(),
340 "consider making the binding mutable if you need to reborrow \
341 multiple times",
342 "mut ".to_string(),
343 Applicability::MaybeIncorrect,
344 );
345 }
346 }
347 _ => {
348 err.span_note(
349 decl.source_info.span,
350 "the binding is already a mutable borrow",
351 );
352 }
353 }
354 if let Ok(snippet) =
355 self.infcx.tcx.sess.source_map().span_to_snippet(source_info.span)
356 {
357 if snippet.starts_with("&mut ") {
358 let mut in_pat_scrutinee = false;
364 let mut is_deref_coerced = false;
365 if let Some(expr) = self.find_expr(source_info.span) {
366 let tcx = self.infcx.tcx;
367 let span = expr.span.source_callsite();
368 for (_, node) in tcx.hir_parent_iter(expr.hir_id) {
369 if let Node::Expr(parent_expr) = node {
370 match parent_expr.kind {
371 ExprKind::Match(scrutinee, ..)
372 if scrutinee
373 .span
374 .source_callsite()
375 .contains(span) =>
376 {
377 in_pat_scrutinee = true;
378 break;
379 }
380 ExprKind::Let(let_expr)
381 if let_expr
382 .init
383 .span
384 .source_callsite()
385 .contains(span) =>
386 {
387 in_pat_scrutinee = true;
388 break;
389 }
390 _ => {}
391 }
392 }
393 }
394
395 let typeck = tcx.typeck(expr.hir_id.owner.def_id);
396 is_deref_coerced =
397 typeck.expr_adjustments(expr).iter().any(|adj| {
398 #[allow(non_exhaustive_omitted_patterns)] match adj.kind {
ty::adjustment::Adjust::Deref(_) => true,
_ => false,
}matches!(adj.kind, ty::adjustment::Adjust::Deref(_))
399 });
400 }
401
402 if in_pat_scrutinee {
403 err.span_suggestion_verbose(
405 source_info
406 .span
407 .with_lo(source_info.span.lo() + BytePos(5))
408 .shrink_to_lo(),
409 "to reborrow the mutable reference, add `*`",
410 "*",
411 Applicability::MaybeIncorrect,
412 );
413 } else if is_deref_coerced {
414 err.span_suggestion_verbose(
417 source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
418 "if there is only one mutable reborrow, remove the `&mut`",
419 "",
420 Applicability::MaybeIncorrect,
421 );
422 }
423 } else {
424 err.span_help(source_info.span, "try removing `&mut` here");
426 }
427 } else {
428 err.span_help(source_info.span, "try removing `&mut` here");
429 }
430 } else if decl.mutability.is_not() {
431 if #[allow(non_exhaustive_omitted_patterns)] match decl.local_info() {
LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::RefMut))
=> true,
_ => false,
}matches!(
432 decl.local_info(),
433 LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::RefMut))
434 ) {
435 err.note(
436 "as `Self` may be unsized, this call attempts to take `&mut &mut self`",
437 );
438 err.note("however, `&mut self` expands to `self: &mut Self`, therefore `self` cannot be borrowed mutably");
439 } else {
440 err.span_suggestion_verbose(
441 decl.source_info.span.shrink_to_lo(),
442 "consider making the binding mutable",
443 "mut ",
444 Applicability::MachineApplicable,
445 );
446 };
447 }
448 }
449
450 PlaceRef { local, projection: [] }
453 if self.body.local_decls[local].can_be_made_mutable() =>
454 {
455 let local_decl = &self.body.local_decls[local];
460 {
match (&local_decl.mutability, &Mutability::Not) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};assert_eq!(local_decl.mutability, Mutability::Not);
461
462 if count < 10 {
463 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
464 }
465 if suggest {
466 self.construct_mut_suggestion_for_local_binding_patterns(&mut err, local);
467 let tcx = self.infcx.tcx;
468 if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() {
469 self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
470 }
471 }
472 }
473
474 PlaceRef {
476 local,
477 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
478 } => {
479 if true {
if !is_closure_like(Place::ty_from(local, proj_base, self.body,
self.infcx.tcx).ty) {
::core::panicking::panic("assertion failed: is_closure_like(Place::ty_from(local, proj_base, self.body,\n self.infcx.tcx).ty)")
};
};debug_assert!(is_closure_like(
480 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
481 ));
482
483 let captured_place = self.upvars[upvar_index.index()];
484
485 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
486
487 let upvar_hir_id = captured_place.get_root_variable();
488
489 if let Node::Pat(pat) = self.infcx.tcx.hir_node(upvar_hir_id)
490 && let hir::PatKind::Binding(hir::BindingMode::NONE, _, upvar_ident, _) =
491 pat.kind
492 {
493 if upvar_ident.name == kw::SelfLower {
494 for (_, node) in self.infcx.tcx.hir_parent_iter(upvar_hir_id) {
495 if let Some(fn_decl) = node.fn_decl() {
496 if !#[allow(non_exhaustive_omitted_patterns)] match fn_decl.implicit_self() {
hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut => true,
_ => false,
}matches!(
497 fn_decl.implicit_self(),
498 hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut
499 ) {
500 err.span_suggestion_verbose(
501 upvar_ident.span.shrink_to_lo(),
502 "consider changing this to be mutable",
503 "mut ",
504 Applicability::MachineApplicable,
505 );
506 break;
507 }
508 }
509 }
510 } else {
511 err.span_suggestion_verbose(
512 upvar_ident.span.shrink_to_lo(),
513 "consider changing this to be mutable",
514 "mut ",
515 Applicability::MachineApplicable,
516 );
517 }
518 }
519
520 let tcx = self.infcx.tcx;
521 if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
522 && let ty::Closure(id, _) = *ty.kind()
523 {
524 self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
525 }
526 }
527
528 PlaceRef { local: _, projection: [] }
531 if self
532 .infcx
533 .tcx
534 .sess
535 .source_map()
536 .span_to_snippet(span)
537 .is_ok_and(|snippet| snippet.starts_with("&mut ")) =>
538 {
539 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
540 err.span_suggestion_verbose(
541 span.with_hi(span.lo() + BytePos(5)),
542 "try removing `&mut` here",
543 "",
544 Applicability::MaybeIncorrect,
545 );
546 }
547
548 PlaceRef { local, projection: [ProjectionElem::Deref] }
549 if self.body.local_decls[local].is_ref_for_guard() =>
550 {
551 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
552 err.note(
553 "variables bound in patterns are immutable until the end of the pattern guard",
554 );
555 }
556
557 PlaceRef { local, projection: [ProjectionElem::Deref] }
563 if self.body.local_decls[local].is_user_variable() =>
564 {
565 let local_decl = &self.body.local_decls[local];
566
567 let (pointer_sigil, pointer_desc) =
568 if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
569
570 match self.local_name(local) {
571 Some(name) if !local_decl.from_compiler_desugaring() => {
572 err.span_label(
573 span,
574 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` is a `{1}` {2}, so it cannot be {3}",
name, pointer_sigil, pointer_desc, acted_on))
})format!(
575 "`{name}` is a `{pointer_sigil}` {pointer_desc}, so it cannot be \
576 {acted_on}",
577 ),
578 );
579
580 self.suggest_using_iter_mut(&mut err);
581 self.suggest_make_local_mut(&mut err, local, name);
582 }
583 _ => {
584 err.span_label(
585 span,
586 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0} through `{1}` {2}", act,
pointer_sigil, pointer_desc))
})format!("cannot {act} through `{pointer_sigil}` {pointer_desc}"),
587 );
588 }
589 }
590 }
591
592 PlaceRef { local, projection: [ProjectionElem::Deref] }
593 if local == ty::CAPTURE_STRUCT_LOCAL && !self.upvars.is_empty() =>
594 {
595 self.point_at_binding_outside_closure(&mut err, local, access_place);
596 self.expected_fn_found_fn_mut_call(&mut err, span, act);
597 }
598
599 PlaceRef { local, projection: [.., ProjectionElem::Deref] } => {
600 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
601
602 match opt_source {
603 Some(BorrowedContentSource::OverloadedDeref(ty)) => {
604 err.help(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("trait `DerefMut` is required to modify through a dereference, but it is not implemented for `{0}`",
ty))
})format!(
605 "trait `DerefMut` is required to modify through a dereference, \
606 but it is not implemented for `{ty}`",
607 ));
608 }
609 Some(BorrowedContentSource::OverloadedIndex(ty)) => {
610 err.help(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("trait `IndexMut` is required to modify indexed content, but it is not implemented for `{0}`",
ty))
})format!(
611 "trait `IndexMut` is required to modify indexed content, \
612 but it is not implemented for `{ty}`",
613 ));
614 self.suggest_map_index_mut_alternatives(ty, &mut err, span);
615 }
616 _ => {
617 let local = &self.body.local_decls[local];
618 match *local.local_info() {
619 LocalInfo::StaticRef { def_id, .. } => {
620 let span = self.infcx.tcx.def_span(def_id);
621 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("this `static` cannot be {0}",
acted_on))
})format!("this `static` cannot be {acted_on}"));
622 }
623 LocalInfo::ConstRef { def_id } => {
624 let span = self.infcx.tcx.def_span(def_id);
625 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("this `const` cannot be {0}",
acted_on))
})format!("this `const` cannot be {acted_on}"));
626 }
627 LocalInfo::BlockTailTemp(_) | LocalInfo::Boring
628 if !local.source_info.span.overlaps(span) =>
629 {
630 err.span_label(
631 local.source_info.span,
632 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("this cannot be {0}", acted_on))
})format!("this cannot be {acted_on}"),
633 );
634 }
635 _ => {}
636 }
637 }
638 }
639 }
640
641 PlaceRef { local, .. } => {
642 let local = &self.body.local_decls[local];
643 if !local.source_info.span.overlaps(span) {
644 err.span_label(local.source_info.span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("this cannot be {0}", acted_on))
})format!("this cannot be {acted_on}"));
645 }
646 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
647 }
648 }
649
650 if let Some(span) = mut_error {
651 self.buffer_mut_error(span, err, count);
652 } else {
653 self.buffer_error(err);
654 }
655 }
656
657 fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'infcx>, span: Span) {
659 let Some(adt) = ty.ty_adt_def() else { return };
660 let did = adt.did();
661 if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did)
662 || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did)
663 {
664 struct SuggestIndexOperatorAlternativeVisitor<'a, 'infcx, 'tcx> {
668 assign_span: Span,
669 err: &'a mut Diag<'infcx>,
670 ty: Ty<'tcx>,
671 suggested: bool,
672 infcx: &'a rustc_infer::infer::InferCtxt<'tcx>,
673 }
674
675 impl<'a, 'infcx, 'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'a, 'infcx, 'tcx> {
676 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
677 hir::intravisit::walk_stmt(self, stmt);
678 let expr = match stmt.kind {
679 hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr,
680 hir::StmtKind::Let(hir::LetStmt { init: Some(expr), .. }) => expr,
681 _ => {
682 return;
683 }
684 };
685
686 fn count_ty_refs<'tcx>(mut ty: Ty<'tcx>) -> usize {
700 let mut count = 0;
701 while let ty::Ref(_, inner_ty, _) = ty.kind() {
702 ty = *inner_ty;
703 count += 1;
704 }
705 count
706 }
707
708 fn strip_n_refs<'a, 'b>(
713 mut expr: &'a Expr<'b>,
714 n: usize,
715 ) -> Result<&'a Expr<'b>, (usize, &'a Expr<'b>)> {
716 for count in 0..n {
717 match expr {
718 Expr {
719 kind: ExprKind::AddrOf(hir::BorrowKind::Ref, _, inner),
720 ..
721 } => expr = inner,
722 _ => return Err((count, expr)),
723 }
724 }
725 Ok(expr)
726 }
727
728 let key_ty = self.ty.walk().nth(1).unwrap().expect_ty();
730
731 if let hir::ExprKind::Assign(place, rv, _sp) = expr.kind
732 && let hir::ExprKind::Index(val, index, _) = place.kind
733 && (expr.span == self.assign_span || place.span == self.assign_span)
734 {
735 let index_ty =
737 self.infcx.tcx.typeck(val.hir_id.owner.def_id).expr_ty(index);
738
739 let (borrowed_prefix, borrowed_index);
740
741 if index_ty.peel_refs() == key_ty.peel_refs() {
745 let (index_refs, key_refs) =
746 (count_ty_refs(index_ty), count_ty_refs(key_ty));
747
748 let (deref_prefix, deref_index) = if index_refs >= key_refs {
749 strip_n_refs(index, index_refs - key_refs)
751 .map(|val| ("".to_string(), val))
752 .unwrap_or_else(|(depth, val)| {
753 (
754 if key_refs == 0 {
755 "*".repeat(
756 (index_refs-key_refs).checked_sub(depth).expect("return depth from strip_n_refs should be smaller than the input")
757 )
758 } else {
759 String::new() },
761 val,
762 )
763 })
764 } else {
765 ("&".repeat(key_refs - index_refs), index)
767 };
768
769 self.err.multipart_suggestion(
770 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use `.insert()` to insert a value into a `{0}`",
self.ty))
})format!("use `.insert()` to insert a value into a `{}`", self.ty),
771 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(val.span.shrink_to_hi().with_hi(deref_index.span.lo()),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".insert({0}",
deref_prefix))
})),
(deref_index.span.shrink_to_hi().with_hi(rv.span.lo()),
", ".to_string()),
(rv.span.shrink_to_hi(), ")".to_string())]))vec![
772 (
774 val.span.shrink_to_hi().with_hi(deref_index.span.lo()),
775 format!(".insert({deref_prefix}"),
776 ),
777 (
778 deref_index.span.shrink_to_hi().with_hi(rv.span.lo()),
779 ", ".to_string(),
780 ),
781 (rv.span.shrink_to_hi(), ")".to_string()),
782 ],
783 Applicability::MaybeIncorrect,
784 );
785 self.err.multipart_suggestion(
786 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use the entry API to modify a `{0}` for more flexibility",
self.ty))
})format!(
787 "use the entry API to modify a `{}` for more flexibility",
788 self.ty
789 ),
790 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(val.span.shrink_to_lo(), "let val = ".to_string()),
(val.span.shrink_to_hi().with_hi(deref_index.span.lo()),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".entry({0}",
deref_prefix))
})),
(deref_index.span.shrink_to_hi().with_hi(rv.span.lo()),
").insert_entry(".to_string()),
(rv.span.shrink_to_hi(), ")".to_string())]))vec![
791 (val.span.shrink_to_lo(), "let val = ".to_string()),
793 (
794 val.span.shrink_to_hi().with_hi(deref_index.span.lo()),
795 format!(".entry({deref_prefix}"),
796 ),
797 (
798 deref_index.span.shrink_to_hi().with_hi(rv.span.lo()),
799 ").insert_entry(".to_string(),
800 ),
801 (rv.span.shrink_to_hi(), ")".to_string()),
802 ],
803 Applicability::MaybeIncorrect,
804 );
805
806 (borrowed_prefix, borrowed_index) = (
809 String::new(),
810 if index_refs > key_refs {
811 strip_n_refs(index, index_refs - key_refs - 1)
812 .unwrap_or_else(|(_depth, val)| val)
813 } else {
815 index
817 },
818 );
819 } else {
820 (borrowed_prefix, borrowed_index) = (String::new(), index)
821 }
822 self.err.multipart_suggestion(
825 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use `.get_mut()` to modify an existing key in a `{0}`",
self.ty))
})format!(
826 "use `.get_mut()` to modify an existing key in a `{}`",
827 self.ty,
828 ),
829 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(val.span.shrink_to_lo(), "if let Some(val) = ".to_string()),
(val.span.shrink_to_hi().with_hi(borrowed_index.span.lo()),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".get_mut({0}",
borrowed_prefix))
})),
(borrowed_index.span.shrink_to_hi().with_hi(place.span.hi()),
") { *val".to_string()),
(rv.span.shrink_to_hi(), "; }".to_string())]))vec![
830 (val.span.shrink_to_lo(), "if let Some(val) = ".to_string()),
832 (
833 val.span.shrink_to_hi().with_hi(borrowed_index.span.lo()),
834 format!(".get_mut({borrowed_prefix}"),
835 ),
836 (
837 borrowed_index.span.shrink_to_hi().with_hi(place.span.hi()),
838 ") { *val".to_string(),
839 ),
840 (rv.span.shrink_to_hi(), "; }".to_string()),
841 ],
842 Applicability::MaybeIncorrect,
843 );
844
845 self.suggested = true;
846 } else if let hir::ExprKind::MethodCall(_path, receiver, _, sp) = expr.kind
847 && let hir::ExprKind::Index(val, index, _) = receiver.kind
848 && receiver.span == self.assign_span
849 {
850 self.err.multipart_suggestion(
852 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("to modify a `{0}` use `.get_mut()`",
self.ty))
})format!("to modify a `{}` use `.get_mut()`", self.ty),
853 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(val.span.shrink_to_lo(), "if let Some(val) = ".to_string()),
(val.span.shrink_to_hi().with_hi(index.span.lo()),
".get_mut(".to_string()),
(index.span.shrink_to_hi().with_hi(receiver.span.hi()),
") { val".to_string()),
(sp.shrink_to_hi(), "; }".to_string())]))vec![
854 (val.span.shrink_to_lo(), "if let Some(val) = ".to_string()),
855 (
856 val.span.shrink_to_hi().with_hi(index.span.lo()),
857 ".get_mut(".to_string(),
858 ),
859 (
860 index.span.shrink_to_hi().with_hi(receiver.span.hi()),
861 ") { val".to_string(),
862 ),
863 (sp.shrink_to_hi(), "; }".to_string()),
864 ],
865 Applicability::MachineApplicable,
866 );
867 self.suggested = true;
868 }
869 }
870 }
871 let def_id = self.body.source.def_id();
872 let Some(local_def_id) = def_id.as_local() else { return };
873 let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id) else { return };
874
875 let mut v = SuggestIndexOperatorAlternativeVisitor {
876 assign_span: span,
877 err,
878 ty,
879 suggested: false,
880 infcx: self.infcx,
881 };
882 v.visit_body(&body);
883 if !v.suggested {
884 err.help(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("to modify a `{0}`, use `.get_mut()`, `.insert()` or the entry API",
ty))
})format!(
885 "to modify a `{ty}`, use `.get_mut()`, `.insert()` or the entry API",
886 ));
887 }
888 }
889 }
890
891 fn is_error_in_trait(&self, local: Local) -> (bool, bool, Option<Span>) {
901 let tcx = self.infcx.tcx;
902 if self.body.local_kind(local) != LocalKind::Arg {
903 return (false, false, None);
904 }
905 let my_def = self.body.source.def_id();
906 let Some(td) = tcx.trait_impl_of_assoc(my_def).map(|id| self.infcx.tcx.impl_trait_id(id))
907 else {
908 return (false, false, None);
909 };
910
911 let implemented_trait_item = self.infcx.tcx.trait_item_of(my_def);
912
913 (
914 true,
915 td.is_local(),
916 implemented_trait_item.and_then(|f_in_trait| {
917 let f_in_trait = f_in_trait.as_local()?;
918 if let Node::TraitItem(ti) = self.infcx.tcx.hir_node_by_def_id(f_in_trait)
919 && let hir::TraitItemKind::Fn(sig, _) = ti.kind
920 && let Some(ty) = sig.decl.inputs.get(local.index() - 1)
921 && let hir::TyKind::Ref(_, mut_ty) = ty.kind
922 && let hir::Mutability::Not = mut_ty.mutbl
923 && sig.decl.implicit_self().has_implicit_self()
924 {
925 Some(ty.span)
926 } else {
927 None
928 }
929 }),
930 )
931 }
932
933 fn construct_mut_suggestion_for_local_binding_patterns(
934 &self,
935 err: &mut Diag<'_>,
936 local: Local,
937 ) {
938 let local_decl = &self.body.local_decls[local];
939 {
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/mutability_errors.rs:939",
"rustc_borrowck::diagnostics::mutability_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs"),
::tracing_core::__macro_support::Option::Some(939u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::mutability_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!("local_decl: {0:?}",
local_decl) as &dyn Value))])
});
} else { ; }
};debug!("local_decl: {:?}", local_decl);
940 let pat_span = match *local_decl.local_info() {
941 LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
942 binding_mode: BindingMode(ByRef::No, Mutability::Not),
943 opt_ty_info: _,
944 opt_match_place: _,
945 pat_span,
946 introductions: _,
947 })) => pat_span,
948 _ => local_decl.source_info.span,
949 };
950
951 let def_id = self.body.source.def_id();
958 if let Some(local_def_id) = def_id.as_local()
959 && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
960 && let Some(hir_id) = (BindingFinder { span: pat_span }).visit_body(&body).break_value()
961 && let node = self.infcx.tcx.hir_node(hir_id)
962 && let hir::Node::LetStmt(hir::LetStmt {
963 pat: hir::Pat { kind: hir::PatKind::Ref(_, _, _), .. },
964 ..
965 })
966 | hir::Node::Param(Param {
967 pat: hir::Pat { kind: hir::PatKind::Ref(_, _, _), .. },
968 ..
969 }) = node
970 {
971 err.multipart_suggestion(
972 "consider changing this to be mutable",
973 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(pat_span.until(local_decl.source_info.span), "&(mut ".to_string()),
(local_decl.source_info.span.shrink_to_hi().with_hi(pat_span.hi()),
")".to_string())]))vec![
974 (pat_span.until(local_decl.source_info.span), "&(mut ".to_string()),
975 (
976 local_decl.source_info.span.shrink_to_hi().with_hi(pat_span.hi()),
977 ")".to_string(),
978 ),
979 ],
980 Applicability::MachineApplicable,
981 );
982 return;
983 }
984
985 err.span_suggestion_verbose(
986 local_decl.source_info.span.shrink_to_lo(),
987 "consider changing this to be mutable",
988 "mut ",
989 Applicability::MachineApplicable,
990 );
991 }
992
993 fn show_mutating_upvar(
995 &self,
996 tcx: TyCtxt<'_>,
997 closure_local_def_id: hir::def_id::LocalDefId,
998 the_place_err: PlaceRef<'tcx>,
999 err: &mut Diag<'_>,
1000 ) {
1001 let tables = tcx.typeck(closure_local_def_id);
1002 if let Some((span, closure_kind_origin)) = tcx.closure_kind_origin(closure_local_def_id) {
1003 let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base {
1004 let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin);
1005 let root_hir_id = upvar_id.var_path.hir_id;
1006 let captured_places =
1009 tables.closure_min_captures[&closure_local_def_id].get(&root_hir_id).unwrap();
1010
1011 let origin_projection = closure_kind_origin
1012 .projections
1013 .iter()
1014 .map(|proj| proj.kind)
1015 .collect::<Vec<_>>();
1016 let mut capture_reason = String::new();
1017 for captured_place in captured_places {
1018 let captured_place_kinds = captured_place
1019 .place
1020 .projections
1021 .iter()
1022 .map(|proj| proj.kind)
1023 .collect::<Vec<_>>();
1024 if rustc_middle::ty::is_ancestor_or_same_capture(
1025 &captured_place_kinds,
1026 &origin_projection,
1027 ) {
1028 match captured_place.info.capture_kind {
1029 ty::UpvarCapture::ByRef(
1030 ty::BorrowKind::Mutable | ty::BorrowKind::UniqueImmutable,
1031 ) => {
1032 capture_reason = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("mutable borrow of `{0}`", upvar))
})format!("mutable borrow of `{upvar}`");
1033 }
1034 ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {
1035 capture_reason = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("possible mutation of `{0}`",
upvar))
})format!("possible mutation of `{upvar}`");
1036 }
1037 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("upvar `{0}` borrowed, but not mutably",
upvar))bug!("upvar `{upvar}` borrowed, but not mutably"),
1038 }
1039 break;
1040 }
1041 }
1042 if capture_reason.is_empty() {
1043 ::rustc_middle::util::bug::bug_fmt(format_args!("upvar `{0}` borrowed, but cannot find reason",
upvar));bug!("upvar `{upvar}` borrowed, but cannot find reason");
1044 }
1045 capture_reason
1046 } else {
1047 ::rustc_middle::util::bug::bug_fmt(format_args!("not an upvar"))bug!("not an upvar")
1048 };
1049 if let Some(place_name) = self.describe_place(the_place_err) {
1055 err.span_label(
1056 *span,
1057 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("calling `{0}` requires mutable binding due to {1}",
place_name, reason))
})format!("calling `{place_name}` requires mutable binding due to {reason}"),
1058 );
1059 } else if span.from_expansion() {
1060 err.span_label(
1061 *span,
1062 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("a call in this macro requires a mutable binding due to {0}",
reason))
})format!("a call in this macro requires a mutable binding due to {reason}",),
1063 );
1064 }
1065 }
1066 }
1067
1068 fn suggest_similar_mut_method_for_for_loop(&self, err: &mut Diag<'_>, span: Span) {
1071 use hir::ExprKind::{AddrOf, Block, Call, MethodCall};
1072 use hir::{BorrowKind, Expr};
1073
1074 let tcx = self.infcx.tcx;
1075 struct Finder {
1076 span: Span,
1077 }
1078
1079 impl<'tcx> Visitor<'tcx> for Finder {
1080 type Result = ControlFlow<&'tcx Expr<'tcx>>;
1081 fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) -> Self::Result {
1082 if e.span == self.span {
1083 ControlFlow::Break(e)
1084 } else {
1085 hir::intravisit::walk_expr(self, e)
1086 }
1087 }
1088 }
1089 if let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id())
1090 && let Block(block, _) = body.value.kind
1091 {
1092 if let ControlFlow::Break(expr) = (Finder { span }).visit_block(block)
1096 && let Call(_, [expr]) = expr.kind
1097 {
1098 match expr.kind {
1099 MethodCall(path_segment, _, _, span) => {
1100 let opt_suggestions = tcx
1103 .typeck(path_segment.hir_id.owner.def_id)
1104 .type_dependent_def_id(expr.hir_id)
1105 .and_then(|def_id| tcx.impl_of_assoc(def_id))
1106 .map(|def_id| tcx.associated_items(def_id))
1107 .map(|assoc_items| {
1108 assoc_items
1109 .in_definition_order()
1110 .map(|assoc_item_def| assoc_item_def.ident(tcx))
1111 .filter(|&ident| {
1112 let original_method_ident = path_segment.ident;
1113 original_method_ident != ident
1114 && ident.as_str().starts_with(
1115 &original_method_ident.name.to_string(),
1116 )
1117 })
1118 .map(|ident| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}()", ident))
})format!("{ident}()"))
1119 .peekable()
1120 });
1121
1122 if let Some(mut suggestions) = opt_suggestions
1123 && suggestions.peek().is_some()
1124 {
1125 err.span_suggestions(
1126 span,
1127 "use mutable method",
1128 suggestions,
1129 Applicability::MaybeIncorrect,
1130 );
1131 }
1132 }
1133 AddrOf(BorrowKind::Ref, Mutability::Not, expr) => {
1134 err.span_suggestion_verbose(
1136 expr.span.shrink_to_lo(),
1137 "use a mutable iterator instead",
1138 "mut ",
1139 Applicability::MachineApplicable,
1140 );
1141 }
1142 _ => {}
1143 }
1144 }
1145 }
1146 }
1147
1148 fn point_at_binding_outside_closure(
1150 &self,
1151 err: &mut Diag<'_>,
1152 local: Local,
1153 access_place: Place<'tcx>,
1154 ) {
1155 let place = access_place.as_ref();
1156 for (index, elem) in place.projection.into_iter().enumerate() {
1157 if let ProjectionElem::Deref = elem {
1158 if index == 0 {
1159 if self.body.local_decls[local].is_ref_for_guard() {
1160 continue;
1161 }
1162 if let LocalInfo::StaticRef { .. } = *self.body.local_decls[local].local_info()
1163 {
1164 continue;
1165 }
1166 }
1167 if let Some(field) = self.is_upvar_field_projection(PlaceRef {
1168 local,
1169 projection: place.projection.split_at(index + 1).0,
1170 }) {
1171 let var_index = field.index();
1172 let upvar = self.upvars[var_index];
1173 if let Some(hir_id) = upvar.info.capture_kind_expr_id {
1174 let node = self.infcx.tcx.hir_node(hir_id);
1175 if let hir::Node::Expr(expr) = node
1176 && let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
1177 && let hir::def::Res::Local(hir_id) = path.res
1178 && let hir::Node::Pat(pat) = self.infcx.tcx.hir_node(hir_id)
1179 {
1180 let name = upvar.to_string(self.infcx.tcx);
1181 err.span_label(
1182 pat.span,
1183 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` declared here, outside the closure",
name))
})format!("`{name}` declared here, outside the closure"),
1184 );
1185 break;
1186 }
1187 }
1188 }
1189 }
1190 }
1191 }
1192 fn expected_fn_found_fn_mut_call(&self, err: &mut Diag<'_>, sp: Span, act: &str) {
1194 err.span_label(sp, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
1195
1196 let tcx = self.infcx.tcx;
1197 let closure_id = self.mir_hir_id();
1198 let closure_span = tcx.def_span(self.mir_def_id());
1199 let fn_call_id = tcx.parent_hir_id(closure_id);
1200 let node = tcx.hir_node(fn_call_id);
1201 let def_id = tcx.hir_enclosing_body_owner(fn_call_id);
1202 let mut look_at_return = true;
1203
1204 err.span_label(closure_span, "in this closure");
1205 let closure_arg_has_fn_trait_bound =
1206 |callee_def_id, input_index, generic_args: ty::GenericArgsRef<'tcx>| {
1207 let sig = tcx.fn_sig(callee_def_id).instantiate(tcx, generic_args).skip_binder();
1208 let Some(input_ty): Option<Ty<'tcx>> = sig.inputs().get(input_index).copied()
1209 else {
1210 return false;
1211 };
1212
1213 tcx.predicates_of(callee_def_id)
1214 .instantiate(tcx, generic_args)
1215 .predicates
1216 .iter()
1217 .any(|predicate| {
1218 predicate.as_trait_clause().is_some_and(|trait_pred| {
1219 trait_pred.polarity() == ty::PredicatePolarity::Positive
1220 && tcx.fn_trait_kind_from_def_id(trait_pred.def_id())
1221 == Some(ty::ClosureKind::Fn)
1222 && trait_pred.self_ty().skip_binder().peel_refs()
1223 == input_ty.peel_refs()
1224 })
1225 })
1226 };
1227
1228 let get_call_details =
1231 || -> Option<(DefId, Span, usize, usize, ty::GenericArgsRef<'tcx>)> {
1232 let hir::Node::Expr(hir::Expr { hir_id, kind, .. }) = node else {
1233 return None;
1234 };
1235
1236 let typeck_results = tcx.typeck(def_id);
1237
1238 match kind {
1239 hir::ExprKind::Call(expr, args) => {
1240 if let Some(ty::FnDef(def_id, generic_args)) =
1241 typeck_results.node_type_opt(expr.hir_id).as_ref().map(|ty| ty.kind())
1242 {
1243 let arg_pos = args.iter().position(|arg| arg.hir_id == closure_id)?;
1244 Some((
1245 *def_id,
1246 expr.span,
1247 arg_pos,
1248 arg_pos,
1249 generic_args.no_bound_vars().unwrap(),
1250 ))
1251 } else {
1252 None
1253 }
1254 }
1255 hir::ExprKind::MethodCall(_, _, args, span) => {
1256 let arg_pos = args.iter().position(|arg| arg.hir_id == closure_id)?;
1257 let def_id = typeck_results.type_dependent_def_id(*hir_id)?;
1258 let generic_args = typeck_results.node_args_opt(*hir_id)?;
1259 Some((def_id, *span, arg_pos, arg_pos + 1, generic_args))
1260 }
1261 _ => None,
1262 }
1263 };
1264
1265 if let Some((callee_def_id, call_span, arg_pos, input_index, generic_args)) =
1268 get_call_details()
1269 {
1270 let arg = match tcx.hir_get_if_local(callee_def_id) {
1271 Some(
1272 hir::Node::Item(hir::Item {
1273 kind: hir::ItemKind::Fn { ident, sig, .. }, ..
1274 })
1275 | hir::Node::TraitItem(hir::TraitItem {
1276 ident,
1277 kind: hir::TraitItemKind::Fn(sig, _),
1278 ..
1279 })
1280 | hir::Node::ImplItem(hir::ImplItem {
1281 ident,
1282 kind: hir::ImplItemKind::Fn(sig, _),
1283 ..
1284 }),
1285 ) => Some(
1286 sig.decl
1287 .inputs
1288 .get(
1289 arg_pos
1290 + if sig.decl.implicit_self().has_implicit_self() { 1 } else { 0 },
1291 )
1292 .map(|arg| arg.span)
1293 .unwrap_or(ident.span),
1294 ),
1295 _ => None,
1296 };
1297 if let Some(span) = arg {
1298 err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
1299 err.span_label(call_span, "expects `Fn` instead of `FnMut`");
1300 look_at_return = false;
1301 } else if closure_arg_has_fn_trait_bound(callee_def_id, input_index, generic_args) {
1302 err.span_label(call_span, "expects `Fn` instead of `FnMut`");
1307 look_at_return = false;
1308 }
1309 }
1310
1311 if look_at_return && tcx.hir_get_fn_id_for_return_block(closure_id).is_some() {
1312 match tcx.hir_node_by_def_id(tcx.hir_get_parent_item(fn_call_id).def_id) {
1315 hir::Node::Item(hir::Item {
1316 kind: hir::ItemKind::Fn { ident, sig, .. }, ..
1317 })
1318 | hir::Node::TraitItem(hir::TraitItem {
1319 ident,
1320 kind: hir::TraitItemKind::Fn(sig, _),
1321 ..
1322 })
1323 | hir::Node::ImplItem(hir::ImplItem {
1324 ident,
1325 kind: hir::ImplItemKind::Fn(sig, _),
1326 ..
1327 }) => {
1328 err.span_label(ident.span, "");
1329 err.span_label(
1330 sig.decl.output.span(),
1331 "change this to return `FnMut` instead of `Fn`",
1332 );
1333 }
1334 _ => {}
1335 }
1336 }
1337 }
1338
1339 fn suggest_using_iter_mut(&self, err: &mut Diag<'_>) {
1340 let source = self.body.source;
1341 if let InstanceKind::Item(def_id) = source.instance
1342 && let Some(Node::Expr(hir::Expr { hir_id, kind, .. })) =
1343 self.infcx.tcx.hir_get_if_local(def_id)
1344 && let ExprKind::Closure(hir::Closure { kind: hir::ClosureKind::Closure, .. }) = kind
1345 && let Node::Expr(expr) = self.infcx.tcx.parent_hir_node(*hir_id)
1346 {
1347 let mut cur_expr = expr;
1348 while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
1349 if path_segment.ident.name == sym::iter {
1350 let res = self
1352 .infcx
1353 .tcx
1354 .typeck(path_segment.hir_id.owner.def_id)
1355 .type_dependent_def_id(cur_expr.hir_id)
1356 .and_then(|def_id| self.infcx.tcx.impl_of_assoc(def_id))
1357 .map(|def_id| self.infcx.tcx.associated_items(def_id))
1358 .map(|assoc_items| {
1359 assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable()
1360 });
1361
1362 if let Some(mut res) = res
1363 && res.peek().is_some()
1364 {
1365 err.span_suggestion_verbose(
1366 path_segment.ident.span,
1367 "you may want to use `iter_mut` here",
1368 "iter_mut",
1369 Applicability::MaybeIncorrect,
1370 );
1371 }
1372 break;
1373 } else {
1374 cur_expr = recv;
1375 }
1376 }
1377 }
1378 }
1379
1380 fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) {
1381 let local_decl = &self.body.local_decls[local];
1382
1383 let (pointer_sigil, pointer_desc) =
1384 if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
1385
1386 let (is_trait_sig, is_local, local_trait) = self.is_error_in_trait(local);
1387
1388 if is_trait_sig && !is_local {
1389 err.span_label(
1391 local_decl.source_info.span,
1392 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("this is an immutable {0}",
pointer_desc))
})format!("this is an immutable {pointer_desc}"),
1393 );
1394 return;
1395 }
1396
1397 if self.is_closure_arg_with_non_locally_decided_type(local) {
1399 return;
1400 }
1401
1402 let decl_span = local_decl.source_info.span;
1403
1404 let (amp_mut_sugg, local_var_ty_info) = match *local_decl.local_info() {
1405 LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
1406 let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
1407 let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span));
1408 (AmpMutSugg::Type { span, suggestion, additional }, None)
1409 }
1410
1411 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1412 binding_mode: BindingMode(ByRef::No, _),
1413 opt_ty_info,
1414 ..
1415 })) => {
1416 let first_assignment = find_assignments(&self.body, local).first().copied();
1418 let first_assignment_stmt = first_assignment
1419 .and_then(|loc| self.body[loc.block].statements.get(loc.statement_index));
1420 {
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/mutability_errors.rs:1420",
"rustc_borrowck::diagnostics::mutability_errors",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs"),
::tracing_core::__macro_support::Option::Some(1420u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::mutability_errors"),
::tracing_core::field::FieldSet::new(&["first_assignment_stmt"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::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(&first_assignment_stmt)
as &dyn Value))])
});
} else { ; }
};trace!(?first_assignment_stmt);
1421 let opt_assignment_rhs_span =
1422 first_assignment.map(|loc| self.body.source_info(loc).span);
1423 let mut source_span = opt_assignment_rhs_span;
1424 if let Some(mir::Statement {
1425 source_info: _,
1426 kind:
1427 mir::StatementKind::Assign((_, mir::Rvalue::Use(mir::Operand::Copy(place), _))),
1428 ..
1429 }) = first_assignment_stmt
1430 {
1431 let local_span = self.body.local_decls[place.local].source_info.span;
1432 source_span = Some(local_span);
1435 if let Some(DesugaringKind::ForLoop) = local_span.desugaring_kind() {
1436 self.suggest_similar_mut_method_for_for_loop(err, local_span);
1438 err.span_label(
1439 local_span,
1440 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("this iterator yields `{0}` {1}s",
pointer_sigil, pointer_desc))
})format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",),
1441 );
1442 return;
1443 }
1444 }
1445
1446 if source_span.is_some_and(|s| {
1448 s.desugaring_kind().is_some() || self.infcx.tcx.sess.source_map().is_imported(s)
1449 }) {
1450 return;
1451 }
1452
1453 if name == kw::SelfLower && opt_ty_info.is_none() {
1455 let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
1456 (AmpMutSugg::Type { span, suggestion, additional: None }, None)
1457 } else if let Some(sugg) =
1458 suggest_ampmut(self.infcx, self.body(), first_assignment_stmt)
1459 {
1460 (sugg, opt_ty_info)
1461 } else {
1462 return;
1463 }
1464 }
1465
1466 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1467 binding_mode: BindingMode(ByRef::Yes(..), _),
1468 ..
1469 })) => {
1470 let pattern_span: Span = local_decl.source_info.span;
1471 let Some(span) = suggest_ref_mut(self.infcx.tcx, pattern_span) else {
1472 return;
1473 };
1474 (AmpMutSugg::Type { span, suggestion: "mut ".to_owned(), additional: None }, None)
1475 }
1476
1477 _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
1478 };
1479
1480 let mut suggest = |suggs: Vec<_>, applicability, extra| {
1481 if suggs.iter().any(|(span, _)| self.infcx.tcx.sess.source_map().is_imported(*span)) {
1482 return;
1483 }
1484
1485 err.multipart_suggestion(
1486 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider changing this to be a mutable {1}{0}{2}",
if is_trait_sig {
" in the `impl` method and the `trait` definition"
} else { "" }, pointer_desc, extra))
})format!(
1487 "consider changing this to be a mutable {pointer_desc}{}{extra}",
1488 if is_trait_sig {
1489 " in the `impl` method and the `trait` definition"
1490 } else {
1491 ""
1492 }
1493 ),
1494 suggs,
1495 applicability,
1496 );
1497 };
1498
1499 let (mut sugg, add_type_annotation_if_not_exists) = match amp_mut_sugg {
1500 AmpMutSugg::Type { span, suggestion, additional } => {
1501 let mut sugg = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span, suggestion)]))vec![(span, suggestion)];
1502 sugg.extend(additional);
1503 suggest(sugg, Applicability::MachineApplicable, "");
1504 return;
1505 }
1506 AmpMutSugg::MapGetMut { span, suggestion } => {
1507 if self.infcx.tcx.sess.source_map().is_imported(span) {
1508 return;
1509 }
1510 err.multipart_suggestion(
1511 "consider using `get_mut`",
1512 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span, suggestion)]))vec![(span, suggestion)],
1513 Applicability::MaybeIncorrect,
1514 );
1515 return;
1516 }
1517 AmpMutSugg::Expr { span, suggestion } => {
1518 (::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span, suggestion)]))vec![(span, suggestion)], false)
1521 }
1522 AmpMutSugg::ChangeBinding => (::alloc::vec::Vec::new()vec![], true),
1523 };
1524
1525 let (binding_exists, span) = match local_var_ty_info {
1527 Some(ty_span) => (true, ty_span),
1531
1532 None => (false, decl_span),
1536 };
1537
1538 if !binding_exists && !add_type_annotation_if_not_exists {
1539 suggest(sugg, Applicability::MachineApplicable, "");
1540 return;
1541 }
1542
1543 let (sugg_span, sugg_str, suggest_now) = if let Ok(src) = self.infcx.tcx.sess.source_map().span_to_snippet(span)
1547 && src.starts_with("&'")
1548 && let Some(ws_pos) = src.find(char::is_whitespace)
1550 {
1551 let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
1552 (span, " mut".to_owned(), true)
1553 } else if binding_exists {
1555 let Ok(src) = self.infcx.tcx.sess.source_map().span_to_snippet(span) else {
1559 return;
1560 };
1561 let (prefix_len, replacement) = if local_decl.ty.is_ref() {
1562 (src.chars().next().map_or(0, char::len_utf8), "&mut ")
1563 } else {
1564 (src.find("const").map_or(1, |i| i + "const".len()), "*mut ")
1565 };
1566 let ws_len = src[prefix_len..].len() - src[prefix_len..].trim_start().len();
1567 let span = span.with_hi(span.lo() + BytePos((prefix_len + ws_len) as u32));
1568 (span, replacement.to_owned(), true)
1569 } else {
1570 let ty = local_decl.ty.builtin_deref(true).unwrap();
1573
1574 (span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}mut {1}",
if local_decl.ty.is_ref() { "&" } else { "*" }, ty))
})format!("{}mut {}", if local_decl.ty.is_ref() { "&" } else { "*" }, ty), false)
1575 };
1576
1577 if suggest_now {
1578 let has_change = !sugg.is_empty();
1580 sugg.push((sugg_span, sugg_str));
1581 suggest(
1582 sugg,
1583 Applicability::MachineApplicable,
1584 if has_change { " and changing the binding's type" } else { "" },
1586 );
1587 return;
1588 } else if !sugg.is_empty() {
1589 suggest(sugg, Applicability::MachineApplicable, "");
1590 return;
1591 }
1592
1593 let def_id = self.body.source.def_id();
1594 let hir_id = if let Some(local_def_id) = def_id.as_local()
1595 && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
1596 {
1597 BindingFinder { span: sugg_span }.visit_body(&body).break_value()
1598 } else {
1599 None
1600 };
1601 let node = hir_id.map(|hir_id| self.infcx.tcx.hir_node(hir_id));
1602
1603 let Some(hir::Node::LetStmt(local)) = node else {
1604 err.span_label(
1605 sugg_span,
1606 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider changing this binding\'s type to be: `{0}`",
sugg_str))
})format!("consider changing this binding's type to be: `{sugg_str}`"),
1607 );
1608 return;
1609 };
1610
1611 let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
1612 if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
1613 && let Some(expr) = local.init
1614 && let ty = tables.node_type_opt(expr.hir_id)
1615 && let Some(ty) = ty
1616 && let ty::Ref(..) = ty.kind()
1617 {
1618 match self
1619 .infcx
1620 .type_implements_trait_shallow(clone_trait, ty.peel_refs(), self.infcx.param_env)
1621 .as_deref()
1622 {
1623 Some([]) => {
1624 }
1633 None => {
1634 if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
1635 && segment.ident.name == sym::clone
1636 {
1637 err.span_help(
1638 span,
1639 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` doesn\'t implement `Clone`, so this call clones the reference `{1}`",
ty.peel_refs(), ty))
})format!(
1640 "`{}` doesn't implement `Clone`, so this call clones \
1641 the reference `{ty}`",
1642 ty.peel_refs(),
1643 ),
1644 );
1645 }
1646 let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
1648 self.infcx.tcx,
1649 clone_trait,
1650 [ty.peel_refs()],
1651 ));
1652 let obligation = traits::Obligation::new(
1653 self.infcx.tcx,
1654 traits::ObligationCause::dummy(),
1655 self.infcx.param_env,
1656 trait_ref,
1657 );
1658 self.infcx.err_ctxt().suggest_derive(
1659 &obligation,
1660 err,
1661 trait_ref.upcast(self.infcx.tcx),
1662 );
1663 }
1664 Some(errors) => {
1665 if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
1666 && segment.ident.name == sym::clone
1667 {
1668 err.span_help(
1669 span,
1670 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` doesn\'t implement `Clone` because its implementations trait bounds could not be met, so this call clones the reference `{1}`",
ty.peel_refs(), ty))
})format!(
1671 "`{}` doesn't implement `Clone` because its \
1672 implementations trait bounds could not be met, so \
1673 this call clones the reference `{ty}`",
1674 ty.peel_refs(),
1675 ),
1676 );
1677 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("the following trait bounds weren\'t met: {0}",
errors.iter().map(|e|
e.obligation.predicate.to_string()).collect::<Vec<_>>().join("\n")))
})format!(
1678 "the following trait bounds weren't met: {}",
1679 errors
1680 .iter()
1681 .map(|e| e.obligation.predicate.to_string())
1682 .collect::<Vec<_>>()
1683 .join("\n"),
1684 ));
1685 }
1686 for error in errors {
1688 if let traits::FulfillmentErrorCode::Select(
1689 traits::SelectionError::Unimplemented,
1690 ) = error.code
1691 && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
1692 error.obligation.predicate.kind().skip_binder()
1693 {
1694 self.infcx.err_ctxt().suggest_derive(
1695 &error.obligation,
1696 err,
1697 error.obligation.predicate.kind().rebind(pred),
1698 );
1699 }
1700 }
1701 }
1702 }
1703 }
1704 let (changing, span, sugg) = match local.ty {
1705 Some(ty) => ("changing", ty.span, sugg_str),
1706 None => ("specifying", local.pat.span.shrink_to_hi(), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(": {0}", sugg_str))
})format!(": {sugg_str}")),
1707 };
1708 err.span_suggestion_verbose(
1709 span,
1710 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider {0} this binding\'s type",
changing))
})format!("consider {changing} this binding's type"),
1711 sugg,
1712 Applicability::HasPlaceholders,
1713 );
1714 }
1715
1716 fn is_closure_arg_with_non_locally_decided_type(&self, local: Local) -> bool {
1726 if self.body.local_kind(local) != LocalKind::Arg {
1728 return false;
1729 }
1730
1731 let InstanceKind::Item(body_def_id) = self.body.source.instance else {
1733 return false;
1734 };
1735 let Some(Node::Expr(hir::Expr { hir_id: body_hir_id, kind, .. })) =
1736 self.infcx.tcx.hir_get_if_local(body_def_id)
1737 else {
1738 return false;
1739 };
1740 let ExprKind::Closure(hir::Closure { kind: hir::ClosureKind::Closure, .. }) = kind else {
1741 return false;
1742 };
1743
1744 let Node::Expr(closure_parent) = self.infcx.tcx.parent_hir_node(*body_hir_id) else {
1747 return false;
1748 };
1749 match closure_parent.kind {
1750 ExprKind::MethodCall(method, _, _, _) => self
1751 .infcx
1752 .tcx
1753 .typeck(method.hir_id.owner.def_id)
1754 .type_dependent_def_id(closure_parent.hir_id)
1755 .is_some_and(|def_id| !def_id.is_local()),
1756 ExprKind::Call(func, _) => self
1757 .infcx
1758 .tcx
1759 .typeck(func.hir_id.owner.def_id)
1760 .node_type_opt(func.hir_id)
1761 .and_then(|ty| match ty.kind() {
1762 ty::FnDef(def_id, _) => Some(def_id),
1763 _ => None,
1764 })
1765 .is_some_and(|def_id| !def_id.is_local()),
1766 _ => false,
1767 }
1768 }
1769}
1770
1771struct BindingFinder {
1772 span: Span,
1773}
1774
1775impl<'tcx> Visitor<'tcx> for BindingFinder {
1776 type Result = ControlFlow<hir::HirId>;
1777 fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) -> Self::Result {
1778 if let hir::StmtKind::Let(local) = s.kind
1779 && local.pat.span == self.span
1780 {
1781 ControlFlow::Break(local.hir_id)
1782 } else {
1783 hir::intravisit::walk_stmt(self, s)
1784 }
1785 }
1786
1787 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) -> Self::Result {
1788 if let hir::Pat { kind: hir::PatKind::Ref(_, _, _), span, .. } = param.pat
1789 && *span == self.span
1790 {
1791 ControlFlow::Break(param.hir_id)
1792 } else {
1793 ControlFlow::Continue(())
1794 }
1795 }
1796}
1797
1798fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
1799 {
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/mutability_errors.rs:1799",
"rustc_borrowck::diagnostics::mutability_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs"),
::tracing_core::__macro_support::Option::Some(1799u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::mutability_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!("local_info: {0:?}, ty.kind(): {1:?}",
local_decl.local_info, local_decl.ty.kind()) as
&dyn Value))])
});
} else { ; }
};debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
1800
1801 match *local_decl.local_info() {
1802 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1804 binding_mode: BindingMode(ByRef::No, Mutability::Not),
1805 ..
1806 })) => #[allow(non_exhaustive_omitted_patterns)] match local_decl.ty.kind() {
ty::Ref(_, _, hir::Mutability::Mut) => true,
_ => false,
}matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)),
1807 LocalInfo::User(mir::BindingForm::ImplicitSelf(kind)) => {
1808 kind == hir::ImplicitSelfKind::RefMut
1814 }
1815 _ if Some(kw::SelfLower) == local_name => {
1816 #[allow(non_exhaustive_omitted_patterns)] match local_decl.ty.kind() {
ty::Ref(_, _, hir::Mutability::Mut) => true,
_ => false,
}matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut))
1820 }
1821 _ => false,
1822 }
1823}
1824
1825fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) {
1826 match tcx.sess.source_map().span_to_snippet(span) {
1827 Ok(snippet) if snippet.ends_with("self") => {
1828 (span.with_hi(span.hi() - BytePos(4)).shrink_to_hi(), "mut ".to_string())
1829 }
1830 _ => (span, "&mut self".to_string()),
1831 }
1832}
1833
1834enum AmpMutSugg {
1835 Type {
1838 span: Span,
1839 suggestion: String,
1840 additional: Option<(Span, String)>,
1841 },
1842 Expr {
1844 span: Span,
1845 suggestion: String,
1846 },
1847 MapGetMut {
1849 span: Span,
1850 suggestion: String,
1851 },
1852 ChangeBinding,
1853}
1854
1855fn suggest_ampmut<'tcx>(
1871 infcx: &crate::BorrowckInferCtxt<'tcx>,
1872 body: &Body<'tcx>,
1873 opt_assignment_rhs_stmt: Option<&Statement<'tcx>>,
1874) -> Option<AmpMutSugg> {
1875 let tcx = infcx.tcx;
1876 if let Some(rhs_stmt) = opt_assignment_rhs_stmt
1885 && let StatementKind::Assign((lhs, rvalue)) = &rhs_stmt.kind
1886 && let mut rhs_span = rhs_stmt.source_info.span
1887 && let Ok(mut rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
1888 {
1889 let mut rvalue = rvalue;
1890
1891 if let Rvalue::Ref(_, BorrowKind::Shared, place) = rvalue
1895 && place.projection.len() == 1
1896 && place.projection[0] == ProjectionElem::Deref
1897 && let Some(assign) = find_assignments(&body, place.local).first()
1898 {
1899 if let Some(user_ty_projs) = body.local_decls[lhs.local].user_ty.as_ref()
1903 && let [user_ty_proj] = user_ty_projs.contents.as_slice()
1904 && user_ty_proj.projs.is_empty()
1905 && let Either::Left(rhs_stmt_new) = body.stmt_at(*assign)
1906 && let StatementKind::Assign((_, rvalue_new)) = &rhs_stmt_new.kind
1907 && let rhs_span_new = rhs_stmt_new.source_info.span
1908 && let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span_new)
1909 {
1910 (rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new);
1911 }
1912
1913 if let Either::Right(call) = body.stmt_at(*assign)
1914 && let TerminatorKind::Call { func: Operand::Constant(const_operand), args, .. } =
1915 &call.kind
1916 && let ty::FnDef(method_def_id, method_args) = *const_operand.ty().kind()
1917 && let Some(trait_) = tcx.trait_of_assoc(method_def_id)
1918 && tcx.is_lang_item(trait_, hir::LangItem::Index)
1919 {
1920 let trait_ref = ty::TraitRef::from_assoc(
1921 tcx,
1922 tcx.require_lang_item(hir::LangItem::IndexMut, rhs_span),
1923 method_args.no_bound_vars().unwrap(),
1924 );
1925 if !infcx
1927 .type_implements_trait(trait_ref.def_id, trait_ref.args, infcx.param_env)
1928 .must_apply_considering_regions()
1929 {
1930 if let ty::Adt(def, _) = trait_ref.self_ty().kind()
1932 && [sym::BTreeMap, sym::HashMap]
1933 .into_iter()
1934 .any(|s| tcx.is_diagnostic_item(s, def.did()))
1935 && let [map, key] = &**args
1936 && let Ok(map) = tcx.sess.source_map().span_to_snippet(map.span)
1937 && let Ok(key) = tcx.sess.source_map().span_to_snippet(key.span)
1938 {
1939 let span = rhs_span;
1940 let suggestion = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}.get_mut({1}).unwrap()", map,
key))
})format!("{map}.get_mut({key}).unwrap()");
1941 return Some(AmpMutSugg::MapGetMut { span, suggestion });
1942 }
1943 return None;
1944 }
1945 }
1946 }
1947
1948 let sugg = match rvalue {
1949 Rvalue::Ref(_, BorrowKind::Shared, _) if let Some(ref_idx) = rhs_str.find('&') => {
1950 Some((
1952 rhs_span.with_lo(rhs_span.lo() + BytePos(ref_idx as u32 + 1)).shrink_to_lo(),
1953 "mut ".to_owned(),
1954 ))
1955 }
1956 Rvalue::RawPtr(RawPtrKind::Const, _) if let Some(const_idx) = rhs_str.find("const") => {
1957 let const_idx = const_idx as u32;
1959 Some((
1960 rhs_span
1961 .with_lo(rhs_span.lo() + BytePos(const_idx))
1962 .with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32)),
1963 "mut".to_owned(),
1964 ))
1965 }
1966 _ => None,
1967 };
1968
1969 if let Some((span, suggestion)) = sugg {
1970 return Some(AmpMutSugg::Expr { span, suggestion });
1971 }
1972 }
1973
1974 Some(AmpMutSugg::ChangeBinding)
1975}
1976
1977fn is_closure_like(ty: Ty<'_>) -> bool {
1979 ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure()
1980}
1981
1982fn get_mut_span_in_struct_field<'tcx>(
1990 tcx: TyCtxt<'tcx>,
1991 ty: Ty<'tcx>,
1992 field: FieldIdx,
1993) -> Option<Span> {
1994 if let ty::Ref(_, ty, _) = ty.kind()
1996 && let ty::Adt(def, _) = ty.kind()
1997 && let field = def.all_fields().nth(field.index())?
1998 && let hir::Node::Field(field) = tcx.hir_node_by_def_id(field.did.as_local()?)
2001 && let hir::TyKind::Ref(lt, hir::MutTy { mutbl: hir::Mutability::Not, ty }) = field.ty.kind
2002 {
2003 return Some(lt.ident.span.between(ty.span));
2004 }
2005
2006 None
2007}
2008
2009fn suggest_ref_mut(tcx: TyCtxt<'_>, span: Span) -> Option<Span> {
2011 let pattern_str = tcx.sess.source_map().span_to_snippet(span).ok()?;
2012 if let Some(rest) = pattern_str.strip_prefix("ref")
2013 && rest.starts_with(rustc_lexer::is_whitespace)
2014 {
2015 let span = span.with_lo(span.lo() + BytePos(4)).shrink_to_lo();
2016 Some(span)
2017 } else {
2018 None
2019 }
2020}