rustc_borrowck/diagnostics/
mutability_errors.rs

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
36/// Finds all statements that assign directly to local (i.e., X = ...) and returns their
37/// locations.
38fn 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 the place is immutable then:
111                //
112                // - Either we deref an immutable ref to get to our final place.
113                //    - We don't capture derefs of raw ptrs
114                // - Or the final place is immut because the root variable of the capture
115                //   isn't marked mut and we should suggest that to the user.
116                if imm_borrow_derefed {
117                    // If we deref an immutable ref then the suggestion here doesn't help.
118                    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                    let LocalInfo::StaticRef { def_id, .. } = *local_info else {
146                        bug!("is_ref_to_static return true, but not ref to static?");
147                    };
148                    let static_name = &self.infcx.tcx.item_name(def_id);
149                    reason = format!(", as `{static_name}` is an immutable static item");
150                }
151            }
152            PlaceRef { local, projection: [proj_base @ .., ProjectionElem::Deref] } => {
153                if local == ty::CAPTURE_STRUCT_LOCAL
154                    && proj_base.is_empty()
155                    && !self.upvars.is_empty()
156                {
157                    item_msg = access_place_desc;
158                    debug_assert!(self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_ref());
159                    debug_assert!(is_closure_like(the_place_err.ty(self.body, self.infcx.tcx).ty));
160
161                    reason = if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
162                        ", as it is a captured variable in a `Fn` closure".to_string()
163                    } else {
164                        ", as `Fn` closures cannot mutate their captured variables".to_string()
165                    }
166                } else {
167                    let source =
168                        self.borrowed_content_source(PlaceRef { local, projection: proj_base });
169                    let pointer_type = source.describe_for_immutable_place(self.infcx.tcx);
170                    opt_source = Some(source);
171                    if let Some(desc) = self.describe_place(access_place.as_ref()) {
172                        item_msg = format!("`{desc}`");
173                        reason = match error_access {
174                            AccessKind::Mutate => format!(", which is behind {pointer_type}"),
175                            AccessKind::MutableBorrow => {
176                                format!(", as it is behind {pointer_type}")
177                            }
178                        }
179                    } else {
180                        item_msg = format!("data in {pointer_type}");
181                        reason = String::new();
182                    }
183                }
184            }
185
186            PlaceRef {
187                local: _,
188                projection:
189                    [
190                        ..,
191                        ProjectionElem::Index(_)
192                        | ProjectionElem::ConstantIndex { .. }
193                        | ProjectionElem::OpaqueCast { .. }
194                        | ProjectionElem::Subslice { .. }
195                        | ProjectionElem::Downcast(..)
196                        | ProjectionElem::UnwrapUnsafeBinder(_),
197                    ],
198            } => bug!("Unexpected immutable place."),
199        }
200
201        debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
202
203        // `act` and `acted_on` are strings that let us abstract over
204        // the verbs used in some diagnostic messages.
205        let act;
206        let acted_on;
207        let mut suggest = true;
208        let mut mut_error = None;
209        let mut count = 1;
210
211        let span = match error_access {
212            AccessKind::Mutate => {
213                err = self.cannot_assign(span, &(item_msg + &reason));
214                act = "assign";
215                acted_on = "written to";
216                span
217            }
218            AccessKind::MutableBorrow => {
219                act = "borrow as mutable";
220                acted_on = "borrowed as mutable";
221
222                let borrow_spans = self.borrow_spans(span, location);
223                let borrow_span = borrow_spans.args_or_use();
224                match the_place_err {
225                    PlaceRef { local, projection: [] }
226                        if self.body.local_decls[local].can_be_made_mutable() =>
227                    {
228                        let span = self.body.local_decls[local].source_info.span;
229                        mut_error = Some(span);
230                        if let Some((buffered_err, c)) = self.get_buffered_mut_error(span) {
231                            // We've encountered a second (or more) attempt to mutably borrow an
232                            // immutable binding, so the likely problem is with the binding
233                            // declaration, not the use. We collect these in a single diagnostic
234                            // and make the binding the primary span of the error.
235                            err = buffered_err;
236                            count = c + 1;
237                            if count == 2 {
238                                err.replace_span_with(span, false);
239                                err.span_label(span, "not mutable");
240                            }
241                            suggest = false;
242                        } else {
243                            err = self.cannot_borrow_path_as_mutable_because(
244                                borrow_span,
245                                &item_msg,
246                                &reason,
247                            );
248                        }
249                    }
250                    _ => {
251                        err = self.cannot_borrow_path_as_mutable_because(
252                            borrow_span,
253                            &item_msg,
254                            &reason,
255                        );
256                    }
257                }
258                if suggest {
259                    borrow_spans.var_subdiag(
260                        &mut err,
261                        Some(mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }),
262                        |_kind, var_span| {
263                            let place = self.describe_any_place(access_place.as_ref());
264                            session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure {
265                                place,
266                                var_span,
267                            }
268                        },
269                    );
270                }
271                borrow_span
272            }
273        };
274
275        debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
276
277        match the_place_err {
278            // Suggest making an existing shared borrow in a struct definition a mutable borrow.
279            //
280            // This is applicable when we have a deref of a field access to a deref of a local -
281            // something like `*((*_1).0`. The local that we get will be a reference to the
282            // struct we've got a field access of (it must be a reference since there's a deref
283            // after the field access).
284            PlaceRef {
285                local,
286                projection:
287                    [
288                        proj_base @ ..,
289                        ProjectionElem::Deref,
290                        ProjectionElem::Field(field, _),
291                        ProjectionElem::Deref,
292                    ],
293            } => {
294                err.span_label(span, format!("cannot {act}"));
295
296                let place = Place::ty_from(local, proj_base, self.body, self.infcx.tcx);
297                if let Some(span) = get_mut_span_in_struct_field(self.infcx.tcx, place.ty, *field) {
298                    err.span_suggestion_verbose(
299                        span,
300                        "consider changing this to be mutable",
301                        " mut ",
302                        Applicability::MaybeIncorrect,
303                    );
304                }
305            }
306
307            // Suggest removing a `&mut` from the use of a mutable reference.
308            PlaceRef { local, projection: [] }
309                if self
310                    .body
311                    .local_decls
312                    .get(local)
313                    .is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_name(local))) =>
314            {
315                let decl = &self.body.local_decls[local];
316                err.span_label(span, format!("cannot {act}"));
317                if let Some(mir::Statement {
318                    source_info,
319                    kind:
320                        mir::StatementKind::Assign(box (
321                            _,
322                            mir::Rvalue::Ref(
323                                _,
324                                mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
325                                _,
326                            ),
327                        )),
328                    ..
329                }) = &self.body[location.block].statements.get(location.statement_index)
330                {
331                    match *decl.local_info() {
332                        LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
333                            binding_mode: BindingMode(ByRef::No, Mutability::Not),
334                            opt_ty_info: Some(sp),
335                            pat_span,
336                            ..
337                        })) => {
338                            if suggest {
339                                err.span_note(sp, "the binding is already a mutable borrow");
340                                err.span_suggestion_verbose(
341                                    pat_span.shrink_to_lo(),
342                                    "consider making the binding mutable if you need to reborrow \
343                                     multiple times",
344                                    "mut ".to_string(),
345                                    Applicability::MaybeIncorrect,
346                                );
347                            }
348                        }
349                        _ => {
350                            err.span_note(
351                                decl.source_info.span,
352                                "the binding is already a mutable borrow",
353                            );
354                        }
355                    }
356                    if let Ok(snippet) =
357                        self.infcx.tcx.sess.source_map().span_to_snippet(source_info.span)
358                    {
359                        if snippet.starts_with("&mut ") {
360                            // We don't have access to the HIR to get accurate spans, but we can
361                            // give a best effort structured suggestion.
362                            err.span_suggestion_verbose(
363                                source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
364                                "if there is only one mutable reborrow, remove the `&mut`",
365                                "",
366                                Applicability::MaybeIncorrect,
367                            );
368                        } else {
369                            // This can occur with things like `(&mut self).foo()`.
370                            err.span_help(source_info.span, "try removing `&mut` here");
371                        }
372                    } else {
373                        err.span_help(source_info.span, "try removing `&mut` here");
374                    }
375                } else if decl.mutability.is_not() {
376                    if matches!(
377                        decl.local_info(),
378                        LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::RefMut))
379                    ) {
380                        err.note(
381                            "as `Self` may be unsized, this call attempts to take `&mut &mut self`",
382                        );
383                        err.note("however, `&mut self` expands to `self: &mut Self`, therefore `self` cannot be borrowed mutably");
384                    } else {
385                        err.span_suggestion_verbose(
386                            decl.source_info.span.shrink_to_lo(),
387                            "consider making the binding mutable",
388                            "mut ",
389                            Applicability::MachineApplicable,
390                        );
391                    };
392                }
393            }
394
395            // We want to suggest users use `let mut` for local (user
396            // variable) mutations...
397            PlaceRef { local, projection: [] }
398                if self.body.local_decls[local].can_be_made_mutable() =>
399            {
400                // ... but it doesn't make sense to suggest it on
401                // variables that are `ref x`, `ref mut x`, `&self`,
402                // or `&mut self` (such variables are simply not
403                // mutable).
404                let local_decl = &self.body.local_decls[local];
405                assert_eq!(local_decl.mutability, Mutability::Not);
406
407                if count < 10 {
408                    err.span_label(span, format!("cannot {act}"));
409                }
410                if suggest {
411                    self.construct_mut_suggestion_for_local_binding_patterns(&mut err, local);
412                    let tcx = self.infcx.tcx;
413                    if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() {
414                        self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
415                    }
416                }
417            }
418
419            // Also suggest adding mut for upvars.
420            PlaceRef {
421                local,
422                projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
423            } => {
424                debug_assert!(is_closure_like(
425                    Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
426                ));
427
428                let captured_place = self.upvars[upvar_index.index()];
429
430                err.span_label(span, format!("cannot {act}"));
431
432                let upvar_hir_id = captured_place.get_root_variable();
433
434                if let Node::Pat(pat) = self.infcx.tcx.hir_node(upvar_hir_id)
435                    && let hir::PatKind::Binding(hir::BindingMode::NONE, _, upvar_ident, _) =
436                        pat.kind
437                {
438                    if upvar_ident.name == kw::SelfLower {
439                        for (_, node) in self.infcx.tcx.hir_parent_iter(upvar_hir_id) {
440                            if let Some(fn_decl) = node.fn_decl() {
441                                if !matches!(
442                                    fn_decl.implicit_self,
443                                    hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut
444                                ) {
445                                    err.span_suggestion_verbose(
446                                        upvar_ident.span.shrink_to_lo(),
447                                        "consider changing this to be mutable",
448                                        "mut ",
449                                        Applicability::MachineApplicable,
450                                    );
451                                    break;
452                                }
453                            }
454                        }
455                    } else {
456                        err.span_suggestion_verbose(
457                            upvar_ident.span.shrink_to_lo(),
458                            "consider changing this to be mutable",
459                            "mut ",
460                            Applicability::MachineApplicable,
461                        );
462                    }
463                }
464
465                let tcx = self.infcx.tcx;
466                if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
467                    && let ty::Closure(id, _) = *ty.kind()
468                {
469                    self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
470                }
471            }
472
473            // Complete hack to approximate old AST-borrowck diagnostic: if the span starts
474            // with a mutable borrow of a local variable, then just suggest the user remove it.
475            PlaceRef { local: _, projection: [] }
476                if self
477                    .infcx
478                    .tcx
479                    .sess
480                    .source_map()
481                    .span_to_snippet(span)
482                    .is_ok_and(|snippet| snippet.starts_with("&mut ")) =>
483            {
484                err.span_label(span, format!("cannot {act}"));
485                err.span_suggestion_verbose(
486                    span.with_hi(span.lo() + BytePos(5)),
487                    "try removing `&mut` here",
488                    "",
489                    Applicability::MaybeIncorrect,
490                );
491            }
492
493            PlaceRef { local, projection: [ProjectionElem::Deref] }
494                if self.body.local_decls[local].is_ref_for_guard() =>
495            {
496                err.span_label(span, format!("cannot {act}"));
497                err.note(
498                    "variables bound in patterns are immutable until the end of the pattern guard",
499                );
500            }
501
502            // We want to point out when a `&` can be readily replaced
503            // with an `&mut`.
504            //
505            // FIXME: can this case be generalized to work for an
506            // arbitrary base for the projection?
507            PlaceRef { local, projection: [ProjectionElem::Deref] }
508                if self.body.local_decls[local].is_user_variable() =>
509            {
510                let local_decl = &self.body.local_decls[local];
511
512                let (pointer_sigil, pointer_desc) =
513                    if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
514
515                match self.local_name(local) {
516                    Some(name) if !local_decl.from_compiler_desugaring() => {
517                        err.span_label(
518                            span,
519                            format!(
520                                "`{name}` is a `{pointer_sigil}` {pointer_desc}, so it cannot be \
521                                 {acted_on}",
522                            ),
523                        );
524
525                        self.suggest_using_iter_mut(&mut err);
526                        self.suggest_make_local_mut(&mut err, local, name);
527                    }
528                    _ => {
529                        err.span_label(
530                            span,
531                            format!("cannot {act} through `{pointer_sigil}` {pointer_desc}"),
532                        );
533                    }
534                }
535            }
536
537            PlaceRef { local, projection: [ProjectionElem::Deref] }
538                if local == ty::CAPTURE_STRUCT_LOCAL && !self.upvars.is_empty() =>
539            {
540                self.point_at_binding_outside_closure(&mut err, local, access_place);
541                self.expected_fn_found_fn_mut_call(&mut err, span, act);
542            }
543
544            PlaceRef { local, projection: [.., ProjectionElem::Deref] } => {
545                err.span_label(span, format!("cannot {act}"));
546
547                match opt_source {
548                    Some(BorrowedContentSource::OverloadedDeref(ty)) => {
549                        err.help(format!(
550                            "trait `DerefMut` is required to modify through a dereference, \
551                             but it is not implemented for `{ty}`",
552                        ));
553                    }
554                    Some(BorrowedContentSource::OverloadedIndex(ty)) => {
555                        err.help(format!(
556                            "trait `IndexMut` is required to modify indexed content, \
557                             but it is not implemented for `{ty}`",
558                        ));
559                        self.suggest_map_index_mut_alternatives(ty, &mut err, span);
560                    }
561                    _ => {
562                        let local = &self.body.local_decls[local];
563                        match local.local_info() {
564                            LocalInfo::StaticRef { def_id, .. } => {
565                                let span = self.infcx.tcx.def_span(def_id);
566                                err.span_label(span, format!("this `static` cannot be {acted_on}"));
567                            }
568                            LocalInfo::ConstRef { def_id } => {
569                                let span = self.infcx.tcx.def_span(def_id);
570                                err.span_label(span, format!("this `const` cannot be {acted_on}"));
571                            }
572                            LocalInfo::BlockTailTemp(_) | LocalInfo::Boring
573                                if !local.source_info.span.overlaps(span) =>
574                            {
575                                err.span_label(
576                                    local.source_info.span,
577                                    format!("this cannot be {acted_on}"),
578                                );
579                            }
580                            _ => {}
581                        }
582                    }
583                }
584            }
585
586            PlaceRef { local, .. } => {
587                let local = &self.body.local_decls[local];
588                if !local.source_info.span.overlaps(span) {
589                    err.span_label(local.source_info.span, format!("this cannot be {acted_on}"));
590                }
591                err.span_label(span, format!("cannot {act}"));
592            }
593        }
594
595        if let Some(span) = mut_error {
596            self.buffer_mut_error(span, err, count);
597        } else {
598            self.buffer_error(err);
599        }
600    }
601
602    /// Suggest `map[k] = v` => `map.insert(k, v)` and the like.
603    fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'infcx>, span: Span) {
604        let Some(adt) = ty.ty_adt_def() else { return };
605        let did = adt.did();
606        if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did)
607            || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did)
608        {
609            /// Walks through the HIR, looking for the corresponding span for this error.
610            /// When it finds it, see if it corresponds to assignment operator whose LHS
611            /// is an index expr.
612            struct SuggestIndexOperatorAlternativeVisitor<'a, 'infcx, 'tcx> {
613                assign_span: Span,
614                err: &'a mut Diag<'infcx>,
615                ty: Ty<'tcx>,
616                suggested: bool,
617            }
618            impl<'a, 'infcx, 'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'a, 'infcx, 'tcx> {
619                fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
620                    hir::intravisit::walk_stmt(self, stmt);
621                    let expr = match stmt.kind {
622                        hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr,
623                        hir::StmtKind::Let(hir::LetStmt { init: Some(expr), .. }) => expr,
624                        _ => {
625                            return;
626                        }
627                    };
628                    if let hir::ExprKind::Assign(place, rv, _sp) = expr.kind
629                        && let hir::ExprKind::Index(val, index, _) = place.kind
630                        && (expr.span == self.assign_span || place.span == self.assign_span)
631                    {
632                        // val[index] = rv;
633                        // ---------- place
634                        self.err.multipart_suggestions(
635                            format!(
636                                "use `.insert()` to insert a value into a `{}`, `.get_mut()` \
637                                to modify it, or the entry API for more flexibility",
638                                self.ty,
639                            ),
640                            vec![
641                                vec![
642                                    // val.insert(index, rv);
643                                    (
644                                        val.span.shrink_to_hi().with_hi(index.span.lo()),
645                                        ".insert(".to_string(),
646                                    ),
647                                    (
648                                        index.span.shrink_to_hi().with_hi(rv.span.lo()),
649                                        ", ".to_string(),
650                                    ),
651                                    (rv.span.shrink_to_hi(), ")".to_string()),
652                                ],
653                                vec![
654                                    // if let Some(v) = val.get_mut(index) { *v = rv; }
655                                    (val.span.shrink_to_lo(), "if let Some(val) = ".to_string()),
656                                    (
657                                        val.span.shrink_to_hi().with_hi(index.span.lo()),
658                                        ".get_mut(".to_string(),
659                                    ),
660                                    (
661                                        index.span.shrink_to_hi().with_hi(place.span.hi()),
662                                        ") { *val".to_string(),
663                                    ),
664                                    (rv.span.shrink_to_hi(), "; }".to_string()),
665                                ],
666                                vec![
667                                    // let x = val.entry(index).or_insert(rv);
668                                    (val.span.shrink_to_lo(), "let val = ".to_string()),
669                                    (
670                                        val.span.shrink_to_hi().with_hi(index.span.lo()),
671                                        ".entry(".to_string(),
672                                    ),
673                                    (
674                                        index.span.shrink_to_hi().with_hi(rv.span.lo()),
675                                        ").or_insert(".to_string(),
676                                    ),
677                                    (rv.span.shrink_to_hi(), ")".to_string()),
678                                ],
679                            ],
680                            Applicability::MachineApplicable,
681                        );
682                        self.suggested = true;
683                    } else if let hir::ExprKind::MethodCall(_path, receiver, _, sp) = expr.kind
684                        && let hir::ExprKind::Index(val, index, _) = receiver.kind
685                        && receiver.span == self.assign_span
686                    {
687                        // val[index].path(args..);
688                        self.err.multipart_suggestion(
689                            format!("to modify a `{}` use `.get_mut()`", self.ty),
690                            vec![
691                                (val.span.shrink_to_lo(), "if let Some(val) = ".to_string()),
692                                (
693                                    val.span.shrink_to_hi().with_hi(index.span.lo()),
694                                    ".get_mut(".to_string(),
695                                ),
696                                (
697                                    index.span.shrink_to_hi().with_hi(receiver.span.hi()),
698                                    ") { val".to_string(),
699                                ),
700                                (sp.shrink_to_hi(), "; }".to_string()),
701                            ],
702                            Applicability::MachineApplicable,
703                        );
704                        self.suggested = true;
705                    }
706                }
707            }
708            let def_id = self.body.source.def_id();
709            let Some(local_def_id) = def_id.as_local() else { return };
710            let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id) else { return };
711
712            let mut v = SuggestIndexOperatorAlternativeVisitor {
713                assign_span: span,
714                err,
715                ty,
716                suggested: false,
717            };
718            v.visit_body(&body);
719            if !v.suggested {
720                err.help(format!(
721                    "to modify a `{ty}`, use `.get_mut()`, `.insert()` or the entry API",
722                ));
723            }
724        }
725    }
726
727    /// User cannot make signature of a trait mutable without changing the
728    /// trait. So we find if this error belongs to a trait and if so we move
729    /// suggestion to the trait or disable it if it is out of scope of this crate
730    ///
731    /// The returned values are:
732    ///  - is the current item an assoc `fn` of an impl that corresponds to a trait def? if so, we
733    ///    have to suggest changing both the impl `fn` arg and the trait `fn` arg
734    ///  - is the trait from the local crate? If not, we can't suggest changing signatures
735    ///  - `Span` of the argument in the trait definition
736    fn is_error_in_trait(&self, local: Local) -> (bool, bool, Option<Span>) {
737        let tcx = self.infcx.tcx;
738        if self.body.local_kind(local) != LocalKind::Arg {
739            return (false, false, None);
740        }
741        let my_def = self.body.source.def_id();
742        let Some(td) = tcx.trait_impl_of_assoc(my_def).map(|id| self.infcx.tcx.impl_trait_id(id))
743        else {
744            return (false, false, None);
745        };
746
747        let implemented_trait_item = self.infcx.tcx.trait_item_of(my_def);
748
749        (
750            true,
751            td.is_local(),
752            implemented_trait_item.and_then(|f_in_trait| {
753                let f_in_trait = f_in_trait.as_local()?;
754                if let Node::TraitItem(ti) = self.infcx.tcx.hir_node_by_def_id(f_in_trait)
755                    && let hir::TraitItemKind::Fn(sig, _) = ti.kind
756                    && let Some(ty) = sig.decl.inputs.get(local.index() - 1)
757                    && let hir::TyKind::Ref(_, mut_ty) = ty.kind
758                    && let hir::Mutability::Not = mut_ty.mutbl
759                    && sig.decl.implicit_self.has_implicit_self()
760                {
761                    Some(ty.span)
762                } else {
763                    None
764                }
765            }),
766        )
767    }
768
769    fn construct_mut_suggestion_for_local_binding_patterns(
770        &self,
771        err: &mut Diag<'_>,
772        local: Local,
773    ) {
774        let local_decl = &self.body.local_decls[local];
775        debug!("local_decl: {:?}", local_decl);
776        let pat_span = match *local_decl.local_info() {
777            LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
778                binding_mode: BindingMode(ByRef::No, Mutability::Not),
779                opt_ty_info: _,
780                opt_match_place: _,
781                pat_span,
782                introductions: _,
783            })) => pat_span,
784            _ => local_decl.source_info.span,
785        };
786
787        // With ref-binding patterns, the mutability suggestion has to apply to
788        // the binding, not the reference (which would be a type error):
789        //
790        // `let &b = a;` -> `let &(mut b) = a;`
791        // or
792        // `fn foo(&x: &i32)` -> `fn foo(&(mut x): &i32)`
793        let def_id = self.body.source.def_id();
794        if let Some(local_def_id) = def_id.as_local()
795            && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
796            && let Some(hir_id) = (BindingFinder { span: pat_span }).visit_body(&body).break_value()
797            && let node = self.infcx.tcx.hir_node(hir_id)
798            && let hir::Node::LetStmt(hir::LetStmt {
799                pat: hir::Pat { kind: hir::PatKind::Ref(_, _, _), .. },
800                ..
801            })
802            | hir::Node::Param(Param {
803                pat: hir::Pat { kind: hir::PatKind::Ref(_, _, _), .. },
804                ..
805            }) = node
806        {
807            err.multipart_suggestion(
808                "consider changing this to be mutable",
809                vec![
810                    (pat_span.until(local_decl.source_info.span), "&(mut ".to_string()),
811                    (
812                        local_decl.source_info.span.shrink_to_hi().with_hi(pat_span.hi()),
813                        ")".to_string(),
814                    ),
815                ],
816                Applicability::MachineApplicable,
817            );
818            return;
819        }
820
821        err.span_suggestion_verbose(
822            local_decl.source_info.span.shrink_to_lo(),
823            "consider changing this to be mutable",
824            "mut ",
825            Applicability::MachineApplicable,
826        );
827    }
828
829    // Point to span of upvar making closure call that requires a mutable borrow
830    fn show_mutating_upvar(
831        &self,
832        tcx: TyCtxt<'_>,
833        closure_local_def_id: hir::def_id::LocalDefId,
834        the_place_err: PlaceRef<'tcx>,
835        err: &mut Diag<'_>,
836    ) {
837        let tables = tcx.typeck(closure_local_def_id);
838        if let Some((span, closure_kind_origin)) = tcx.closure_kind_origin(closure_local_def_id) {
839            let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base {
840                let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin);
841                let root_hir_id = upvar_id.var_path.hir_id;
842                // We have an origin for this closure kind starting at this root variable so it's
843                // safe to unwrap here.
844                let captured_places =
845                    tables.closure_min_captures[&closure_local_def_id].get(&root_hir_id).unwrap();
846
847                let origin_projection = closure_kind_origin
848                    .projections
849                    .iter()
850                    .map(|proj| proj.kind)
851                    .collect::<Vec<_>>();
852                let mut capture_reason = String::new();
853                for captured_place in captured_places {
854                    let captured_place_kinds = captured_place
855                        .place
856                        .projections
857                        .iter()
858                        .map(|proj| proj.kind)
859                        .collect::<Vec<_>>();
860                    if rustc_middle::ty::is_ancestor_or_same_capture(
861                        &captured_place_kinds,
862                        &origin_projection,
863                    ) {
864                        match captured_place.info.capture_kind {
865                            ty::UpvarCapture::ByRef(
866                                ty::BorrowKind::Mutable | ty::BorrowKind::UniqueImmutable,
867                            ) => {
868                                capture_reason = format!("mutable borrow of `{upvar}`");
869                            }
870                            ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {
871                                capture_reason = format!("possible mutation of `{upvar}`");
872                            }
873                            _ => bug!("upvar `{upvar}` borrowed, but not mutably"),
874                        }
875                        break;
876                    }
877                }
878                if capture_reason.is_empty() {
879                    bug!("upvar `{upvar}` borrowed, but cannot find reason");
880                }
881                capture_reason
882            } else {
883                bug!("not an upvar")
884            };
885            // Sometimes we deliberately don't store the name of a place when coming from a macro in
886            // another crate. We generally want to limit those diagnostics a little, to hide
887            // implementation details (such as those from pin!() or format!()). In that case show a
888            // slightly different error message, or none at all if something else happened. In other
889            // cases the message is likely not useful.
890            if let Some(place_name) = self.describe_place(the_place_err) {
891                err.span_label(
892                    *span,
893                    format!("calling `{place_name}` requires mutable binding due to {reason}"),
894                );
895            } else if span.from_expansion() {
896                err.span_label(
897                    *span,
898                    format!("a call in this macro requires a mutable binding due to {reason}",),
899                );
900            }
901        }
902    }
903
904    // Attempt to search similar mutable associated items for suggestion.
905    // In the future, attempt in all path but initially for RHS of for_loop
906    fn suggest_similar_mut_method_for_for_loop(&self, err: &mut Diag<'_>, span: Span) {
907        use hir::ExprKind::{AddrOf, Block, Call, MethodCall};
908        use hir::{BorrowKind, Expr};
909
910        let tcx = self.infcx.tcx;
911        struct Finder {
912            span: Span,
913        }
914
915        impl<'tcx> Visitor<'tcx> for Finder {
916            type Result = ControlFlow<&'tcx Expr<'tcx>>;
917            fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) -> Self::Result {
918                if e.span == self.span {
919                    ControlFlow::Break(e)
920                } else {
921                    hir::intravisit::walk_expr(self, e)
922                }
923            }
924        }
925        if let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id())
926            && let Block(block, _) = body.value.kind
927        {
928            // `span` corresponds to the expression being iterated, find the `for`-loop desugared
929            // expression with that span in order to identify potential fixes when encountering a
930            // read-only iterator that should be mutable.
931            if let ControlFlow::Break(expr) = (Finder { span }).visit_block(block)
932                && let Call(_, [expr]) = expr.kind
933            {
934                match expr.kind {
935                    MethodCall(path_segment, _, _, span) => {
936                        // We have `for _ in iter.read_only_iter()`, try to
937                        // suggest `for _ in iter.mutable_iter()` instead.
938                        let opt_suggestions = tcx
939                            .typeck(path_segment.hir_id.owner.def_id)
940                            .type_dependent_def_id(expr.hir_id)
941                            .and_then(|def_id| tcx.impl_of_assoc(def_id))
942                            .map(|def_id| tcx.associated_items(def_id))
943                            .map(|assoc_items| {
944                                assoc_items
945                                    .in_definition_order()
946                                    .map(|assoc_item_def| assoc_item_def.ident(tcx))
947                                    .filter(|&ident| {
948                                        let original_method_ident = path_segment.ident;
949                                        original_method_ident != ident
950                                            && ident.as_str().starts_with(
951                                                &original_method_ident.name.to_string(),
952                                            )
953                                    })
954                                    .map(|ident| format!("{ident}()"))
955                                    .peekable()
956                            });
957
958                        if let Some(mut suggestions) = opt_suggestions
959                            && suggestions.peek().is_some()
960                        {
961                            err.span_suggestions(
962                                span,
963                                "use mutable method",
964                                suggestions,
965                                Applicability::MaybeIncorrect,
966                            );
967                        }
968                    }
969                    AddrOf(BorrowKind::Ref, Mutability::Not, expr) => {
970                        // We have `for _ in &i`, suggest `for _ in &mut i`.
971                        err.span_suggestion_verbose(
972                            expr.span.shrink_to_lo(),
973                            "use a mutable iterator instead",
974                            "mut ",
975                            Applicability::MachineApplicable,
976                        );
977                    }
978                    _ => {}
979                }
980            }
981        }
982    }
983
984    /// When modifying a binding from inside of an `Fn` closure, point at the binding definition.
985    fn point_at_binding_outside_closure(
986        &self,
987        err: &mut Diag<'_>,
988        local: Local,
989        access_place: Place<'tcx>,
990    ) {
991        let place = access_place.as_ref();
992        for (index, elem) in place.projection.into_iter().enumerate() {
993            if let ProjectionElem::Deref = elem {
994                if index == 0 {
995                    if self.body.local_decls[local].is_ref_for_guard() {
996                        continue;
997                    }
998                    if let LocalInfo::StaticRef { .. } = *self.body.local_decls[local].local_info()
999                    {
1000                        continue;
1001                    }
1002                }
1003                if let Some(field) = self.is_upvar_field_projection(PlaceRef {
1004                    local,
1005                    projection: place.projection.split_at(index + 1).0,
1006                }) {
1007                    let var_index = field.index();
1008                    let upvar = self.upvars[var_index];
1009                    if let Some(hir_id) = upvar.info.capture_kind_expr_id {
1010                        let node = self.infcx.tcx.hir_node(hir_id);
1011                        if let hir::Node::Expr(expr) = node
1012                            && let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
1013                            && let hir::def::Res::Local(hir_id) = path.res
1014                            && let hir::Node::Pat(pat) = self.infcx.tcx.hir_node(hir_id)
1015                        {
1016                            let name = upvar.to_string(self.infcx.tcx);
1017                            err.span_label(
1018                                pat.span,
1019                                format!("`{name}` declared here, outside the closure"),
1020                            );
1021                            break;
1022                        }
1023                    }
1024                }
1025            }
1026        }
1027    }
1028    /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
1029    fn expected_fn_found_fn_mut_call(&self, err: &mut Diag<'_>, sp: Span, act: &str) {
1030        err.span_label(sp, format!("cannot {act}"));
1031
1032        let tcx = self.infcx.tcx;
1033        let closure_id = self.mir_hir_id();
1034        let closure_span = tcx.def_span(self.mir_def_id());
1035        let fn_call_id = tcx.parent_hir_id(closure_id);
1036        let node = tcx.hir_node(fn_call_id);
1037        let def_id = tcx.hir_enclosing_body_owner(fn_call_id);
1038        let mut look_at_return = true;
1039
1040        err.span_label(closure_span, "in this closure");
1041        // If the HIR node is a function or method call, get the DefId
1042        // of the callee function or method, the span, and args of the call expr
1043        let get_call_details = || {
1044            let hir::Node::Expr(hir::Expr { hir_id, kind, .. }) = node else {
1045                return None;
1046            };
1047
1048            let typeck_results = tcx.typeck(def_id);
1049
1050            match kind {
1051                hir::ExprKind::Call(expr, args) => {
1052                    if let Some(ty::FnDef(def_id, _)) =
1053                        typeck_results.node_type_opt(expr.hir_id).as_ref().map(|ty| ty.kind())
1054                    {
1055                        Some((*def_id, expr.span, *args))
1056                    } else {
1057                        None
1058                    }
1059                }
1060                hir::ExprKind::MethodCall(_, _, args, span) => typeck_results
1061                    .type_dependent_def_id(*hir_id)
1062                    .map(|def_id| (def_id, *span, *args)),
1063                _ => None,
1064            }
1065        };
1066
1067        // If we can detect the expression to be a function or method call where the closure was
1068        // an argument, we point at the function or method definition argument...
1069        if let Some((callee_def_id, call_span, call_args)) = get_call_details() {
1070            let arg_pos = call_args
1071                .iter()
1072                .enumerate()
1073                .filter(|(_, arg)| arg.hir_id == closure_id)
1074                .map(|(pos, _)| pos)
1075                .next();
1076
1077            let arg = match tcx.hir_get_if_local(callee_def_id) {
1078                Some(
1079                    hir::Node::Item(hir::Item {
1080                        kind: hir::ItemKind::Fn { ident, sig, .. }, ..
1081                    })
1082                    | hir::Node::TraitItem(hir::TraitItem {
1083                        ident,
1084                        kind: hir::TraitItemKind::Fn(sig, _),
1085                        ..
1086                    })
1087                    | hir::Node::ImplItem(hir::ImplItem {
1088                        ident,
1089                        kind: hir::ImplItemKind::Fn(sig, _),
1090                        ..
1091                    }),
1092                ) => Some(
1093                    arg_pos
1094                        .and_then(|pos| {
1095                            sig.decl.inputs.get(
1096                                pos + if sig.decl.implicit_self.has_implicit_self() {
1097                                    1
1098                                } else {
1099                                    0
1100                                },
1101                            )
1102                        })
1103                        .map(|arg| arg.span)
1104                        .unwrap_or(ident.span),
1105                ),
1106                _ => None,
1107            };
1108            if let Some(span) = arg {
1109                err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
1110                err.span_label(call_span, "expects `Fn` instead of `FnMut`");
1111                look_at_return = false;
1112            }
1113        }
1114
1115        if look_at_return && tcx.hir_get_fn_id_for_return_block(closure_id).is_some() {
1116            // ...otherwise we are probably in the tail expression of the function, point at the
1117            // return type.
1118            match tcx.hir_node_by_def_id(tcx.hir_get_parent_item(fn_call_id).def_id) {
1119                hir::Node::Item(hir::Item {
1120                    kind: hir::ItemKind::Fn { ident, sig, .. }, ..
1121                })
1122                | hir::Node::TraitItem(hir::TraitItem {
1123                    ident,
1124                    kind: hir::TraitItemKind::Fn(sig, _),
1125                    ..
1126                })
1127                | hir::Node::ImplItem(hir::ImplItem {
1128                    ident,
1129                    kind: hir::ImplItemKind::Fn(sig, _),
1130                    ..
1131                }) => {
1132                    err.span_label(ident.span, "");
1133                    err.span_label(
1134                        sig.decl.output.span(),
1135                        "change this to return `FnMut` instead of `Fn`",
1136                    );
1137                }
1138                _ => {}
1139            }
1140        }
1141    }
1142
1143    fn suggest_using_iter_mut(&self, err: &mut Diag<'_>) {
1144        let source = self.body.source;
1145        if let InstanceKind::Item(def_id) = source.instance
1146            && let Some(Node::Expr(hir::Expr { hir_id, kind, .. })) =
1147                self.infcx.tcx.hir_get_if_local(def_id)
1148            && let ExprKind::Closure(hir::Closure { kind: hir::ClosureKind::Closure, .. }) = kind
1149            && let Node::Expr(expr) = self.infcx.tcx.parent_hir_node(*hir_id)
1150        {
1151            let mut cur_expr = expr;
1152            while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
1153                if path_segment.ident.name == sym::iter {
1154                    // Check that the type has an `iter_mut` method.
1155                    let res = self
1156                        .infcx
1157                        .tcx
1158                        .typeck(path_segment.hir_id.owner.def_id)
1159                        .type_dependent_def_id(cur_expr.hir_id)
1160                        .and_then(|def_id| self.infcx.tcx.impl_of_assoc(def_id))
1161                        .map(|def_id| self.infcx.tcx.associated_items(def_id))
1162                        .map(|assoc_items| {
1163                            assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable()
1164                        });
1165
1166                    if let Some(mut res) = res
1167                        && res.peek().is_some()
1168                    {
1169                        err.span_suggestion_verbose(
1170                            path_segment.ident.span,
1171                            "you may want to use `iter_mut` here",
1172                            "iter_mut",
1173                            Applicability::MaybeIncorrect,
1174                        );
1175                    }
1176                    break;
1177                } else {
1178                    cur_expr = recv;
1179                }
1180            }
1181        }
1182    }
1183
1184    fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) {
1185        let local_decl = &self.body.local_decls[local];
1186
1187        let (pointer_sigil, pointer_desc) =
1188            if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
1189
1190        let (is_trait_sig, is_local, local_trait) = self.is_error_in_trait(local);
1191
1192        if is_trait_sig && !is_local {
1193            // Do not suggest changing the signature when the trait comes from another crate.
1194            err.span_label(
1195                local_decl.source_info.span,
1196                format!("this is an immutable {pointer_desc}"),
1197            );
1198            return;
1199        }
1200
1201        // Do not suggest changing type if that is not under user control.
1202        if self.is_closure_arg_with_non_locally_decided_type(local) {
1203            return;
1204        }
1205
1206        let decl_span = local_decl.source_info.span;
1207
1208        let (amp_mut_sugg, local_var_ty_info) = match *local_decl.local_info() {
1209            LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
1210                let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
1211                let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span));
1212                (AmpMutSugg::Type { span, suggestion, additional }, None)
1213            }
1214
1215            LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1216                binding_mode: BindingMode(ByRef::No, _),
1217                opt_ty_info,
1218                ..
1219            })) => {
1220                // Check if the RHS is from desugaring.
1221                let first_assignment = find_assignments(&self.body, local).first().copied();
1222                let first_assignment_stmt = first_assignment
1223                    .and_then(|loc| self.body[loc.block].statements.get(loc.statement_index));
1224                trace!(?first_assignment_stmt);
1225                let opt_assignment_rhs_span =
1226                    first_assignment.map(|loc| self.body.source_info(loc).span);
1227                let mut source_span = opt_assignment_rhs_span;
1228                if let Some(mir::Statement {
1229                    source_info: _,
1230                    kind:
1231                        mir::StatementKind::Assign(box (_, mir::Rvalue::Use(mir::Operand::Copy(place)))),
1232                    ..
1233                }) = first_assignment_stmt
1234                {
1235                    let local_span = self.body.local_decls[place.local].source_info.span;
1236                    // `&self` in async functions have a `desugaring_kind`, but the local we assign
1237                    // it with does not, so use the local_span for our checks later.
1238                    source_span = Some(local_span);
1239                    if let Some(DesugaringKind::ForLoop) = local_span.desugaring_kind() {
1240                        // On for loops, RHS points to the iterator part.
1241                        self.suggest_similar_mut_method_for_for_loop(err, local_span);
1242                        err.span_label(
1243                            local_span,
1244                            format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",),
1245                        );
1246                        return;
1247                    }
1248                }
1249
1250                // Don't create labels for compiler-generated spans or spans not from users' code.
1251                if source_span.is_some_and(|s| {
1252                    s.desugaring_kind().is_some() || self.infcx.tcx.sess.source_map().is_imported(s)
1253                }) {
1254                    return;
1255                }
1256
1257                // This could be because we're in an `async fn`.
1258                if name == kw::SelfLower && opt_ty_info.is_none() {
1259                    let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
1260                    (AmpMutSugg::Type { span, suggestion, additional: None }, None)
1261                } else if let Some(sugg) =
1262                    suggest_ampmut(self.infcx, self.body(), first_assignment_stmt)
1263                {
1264                    (sugg, opt_ty_info)
1265                } else {
1266                    return;
1267                }
1268            }
1269
1270            LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1271                binding_mode: BindingMode(ByRef::Yes(..), _),
1272                ..
1273            })) => {
1274                let pattern_span: Span = local_decl.source_info.span;
1275                let Some(span) = suggest_ref_mut(self.infcx.tcx, pattern_span) else {
1276                    return;
1277                };
1278                (AmpMutSugg::Type { span, suggestion: "mut ".to_owned(), additional: None }, None)
1279            }
1280
1281            _ => unreachable!(),
1282        };
1283
1284        let mut suggest = |suggs: Vec<_>, applicability, extra| {
1285            if suggs.iter().any(|(span, _)| self.infcx.tcx.sess.source_map().is_imported(*span)) {
1286                return;
1287            }
1288
1289            err.multipart_suggestion_verbose(
1290                format!(
1291                    "consider changing this to be a mutable {pointer_desc}{}{extra}",
1292                    if is_trait_sig {
1293                        " in the `impl` method and the `trait` definition"
1294                    } else {
1295                        ""
1296                    }
1297                ),
1298                suggs,
1299                applicability,
1300            );
1301        };
1302
1303        let (mut sugg, add_type_annotation_if_not_exists) = match amp_mut_sugg {
1304            AmpMutSugg::Type { span, suggestion, additional } => {
1305                let mut sugg = vec![(span, suggestion)];
1306                sugg.extend(additional);
1307                suggest(sugg, Applicability::MachineApplicable, "");
1308                return;
1309            }
1310            AmpMutSugg::MapGetMut { span, suggestion } => {
1311                if self.infcx.tcx.sess.source_map().is_imported(span) {
1312                    return;
1313                }
1314                err.multipart_suggestion_verbose(
1315                    "consider using `get_mut`",
1316                    vec![(span, suggestion)],
1317                    Applicability::MaybeIncorrect,
1318                );
1319                return;
1320            }
1321            AmpMutSugg::Expr { span, suggestion } => {
1322                // `Expr` suggestions should change type annotations if they already exist (probably immut),
1323                // but do not add new type annotations.
1324                (vec![(span, suggestion)], false)
1325            }
1326            AmpMutSugg::ChangeBinding => (vec![], true),
1327        };
1328
1329        // Find a binding's type to make mutable.
1330        let (binding_exists, span) = match local_var_ty_info {
1331            // If this is a variable binding with an explicit type,
1332            // then we will suggest changing it to be mutable.
1333            // This is `Applicability::MachineApplicable`.
1334            Some(ty_span) => (true, ty_span),
1335
1336            // Otherwise, we'll suggest *adding* an annotated type, we'll suggest
1337            // the RHS's type for that.
1338            // This is `Applicability::HasPlaceholders`.
1339            None => (false, decl_span),
1340        };
1341
1342        if !binding_exists && !add_type_annotation_if_not_exists {
1343            suggest(sugg, Applicability::MachineApplicable, "");
1344            return;
1345        }
1346
1347        // If the binding already exists and is a reference with an explicit
1348        // lifetime, then we can suggest adding ` mut`. This is special-cased from
1349        // the path without an explicit lifetime.
1350        let (sugg_span, sugg_str, suggest_now) = if let Ok(src) = self.infcx.tcx.sess.source_map().span_to_snippet(span)
1351            && src.starts_with("&'")
1352            // Note that `&' a T` is invalid so this is correct.
1353            && let Some(ws_pos) = src.find(char::is_whitespace)
1354        {
1355            let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
1356            (span, " mut".to_owned(), true)
1357        // If there is already a binding, we modify it to be `mut`.
1358        } else if binding_exists {
1359            // Shrink the span to just after the `&` in `&variable`.
1360            let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
1361            (span, "mut ".to_owned(), true)
1362        } else {
1363            // Otherwise, suggest that the user annotates the binding; We provide the
1364            // type of the local.
1365            let ty = local_decl.ty.builtin_deref(true).unwrap();
1366
1367            (span, format!("{}mut {}", if local_decl.ty.is_ref() { "&" } else { "*" }, ty), false)
1368        };
1369
1370        if suggest_now {
1371            // Suggest changing `&x` to `&mut x` and changing `&T` to `&mut T` at the same time.
1372            let has_change = !sugg.is_empty();
1373            sugg.push((sugg_span, sugg_str));
1374            suggest(
1375                sugg,
1376                Applicability::MachineApplicable,
1377                // FIXME(fee1-dead) this somehow doesn't fire
1378                if has_change { " and changing the binding's type" } else { "" },
1379            );
1380            return;
1381        } else if !sugg.is_empty() {
1382            suggest(sugg, Applicability::MachineApplicable, "");
1383            return;
1384        }
1385
1386        let def_id = self.body.source.def_id();
1387        let hir_id = if let Some(local_def_id) = def_id.as_local()
1388            && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
1389        {
1390            BindingFinder { span: sugg_span }.visit_body(&body).break_value()
1391        } else {
1392            None
1393        };
1394        let node = hir_id.map(|hir_id| self.infcx.tcx.hir_node(hir_id));
1395
1396        let Some(hir::Node::LetStmt(local)) = node else {
1397            err.span_label(
1398                sugg_span,
1399                format!("consider changing this binding's type to be: `{sugg_str}`"),
1400            );
1401            return;
1402        };
1403
1404        let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
1405        if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
1406            && let Some(expr) = local.init
1407            && let ty = tables.node_type_opt(expr.hir_id)
1408            && let Some(ty) = ty
1409            && let ty::Ref(..) = ty.kind()
1410        {
1411            match self
1412                .infcx
1413                .type_implements_trait_shallow(clone_trait, ty.peel_refs(), self.infcx.param_env)
1414                .as_deref()
1415            {
1416                Some([]) => {
1417                    // FIXME: This error message isn't useful, since we're just
1418                    // vaguely suggesting to clone a value that already
1419                    // implements `Clone`.
1420                    //
1421                    // A correct suggestion here would take into account the fact
1422                    // that inference may be affected by missing types on bindings,
1423                    // etc., to improve "tests/ui/borrowck/issue-91206.stderr", for
1424                    // example.
1425                }
1426                None => {
1427                    if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
1428                        && segment.ident.name == sym::clone
1429                    {
1430                        err.span_help(
1431                            span,
1432                            format!(
1433                                "`{}` doesn't implement `Clone`, so this call clones \
1434                                             the reference `{ty}`",
1435                                ty.peel_refs(),
1436                            ),
1437                        );
1438                    }
1439                    // The type doesn't implement Clone.
1440                    let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
1441                        self.infcx.tcx,
1442                        clone_trait,
1443                        [ty.peel_refs()],
1444                    ));
1445                    let obligation = traits::Obligation::new(
1446                        self.infcx.tcx,
1447                        traits::ObligationCause::dummy(),
1448                        self.infcx.param_env,
1449                        trait_ref,
1450                    );
1451                    self.infcx.err_ctxt().suggest_derive(
1452                        &obligation,
1453                        err,
1454                        trait_ref.upcast(self.infcx.tcx),
1455                    );
1456                }
1457                Some(errors) => {
1458                    if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
1459                        && segment.ident.name == sym::clone
1460                    {
1461                        err.span_help(
1462                            span,
1463                            format!(
1464                                "`{}` doesn't implement `Clone` because its \
1465                                             implementations trait bounds could not be met, so \
1466                                             this call clones the reference `{ty}`",
1467                                ty.peel_refs(),
1468                            ),
1469                        );
1470                        err.note(format!(
1471                            "the following trait bounds weren't met: {}",
1472                            errors
1473                                .iter()
1474                                .map(|e| e.obligation.predicate.to_string())
1475                                .collect::<Vec<_>>()
1476                                .join("\n"),
1477                        ));
1478                    }
1479                    // The type doesn't implement Clone because of unmet obligations.
1480                    for error in errors {
1481                        if let traits::FulfillmentErrorCode::Select(
1482                            traits::SelectionError::Unimplemented,
1483                        ) = error.code
1484                            && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
1485                                error.obligation.predicate.kind().skip_binder()
1486                        {
1487                            self.infcx.err_ctxt().suggest_derive(
1488                                &error.obligation,
1489                                err,
1490                                error.obligation.predicate.kind().rebind(pred),
1491                            );
1492                        }
1493                    }
1494                }
1495            }
1496        }
1497        let (changing, span, sugg) = match local.ty {
1498            Some(ty) => ("changing", ty.span, sugg_str),
1499            None => ("specifying", local.pat.span.shrink_to_hi(), format!(": {sugg_str}")),
1500        };
1501        err.span_suggestion_verbose(
1502            span,
1503            format!("consider {changing} this binding's type"),
1504            sugg,
1505            Applicability::HasPlaceholders,
1506        );
1507    }
1508
1509    /// Returns `true` if `local` is an argument in a closure passed to a
1510    /// function defined in another crate.
1511    ///
1512    /// For example, in the following code this function returns `true` for `x`
1513    /// since `Option::inspect()` is not defined in the current crate:
1514    ///
1515    /// ```text
1516    /// some_option.as_mut().inspect(|x| {
1517    /// ```
1518    fn is_closure_arg_with_non_locally_decided_type(&self, local: Local) -> bool {
1519        // We don't care about regular local variables, only args.
1520        if self.body.local_kind(local) != LocalKind::Arg {
1521            return false;
1522        }
1523
1524        // Make sure we are inside a closure.
1525        let InstanceKind::Item(body_def_id) = self.body.source.instance else {
1526            return false;
1527        };
1528        let Some(Node::Expr(hir::Expr { hir_id: body_hir_id, kind, .. })) =
1529            self.infcx.tcx.hir_get_if_local(body_def_id)
1530        else {
1531            return false;
1532        };
1533        let ExprKind::Closure(hir::Closure { kind: hir::ClosureKind::Closure, .. }) = kind else {
1534            return false;
1535        };
1536
1537        // Check if the method/function that our closure is passed to is defined
1538        // in another crate.
1539        let Node::Expr(closure_parent) = self.infcx.tcx.parent_hir_node(*body_hir_id) else {
1540            return false;
1541        };
1542        match closure_parent.kind {
1543            ExprKind::MethodCall(method, _, _, _) => self
1544                .infcx
1545                .tcx
1546                .typeck(method.hir_id.owner.def_id)
1547                .type_dependent_def_id(closure_parent.hir_id)
1548                .is_some_and(|def_id| !def_id.is_local()),
1549            ExprKind::Call(func, _) => self
1550                .infcx
1551                .tcx
1552                .typeck(func.hir_id.owner.def_id)
1553                .node_type_opt(func.hir_id)
1554                .and_then(|ty| match ty.kind() {
1555                    ty::FnDef(def_id, _) => Some(def_id),
1556                    _ => None,
1557                })
1558                .is_some_and(|def_id| !def_id.is_local()),
1559            _ => false,
1560        }
1561    }
1562}
1563
1564struct BindingFinder {
1565    span: Span,
1566}
1567
1568impl<'tcx> Visitor<'tcx> for BindingFinder {
1569    type Result = ControlFlow<hir::HirId>;
1570    fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) -> Self::Result {
1571        if let hir::StmtKind::Let(local) = s.kind
1572            && local.pat.span == self.span
1573        {
1574            ControlFlow::Break(local.hir_id)
1575        } else {
1576            hir::intravisit::walk_stmt(self, s)
1577        }
1578    }
1579
1580    fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) -> Self::Result {
1581        if let hir::Pat { kind: hir::PatKind::Ref(_, _, _), span, .. } = param.pat
1582            && *span == self.span
1583        {
1584            ControlFlow::Break(param.hir_id)
1585        } else {
1586            ControlFlow::Continue(())
1587        }
1588    }
1589}
1590
1591fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
1592    debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
1593
1594    match *local_decl.local_info() {
1595        // Check if mutably borrowing a mutable reference.
1596        LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1597            binding_mode: BindingMode(ByRef::No, Mutability::Not),
1598            ..
1599        })) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)),
1600        LocalInfo::User(mir::BindingForm::ImplicitSelf(kind)) => {
1601            // Check if the user variable is a `&mut self` and we can therefore
1602            // suggest removing the `&mut`.
1603            //
1604            // Deliberately fall into this case for all implicit self types,
1605            // so that we don't fall into the next case with them.
1606            kind == hir::ImplicitSelfKind::RefMut
1607        }
1608        _ if Some(kw::SelfLower) == local_name => {
1609            // Otherwise, check if the name is the `self` keyword - in which case
1610            // we have an explicit self. Do the same thing in this case and check
1611            // for a `self: &mut Self` to suggest removing the `&mut`.
1612            matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut))
1613        }
1614        _ => false,
1615    }
1616}
1617
1618fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) {
1619    match tcx.sess.source_map().span_to_snippet(span) {
1620        Ok(snippet) if snippet.ends_with("self") => {
1621            (span.with_hi(span.hi() - BytePos(4)).shrink_to_hi(), "mut ".to_string())
1622        }
1623        _ => (span, "&mut self".to_string()),
1624    }
1625}
1626
1627enum AmpMutSugg {
1628    /// Type suggestion. Changes `&self` to `&mut self`, `x: &T` to `x: &mut T`,
1629    /// `ref x` to `ref mut x`, etc.
1630    Type {
1631        span: Span,
1632        suggestion: String,
1633        additional: Option<(Span, String)>,
1634    },
1635    /// Suggestion for expressions, `&x` to `&mut x`, `&x[i]` to `&mut x[i]`, etc.
1636    Expr {
1637        span: Span,
1638        suggestion: String,
1639    },
1640    /// Suggests `.get_mut` in the case of `&map[&key]` for Hash/BTreeMap.
1641    MapGetMut {
1642        span: Span,
1643        suggestion: String,
1644    },
1645    ChangeBinding,
1646}
1647
1648// When we want to suggest a user change a local variable to be a `&mut`, there
1649// are three potential "obvious" things to highlight:
1650//
1651// let ident [: Type] [= RightHandSideExpression];
1652//     ^^^^^    ^^^^     ^^^^^^^^^^^^^^^^^^^^^^^
1653//     (1.)     (2.)              (3.)
1654//
1655// We can always fallback on highlighting the first. But chances are good that
1656// the user experience will be better if we highlight one of the others if possible;
1657// for example, if the RHS is present and the Type is not, then the type is going to
1658// be inferred *from* the RHS, which means we should highlight that (and suggest
1659// that they borrow the RHS mutably).
1660//
1661// This implementation attempts to emulate AST-borrowck prioritization
1662// by trying (3.), then (2.) and finally falling back on (1.).
1663fn suggest_ampmut<'tcx>(
1664    infcx: &crate::BorrowckInferCtxt<'tcx>,
1665    body: &Body<'tcx>,
1666    opt_assignment_rhs_stmt: Option<&Statement<'tcx>>,
1667) -> Option<AmpMutSugg> {
1668    let tcx = infcx.tcx;
1669    // If there is a RHS and it starts with a `&` from it, then check if it is
1670    // mutable, and if not, put suggest putting `mut ` to make it mutable.
1671    // We don't have to worry about lifetime annotations here because they are
1672    // not valid when taking a reference. For example, the following is not valid Rust:
1673    //
1674    // let x: &i32 = &'a 5;
1675    //                ^^ lifetime annotation not allowed
1676    //
1677    if let Some(rhs_stmt) = opt_assignment_rhs_stmt
1678        && let StatementKind::Assign(box (lhs, rvalue)) = &rhs_stmt.kind
1679        && let mut rhs_span = rhs_stmt.source_info.span
1680        && let Ok(mut rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
1681    {
1682        let mut rvalue = rvalue;
1683
1684        // Take some special care when handling `let _x = &*_y`:
1685        // We want to know if this is part of an overloaded index, so `let x = &a[0]`,
1686        // or whether this is a usertype ascription (`let _x: &T = y`).
1687        if let Rvalue::Ref(_, BorrowKind::Shared, place) = rvalue
1688            && place.projection.len() == 1
1689            && place.projection[0] == ProjectionElem::Deref
1690            && let Some(assign) = find_assignments(&body, place.local).first()
1691        {
1692            // If this is a usertype ascription (`let _x: &T = _y`) then pierce through it as either we want
1693            // to suggest `&mut` on the expression (handled here) or we return `None` and let the caller
1694            // suggest `&mut` on the type if the expression seems fine (e.g. `let _x: &T = &mut _y`).
1695            if let Some(user_ty_projs) = body.local_decls[lhs.local].user_ty.as_ref()
1696                && let [user_ty_proj] = user_ty_projs.contents.as_slice()
1697                && user_ty_proj.projs.is_empty()
1698                && let Either::Left(rhs_stmt_new) = body.stmt_at(*assign)
1699                && let StatementKind::Assign(box (_, rvalue_new)) = &rhs_stmt_new.kind
1700                && let rhs_span_new = rhs_stmt_new.source_info.span
1701                && let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span)
1702            {
1703                (rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new);
1704            }
1705
1706            if let Either::Right(call) = body.stmt_at(*assign)
1707                && let TerminatorKind::Call {
1708                    func: Operand::Constant(box const_operand), args, ..
1709                } = &call.kind
1710                && let ty::FnDef(method_def_id, method_args) = *const_operand.ty().kind()
1711                && let Some(trait_) = tcx.trait_of_assoc(method_def_id)
1712                && tcx.is_lang_item(trait_, hir::LangItem::Index)
1713            {
1714                let trait_ref = ty::TraitRef::from_assoc(
1715                    tcx,
1716                    tcx.require_lang_item(hir::LangItem::IndexMut, rhs_span),
1717                    method_args,
1718                );
1719                // The type only implements `Index` but not `IndexMut`, we must not suggest `&mut`.
1720                if !infcx
1721                    .type_implements_trait(trait_ref.def_id, trait_ref.args, infcx.param_env)
1722                    .must_apply_considering_regions()
1723                {
1724                    // Suggest `get_mut` if type is a `BTreeMap` or `HashMap`.
1725                    if let ty::Adt(def, _) = trait_ref.self_ty().kind()
1726                        && [sym::BTreeMap, sym::HashMap]
1727                            .into_iter()
1728                            .any(|s| tcx.is_diagnostic_item(s, def.did()))
1729                        && let [map, key] = &**args
1730                        && let Ok(map) = tcx.sess.source_map().span_to_snippet(map.span)
1731                        && let Ok(key) = tcx.sess.source_map().span_to_snippet(key.span)
1732                    {
1733                        let span = rhs_span;
1734                        let suggestion = format!("{map}.get_mut({key}).unwrap()");
1735                        return Some(AmpMutSugg::MapGetMut { span, suggestion });
1736                    }
1737                    return None;
1738                }
1739            }
1740        }
1741
1742        let sugg = match rvalue {
1743            Rvalue::Ref(_, BorrowKind::Shared, _) if let Some(ref_idx) = rhs_str.find('&') => {
1744                // Shrink the span to just after the `&` in `&variable`.
1745                Some((
1746                    rhs_span.with_lo(rhs_span.lo() + BytePos(ref_idx as u32 + 1)).shrink_to_lo(),
1747                    "mut ".to_owned(),
1748                ))
1749            }
1750            Rvalue::RawPtr(RawPtrKind::Const, _) if let Some(const_idx) = rhs_str.find("const") => {
1751                // Suggest changing `&raw const` to `&raw mut` if applicable.
1752                let const_idx = const_idx as u32;
1753                Some((
1754                    rhs_span
1755                        .with_lo(rhs_span.lo() + BytePos(const_idx))
1756                        .with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32)),
1757                    "mut".to_owned(),
1758                ))
1759            }
1760            _ => None,
1761        };
1762
1763        if let Some((span, suggestion)) = sugg {
1764            return Some(AmpMutSugg::Expr { span, suggestion });
1765        }
1766    }
1767
1768    Some(AmpMutSugg::ChangeBinding)
1769}
1770
1771/// If the type is a `Coroutine`, `Closure`, or `CoroutineClosure`
1772fn is_closure_like(ty: Ty<'_>) -> bool {
1773    ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure()
1774}
1775
1776/// Given a field that needs to be mutable, returns a span where the " mut " could go.
1777/// This function expects the local to be a reference to a struct in order to produce a span.
1778///
1779/// ```text
1780/// LL |     s: &'a   String
1781///    |           ^^^ returns a span taking up the space here
1782/// ```
1783fn get_mut_span_in_struct_field<'tcx>(
1784    tcx: TyCtxt<'tcx>,
1785    ty: Ty<'tcx>,
1786    field: FieldIdx,
1787) -> Option<Span> {
1788    // Expect our local to be a reference to a struct of some kind.
1789    if let ty::Ref(_, ty, _) = ty.kind()
1790        && let ty::Adt(def, _) = ty.kind()
1791        && let field = def.all_fields().nth(field.index())?
1792        // Now we're dealing with the actual struct that we're going to suggest a change to,
1793        // we can expect a field that is an immutable reference to a type.
1794        && let hir::Node::Field(field) = tcx.hir_node_by_def_id(field.did.as_local()?)
1795        && let hir::TyKind::Ref(lt, hir::MutTy { mutbl: hir::Mutability::Not, ty }) = field.ty.kind
1796    {
1797        return Some(lt.ident.span.between(ty.span));
1798    }
1799
1800    None
1801}
1802
1803/// If possible, suggest replacing `ref` with `ref mut`.
1804fn suggest_ref_mut(tcx: TyCtxt<'_>, span: Span) -> Option<Span> {
1805    let pattern_str = tcx.sess.source_map().span_to_snippet(span).ok()?;
1806    if let Some(rest) = pattern_str.strip_prefix("ref")
1807        && rest.starts_with(rustc_lexer::is_whitespace)
1808    {
1809        let span = span.with_lo(span.lo() + BytePos(4)).shrink_to_lo();
1810        Some(span)
1811    } else {
1812        None
1813    }
1814}