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