1use core::ops::ControlFlow;
2
3use either::Either;
4use hir::{ExprKind, Param};
5use rustc_abi::FieldIdx;
6use rustc_attr_ir::lang_items::LangItem;
7use rustc_errors::{Applicability, Diag};
8use rustc_hir::def_id::DefId;
9use rustc_hir::intravisit::Visitor;
10use rustc_hir::{self as hir, BindingMode, ByRef, Expr, Node};
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, bug, 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]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
for AccessKind {
}
#[automatically_derived]impl ::core::clone::Clone for AccessKind {
#[inline]
#[rustc_trivial_field_reads]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::fmt::Debug for AccessKind {
#[inline]
#[rustc_trivial_field_reads]
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 { }Eq, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
AccessKind {
}
#[automatically_derived]impl ::core::cmp::PartialEq for AccessKind {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other)
}
}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<'tcx> MirBorrowckCtxt<'_, '_, '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 /rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs:70",
"rustc_borrowck::diagnostics::mutability_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/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};
__CALLSITE.metadata().fields().value_set_all(&[(::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 ::tracing::field::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 /rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs:82",
"rustc_borrowck::diagnostics::mutability_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("report_mutability_error: access_place_desc={0:?}",
access_place_desc) as &dyn ::tracing::field::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_span::macros::bug_impl(None,
format_args!("is_ref_to_static return true, but not ref to static?"),
Location::caller());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 { local: _, projection: [ProjectionElem::PhantomDeref] } => {
185 item_msg = String::new();
186 reason = String::new();
187 }
188 PlaceRef { local: _, projection: [_proj_base @ .., ProjectionElem::PhantomDeref] } => {
189 item_msg = String::new();
190 reason = String::new();
191 }
192
193 PlaceRef {
194 local: _,
195 projection:
196 [
197 ..,
198 ProjectionElem::Index(_)
199 | ProjectionElem::ConstantIndex { .. }
200 | ProjectionElem::OpaqueCast { .. }
201 | ProjectionElem::Subslice { .. }
202 | ProjectionElem::Downcast(..)
203 | ProjectionElem::UnwrapUnsafeBinder(_),
204 ],
205 } => ::rustc_span::macros::bug_impl(None,
format_args!("Unexpected immutable place."), Location::caller())bug!("Unexpected immutable place."),
206 }
207
208 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs:208",
"rustc_borrowck::diagnostics::mutability_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs"),
::tracing_core::__macro_support::Option::Some(208u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("report_mutability_error: item_msg={0:?}, reason={1:?}",
item_msg, reason) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
209
210 let act;
213 let acted_on;
214 let mut suggest = true;
215 let mut mut_error = None;
216 let mut count = 1;
217
218 let span = match error_access {
219 AccessKind::Mutate => {
220 err = self.cannot_assign(span, &(item_msg + &reason));
221 act = "assign";
222 acted_on = "written to";
223 span
224 }
225 AccessKind::MutableBorrow => {
226 act = "borrow as mutable";
227 acted_on = "borrowed as mutable";
228
229 let borrow_spans = self.borrow_spans(span, location);
230 let borrow_span = borrow_spans.args_or_use();
231 match the_place_err {
232 PlaceRef { local, projection: [] }
233 if self.body.local_decls[local].can_be_made_mutable() =>
234 {
235 let span = self.body.local_decls[local].source_info.span;
236 mut_error = Some(span);
237 if let Some((buffered_err, c)) = self.get_buffered_mut_error(span) {
238 err = buffered_err;
243 count = c + 1;
244 if count == 2 {
245 err.replace_span_with(span, false);
246 err.span_label(span, "not mutable");
247 }
248 suggest = false;
249 } else {
250 err = self.cannot_borrow_path_as_mutable_because(
251 borrow_span,
252 &item_msg,
253 &reason,
254 );
255 }
256 }
257 _ => {
258 err = self.cannot_borrow_path_as_mutable_because(
259 borrow_span,
260 &item_msg,
261 &reason,
262 );
263 }
264 }
265 if suggest {
266 borrow_spans.var_subdiag(
267 &mut err,
268 Some(mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }),
269 |_kind, var_span| {
270 let place = self.describe_any_place(access_place.as_ref());
271 session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure {
272 place,
273 var_span,
274 }
275 },
276 );
277 }
278 borrow_span
279 }
280 };
281
282 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs:282",
"rustc_borrowck::diagnostics::mutability_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs"),
::tracing_core::__macro_support::Option::Some(282u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("report_mutability_error: act={0:?}, acted_on={1:?}",
act, acted_on) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
283
284 match the_place_err {
285 PlaceRef {
292 local,
293 projection:
294 [
295 proj_base @ ..,
296 ProjectionElem::Deref,
297 ProjectionElem::Field(field, _),
298 ProjectionElem::Deref,
299 ],
300 } => {
301 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
302
303 let place = Place::ty_from(local, proj_base, self.body, self.infcx.tcx);
304 if let Some(span) = get_mut_span_in_struct_field(self.infcx.tcx, place.ty, *field) {
305 err.span_suggestion_verbose(
306 span,
307 "consider changing this to be mutable",
308 " mut ",
309 Applicability::MaybeIncorrect,
310 );
311 }
312 }
313
314 PlaceRef { local, projection: [] }
316 if self
317 .body
318 .local_decls
319 .get(local)
320 .is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_name(local))) =>
321 {
322 let decl = &self.body.local_decls[local];
323 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
324 if let Some(mir::Statement {
325 source_info,
326 kind:
327 mir::StatementKind::Assign((
328 _,
329 mir::Rvalue::Ref(
330 _,
331 mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
332 _,
333 ),
334 )),
335 ..
336 }) = &self.body[location.block].statements.get(location.statement_index)
337 {
338 match *decl.local_info() {
339 LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
340 binding_mode: BindingMode(ByRef::No, Mutability::Not),
341 opt_ty_info: Some(sp),
342 pat_span,
343 ..
344 })) => {
345 if suggest {
346 err.span_note(sp, "the binding is already a mutable borrow");
347 err.span_suggestion_verbose(
348 pat_span.shrink_to_lo(),
349 "consider making the binding mutable if you need to reborrow \
350 multiple times",
351 "mut ".to_string(),
352 Applicability::MaybeIncorrect,
353 );
354 }
355 }
356 _ => {
357 err.span_note(
358 decl.source_info.span,
359 "the binding is already a mutable borrow",
360 );
361 }
362 }
363 if let Ok(snippet) =
364 self.infcx.tcx.sess.source_map().span_to_snippet(source_info.span)
365 {
366 if snippet.starts_with("&mut ") {
367 let mut in_pat_scrutinee = false;
373 let mut is_deref_coerced = false;
374 if let Some(expr) = self.find_expr(source_info.span) {
375 let tcx = self.infcx.tcx;
376 let span = expr.span.source_callsite();
377 for (_, node) in tcx.hir_parent_iter(expr.hir_id) {
378 if let Node::Expr(parent_expr) = node {
379 match parent_expr.kind {
380 ExprKind::Match(scrutinee, ..)
381 if scrutinee
382 .span
383 .source_callsite()
384 .contains(span) =>
385 {
386 in_pat_scrutinee = true;
387 break;
388 }
389 ExprKind::Let(let_expr)
390 if let_expr
391 .init
392 .span
393 .source_callsite()
394 .contains(span) =>
395 {
396 in_pat_scrutinee = true;
397 break;
398 }
399 _ => {}
400 }
401 }
402 }
403
404 let typeck = tcx.typeck(expr.hir_id.owner.def_id);
405 is_deref_coerced =
406 typeck.expr_adjustments(expr).iter().any(|adj| {
407 #[allow(non_exhaustive_omitted_patterns)] match adj.kind {
ty::adjustment::Adjust::Deref(_) => true,
_ => false,
}matches!(adj.kind, ty::adjustment::Adjust::Deref(_))
408 });
409 }
410
411 if in_pat_scrutinee {
412 err.span_suggestion_verbose(
414 source_info
415 .span
416 .with_lo(source_info.span.lo() + BytePos(5))
417 .shrink_to_lo(),
418 "to reborrow the mutable reference, add `*`",
419 "*",
420 Applicability::MaybeIncorrect,
421 );
422 } else if is_deref_coerced {
423 err.span_suggestion_verbose(
426 source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
427 "if there is only one mutable reborrow, remove the `&mut`",
428 "",
429 Applicability::MaybeIncorrect,
430 );
431 }
432 } else {
433 err.span_help(source_info.span, "try removing `&mut` here");
435 }
436 } else {
437 err.span_help(source_info.span, "try removing `&mut` here");
438 }
439 } else if decl.mutability.is_not() {
440 if #[allow(non_exhaustive_omitted_patterns)] match decl.local_info() {
LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::RefMut))
=> true,
_ => false,
}matches!(
441 decl.local_info(),
442 LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::RefMut))
443 ) {
444 err.note(
445 "as `Self` may be unsized, this call attempts to take `&mut &mut self`",
446 );
447 err.note("however, `&mut self` expands to `self: &mut Self`, therefore `self` cannot be borrowed mutably");
448 } else {
449 err.span_suggestion_verbose(
450 decl.source_info.span.shrink_to_lo(),
451 "consider making the binding mutable",
452 "mut ",
453 Applicability::MachineApplicable,
454 );
455 };
456 }
457 }
458
459 PlaceRef { local, projection: [] }
462 if self.body.local_decls[local].can_be_made_mutable() =>
463 {
464 let local_decl = &self.body.local_decls[local];
469 {
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);
470
471 if count < 10 {
472 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
473 }
474 if suggest {
475 self.construct_mut_suggestion_for_local_binding_patterns(&mut err, local);
476 let tcx = self.infcx.tcx;
477 if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() {
478 self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
479 }
480 }
481 }
482
483 PlaceRef {
485 local,
486 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
487 } => {
488 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(
489 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
490 ));
491
492 let captured_place = self.upvars[upvar_index.index()];
493
494 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
495
496 let upvar_hir_id = captured_place.get_root_variable();
497
498 if let Node::Pat(pat) = self.infcx.tcx.hir_node(upvar_hir_id)
499 && let hir::PatKind::Binding(hir::BindingMode::NONE, _, upvar_ident, _) =
500 pat.kind
501 {
502 if upvar_ident.name == kw::SelfLower {
503 for (_, node) in self.infcx.tcx.hir_parent_iter(upvar_hir_id) {
504 if let Some(fn_decl) = node.fn_decl() {
505 if !#[allow(non_exhaustive_omitted_patterns)] match fn_decl.implicit_self() {
hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut => true,
_ => false,
}matches!(
506 fn_decl.implicit_self(),
507 hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut
508 ) {
509 err.span_suggestion_verbose(
510 upvar_ident.span.shrink_to_lo(),
511 "consider changing this to be mutable",
512 "mut ",
513 Applicability::MachineApplicable,
514 );
515 break;
516 }
517 }
518 }
519 } else {
520 err.span_suggestion_verbose(
521 upvar_ident.span.shrink_to_lo(),
522 "consider changing this to be mutable",
523 "mut ",
524 Applicability::MachineApplicable,
525 );
526 }
527 }
528
529 let tcx = self.infcx.tcx;
530 if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
531 && let ty::Closure(id, _) = *ty.kind()
532 {
533 self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
534 }
535 }
536
537 PlaceRef { local: _, projection: [] }
540 if self
541 .infcx
542 .tcx
543 .sess
544 .source_map()
545 .span_to_snippet(span)
546 .is_ok_and(|snippet| snippet.starts_with("&mut ")) =>
547 {
548 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
549 err.span_suggestion_verbose(
550 span.with_hi(span.lo() + BytePos(5)),
551 "try removing `&mut` here",
552 "",
553 Applicability::MaybeIncorrect,
554 );
555 }
556
557 PlaceRef { local, projection: [ProjectionElem::Deref] }
558 if self.body.local_decls[local].is_ref_for_guard() =>
559 {
560 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
561 err.note(
562 "variables bound in patterns are immutable until the end of the pattern guard",
563 );
564 }
565
566 PlaceRef { local, projection: [ProjectionElem::Deref] }
572 if self.body.local_decls[local].is_user_variable() =>
573 {
574 let local_decl = &self.body.local_decls[local];
575
576 let (pointer_sigil, pointer_desc) =
577 if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
578
579 match self.local_name(local) {
580 Some(name) if !local_decl.from_compiler_desugaring() => {
581 err.span_label(
582 span,
583 ::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!(
584 "`{name}` is a `{pointer_sigil}` {pointer_desc}, so it cannot be \
585 {acted_on}",
586 ),
587 );
588
589 self.suggest_using_iter_mut(&mut err);
590 self.suggest_make_local_mut(&mut err, local, name);
591 }
592 _ => {
593 err.span_label(
594 span,
595 ::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}"),
596 );
597 }
598 }
599 }
600
601 PlaceRef { local, projection: [ProjectionElem::Deref] }
602 if local == ty::CAPTURE_STRUCT_LOCAL && !self.upvars.is_empty() =>
603 {
604 self.point_at_binding_outside_closure(&mut err, local, access_place);
605 self.expected_fn_found_fn_mut_call(&mut err, span, act);
606 }
607
608 PlaceRef { local, projection: [.., ProjectionElem::Deref] } => {
609 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
610
611 match opt_source {
612 Some(BorrowedContentSource::OverloadedDeref(ty)) => {
613 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!(
614 "trait `DerefMut` is required to modify through a dereference, \
615 but it is not implemented for `{ty}`",
616 ));
617 }
618 Some(BorrowedContentSource::OverloadedIndex(ty)) => {
619 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!(
620 "trait `IndexMut` is required to modify indexed content, \
621 but it is not implemented for `{ty}`",
622 ));
623 self.suggest_map_index_mut_alternatives(ty, &mut err, span);
624 }
625 _ => {
626 let local = &self.body.local_decls[local];
627 match *local.local_info() {
628 LocalInfo::StaticRef { def_id, .. } => {
629 let span = self.infcx.tcx.def_span(def_id);
630 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}"));
631 }
632 LocalInfo::ConstRef { def_id } => {
633 let span = self.infcx.tcx.def_span(def_id);
634 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}"));
635 }
636 LocalInfo::BlockTailTemp(_) | LocalInfo::Boring
637 if !local.source_info.span.overlaps(span) =>
638 {
639 err.span_label(
640 local.source_info.span,
641 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("this cannot be {0}", acted_on))
})format!("this cannot be {acted_on}"),
642 );
643 }
644 _ => {}
645 }
646 }
647 }
648 }
649
650 PlaceRef { local, .. } => {
651 let local = &self.body.local_decls[local];
652 if !local.source_info.span.overlaps(span) {
653 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}"));
654 }
655 err.span_label(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
656 }
657 }
658
659 if let Some(span) = mut_error {
660 self.buffer_mut_error(span, err, count);
661 } else {
662 self.buffer_error(err);
663 }
664 }
665
666 fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'_>, span: Span) {
668 let Some(adt) = ty.ty_adt_def() else { return };
669 let did = adt.did();
670 if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did)
671 || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did)
672 {
673 struct SuggestIndexOperatorAlternativeVisitor<'a, 'diag, 'tcx> {
677 assign_span: Span,
678 err: &'a mut Diag<'diag>,
679 ty: Ty<'tcx>,
680 suggested: bool,
681 infcx: &'a rustc_infer::infer::InferCtxt<'tcx>,
682 }
683
684 impl<'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'_, '_, 'tcx> {
685 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
686 hir::intravisit::walk_stmt(self, stmt);
687 let expr = match stmt.kind {
688 hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr,
689 hir::StmtKind::Let(hir::LetStmt { init: Some(expr), .. }) => expr,
690 _ => {
691 return;
692 }
693 };
694
695 fn count_ty_refs<'tcx>(mut ty: Ty<'tcx>) -> usize {
709 let mut count = 0;
710 while let ty::Ref(_, inner_ty, _) = ty.kind() {
711 ty = *inner_ty;
712 count += 1;
713 }
714 count
715 }
716
717 fn strip_n_refs<'a, 'b>(
722 mut expr: &'a Expr<'b>,
723 n: usize,
724 ) -> Result<&'a Expr<'b>, (usize, &'a Expr<'b>)> {
725 for count in 0..n {
726 match expr {
727 Expr {
728 kind: ExprKind::AddrOf(hir::BorrowKind::Ref, _, inner),
729 ..
730 } => expr = inner,
731 _ => return Err((count, expr)),
732 }
733 }
734 Ok(expr)
735 }
736
737 let key_ty = self.ty.walk().nth(1).unwrap().expect_ty();
739
740 if let hir::ExprKind::Assign(place, rv, _sp) = expr.kind
741 && let hir::ExprKind::Index(val, index, _) = place.kind
742 && (expr.span == self.assign_span || place.span == self.assign_span)
743 {
744 let index_ty =
746 self.infcx.tcx.typeck(val.hir_id.owner.def_id).expr_ty(index);
747
748 let (borrowed_prefix, borrowed_index);
749
750 if index_ty.peel_refs() == key_ty.peel_refs() {
754 let (index_refs, key_refs) =
755 (count_ty_refs(index_ty), count_ty_refs(key_ty));
756
757 let (deref_prefix, deref_index) = if index_refs >= key_refs {
758 strip_n_refs(index, index_refs - key_refs)
760 .map(|val| ("".to_string(), val))
761 .unwrap_or_else(|(depth, val)| {
762 (
763 if key_refs == 0 {
764 "*".repeat(
765 (index_refs-key_refs).checked_sub(depth).expect("return depth from strip_n_refs should be smaller than the input")
766 )
767 } else {
768 String::new() },
770 val,
771 )
772 })
773 } else {
774 ("&".repeat(key_refs - index_refs), index)
776 };
777
778 self.err.multipart_suggestion(
779 ::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),
780 ::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![
781 (
783 val.span.shrink_to_hi().with_hi(deref_index.span.lo()),
784 format!(".insert({deref_prefix}"),
785 ),
786 (
787 deref_index.span.shrink_to_hi().with_hi(rv.span.lo()),
788 ", ".to_string(),
789 ),
790 (rv.span.shrink_to_hi(), ")".to_string()),
791 ],
792 Applicability::MaybeIncorrect,
793 );
794 self.err.multipart_suggestion(
795 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use the entry API to modify a `{0}` for more flexibility",
self.ty))
})format!(
796 "use the entry API to modify a `{}` for more flexibility",
797 self.ty
798 ),
799 ::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![
800 (val.span.shrink_to_lo(), "let val = ".to_string()),
802 (
803 val.span.shrink_to_hi().with_hi(deref_index.span.lo()),
804 format!(".entry({deref_prefix}"),
805 ),
806 (
807 deref_index.span.shrink_to_hi().with_hi(rv.span.lo()),
808 ").insert_entry(".to_string(),
809 ),
810 (rv.span.shrink_to_hi(), ")".to_string()),
811 ],
812 Applicability::MaybeIncorrect,
813 );
814
815 (borrowed_prefix, borrowed_index) = (
818 String::new(),
819 if index_refs > key_refs {
820 strip_n_refs(index, index_refs - key_refs - 1)
821 .unwrap_or_else(|(_depth, val)| val)
822 } else {
824 index
826 },
827 );
828 } else {
829 (borrowed_prefix, borrowed_index) = (String::new(), index)
830 }
831 self.err.multipart_suggestion(
834 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use `.get_mut()` to modify an existing key in a `{0}`",
self.ty))
})format!(
835 "use `.get_mut()` to modify an existing key in a `{}`",
836 self.ty,
837 ),
838 ::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![
839 (val.span.shrink_to_lo(), "if let Some(val) = ".to_string()),
841 (
842 val.span.shrink_to_hi().with_hi(borrowed_index.span.lo()),
843 format!(".get_mut({borrowed_prefix}"),
844 ),
845 (
846 borrowed_index.span.shrink_to_hi().with_hi(place.span.hi()),
847 ") { *val".to_string(),
848 ),
849 (rv.span.shrink_to_hi(), "; }".to_string()),
850 ],
851 Applicability::MaybeIncorrect,
852 );
853
854 self.suggested = true;
855 } else if let hir::ExprKind::MethodCall(_path, receiver, _, sp) = expr.kind
856 && let hir::ExprKind::Index(val, index, _) = receiver.kind
857 && receiver.span == self.assign_span
858 {
859 self.err.multipart_suggestion(
861 ::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),
862 ::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![
863 (val.span.shrink_to_lo(), "if let Some(val) = ".to_string()),
864 (
865 val.span.shrink_to_hi().with_hi(index.span.lo()),
866 ".get_mut(".to_string(),
867 ),
868 (
869 index.span.shrink_to_hi().with_hi(receiver.span.hi()),
870 ") { val".to_string(),
871 ),
872 (sp.shrink_to_hi(), "; }".to_string()),
873 ],
874 Applicability::MachineApplicable,
875 );
876 self.suggested = true;
877 }
878 }
879 }
880 let def_id = self.body.source.def_id();
881 let Some(local_def_id) = def_id.as_local() else { return };
882 let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id) else { return };
883
884 let mut v = SuggestIndexOperatorAlternativeVisitor {
885 assign_span: span,
886 err,
887 ty,
888 suggested: false,
889 infcx: self.infcx,
890 };
891 v.visit_body(&body);
892 if !v.suggested {
893 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!(
894 "to modify a `{ty}`, use `.get_mut()`, `.insert()` or the entry API",
895 ));
896 }
897 }
898 }
899
900 fn is_error_in_trait(&self, local: Local) -> (bool, bool, Option<Span>) {
910 let tcx = self.infcx.tcx;
911 if self.body.local_kind(local) != LocalKind::Arg {
912 return (false, false, None);
913 }
914 let my_def = self.body.source.def_id();
915 let Some(td) = tcx.trait_impl_of_assoc(my_def).map(|id| self.infcx.tcx.impl_trait_id(id))
916 else {
917 return (false, false, None);
918 };
919
920 let implemented_trait_item = self.infcx.tcx.trait_item_of(my_def);
921
922 (
923 true,
924 td.is_local(),
925 implemented_trait_item.and_then(|f_in_trait| {
926 let f_in_trait = f_in_trait.as_local()?;
927 if let Node::TraitItem(ti) = self.infcx.tcx.hir_node_by_def_id(f_in_trait)
928 && let hir::TraitItemKind::Fn(sig, _) = ti.kind
929 && let Some(ty) = sig.decl.inputs.get(local.index() - 1)
930 && let hir::TyKind::Ref(_, _, hir::Mutability::Not) = ty.kind
931 && sig.decl.implicit_self().has_implicit_self()
932 {
933 Some(ty.span)
934 } else {
935 None
936 }
937 }),
938 )
939 }
940
941 fn construct_mut_suggestion_for_local_binding_patterns(
942 &self,
943 err: &mut Diag<'_>,
944 local: Local,
945 ) {
946 let local_decl = &self.body.local_decls[local];
947 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs:947",
"rustc_borrowck::diagnostics::mutability_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs"),
::tracing_core::__macro_support::Option::Some(947u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("local_decl: {0:?}",
local_decl) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("local_decl: {:?}", local_decl);
948 let pat_span = match *local_decl.local_info() {
949 LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
950 binding_mode: BindingMode(ByRef::No, Mutability::Not),
951 opt_ty_info: _,
952 opt_match_place: _,
953 pat_span,
954 introductions: _,
955 })) => pat_span,
956 _ => local_decl.source_info.span,
957 };
958
959 let def_id = self.body.source.def_id();
966 if let Some(local_def_id) = def_id.as_local()
967 && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
968 && let Some(hir_id) = (BindingFinder { span: pat_span }).visit_body(&body).break_value()
969 && let node = self.infcx.tcx.hir_node(hir_id)
970 && let hir::Node::LetStmt(hir::LetStmt {
971 pat: hir::Pat { kind: hir::PatKind::Ref(_, _, _), .. },
972 ..
973 })
974 | hir::Node::Param(Param {
975 pat: hir::Pat { kind: hir::PatKind::Ref(_, _, _), .. },
976 ..
977 }) = node
978 {
979 err.multipart_suggestion(
980 "consider changing this to be mutable",
981 ::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![
982 (pat_span.until(local_decl.source_info.span), "&(mut ".to_string()),
983 (
984 local_decl.source_info.span.shrink_to_hi().with_hi(pat_span.hi()),
985 ")".to_string(),
986 ),
987 ],
988 Applicability::MachineApplicable,
989 );
990 return;
991 }
992
993 err.span_suggestion_verbose(
994 local_decl.source_info.span.shrink_to_lo(),
995 "consider changing this to be mutable",
996 "mut ",
997 Applicability::MachineApplicable,
998 );
999 }
1000
1001 fn show_mutating_upvar(
1003 &self,
1004 tcx: TyCtxt<'_>,
1005 closure_local_def_id: hir::def_id::LocalDefId,
1006 the_place_err: PlaceRef<'tcx>,
1007 err: &mut Diag<'_>,
1008 ) {
1009 let tables = tcx.typeck(closure_local_def_id);
1010 if let Some((span, closure_kind_origin)) = tcx.closure_kind_origin(closure_local_def_id) {
1011 let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base {
1012 let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin);
1013 let root_hir_id = upvar_id.var_path.hir_id;
1014 let captured_places =
1017 tables.closure_min_captures[&closure_local_def_id].get(&root_hir_id).unwrap();
1018
1019 let origin_projection = closure_kind_origin
1020 .projections
1021 .iter()
1022 .map(|proj| proj.kind)
1023 .collect::<Vec<_>>();
1024 let mut capture_reason = String::new();
1025 for captured_place in captured_places {
1026 let captured_place_kinds = captured_place
1027 .place
1028 .projections
1029 .iter()
1030 .map(|proj| proj.kind)
1031 .collect::<Vec<_>>();
1032 if rustc_middle::ty::is_ancestor_or_same_capture(
1033 &captured_place_kinds,
1034 &origin_projection,
1035 ) {
1036 match captured_place.info.capture_kind {
1037 ty::UpvarCapture::ByRef(
1038 ty::BorrowKind::Mutable | ty::BorrowKind::UniqueImmutable,
1039 ) => {
1040 capture_reason = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("mutable borrow of `{0}`", upvar))
})format!("mutable borrow of `{upvar}`");
1041 }
1042 ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {
1043 capture_reason = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("possible mutation of `{0}`",
upvar))
})format!("possible mutation of `{upvar}`");
1044 }
1045 _ => ::rustc_span::macros::bug_impl(None,
format_args!("upvar `{0}` borrowed, but not mutably", upvar),
Location::caller())bug!("upvar `{upvar}` borrowed, but not mutably"),
1046 }
1047 break;
1048 }
1049 }
1050 if capture_reason.is_empty() {
1051 ::rustc_span::macros::bug_impl(None,
format_args!("upvar `{0}` borrowed, but cannot find reason", upvar),
Location::caller());bug!("upvar `{upvar}` borrowed, but cannot find reason");
1052 }
1053 capture_reason
1054 } else {
1055 ::rustc_span::macros::bug_impl(None, format_args!("not an upvar"),
Location::caller())bug!("not an upvar")
1056 };
1057 if let Some(place_name) = self.describe_place(the_place_err) {
1063 err.span_label(
1064 *span,
1065 ::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}"),
1066 );
1067 } else if span.from_expansion() {
1068 err.span_label(
1069 *span,
1070 ::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}",),
1071 );
1072 }
1073 }
1074 }
1075
1076 fn suggest_similar_mut_method_for_for_loop(&self, err: &mut Diag<'_>, span: Span) {
1079 use hir::ExprKind::{AddrOf, Block, Call, MethodCall};
1080 use hir::{BorrowKind, Expr};
1081
1082 let tcx = self.infcx.tcx;
1083 struct Finder {
1084 span: Span,
1085 }
1086
1087 impl<'tcx> Visitor<'tcx> for Finder {
1088 type Result = ControlFlow<&'tcx Expr<'tcx>>;
1089 fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) -> Self::Result {
1090 if e.span == self.span {
1091 ControlFlow::Break(e)
1092 } else {
1093 hir::intravisit::walk_expr(self, e)
1094 }
1095 }
1096 }
1097 let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) else { return };
1098 let Block(block, _) = body.value.kind else { return };
1099 let mut expr = if let ControlFlow::Break(expr) = (Finder { span }).visit_block(block)
1103 && let Call(_, [expr]) = expr.kind
1104 {
1105 expr
1106 } else {
1107 return;
1108 };
1109 loop {
1110 match expr.kind {
1111 MethodCall(path_segment, _, _, span) => {
1112 let opt_suggestions = tcx
1115 .typeck(path_segment.hir_id.owner.def_id)
1116 .type_dependent_def_id(expr.hir_id)
1117 .and_then(|def_id| tcx.impl_of_assoc(def_id))
1118 .map(|def_id| tcx.associated_items(def_id))
1119 .map(|assoc_items| {
1120 assoc_items
1121 .in_definition_order()
1122 .map(|assoc_item_def| assoc_item_def.ident(tcx))
1123 .filter(|&ident| {
1124 let original_method_ident = path_segment.ident;
1125 original_method_ident != ident
1126 && ident
1127 .as_str()
1128 .starts_with(&original_method_ident.name.to_string())
1129 })
1130 .map(|ident| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}()", ident))
})format!("{ident}()"))
1131 .peekable()
1132 });
1133
1134 if let Some(mut suggestions) = opt_suggestions
1135 && suggestions.peek().is_some()
1136 {
1137 err.span_suggestions(
1138 span,
1139 "use mutable method",
1140 suggestions,
1141 Applicability::MaybeIncorrect,
1142 );
1143 }
1144 }
1145 AddrOf(BorrowKind::Ref, Mutability::Not, expr) => {
1146 err.span_suggestion_verbose(
1148 expr.span.shrink_to_lo(),
1149 "use a mutable iterator instead",
1150 "mut ",
1151 Applicability::MachineApplicable,
1152 );
1153 }
1154 ExprKind::Path(hir::QPath::Resolved(None, path))
1155 if let hir::def::Res::Local(hir_id) = path.res
1156 && let hir::Node::LetStmt(stmt) =
1157 self.infcx.tcx.parent_hir_node(hir_id)
1158 && let Some(init) = stmt.init =>
1159 {
1160 expr = init;
1162 continue;
1163 }
1164 _ => {}
1165 }
1166 break;
1167 }
1168 }
1169
1170 fn point_at_binding_outside_closure(
1172 &self,
1173 err: &mut Diag<'_>,
1174 local: Local,
1175 access_place: Place<'tcx>,
1176 ) {
1177 let place = access_place.as_ref();
1178 for (index, elem) in place.projection.into_iter().enumerate() {
1179 if let ProjectionElem::Deref = elem {
1180 if index == 0 {
1181 if self.body.local_decls[local].is_ref_for_guard() {
1182 continue;
1183 }
1184 if let LocalInfo::StaticRef { .. } = *self.body.local_decls[local].local_info()
1185 {
1186 continue;
1187 }
1188 }
1189 if let Some(field) = self.is_upvar_field_projection(PlaceRef {
1190 local,
1191 projection: place.projection.split_at(index + 1).0,
1192 }) {
1193 let var_index = field.index();
1194 let upvar = self.upvars[var_index];
1195 if let Some(hir_id) = upvar.info.capture_kind_expr_id {
1196 let node = self.infcx.tcx.hir_node(hir_id);
1197 if let hir::Node::Expr(expr) = node
1198 && let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
1199 && let hir::def::Res::Local(hir_id) = path.res
1200 && let hir::Node::Pat(pat) = self.infcx.tcx.hir_node(hir_id)
1201 {
1202 let name = upvar.to_string(self.infcx.tcx);
1203 err.span_label(
1204 pat.span,
1205 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` declared here, outside the closure",
name))
})format!("`{name}` declared here, outside the closure"),
1206 );
1207 break;
1208 }
1209 }
1210 }
1211 }
1212 }
1213 }
1214 fn expected_fn_found_fn_mut_call(&self, err: &mut Diag<'_>, sp: Span, act: &str) {
1216 err.span_label(sp, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot {0}", act))
})format!("cannot {act}"));
1217
1218 let tcx = self.infcx.tcx;
1219 let closure_id = self.mir_hir_id();
1220 let closure_span = tcx.def_span(self.mir_def_id());
1221 let fn_call_id = tcx.parent_hir_id(closure_id);
1222 let node = tcx.hir_node(fn_call_id);
1223 let def_id = tcx.hir_enclosing_body_owner(fn_call_id);
1224 let mut look_at_return = true;
1225
1226 err.span_label(closure_span, "in this closure");
1227 let closure_arg_has_fn_trait_bound =
1228 |callee_def_id, input_index, generic_args: ty::GenericArgs<'tcx>| {
1229 let sig = tcx.fn_sig(callee_def_id).instantiate(tcx, generic_args).skip_binder();
1230 let Some(input_ty): Option<Ty<'tcx>> = sig.inputs().get(input_index).copied()
1231 else {
1232 return false;
1233 };
1234
1235 tcx.clauses_of(callee_def_id).instantiate(tcx, generic_args).clauses.iter().any(
1236 |clause| {
1237 clause.as_trait_clause().is_some_and(|trait_pred| {
1238 trait_pred.polarity() == ty::ClausePolarity::Positive
1239 && tcx.fn_trait_kind_from_def_id(trait_pred.def_id())
1240 == Some(ty::ClosureKind::Fn)
1241 && trait_pred.self_ty().skip_binder().peel_refs()
1242 == input_ty.peel_refs()
1243 })
1244 },
1245 )
1246 };
1247
1248 let get_call_details = || -> Option<(DefId, Span, usize, usize, ty::GenericArgs<'tcx>)> {
1251 let hir::Node::Expr(hir::Expr { hir_id, kind, .. }) = node else {
1252 return None;
1253 };
1254
1255 let typeck_results = tcx.typeck(def_id);
1256
1257 match kind {
1258 hir::ExprKind::Call(expr, args) => {
1259 if let Some(ty::FnDef(def_id, generic_args)) =
1260 typeck_results.node_type_opt(expr.hir_id).as_ref().map(|ty| ty.kind())
1261 {
1262 let arg_pos = args.iter().position(|arg| arg.hir_id == closure_id)?;
1263 Some((
1264 *def_id,
1265 expr.span,
1266 arg_pos,
1267 arg_pos,
1268 generic_args.no_bound_vars().unwrap(),
1269 ))
1270 } else {
1271 None
1272 }
1273 }
1274 hir::ExprKind::MethodCall(_, _, args, span) => {
1275 let arg_pos = args.iter().position(|arg| arg.hir_id == closure_id)?;
1276 let def_id = typeck_results.type_dependent_def_id(*hir_id)?;
1277 let generic_args = typeck_results.node_args_opt(*hir_id)?;
1278 Some((def_id, *span, arg_pos, arg_pos + 1, generic_args))
1279 }
1280 _ => None,
1281 }
1282 };
1283
1284 if let Some((callee_def_id, call_span, arg_pos, input_index, generic_args)) =
1287 get_call_details()
1288 {
1289 let arg = match tcx.hir_get_if_local(callee_def_id) {
1290 Some(
1291 hir::Node::Item(hir::Item {
1292 kind: hir::ItemKind::Fn { ident, sig, .. }, ..
1293 })
1294 | hir::Node::TraitItem(hir::TraitItem {
1295 ident,
1296 kind: hir::TraitItemKind::Fn(sig, _),
1297 ..
1298 })
1299 | hir::Node::ImplItem(hir::ImplItem {
1300 ident,
1301 kind: hir::ImplItemKind::Fn(sig, _),
1302 ..
1303 }),
1304 ) => Some(
1305 sig.decl
1306 .inputs
1307 .get(
1308 arg_pos
1309 + if sig.decl.implicit_self().has_implicit_self() { 1 } else { 0 },
1310 )
1311 .map(|arg| arg.span)
1312 .unwrap_or(ident.span),
1313 ),
1314 _ => None,
1315 };
1316 if let Some(span) = arg {
1317 err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
1318 err.span_label(call_span, "expects `Fn` instead of `FnMut`");
1319 look_at_return = false;
1320 } else if closure_arg_has_fn_trait_bound(callee_def_id, input_index, generic_args) {
1321 err.span_label(call_span, "expects `Fn` instead of `FnMut`");
1326 look_at_return = false;
1327 }
1328 }
1329
1330 if look_at_return && tcx.hir_get_fn_id_for_return_block(closure_id).is_some() {
1331 match tcx.hir_node_by_def_id(tcx.hir_get_parent_item(fn_call_id).def_id) {
1334 hir::Node::Item(hir::Item {
1335 kind: hir::ItemKind::Fn { ident, sig, .. }, ..
1336 })
1337 | hir::Node::TraitItem(hir::TraitItem {
1338 ident,
1339 kind: hir::TraitItemKind::Fn(sig, _),
1340 ..
1341 })
1342 | hir::Node::ImplItem(hir::ImplItem {
1343 ident,
1344 kind: hir::ImplItemKind::Fn(sig, _),
1345 ..
1346 }) => {
1347 err.span_context(ident.span);
1348 err.span_label(
1349 sig.decl.output.span(),
1350 "change this to return `FnMut` instead of `Fn`",
1351 );
1352 }
1353 _ => {}
1354 }
1355 }
1356 }
1357
1358 fn suggest_using_iter_mut(&self, err: &mut Diag<'_>) {
1359 let source = self.body.source;
1360 if let InstanceKind::Item(def_id) = source.instance
1361 && let Some(Node::Expr(hir::Expr { hir_id, kind, .. })) =
1362 self.infcx.tcx.hir_get_if_local(def_id)
1363 && let ExprKind::Closure(hir::Closure { kind: hir::ClosureKind::Closure, .. }) = kind
1364 && let Node::Expr(expr) = self.infcx.tcx.parent_hir_node(*hir_id)
1365 {
1366 let mut cur_expr = expr;
1367 while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
1368 if path_segment.ident.name == sym::iter {
1369 let res = self
1371 .infcx
1372 .tcx
1373 .typeck(path_segment.hir_id.owner.def_id)
1374 .type_dependent_def_id(cur_expr.hir_id)
1375 .and_then(|def_id| self.infcx.tcx.impl_of_assoc(def_id))
1376 .map(|def_id| self.infcx.tcx.associated_items(def_id))
1377 .map(|assoc_items| {
1378 assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable()
1379 });
1380
1381 if let Some(mut res) = res
1382 && res.peek().is_some()
1383 {
1384 err.span_suggestion_verbose(
1385 path_segment.ident.span,
1386 "you may want to use `iter_mut` here",
1387 "iter_mut",
1388 Applicability::MaybeIncorrect,
1389 );
1390 }
1391 break;
1392 } else {
1393 cur_expr = recv;
1394 }
1395 }
1396 }
1397 }
1398
1399 fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) {
1400 let local_decl = &self.body.local_decls[local];
1401
1402 let (pointer_sigil, pointer_desc) =
1403 if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
1404
1405 let (is_trait_sig, is_local, local_trait) = self.is_error_in_trait(local);
1406
1407 if is_trait_sig && !is_local {
1408 err.span_label(
1410 local_decl.source_info.span,
1411 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("this is an immutable {0}",
pointer_desc))
})format!("this is an immutable {pointer_desc}"),
1412 );
1413 return;
1414 }
1415
1416 if self.is_closure_arg_with_non_locally_decided_type(local) {
1418 return;
1419 }
1420
1421 let decl_span = local_decl.source_info.span;
1422
1423 let (amp_mut_sugg, local_var_ty_info) = match *local_decl.local_info() {
1424 LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
1425 let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
1426 let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span));
1427 (AmpMutSugg::Type { span, suggestion, additional }, None)
1428 }
1429
1430 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1431 binding_mode: BindingMode(ByRef::No, _),
1432 opt_ty_info,
1433 ..
1434 })) => {
1435 let first_assignment = find_assignments(&self.body, local).first().copied();
1437 let first_assignment_stmt = first_assignment
1438 .and_then(|loc| self.body[loc.block].statements.get(loc.statement_index));
1439 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs:1439",
"rustc_borrowck::diagnostics::mutability_errors",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("/rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs"),
::tracing_core::__macro_support::Option::Some(1439u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::diagnostics::mutability_errors"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("first_assignment_stmt")
}> =
::tracing::__macro_support::FieldName::new("first_assignment_stmt");
NAME.as_str()
}], ::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&first_assignment_stmt)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};trace!(?first_assignment_stmt);
1440 let opt_assignment_rhs_span =
1441 first_assignment.map(|loc| self.body.source_info(loc).span);
1442 let mut source_span = opt_assignment_rhs_span;
1443 if let Some(mir::Statement {
1444 source_info: _,
1445 kind:
1446 mir::StatementKind::Assign((_, mir::Rvalue::Use(mir::Operand::Copy(place), _))),
1447 ..
1448 }) = first_assignment_stmt
1449 {
1450 let local_span = self.body.local_decls[place.local].source_info.span;
1451 source_span = Some(local_span);
1454 if let Some(DesugaringKind::ForLoop) = local_span.desugaring_kind() {
1455 self.suggest_similar_mut_method_for_for_loop(err, local_span);
1457 err.span_label(
1458 local_span,
1459 ::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",),
1460 );
1461 return;
1462 }
1463 }
1464
1465 if source_span.is_some_and(|s| {
1467 s.desugaring_kind().is_some() || self.infcx.tcx.sess.source_map().is_imported(s)
1468 }) {
1469 return;
1470 }
1471
1472 if name == kw::SelfLower && opt_ty_info.is_none() {
1474 let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
1475 (AmpMutSugg::Type { span, suggestion, additional: None }, None)
1476 } else if let Some(sugg) =
1477 suggest_ampmut(self.infcx, self.body(), first_assignment_stmt)
1478 {
1479 (sugg, opt_ty_info)
1480 } else {
1481 return;
1482 }
1483 }
1484
1485 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1486 binding_mode: BindingMode(ByRef::Yes(..), _),
1487 ..
1488 })) => {
1489 let pattern_span: Span = local_decl.source_info.span;
1490 let Some(span) = suggest_ref_mut(self.infcx.tcx, pattern_span) else {
1491 return;
1492 };
1493 (AmpMutSugg::Type { span, suggestion: "mut ".to_owned(), additional: None }, None)
1494 }
1495
1496 _ => ::core::panicking::unreachable_default_msg()unreachable!(),
1497 };
1498
1499 let mut suggest = |suggs: Vec<_>, applicability, extra| {
1500 if suggs.iter().any(|(span, _)| self.infcx.tcx.sess.source_map().is_imported(*span)) {
1501 return;
1502 }
1503
1504 err.multipart_suggestion(
1505 ::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!(
1506 "consider changing this to be a mutable {pointer_desc}{}{extra}",
1507 if is_trait_sig {
1508 " in the `impl` method and the `trait` definition"
1509 } else {
1510 ""
1511 }
1512 ),
1513 suggs,
1514 applicability,
1515 );
1516 };
1517
1518 let (mut sugg, add_type_annotation_if_not_exists) = match amp_mut_sugg {
1519 AmpMutSugg::Type { span, suggestion, additional } => {
1520 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)];
1521 sugg.extend(additional);
1522 suggest(sugg, Applicability::MachineApplicable, "");
1523 return;
1524 }
1525 AmpMutSugg::MapGetMut { span, suggestion } => {
1526 if self.infcx.tcx.sess.source_map().is_imported(span) {
1527 return;
1528 }
1529 err.multipart_suggestion(
1530 "consider using `get_mut`",
1531 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span, suggestion)]))vec![(span, suggestion)],
1532 Applicability::MaybeIncorrect,
1533 );
1534 return;
1535 }
1536 AmpMutSugg::Expr { span, suggestion } => {
1537 (::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)
1540 }
1541 AmpMutSugg::ChangeBinding => (::alloc::vec::Vec::new()vec![], true),
1542 };
1543
1544 let (binding_exists, span) = match local_var_ty_info {
1546 Some(ty_span) => (true, ty_span),
1550
1551 None => (false, decl_span),
1555 };
1556
1557 if !binding_exists && !add_type_annotation_if_not_exists {
1558 suggest(sugg, Applicability::MachineApplicable, "");
1559 return;
1560 }
1561
1562 let (sugg_span, sugg_str, suggest_now) = if let Ok(src) = self.infcx.tcx.sess.source_map().span_to_snippet(span)
1566 && src.starts_with("&'")
1567 && let Some(ws_pos) = src.find(char::is_whitespace)
1569 {
1570 let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
1571 (span, " mut".to_owned(), true)
1572 } else if binding_exists {
1574 let Ok(src) = self.infcx.tcx.sess.source_map().span_to_snippet(span) else {
1578 return;
1579 };
1580 let (prefix_len, replacement) = if local_decl.ty.is_ref() {
1581 (src.chars().next().map_or(0, char::len_utf8), "&mut ")
1582 } else {
1583 (src.find("const").map_or(1, |i| i + "const".len()), "*mut ")
1584 };
1585 let ws_len = src[prefix_len..].len() - src[prefix_len..].trim_start().len();
1586 let span = span.with_hi(span.lo() + BytePos((prefix_len + ws_len) as u32));
1587 (span, replacement.to_owned(), true)
1588 } else {
1589 let ty = local_decl.ty.builtin_deref(true).unwrap();
1592
1593 (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)
1594 };
1595
1596 if suggest_now {
1597 let has_change = !sugg.is_empty();
1599 sugg.push((sugg_span, sugg_str));
1600 suggest(
1601 sugg,
1602 Applicability::MachineApplicable,
1603 if has_change { " and changing the binding's type" } else { "" },
1605 );
1606 return;
1607 } else if !sugg.is_empty() {
1608 suggest(sugg, Applicability::MachineApplicable, "");
1609 return;
1610 }
1611
1612 let def_id = self.body.source.def_id();
1613 let hir_id = if let Some(local_def_id) = def_id.as_local()
1614 && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
1615 {
1616 BindingFinder { span: sugg_span }.visit_body(&body).break_value()
1617 } else {
1618 None
1619 };
1620 let node = hir_id.map(|hir_id| self.infcx.tcx.hir_node(hir_id));
1621
1622 let Some(hir::Node::LetStmt(local)) = node else {
1623 err.span_label(
1624 sugg_span,
1625 ::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}`"),
1626 );
1627 return;
1628 };
1629
1630 let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
1631 if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
1632 && let Some(expr) = local.init
1633 && let ty = tables.node_type_opt(expr.hir_id)
1634 && let Some(ty) = ty
1635 && let ty::Ref(..) = ty.kind()
1636 {
1637 match self
1638 .infcx
1639 .type_implements_trait_shallow(clone_trait, ty.peel_refs(), self.infcx.param_env)
1640 .as_ref()
1641 .map(|it| it.as_slice())
1642 {
1643 Some([]) => {
1644 }
1653 None => {
1654 if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
1655 && segment.ident.name == sym::clone
1656 {
1657 err.span_help(
1658 span,
1659 ::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!(
1660 "`{}` doesn't implement `Clone`, so this call clones \
1661 the reference `{ty}`",
1662 ty.peel_refs(),
1663 ),
1664 );
1665 }
1666 let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
1668 self.infcx.tcx,
1669 clone_trait,
1670 [ty.peel_refs()],
1671 ));
1672 let obligation = traits::Obligation::new(
1673 self.infcx.tcx,
1674 traits::ObligationCause::dummy(),
1675 self.infcx.param_env,
1676 trait_ref,
1677 );
1678 self.infcx.err_ctxt().suggest_derive(
1679 &obligation,
1680 err,
1681 trait_ref.upcast(self.infcx.tcx),
1682 );
1683 }
1684 Some(errors) => {
1685 if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
1686 && segment.ident.name == sym::clone
1687 {
1688 err.span_help(
1689 span,
1690 ::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!(
1691 "`{}` doesn't implement `Clone` because its \
1692 implementations trait bounds could not be met, so \
1693 this call clones the reference `{ty}`",
1694 ty.peel_refs(),
1695 ),
1696 );
1697 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!(
1698 "the following trait bounds weren't met: {}",
1699 errors
1700 .iter()
1701 .map(|e| e.obligation.predicate.to_string())
1702 .collect::<Vec<_>>()
1703 .join("\n"),
1704 ));
1705 }
1706 for error in errors {
1708 if let traits::FulfillmentErrorCode::Select(
1709 traits::SelectionError::Unimplemented,
1710 ) = error.code
1711 && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
1712 error.obligation.predicate.kind().skip_binder()
1713 {
1714 self.infcx.err_ctxt().suggest_derive(
1715 &error.obligation,
1716 err,
1717 error.obligation.predicate.kind().rebind(pred),
1718 );
1719 }
1720 }
1721 }
1722 }
1723 }
1724 let (changing, span, sugg) = match local.ty {
1725 Some(ty) => ("changing", ty.span, sugg_str),
1726 None => ("specifying", local.pat.span.shrink_to_hi(), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(": {0}", sugg_str))
})format!(": {sugg_str}")),
1727 };
1728 err.span_suggestion_verbose(
1729 span,
1730 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider {0} this binding\'s type",
changing))
})format!("consider {changing} this binding's type"),
1731 sugg,
1732 Applicability::HasPlaceholders,
1733 );
1734 }
1735
1736 fn is_closure_arg_with_non_locally_decided_type(&self, local: Local) -> bool {
1746 if self.body.local_kind(local) != LocalKind::Arg {
1748 return false;
1749 }
1750
1751 let InstanceKind::Item(body_def_id) = self.body.source.instance else {
1753 return false;
1754 };
1755 let Some(Node::Expr(hir::Expr { hir_id: body_hir_id, kind, .. })) =
1756 self.infcx.tcx.hir_get_if_local(body_def_id)
1757 else {
1758 return false;
1759 };
1760 let ExprKind::Closure(hir::Closure { kind: hir::ClosureKind::Closure, .. }) = kind else {
1761 return false;
1762 };
1763
1764 let Node::Expr(closure_parent) = self.infcx.tcx.parent_hir_node(*body_hir_id) else {
1767 return false;
1768 };
1769 match closure_parent.kind {
1770 ExprKind::MethodCall(method, _, _, _) => self
1771 .infcx
1772 .tcx
1773 .typeck(method.hir_id.owner.def_id)
1774 .type_dependent_def_id(closure_parent.hir_id)
1775 .is_some_and(|def_id| !def_id.is_local()),
1776 ExprKind::Call(func, _) => self
1777 .infcx
1778 .tcx
1779 .typeck(func.hir_id.owner.def_id)
1780 .node_type_opt(func.hir_id)
1781 .and_then(|ty| match ty.kind() {
1782 ty::FnDef(def_id, _) => Some(def_id),
1783 _ => None,
1784 })
1785 .is_some_and(|def_id| !def_id.is_local()),
1786 _ => false,
1787 }
1788 }
1789}
1790
1791struct BindingFinder {
1792 span: Span,
1793}
1794
1795impl<'tcx> Visitor<'tcx> for BindingFinder {
1796 type Result = ControlFlow<hir::HirId>;
1797 fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) -> Self::Result {
1798 if let hir::StmtKind::Let(local) = s.kind
1799 && local.pat.span == self.span
1800 {
1801 ControlFlow::Break(local.hir_id)
1802 } else {
1803 hir::intravisit::walk_stmt(self, s)
1804 }
1805 }
1806
1807 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) -> Self::Result {
1808 if let hir::Pat { kind: hir::PatKind::Ref(_, _, _), span, .. } = param.pat
1809 && *span == self.span
1810 {
1811 ControlFlow::Break(param.hir_id)
1812 } else {
1813 ControlFlow::Continue(())
1814 }
1815 }
1816}
1817
1818fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
1819 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs:1819",
"rustc_borrowck::diagnostics::mutability_errors",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/9c5648833e5ceb667a708d53c00b83ecc9e610d5/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs"),
::tracing_core::__macro_support::Option::Some(1819u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("local_info: {0:?}, ty.kind(): {1:?}",
local_decl.local_info, local_decl.ty.kind()) as
&dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
1820
1821 match *local_decl.local_info() {
1822 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1824 binding_mode: BindingMode(ByRef::No, Mutability::Not),
1825 ..
1826 })) => #[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)),
1827 LocalInfo::User(mir::BindingForm::ImplicitSelf(kind)) => {
1828 kind == hir::ImplicitSelfKind::RefMut
1834 }
1835 _ if Some(kw::SelfLower) == local_name => {
1836 #[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))
1840 }
1841 _ => false,
1842 }
1843}
1844
1845fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) {
1846 match tcx.sess.source_map().span_to_snippet(span) {
1847 Ok(snippet) if snippet.ends_with("self") => {
1848 (span.with_hi(span.hi() - BytePos(4)).shrink_to_hi(), "mut ".to_string())
1849 }
1850 _ => (span, "&mut self".to_string()),
1851 }
1852}
1853
1854enum AmpMutSugg {
1855 Type {
1858 span: Span,
1859 suggestion: String,
1860 additional: Option<(Span, String)>,
1861 },
1862 Expr {
1864 span: Span,
1865 suggestion: String,
1866 },
1867 MapGetMut {
1869 span: Span,
1870 suggestion: String,
1871 },
1872 ChangeBinding,
1873}
1874
1875fn suggest_ampmut<'tcx>(
1891 infcx: &crate::BorrowckInferCtxt<'tcx>,
1892 body: &Body<'tcx>,
1893 opt_assignment_rhs_stmt: Option<&Statement<'tcx>>,
1894) -> Option<AmpMutSugg> {
1895 let tcx = infcx.tcx;
1896 if let Some(rhs_stmt) = opt_assignment_rhs_stmt
1905 && let StatementKind::Assign((lhs, rvalue)) = &rhs_stmt.kind
1906 && let mut rhs_span = rhs_stmt.source_info.span
1907 && let Ok(mut rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
1908 {
1909 let mut rvalue = rvalue;
1910
1911 if let Rvalue::Ref(_, BorrowKind::Shared, place) = rvalue
1915 && place.projection.len() == 1
1916 && place.projection[0] == ProjectionElem::Deref
1917 && let Some(assign) = find_assignments(&body, place.local).first()
1918 {
1919 if let Some(user_ty_projs) = body.local_decls[lhs.local].user_ty.as_ref()
1923 && let [user_ty_proj] = user_ty_projs.contents.as_slice()
1924 && user_ty_proj.projs.is_empty()
1925 && let Either::Left(rhs_stmt_new) = body.stmt_at(*assign)
1926 && let StatementKind::Assign((_, rvalue_new)) = &rhs_stmt_new.kind
1927 && let rhs_span_new = rhs_stmt_new.source_info.span
1928 && let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span_new)
1929 {
1930 (rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new);
1931 }
1932
1933 if let Either::Right(call) = body.stmt_at(*assign)
1934 && let TerminatorKind::Call { func: Operand::Constant(const_operand), args, .. } =
1935 &call.kind
1936 && let ty::FnDef(method_def_id, method_args) = *const_operand.ty().kind()
1937 && let Some(trait_) = tcx.trait_of_assoc(method_def_id)
1938 && tcx.is_lang_item(trait_, LangItem::Index)
1939 {
1940 let trait_ref = ty::TraitRef::from_assoc(
1941 tcx,
1942 tcx.require_lang_item(LangItem::IndexMut, rhs_span),
1943 method_args.no_bound_vars().unwrap(),
1944 );
1945 if !infcx
1947 .type_implements_trait(trait_ref.def_id, trait_ref.args, infcx.param_env)
1948 .must_apply_considering_regions()
1949 {
1950 if let ty::Adt(def, _) = trait_ref.self_ty().kind()
1952 && [sym::BTreeMap, sym::HashMap]
1953 .into_iter()
1954 .any(|s| tcx.is_diagnostic_item(s, def.did()))
1955 && let [map, key] = &**args
1956 && let Ok(map) = tcx.sess.source_map().span_to_snippet(map.span)
1957 && let Ok(key) = tcx.sess.source_map().span_to_snippet(key.span)
1958 {
1959 let span = rhs_span;
1960 let suggestion = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}.get_mut({1}).unwrap()", map,
key))
})format!("{map}.get_mut({key}).unwrap()");
1961 return Some(AmpMutSugg::MapGetMut { span, suggestion });
1962 }
1963 return None;
1964 }
1965 }
1966 }
1967
1968 let sugg = match rvalue {
1969 Rvalue::Ref(_, BorrowKind::Shared, _) if let Some(ref_idx) = rhs_str.find('&') => {
1970 Some((
1972 rhs_span.with_lo(rhs_span.lo() + BytePos(ref_idx as u32 + 1)).shrink_to_lo(),
1973 "mut ".to_owned(),
1974 ))
1975 }
1976 Rvalue::RawPtr(RawPtrKind::Const, _) if let Some(const_idx) = rhs_str.find("const") => {
1977 let const_idx = const_idx as u32;
1979 Some((
1980 rhs_span
1981 .with_lo(rhs_span.lo() + BytePos(const_idx))
1982 .with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32)),
1983 "mut".to_owned(),
1984 ))
1985 }
1986 _ => None,
1987 };
1988
1989 if let Some((span, suggestion)) = sugg {
1990 return Some(AmpMutSugg::Expr { span, suggestion });
1991 }
1992 }
1993
1994 Some(AmpMutSugg::ChangeBinding)
1995}
1996
1997fn is_closure_like(ty: Ty<'_>) -> bool {
1999 ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure()
2000}
2001
2002fn get_mut_span_in_struct_field<'tcx>(
2010 tcx: TyCtxt<'tcx>,
2011 ty: Ty<'tcx>,
2012 field: FieldIdx,
2013) -> Option<Span> {
2014 if let ty::Ref(_, ty, _) = ty.kind()
2016 && let ty::Adt(def, _) = ty.kind()
2017 && let field = def.all_fields().nth(field.index())?
2018 && let hir::Node::Field(field) = tcx.hir_node_by_def_id(field.did.as_local()?)
2021 && let hir::TyKind::Ref(lt, ty, hir::Mutability::Not) = field.ty.kind
2022 {
2023 return Some(lt.ident.span.between(ty.span));
2024 }
2025
2026 None
2027}
2028
2029fn suggest_ref_mut(tcx: TyCtxt<'_>, span: Span) -> Option<Span> {
2031 let pattern_str = tcx.sess.source_map().span_to_snippet(span).ok()?;
2032 if let Some(rest) = pattern_str.strip_prefix("ref")
2033 && rest.starts_with(rustc_lexer::is_whitespace)
2034 {
2035 let span = span.with_lo(span.lo() + BytePos(4)).shrink_to_lo();
2036 Some(span)
2037 } else {
2038 None
2039 }
2040}