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