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