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