1#![allow(rustc::diagnostic_outside_of_impl)]
2#![allow(rustc::untranslatable_diagnostic)]
3
4use core::ops::ControlFlow;
5
6use either::Either;
7use hir::{ExprKind, Param};
8use rustc_abi::FieldIdx;
9use rustc_errors::{Applicability, Diag};
10use rustc_hir::intravisit::Visitor;
11use rustc_hir::{self as hir, BindingMode, ByRef, Node};
12use rustc_middle::bug;
13use rustc_middle::hir::place::PlaceBase;
14use rustc_middle::mir::visit::PlaceContext;
15use rustc_middle::mir::{
16 self, BindingForm, Body, BorrowKind, Local, LocalDecl, LocalInfo, LocalKind, Location,
17 Mutability, Operand, Place, PlaceRef, ProjectionElem, RawPtrKind, Rvalue, Statement,
18 StatementKind, TerminatorKind,
19};
20use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, Upcast};
21use rustc_span::{BytePos, DesugaringKind, Span, Symbol, kw, sym};
22use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
23use rustc_trait_selection::infer::InferCtxtExt;
24use rustc_trait_selection::traits;
25use tracing::{debug, trace};
26
27use crate::diagnostics::BorrowedContentSource;
28use crate::{MirBorrowckCtxt, session_diagnostics};
29
30#[derive(Copy, Clone, Debug, Eq, PartialEq)]
31pub(crate) enum AccessKind {
32 MutableBorrow,
33 Mutate,
34}
35
36fn find_assignments(body: &Body<'_>, local: Local) -> Vec<Location> {
39 use rustc_middle::mir::visit::Visitor;
40
41 struct FindLocalAssignmentVisitor {
42 needle: Local,
43 locations: Vec<Location>,
44 }
45
46 impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
47 fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) {
48 if self.needle != local {
49 return;
50 }
51
52 if place_context.is_place_assignment() {
53 self.locations.push(location);
54 }
55 }
56 }
57
58 let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
59 visitor.visit_body(body);
60 visitor.locations
61}
62
63impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
64 pub(crate) fn report_mutability_error(
65 &mut self,
66 access_place: Place<'tcx>,
67 span: Span,
68 the_place_err: PlaceRef<'tcx>,
69 error_access: AccessKind,
70 location: Location,
71 ) {
72 debug!(
73 "report_mutability_error(\
74 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
75 )",
76 access_place, span, the_place_err, error_access, location,
77 );
78
79 let mut err;
80 let item_msg;
81 let reason;
82 let mut opt_source = None;
83 let access_place_desc = self.describe_any_place(access_place.as_ref());
84 debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
85
86 match the_place_err {
87 PlaceRef { local, projection: [] } => {
88 item_msg = access_place_desc;
89 if access_place.as_local().is_some() {
90 reason = ", as it is not declared as mutable".to_string();
91 } else {
92 let name = self.local_name(local).expect("immutable unnamed local");
93 reason = format!(", as `{name}` is not declared as mutable");
94 }
95 }
96
97 PlaceRef {
98 local,
99 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
100 } => {
101 debug_assert!(is_closure_like(
102 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
103 ));
104
105 let imm_borrow_derefed = self.upvars[upvar_index.index()]
106 .place
107 .deref_tys()
108 .any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)));
109
110 if imm_borrow_derefed {
117 return;
119 } else {
120 item_msg = access_place_desc;
121 if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
122 reason = ", as it is not declared as mutable".to_string();
123 } else {
124 let name = self.upvars[upvar_index.index()].to_string(self.infcx.tcx);
125 reason = format!(", as `{name}` is not declared as mutable");
126 }
127 }
128 }
129
130 PlaceRef { local, projection: [ProjectionElem::Deref] }
131 if self.body.local_decls[local].is_ref_for_guard() =>
132 {
133 item_msg = access_place_desc;
134 reason = ", as it is immutable for the pattern guard".to_string();
135 }
136 PlaceRef { local, projection: [ProjectionElem::Deref] }
137 if self.body.local_decls[local].is_ref_to_static() =>
138 {
139 if access_place.projection.len() == 1 {
140 item_msg = format!("immutable static item {access_place_desc}");
141 reason = String::new();
142 } else {
143 item_msg = access_place_desc;
144 let local_info = self.body.local_decls[local].local_info();
145 if let LocalInfo::StaticRef { def_id, .. } = *local_info {
146 let static_name = &self.infcx.tcx.item_name(def_id);
147 reason = format!(", as `{static_name}` is an immutable static item");
148 } else {
149 bug!("is_ref_to_static return true, but not ref to static?");
150 }
151 }
152 }
153 PlaceRef { local: _, projection: [proj_base @ .., ProjectionElem::Deref] } => {
154 if the_place_err.local == ty::CAPTURE_STRUCT_LOCAL
155 && proj_base.is_empty()
156 && !self.upvars.is_empty()
157 {
158 item_msg = access_place_desc;
159 debug_assert!(self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_ref());
160 debug_assert!(is_closure_like(the_place_err.ty(self.body, self.infcx.tcx).ty));
161
162 reason = if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
163 ", as it is a captured variable in a `Fn` closure".to_string()
164 } else {
165 ", as `Fn` closures cannot mutate their captured variables".to_string()
166 }
167 } else {
168 let source = self.borrowed_content_source(PlaceRef {
169 local: the_place_err.local,
170 projection: proj_base,
171 });
172 let pointer_type = source.describe_for_immutable_place(self.infcx.tcx);
173 opt_source = Some(source);
174 if let Some(desc) = self.describe_place(access_place.as_ref()) {
175 item_msg = format!("`{desc}`");
176 reason = match error_access {
177 AccessKind::Mutate => format!(", which is behind {pointer_type}"),
178 AccessKind::MutableBorrow => {
179 format!(", as it is behind {pointer_type}")
180 }
181 }
182 } else {
183 item_msg = format!("data in {pointer_type}");
184 reason = String::new();
185 }
186 }
187 }
188
189 PlaceRef {
190 local: _,
191 projection:
192 [
193 ..,
194 ProjectionElem::Index(_)
195 | ProjectionElem::ConstantIndex { .. }
196 | ProjectionElem::OpaqueCast { .. }
197 | ProjectionElem::Subslice { .. }
198 | ProjectionElem::Downcast(..)
199 | ProjectionElem::UnwrapUnsafeBinder(_),
200 ],
201 } => bug!("Unexpected immutable place."),
202 }
203
204 debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
205
206 let act;
209 let acted_on;
210 let mut suggest = true;
211 let mut mut_error = None;
212 let mut count = 1;
213
214 let span = match error_access {
215 AccessKind::Mutate => {
216 err = self.cannot_assign(span, &(item_msg + &reason));
217 act = "assign";
218 acted_on = "written";
219 span
220 }
221 AccessKind::MutableBorrow => {
222 act = "borrow as mutable";
223 acted_on = "borrowed as mutable";
224
225 let borrow_spans = self.borrow_spans(span, location);
226 let borrow_span = borrow_spans.args_or_use();
227 match the_place_err {
228 PlaceRef { local, projection: [] }
229 if self.body.local_decls[local].can_be_made_mutable() =>
230 {
231 let span = self.body.local_decls[local].source_info.span;
232 mut_error = Some(span);
233 if let Some((buffered_err, c)) = self.get_buffered_mut_error(span) {
234 err = buffered_err;
239 count = c + 1;
240 if count == 2 {
241 err.replace_span_with(span, false);
242 err.span_label(span, "not mutable");
243 }
244 suggest = false;
245 } else {
246 err = self.cannot_borrow_path_as_mutable_because(
247 borrow_span,
248 &item_msg,
249 &reason,
250 );
251 }
252 }
253 _ => {
254 err = self.cannot_borrow_path_as_mutable_because(
255 borrow_span,
256 &item_msg,
257 &reason,
258 );
259 }
260 }
261 if suggest {
262 borrow_spans.var_subdiag(
263 &mut err,
264 Some(mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }),
265 |_kind, var_span| {
266 let place = self.describe_any_place(access_place.as_ref());
267 session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure {
268 place,
269 var_span,
270 }
271 },
272 );
273 }
274 borrow_span
275 }
276 };
277
278 debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
279
280 match the_place_err {
281 PlaceRef {
288 local,
289 projection:
290 [
291 proj_base @ ..,
292 ProjectionElem::Deref,
293 ProjectionElem::Field(field, _),
294 ProjectionElem::Deref,
295 ],
296 } => {
297 err.span_label(span, format!("cannot {act}"));
298
299 let place = Place::ty_from(local, proj_base, self.body, self.infcx.tcx);
300 if let Some(span) = get_mut_span_in_struct_field(self.infcx.tcx, place.ty, *field) {
301 err.span_suggestion_verbose(
302 span,
303 "consider changing this to be mutable",
304 " mut ",
305 Applicability::MaybeIncorrect,
306 );
307 }
308 }
309
310 PlaceRef { local, projection: [] }
312 if self
313 .body
314 .local_decls
315 .get(local)
316 .is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_name(local))) =>
317 {
318 let decl = &self.body.local_decls[local];
319 err.span_label(span, format!("cannot {act}"));
320 if let Some(mir::Statement {
321 source_info,
322 kind:
323 mir::StatementKind::Assign(box (
324 _,
325 mir::Rvalue::Ref(
326 _,
327 mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
328 _,
329 ),
330 )),
331 ..
332 }) = &self.body[location.block].statements.get(location.statement_index)
333 {
334 match *decl.local_info() {
335 LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
336 binding_mode: BindingMode(ByRef::No, Mutability::Not),
337 opt_ty_info: Some(sp),
338 opt_match_place: _,
339 pat_span: _,
340 })) => {
341 if suggest {
342 err.span_note(sp, "the binding is already a mutable borrow");
343 }
344 }
345 _ => {
346 err.span_note(
347 decl.source_info.span,
348 "the binding is already a mutable borrow",
349 );
350 }
351 }
352 if let Ok(snippet) =
353 self.infcx.tcx.sess.source_map().span_to_snippet(source_info.span)
354 {
355 if snippet.starts_with("&mut ") {
356 err.span_suggestion_verbose(
359 source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
360 "try removing `&mut` here",
361 "",
362 Applicability::MachineApplicable,
363 );
364 } else {
365 err.span_help(source_info.span, "try removing `&mut` here");
367 }
368 } else {
369 err.span_help(source_info.span, "try removing `&mut` here");
370 }
371 } else if decl.mutability.is_not() {
372 if matches!(
373 decl.local_info(),
374 LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::RefMut))
375 ) {
376 err.note(
377 "as `Self` may be unsized, this call attempts to take `&mut &mut self`",
378 );
379 err.note("however, `&mut self` expands to `self: &mut Self`, therefore `self` cannot be borrowed mutably");
380 } else {
381 err.span_suggestion_verbose(
382 decl.source_info.span.shrink_to_lo(),
383 "consider making the binding mutable",
384 "mut ",
385 Applicability::MachineApplicable,
386 );
387 };
388 }
389 }
390
391 PlaceRef { local, projection: [] }
394 if self.body.local_decls[local].can_be_made_mutable() =>
395 {
396 let local_decl = &self.body.local_decls[local];
401 assert_eq!(local_decl.mutability, Mutability::Not);
402
403 if count < 10 {
404 err.span_label(span, format!("cannot {act}"));
405 }
406 if suggest {
407 self.construct_mut_suggestion_for_local_binding_patterns(&mut err, local);
408 let tcx = self.infcx.tcx;
409 if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() {
410 self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
411 }
412 }
413 }
414
415 PlaceRef {
417 local,
418 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
419 } => {
420 debug_assert!(is_closure_like(
421 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
422 ));
423
424 let captured_place = self.upvars[upvar_index.index()];
425
426 err.span_label(span, format!("cannot {act}"));
427
428 let upvar_hir_id = captured_place.get_root_variable();
429
430 if let Node::Pat(pat) = self.infcx.tcx.hir_node(upvar_hir_id)
431 && let hir::PatKind::Binding(hir::BindingMode::NONE, _, upvar_ident, _) =
432 pat.kind
433 {
434 if upvar_ident.name == kw::SelfLower {
435 for (_, node) in self.infcx.tcx.hir_parent_iter(upvar_hir_id) {
436 if let Some(fn_decl) = node.fn_decl() {
437 if !matches!(
438 fn_decl.implicit_self,
439 hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut
440 ) {
441 err.span_suggestion_verbose(
442 upvar_ident.span.shrink_to_lo(),
443 "consider changing this to be mutable",
444 "mut ",
445 Applicability::MachineApplicable,
446 );
447 break;
448 }
449 }
450 }
451 } else {
452 err.span_suggestion_verbose(
453 upvar_ident.span.shrink_to_lo(),
454 "consider changing this to be mutable",
455 "mut ",
456 Applicability::MachineApplicable,
457 );
458 }
459 }
460
461 let tcx = self.infcx.tcx;
462 if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
463 && let ty::Closure(id, _) = *ty.kind()
464 {
465 self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
466 }
467 }
468
469 PlaceRef { local: _, projection: [] }
472 if self
473 .infcx
474 .tcx
475 .sess
476 .source_map()
477 .span_to_snippet(span)
478 .is_ok_and(|snippet| snippet.starts_with("&mut ")) =>
479 {
480 err.span_label(span, format!("cannot {act}"));
481 err.span_suggestion_verbose(
482 span.with_hi(span.lo() + BytePos(5)),
483 "try removing `&mut` here",
484 "",
485 Applicability::MaybeIncorrect,
486 );
487 }
488
489 PlaceRef { local, projection: [ProjectionElem::Deref] }
490 if self.body.local_decls[local].is_ref_for_guard() =>
491 {
492 err.span_label(span, format!("cannot {act}"));
493 err.note(
494 "variables bound in patterns are immutable until the end of the pattern guard",
495 );
496 }
497
498 PlaceRef { local, projection: [ProjectionElem::Deref] }
504 if self.body.local_decls[local].is_user_variable() =>
505 {
506 let local_decl = &self.body.local_decls[local];
507
508 let (pointer_sigil, pointer_desc) =
509 if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
510
511 match self.local_name(local) {
512 Some(name) if !local_decl.from_compiler_desugaring() => {
513 err.span_label(
514 span,
515 format!(
516 "`{name}` is a `{pointer_sigil}` {pointer_desc}, \
517 so the data it refers to cannot be {acted_on}",
518 ),
519 );
520
521 self.suggest_using_iter_mut(&mut err);
522 self.suggest_make_local_mut(&mut err, local, name);
523 }
524 _ => {
525 err.span_label(
526 span,
527 format!("cannot {act} through `{pointer_sigil}` {pointer_desc}"),
528 );
529 }
530 }
531 }
532
533 PlaceRef { local, projection: [ProjectionElem::Deref] }
534 if local == ty::CAPTURE_STRUCT_LOCAL && !self.upvars.is_empty() =>
535 {
536 self.expected_fn_found_fn_mut_call(&mut err, span, act);
537 }
538
539 PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => {
540 err.span_label(span, format!("cannot {act}"));
541
542 match opt_source {
543 Some(BorrowedContentSource::OverloadedDeref(ty)) => {
544 err.help(format!(
545 "trait `DerefMut` is required to modify through a dereference, \
546 but it is not implemented for `{ty}`",
547 ));
548 }
549 Some(BorrowedContentSource::OverloadedIndex(ty)) => {
550 err.help(format!(
551 "trait `IndexMut` is required to modify indexed content, \
552 but it is not implemented for `{ty}`",
553 ));
554 self.suggest_map_index_mut_alternatives(ty, &mut err, span);
555 }
556 _ => (),
557 }
558 }
559
560 _ => {
561 err.span_label(span, format!("cannot {act}"));
562 }
563 }
564
565 if let Some(span) = mut_error {
566 self.buffer_mut_error(span, err, count);
567 } else {
568 self.buffer_error(err);
569 }
570 }
571
572 fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'infcx>, span: Span) {
574 let Some(adt) = ty.ty_adt_def() else { return };
575 let did = adt.did();
576 if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did)
577 || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did)
578 {
579 struct SuggestIndexOperatorAlternativeVisitor<'a, 'infcx, 'tcx> {
583 assign_span: Span,
584 err: &'a mut Diag<'infcx>,
585 ty: Ty<'tcx>,
586 suggested: bool,
587 }
588 impl<'a, 'infcx, 'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'a, 'infcx, 'tcx> {
589 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
590 hir::intravisit::walk_stmt(self, stmt);
591 let expr = match stmt.kind {
592 hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr,
593 hir::StmtKind::Let(hir::LetStmt { init: Some(expr), .. }) => expr,
594 _ => {
595 return;
596 }
597 };
598 if let hir::ExprKind::Assign(place, rv, _sp) = expr.kind
599 && let hir::ExprKind::Index(val, index, _) = place.kind
600 && (expr.span == self.assign_span || place.span == self.assign_span)
601 {
602 self.err.multipart_suggestions(
605 format!(
606 "use `.insert()` to insert a value into a `{}`, `.get_mut()` \
607 to modify it, or the entry API for more flexibility",
608 self.ty,
609 ),
610 vec![
611 vec![
612 (
614 val.span.shrink_to_hi().with_hi(index.span.lo()),
615 ".insert(".to_string(),
616 ),
617 (
618 index.span.shrink_to_hi().with_hi(rv.span.lo()),
619 ", ".to_string(),
620 ),
621 (rv.span.shrink_to_hi(), ")".to_string()),
622 ],
623 vec![
624 (val.span.shrink_to_lo(), "if let Some(val) = ".to_string()),
626 (
627 val.span.shrink_to_hi().with_hi(index.span.lo()),
628 ".get_mut(".to_string(),
629 ),
630 (
631 index.span.shrink_to_hi().with_hi(place.span.hi()),
632 ") { *val".to_string(),
633 ),
634 (rv.span.shrink_to_hi(), "; }".to_string()),
635 ],
636 vec![
637 (val.span.shrink_to_lo(), "let val = ".to_string()),
639 (
640 val.span.shrink_to_hi().with_hi(index.span.lo()),
641 ".entry(".to_string(),
642 ),
643 (
644 index.span.shrink_to_hi().with_hi(rv.span.lo()),
645 ").or_insert(".to_string(),
646 ),
647 (rv.span.shrink_to_hi(), ")".to_string()),
648 ],
649 ],
650 Applicability::MachineApplicable,
651 );
652 self.suggested = true;
653 } else if let hir::ExprKind::MethodCall(_path, receiver, _, sp) = expr.kind
654 && let hir::ExprKind::Index(val, index, _) = receiver.kind
655 && receiver.span == self.assign_span
656 {
657 self.err.multipart_suggestion(
659 format!("to modify a `{}` use `.get_mut()`", self.ty),
660 vec![
661 (val.span.shrink_to_lo(), "if let Some(val) = ".to_string()),
662 (
663 val.span.shrink_to_hi().with_hi(index.span.lo()),
664 ".get_mut(".to_string(),
665 ),
666 (
667 index.span.shrink_to_hi().with_hi(receiver.span.hi()),
668 ") { val".to_string(),
669 ),
670 (sp.shrink_to_hi(), "; }".to_string()),
671 ],
672 Applicability::MachineApplicable,
673 );
674 self.suggested = true;
675 }
676 }
677 }
678 let def_id = self.body.source.def_id();
679 let Some(local_def_id) = def_id.as_local() else { return };
680 let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id) else { return };
681
682 let mut v = SuggestIndexOperatorAlternativeVisitor {
683 assign_span: span,
684 err,
685 ty,
686 suggested: false,
687 };
688 v.visit_body(&body);
689 if !v.suggested {
690 err.help(format!(
691 "to modify a `{ty}`, use `.get_mut()`, `.insert()` or the entry API",
692 ));
693 }
694 }
695 }
696
697 fn is_error_in_trait(&self, local: Local) -> (bool, bool, Option<Span>) {
707 let tcx = self.infcx.tcx;
708 if self.body.local_kind(local) != LocalKind::Arg {
709 return (false, false, None);
710 }
711 let my_def = self.body.source.def_id();
712 let Some(td) =
713 tcx.trait_impl_of_assoc(my_def).and_then(|id| self.infcx.tcx.trait_id_of_impl(id))
714 else {
715 return (false, false, None);
716 };
717
718 let implemented_trait_item = self.infcx.tcx.trait_item_of(my_def);
719
720 (
721 true,
722 td.is_local(),
723 implemented_trait_item.and_then(|f_in_trait| {
724 let f_in_trait = f_in_trait.as_local()?;
725 if let Node::TraitItem(ti) = self.infcx.tcx.hir_node_by_def_id(f_in_trait)
726 && let hir::TraitItemKind::Fn(sig, _) = ti.kind
727 && let Some(ty) = sig.decl.inputs.get(local.index() - 1)
728 && let hir::TyKind::Ref(_, mut_ty) = ty.kind
729 && let hir::Mutability::Not = mut_ty.mutbl
730 && sig.decl.implicit_self.has_implicit_self()
731 {
732 Some(ty.span)
733 } else {
734 None
735 }
736 }),
737 )
738 }
739
740 fn construct_mut_suggestion_for_local_binding_patterns(
741 &self,
742 err: &mut Diag<'_>,
743 local: Local,
744 ) {
745 let local_decl = &self.body.local_decls[local];
746 debug!("local_decl: {:?}", local_decl);
747 let pat_span = match *local_decl.local_info() {
748 LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
749 binding_mode: BindingMode(ByRef::No, Mutability::Not),
750 opt_ty_info: _,
751 opt_match_place: _,
752 pat_span,
753 })) => pat_span,
754 _ => local_decl.source_info.span,
755 };
756
757 let def_id = self.body.source.def_id();
764 if let Some(local_def_id) = def_id.as_local()
765 && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
766 && let Some(hir_id) = (BindingFinder { span: pat_span }).visit_body(&body).break_value()
767 && let node = self.infcx.tcx.hir_node(hir_id)
768 && let hir::Node::LetStmt(hir::LetStmt {
769 pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. },
770 ..
771 })
772 | hir::Node::Param(Param {
773 pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. },
774 ..
775 }) = node
776 {
777 err.multipart_suggestion(
778 "consider changing this to be mutable",
779 vec![
780 (pat_span.until(local_decl.source_info.span), "&(mut ".to_string()),
781 (
782 local_decl.source_info.span.shrink_to_hi().with_hi(pat_span.hi()),
783 ")".to_string(),
784 ),
785 ],
786 Applicability::MachineApplicable,
787 );
788 return;
789 }
790
791 err.span_suggestion_verbose(
792 local_decl.source_info.span.shrink_to_lo(),
793 "consider changing this to be mutable",
794 "mut ",
795 Applicability::MachineApplicable,
796 );
797 }
798
799 fn show_mutating_upvar(
801 &self,
802 tcx: TyCtxt<'_>,
803 closure_local_def_id: hir::def_id::LocalDefId,
804 the_place_err: PlaceRef<'tcx>,
805 err: &mut Diag<'_>,
806 ) {
807 let tables = tcx.typeck(closure_local_def_id);
808 if let Some((span, closure_kind_origin)) = tcx.closure_kind_origin(closure_local_def_id) {
809 let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base {
810 let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin);
811 let root_hir_id = upvar_id.var_path.hir_id;
812 let captured_places =
815 tables.closure_min_captures[&closure_local_def_id].get(&root_hir_id).unwrap();
816
817 let origin_projection = closure_kind_origin
818 .projections
819 .iter()
820 .map(|proj| proj.kind)
821 .collect::<Vec<_>>();
822 let mut capture_reason = String::new();
823 for captured_place in captured_places {
824 let captured_place_kinds = captured_place
825 .place
826 .projections
827 .iter()
828 .map(|proj| proj.kind)
829 .collect::<Vec<_>>();
830 if rustc_middle::ty::is_ancestor_or_same_capture(
831 &captured_place_kinds,
832 &origin_projection,
833 ) {
834 match captured_place.info.capture_kind {
835 ty::UpvarCapture::ByRef(
836 ty::BorrowKind::Mutable | ty::BorrowKind::UniqueImmutable,
837 ) => {
838 capture_reason = format!("mutable borrow of `{upvar}`");
839 }
840 ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {
841 capture_reason = format!("possible mutation of `{upvar}`");
842 }
843 _ => bug!("upvar `{upvar}` borrowed, but not mutably"),
844 }
845 break;
846 }
847 }
848 if capture_reason.is_empty() {
849 bug!("upvar `{upvar}` borrowed, but cannot find reason");
850 }
851 capture_reason
852 } else {
853 bug!("not an upvar")
854 };
855 if let Some(place_name) = self.describe_place(the_place_err) {
861 err.span_label(
862 *span,
863 format!("calling `{place_name}` requires mutable binding due to {reason}"),
864 );
865 } else if span.from_expansion() {
866 err.span_label(
867 *span,
868 format!("a call in this macro requires a mutable binding due to {reason}",),
869 );
870 }
871 }
872 }
873
874 fn suggest_similar_mut_method_for_for_loop(&self, err: &mut Diag<'_>, span: Span) {
877 use hir::ExprKind::{AddrOf, Block, Call, MethodCall};
878 use hir::{BorrowKind, Expr};
879
880 let tcx = self.infcx.tcx;
881 struct Finder {
882 span: Span,
883 }
884
885 impl<'tcx> Visitor<'tcx> for Finder {
886 type Result = ControlFlow<&'tcx Expr<'tcx>>;
887 fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) -> Self::Result {
888 if e.span == self.span {
889 ControlFlow::Break(e)
890 } else {
891 hir::intravisit::walk_expr(self, e)
892 }
893 }
894 }
895 if let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id())
896 && let Block(block, _) = body.value.kind
897 {
898 if let ControlFlow::Break(expr) = (Finder { span }).visit_block(block)
902 && let Call(_, [expr]) = expr.kind
903 {
904 match expr.kind {
905 MethodCall(path_segment, _, _, span) => {
906 let opt_suggestions = tcx
909 .typeck(path_segment.hir_id.owner.def_id)
910 .type_dependent_def_id(expr.hir_id)
911 .and_then(|def_id| tcx.impl_of_assoc(def_id))
912 .map(|def_id| tcx.associated_items(def_id))
913 .map(|assoc_items| {
914 assoc_items
915 .in_definition_order()
916 .map(|assoc_item_def| assoc_item_def.ident(tcx))
917 .filter(|&ident| {
918 let original_method_ident = path_segment.ident;
919 original_method_ident != ident
920 && ident.as_str().starts_with(
921 &original_method_ident.name.to_string(),
922 )
923 })
924 .map(|ident| format!("{ident}()"))
925 .peekable()
926 });
927
928 if let Some(mut suggestions) = opt_suggestions
929 && suggestions.peek().is_some()
930 {
931 err.span_suggestions(
932 span,
933 "use mutable method",
934 suggestions,
935 Applicability::MaybeIncorrect,
936 );
937 }
938 }
939 AddrOf(BorrowKind::Ref, Mutability::Not, expr) => {
940 err.span_suggestion_verbose(
942 expr.span.shrink_to_lo(),
943 "use a mutable iterator instead",
944 "mut ",
945 Applicability::MachineApplicable,
946 );
947 }
948 _ => {}
949 }
950 }
951 }
952 }
953
954 fn expected_fn_found_fn_mut_call(&self, err: &mut Diag<'_>, sp: Span, act: &str) {
956 err.span_label(sp, format!("cannot {act}"));
957
958 let tcx = self.infcx.tcx;
959 let closure_id = self.mir_hir_id();
960 let closure_span = tcx.def_span(self.mir_def_id());
961 let fn_call_id = tcx.parent_hir_id(closure_id);
962 let node = tcx.hir_node(fn_call_id);
963 let def_id = tcx.hir_enclosing_body_owner(fn_call_id);
964 let mut look_at_return = true;
965
966 let get_call_details = || {
969 let hir::Node::Expr(hir::Expr { hir_id, kind, .. }) = node else {
970 return None;
971 };
972
973 let typeck_results = tcx.typeck(def_id);
974
975 match kind {
976 hir::ExprKind::Call(expr, args) => {
977 if let Some(ty::FnDef(def_id, _)) =
978 typeck_results.node_type_opt(expr.hir_id).as_ref().map(|ty| ty.kind())
979 {
980 Some((*def_id, expr.span, *args))
981 } else {
982 None
983 }
984 }
985 hir::ExprKind::MethodCall(_, _, args, span) => typeck_results
986 .type_dependent_def_id(*hir_id)
987 .map(|def_id| (def_id, *span, *args)),
988 _ => None,
989 }
990 };
991
992 if let Some((callee_def_id, call_span, call_args)) = get_call_details() {
995 let arg_pos = call_args
996 .iter()
997 .enumerate()
998 .filter(|(_, arg)| arg.hir_id == closure_id)
999 .map(|(pos, _)| pos)
1000 .next();
1001
1002 let arg = match tcx.hir_get_if_local(callee_def_id) {
1003 Some(
1004 hir::Node::Item(hir::Item {
1005 kind: hir::ItemKind::Fn { ident, sig, .. }, ..
1006 })
1007 | hir::Node::TraitItem(hir::TraitItem {
1008 ident,
1009 kind: hir::TraitItemKind::Fn(sig, _),
1010 ..
1011 })
1012 | hir::Node::ImplItem(hir::ImplItem {
1013 ident,
1014 kind: hir::ImplItemKind::Fn(sig, _),
1015 ..
1016 }),
1017 ) => Some(
1018 arg_pos
1019 .and_then(|pos| {
1020 sig.decl.inputs.get(
1021 pos + if sig.decl.implicit_self.has_implicit_self() {
1022 1
1023 } else {
1024 0
1025 },
1026 )
1027 })
1028 .map(|arg| arg.span)
1029 .unwrap_or(ident.span),
1030 ),
1031 _ => None,
1032 };
1033 if let Some(span) = arg {
1034 err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
1035 err.span_label(call_span, "expects `Fn` instead of `FnMut`");
1036 err.span_label(closure_span, "in this closure");
1037 look_at_return = false;
1038 }
1039 }
1040
1041 if look_at_return && tcx.hir_get_fn_id_for_return_block(closure_id).is_some() {
1042 match tcx.hir_node_by_def_id(tcx.hir_get_parent_item(fn_call_id).def_id) {
1045 hir::Node::Item(hir::Item {
1046 kind: hir::ItemKind::Fn { ident, sig, .. }, ..
1047 })
1048 | hir::Node::TraitItem(hir::TraitItem {
1049 ident,
1050 kind: hir::TraitItemKind::Fn(sig, _),
1051 ..
1052 })
1053 | hir::Node::ImplItem(hir::ImplItem {
1054 ident,
1055 kind: hir::ImplItemKind::Fn(sig, _),
1056 ..
1057 }) => {
1058 err.span_label(ident.span, "");
1059 err.span_label(
1060 sig.decl.output.span(),
1061 "change this to return `FnMut` instead of `Fn`",
1062 );
1063 err.span_label(closure_span, "in this closure");
1064 }
1065 _ => {}
1066 }
1067 }
1068 }
1069
1070 fn suggest_using_iter_mut(&self, err: &mut Diag<'_>) {
1071 let source = self.body.source;
1072 if let InstanceKind::Item(def_id) = source.instance
1073 && let Some(Node::Expr(hir::Expr { hir_id, kind, .. })) =
1074 self.infcx.tcx.hir_get_if_local(def_id)
1075 && let ExprKind::Closure(hir::Closure { kind: hir::ClosureKind::Closure, .. }) = kind
1076 && let Node::Expr(expr) = self.infcx.tcx.parent_hir_node(*hir_id)
1077 {
1078 let mut cur_expr = expr;
1079 while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
1080 if path_segment.ident.name == sym::iter {
1081 let res = self
1083 .infcx
1084 .tcx
1085 .typeck(path_segment.hir_id.owner.def_id)
1086 .type_dependent_def_id(cur_expr.hir_id)
1087 .and_then(|def_id| self.infcx.tcx.impl_of_assoc(def_id))
1088 .map(|def_id| self.infcx.tcx.associated_items(def_id))
1089 .map(|assoc_items| {
1090 assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable()
1091 });
1092
1093 if let Some(mut res) = res
1094 && res.peek().is_some()
1095 {
1096 err.span_suggestion_verbose(
1097 path_segment.ident.span,
1098 "you may want to use `iter_mut` here",
1099 "iter_mut",
1100 Applicability::MaybeIncorrect,
1101 );
1102 }
1103 break;
1104 } else {
1105 cur_expr = recv;
1106 }
1107 }
1108 }
1109 }
1110
1111 fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) {
1112 let local_decl = &self.body.local_decls[local];
1113
1114 let (pointer_sigil, pointer_desc) =
1115 if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
1116
1117 let (is_trait_sig, is_local, local_trait) = self.is_error_in_trait(local);
1118
1119 if is_trait_sig && !is_local {
1120 err.span_label(
1122 local_decl.source_info.span,
1123 format!("this is an immutable {pointer_desc}"),
1124 );
1125 return;
1126 }
1127 let decl_span = local_decl.source_info.span;
1128
1129 let (amp_mut_sugg, local_var_ty_info) = match *local_decl.local_info() {
1130 LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
1131 let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
1132 let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span));
1133 (AmpMutSugg::Type { span, suggestion, additional }, None)
1134 }
1135
1136 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1137 binding_mode: BindingMode(ByRef::No, _),
1138 opt_ty_info,
1139 ..
1140 })) => {
1141 let first_assignment = find_assignments(&self.body, local).first().copied();
1143 let first_assignment_stmt = first_assignment
1144 .and_then(|loc| self.body[loc.block].statements.get(loc.statement_index));
1145 trace!(?first_assignment_stmt);
1146 let opt_assignment_rhs_span =
1147 first_assignment.map(|loc| self.body.source_info(loc).span);
1148 let mut source_span = opt_assignment_rhs_span;
1149 if let Some(mir::Statement {
1150 source_info: _,
1151 kind:
1152 mir::StatementKind::Assign(box (_, mir::Rvalue::Use(mir::Operand::Copy(place)))),
1153 ..
1154 }) = first_assignment_stmt
1155 {
1156 let local_span = self.body.local_decls[place.local].source_info.span;
1157 source_span = Some(local_span);
1160 if let Some(DesugaringKind::ForLoop) = local_span.desugaring_kind() {
1161 self.suggest_similar_mut_method_for_for_loop(err, local_span);
1163 err.span_label(
1164 local_span,
1165 format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",),
1166 );
1167 return;
1168 }
1169 }
1170
1171 if source_span.is_some_and(|s| {
1173 s.desugaring_kind().is_some() || self.infcx.tcx.sess.source_map().is_imported(s)
1174 }) {
1175 return;
1176 }
1177
1178 if name == kw::SelfLower && opt_ty_info.is_none() {
1180 let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
1181 (AmpMutSugg::Type { span, suggestion, additional: None }, None)
1182 } else if let Some(sugg) =
1183 suggest_ampmut(self.infcx, self.body(), first_assignment_stmt)
1184 {
1185 (sugg, opt_ty_info)
1186 } else {
1187 return;
1188 }
1189 }
1190
1191 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1192 binding_mode: BindingMode(ByRef::Yes(_), _),
1193 ..
1194 })) => {
1195 let pattern_span: Span = local_decl.source_info.span;
1196 let Some(span) = suggest_ref_mut(self.infcx.tcx, pattern_span) else {
1197 return;
1198 };
1199 (AmpMutSugg::Type { span, suggestion: "mut ".to_owned(), additional: None }, None)
1200 }
1201
1202 _ => unreachable!(),
1203 };
1204
1205 let mut suggest = |suggs: Vec<_>, applicability, extra| {
1206 if suggs.iter().any(|(span, _)| self.infcx.tcx.sess.source_map().is_imported(*span)) {
1207 return;
1208 }
1209
1210 err.multipart_suggestion_verbose(
1211 format!(
1212 "consider changing this to be a mutable {pointer_desc}{}{extra}",
1213 if is_trait_sig {
1214 " in the `impl` method and the `trait` definition"
1215 } else {
1216 ""
1217 }
1218 ),
1219 suggs,
1220 applicability,
1221 );
1222 };
1223
1224 let (mut sugg, add_type_annotation_if_not_exists) = match amp_mut_sugg {
1225 AmpMutSugg::Type { span, suggestion, additional } => {
1226 let mut sugg = vec![(span, suggestion)];
1227 sugg.extend(additional);
1228 suggest(sugg, Applicability::MachineApplicable, "");
1229 return;
1230 }
1231 AmpMutSugg::MapGetMut { span, suggestion } => {
1232 if self.infcx.tcx.sess.source_map().is_imported(span) {
1233 return;
1234 }
1235 err.multipart_suggestion_verbose(
1236 "consider using `get_mut`",
1237 vec![(span, suggestion)],
1238 Applicability::MaybeIncorrect,
1239 );
1240 return;
1241 }
1242 AmpMutSugg::Expr { span, suggestion } => {
1243 (vec![(span, suggestion)], false)
1246 }
1247 AmpMutSugg::ChangeBinding => (vec![], true),
1248 };
1249
1250 let (binding_exists, span) = match local_var_ty_info {
1252 Some(ty_span) => (true, ty_span),
1256
1257 None => (false, decl_span),
1261 };
1262
1263 if !binding_exists && !add_type_annotation_if_not_exists {
1264 suggest(sugg, Applicability::MachineApplicable, "");
1265 return;
1266 }
1267
1268 let (sugg_span, sugg_str, suggest_now) = if let Ok(src) = self.infcx.tcx.sess.source_map().span_to_snippet(span)
1272 && src.starts_with("&'")
1273 && let Some(ws_pos) = src.find(char::is_whitespace)
1275 {
1276 let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
1277 (span, " mut".to_owned(), true)
1278 } else if binding_exists {
1280 let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
1282 (span, "mut ".to_owned(), true)
1283 } else {
1284 let ty = local_decl.ty.builtin_deref(true).unwrap();
1287
1288 (span, format!("{}mut {}", if local_decl.ty.is_ref() { "&" } else { "*" }, ty), false)
1289 };
1290
1291 if suggest_now {
1292 let has_change = !sugg.is_empty();
1294 sugg.push((sugg_span, sugg_str));
1295 suggest(
1296 sugg,
1297 Applicability::MachineApplicable,
1298 if has_change { " and changing the binding's type" } else { "" },
1300 );
1301 return;
1302 } else if !sugg.is_empty() {
1303 suggest(sugg, Applicability::MachineApplicable, "");
1304 return;
1305 }
1306
1307 let def_id = self.body.source.def_id();
1308 let hir_id = if let Some(local_def_id) = def_id.as_local()
1309 && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
1310 {
1311 BindingFinder { span: sugg_span }.visit_body(&body).break_value()
1312 } else {
1313 None
1314 };
1315 let node = hir_id.map(|hir_id| self.infcx.tcx.hir_node(hir_id));
1316
1317 let Some(hir::Node::LetStmt(local)) = node else {
1318 err.span_label(
1319 sugg_span,
1320 format!("consider changing this binding's type to be: `{sugg_str}`"),
1321 );
1322 return;
1323 };
1324
1325 let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
1326 if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
1327 && let Some(expr) = local.init
1328 && let ty = tables.node_type_opt(expr.hir_id)
1329 && let Some(ty) = ty
1330 && let ty::Ref(..) = ty.kind()
1331 {
1332 match self
1333 .infcx
1334 .type_implements_trait_shallow(clone_trait, ty.peel_refs(), self.infcx.param_env)
1335 .as_deref()
1336 {
1337 Some([]) => {
1338 }
1347 None => {
1348 if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
1349 && segment.ident.name == sym::clone
1350 {
1351 err.span_help(
1352 span,
1353 format!(
1354 "`{}` doesn't implement `Clone`, so this call clones \
1355 the reference `{ty}`",
1356 ty.peel_refs(),
1357 ),
1358 );
1359 }
1360 let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
1362 self.infcx.tcx,
1363 clone_trait,
1364 [ty.peel_refs()],
1365 ));
1366 let obligation = traits::Obligation::new(
1367 self.infcx.tcx,
1368 traits::ObligationCause::dummy(),
1369 self.infcx.param_env,
1370 trait_ref,
1371 );
1372 self.infcx.err_ctxt().suggest_derive(
1373 &obligation,
1374 err,
1375 trait_ref.upcast(self.infcx.tcx),
1376 );
1377 }
1378 Some(errors) => {
1379 if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
1380 && segment.ident.name == sym::clone
1381 {
1382 err.span_help(
1383 span,
1384 format!(
1385 "`{}` doesn't implement `Clone` because its \
1386 implementations trait bounds could not be met, so \
1387 this call clones the reference `{ty}`",
1388 ty.peel_refs(),
1389 ),
1390 );
1391 err.note(format!(
1392 "the following trait bounds weren't met: {}",
1393 errors
1394 .iter()
1395 .map(|e| e.obligation.predicate.to_string())
1396 .collect::<Vec<_>>()
1397 .join("\n"),
1398 ));
1399 }
1400 for error in errors {
1402 if let traits::FulfillmentErrorCode::Select(
1403 traits::SelectionError::Unimplemented,
1404 ) = error.code
1405 && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
1406 error.obligation.predicate.kind().skip_binder()
1407 {
1408 self.infcx.err_ctxt().suggest_derive(
1409 &error.obligation,
1410 err,
1411 error.obligation.predicate.kind().rebind(pred),
1412 );
1413 }
1414 }
1415 }
1416 }
1417 }
1418 let (changing, span, sugg) = match local.ty {
1419 Some(ty) => ("changing", ty.span, sugg_str),
1420 None => ("specifying", local.pat.span.shrink_to_hi(), format!(": {sugg_str}")),
1421 };
1422 err.span_suggestion_verbose(
1423 span,
1424 format!("consider {changing} this binding's type"),
1425 sugg,
1426 Applicability::HasPlaceholders,
1427 );
1428 }
1429}
1430
1431struct BindingFinder {
1432 span: Span,
1433}
1434
1435impl<'tcx> Visitor<'tcx> for BindingFinder {
1436 type Result = ControlFlow<hir::HirId>;
1437 fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) -> Self::Result {
1438 if let hir::StmtKind::Let(local) = s.kind
1439 && local.pat.span == self.span
1440 {
1441 ControlFlow::Break(local.hir_id)
1442 } else {
1443 hir::intravisit::walk_stmt(self, s)
1444 }
1445 }
1446
1447 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) -> Self::Result {
1448 if let hir::Pat { kind: hir::PatKind::Ref(_, _), span, .. } = param.pat
1449 && *span == self.span
1450 {
1451 ControlFlow::Break(param.hir_id)
1452 } else {
1453 ControlFlow::Continue(())
1454 }
1455 }
1456}
1457
1458fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
1459 debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
1460
1461 match *local_decl.local_info() {
1462 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1464 binding_mode: BindingMode(ByRef::No, Mutability::Not),
1465 ..
1466 })) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)),
1467 LocalInfo::User(mir::BindingForm::ImplicitSelf(kind)) => {
1468 kind == hir::ImplicitSelfKind::RefMut
1474 }
1475 _ if Some(kw::SelfLower) == local_name => {
1476 matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut))
1480 }
1481 _ => false,
1482 }
1483}
1484
1485fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) {
1486 match tcx.sess.source_map().span_to_snippet(span) {
1487 Ok(snippet) if snippet.ends_with("self") => {
1488 (span.with_hi(span.hi() - BytePos(4)).shrink_to_hi(), "mut ".to_string())
1489 }
1490 _ => (span, "&mut self".to_string()),
1491 }
1492}
1493
1494enum AmpMutSugg {
1495 Type {
1498 span: Span,
1499 suggestion: String,
1500 additional: Option<(Span, String)>,
1501 },
1502 Expr {
1504 span: Span,
1505 suggestion: String,
1506 },
1507 MapGetMut {
1509 span: Span,
1510 suggestion: String,
1511 },
1512 ChangeBinding,
1513}
1514
1515fn suggest_ampmut<'tcx>(
1531 infcx: &crate::BorrowckInferCtxt<'tcx>,
1532 body: &Body<'tcx>,
1533 opt_assignment_rhs_stmt: Option<&Statement<'tcx>>,
1534) -> Option<AmpMutSugg> {
1535 let tcx = infcx.tcx;
1536 if let Some(rhs_stmt) = opt_assignment_rhs_stmt
1545 && let StatementKind::Assign(box (lhs, rvalue)) = &rhs_stmt.kind
1546 && let mut rhs_span = rhs_stmt.source_info.span
1547 && let Ok(mut rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
1548 {
1549 let mut rvalue = rvalue;
1550
1551 if let Rvalue::Ref(_, BorrowKind::Shared, place) = rvalue
1555 && place.projection.len() == 1
1556 && place.projection[0] == ProjectionElem::Deref
1557 && let Some(assign) = find_assignments(&body, place.local).first()
1558 {
1559 if let Some(user_ty_projs) = body.local_decls[lhs.local].user_ty.as_ref()
1563 && let [user_ty_proj] = user_ty_projs.contents.as_slice()
1564 && user_ty_proj.projs.is_empty()
1565 && let Either::Left(rhs_stmt_new) = body.stmt_at(*assign)
1566 && let StatementKind::Assign(box (_, rvalue_new)) = &rhs_stmt_new.kind
1567 && let rhs_span_new = rhs_stmt_new.source_info.span
1568 && let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span)
1569 {
1570 (rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new);
1571 }
1572
1573 if let Either::Right(call) = body.stmt_at(*assign)
1574 && let TerminatorKind::Call {
1575 func: Operand::Constant(box const_operand), args, ..
1576 } = &call.kind
1577 && let ty::FnDef(method_def_id, method_args) = *const_operand.ty().kind()
1578 && let Some(trait_) = tcx.trait_of_assoc(method_def_id)
1579 && tcx.is_lang_item(trait_, hir::LangItem::Index)
1580 {
1581 let trait_ref = ty::TraitRef::from_assoc(
1582 tcx,
1583 tcx.require_lang_item(hir::LangItem::IndexMut, rhs_span),
1584 method_args,
1585 );
1586 if !infcx
1588 .type_implements_trait(trait_ref.def_id, trait_ref.args, infcx.param_env)
1589 .must_apply_considering_regions()
1590 {
1591 if let ty::Adt(def, _) = trait_ref.self_ty().kind()
1593 && [sym::BTreeMap, sym::HashMap]
1594 .into_iter()
1595 .any(|s| tcx.is_diagnostic_item(s, def.did()))
1596 && let [map, key] = &**args
1597 && let Ok(map) = tcx.sess.source_map().span_to_snippet(map.span)
1598 && let Ok(key) = tcx.sess.source_map().span_to_snippet(key.span)
1599 {
1600 let span = rhs_span;
1601 let suggestion = format!("{map}.get_mut({key}).unwrap()");
1602 return Some(AmpMutSugg::MapGetMut { span, suggestion });
1603 }
1604 return None;
1605 }
1606 }
1607 }
1608
1609 let sugg = match rvalue {
1610 Rvalue::Ref(_, BorrowKind::Shared, _) if let Some(ref_idx) = rhs_str.find('&') => {
1611 Some((
1613 rhs_span.with_lo(rhs_span.lo() + BytePos(ref_idx as u32 + 1)).shrink_to_lo(),
1614 "mut ".to_owned(),
1615 ))
1616 }
1617 Rvalue::RawPtr(RawPtrKind::Const, _) if let Some(const_idx) = rhs_str.find("const") => {
1618 let const_idx = const_idx as u32;
1620 Some((
1621 rhs_span
1622 .with_lo(rhs_span.lo() + BytePos(const_idx))
1623 .with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32)),
1624 "mut".to_owned(),
1625 ))
1626 }
1627 _ => None,
1628 };
1629
1630 if let Some((span, suggestion)) = sugg {
1631 return Some(AmpMutSugg::Expr { span, suggestion });
1632 }
1633 }
1634
1635 Some(AmpMutSugg::ChangeBinding)
1636}
1637
1638fn is_closure_like(ty: Ty<'_>) -> bool {
1640 ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure()
1641}
1642
1643fn get_mut_span_in_struct_field<'tcx>(
1651 tcx: TyCtxt<'tcx>,
1652 ty: Ty<'tcx>,
1653 field: FieldIdx,
1654) -> Option<Span> {
1655 if let ty::Ref(_, ty, _) = ty.kind()
1657 && let ty::Adt(def, _) = ty.kind()
1658 && let field = def.all_fields().nth(field.index())?
1659 && let hir::Node::Field(field) = tcx.hir_node_by_def_id(field.did.as_local()?)
1662 && let hir::TyKind::Ref(lt, hir::MutTy { mutbl: hir::Mutability::Not, ty }) = field.ty.kind
1663 {
1664 return Some(lt.ident.span.between(ty.span));
1665 }
1666
1667 None
1668}
1669
1670fn suggest_ref_mut(tcx: TyCtxt<'_>, span: Span) -> Option<Span> {
1672 let pattern_str = tcx.sess.source_map().span_to_snippet(span).ok()?;
1673 if let Some(rest) = pattern_str.strip_prefix("ref")
1674 && rest.starts_with(rustc_lexer::is_whitespace)
1675 {
1676 let span = span.with_lo(span.lo() + BytePos(4)).shrink_to_lo();
1677 Some(span)
1678 } else {
1679 None
1680 }
1681}