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