• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
2 use rustc_hir as hir;
3 use rustc_hir::intravisit::Visitor;
4 use rustc_hir::Node;
5 use rustc_middle::hir::map::Map;
6 use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
7 use rustc_middle::ty::{self, Ty, TyCtxt};
8 use rustc_middle::{
9     hir::place::PlaceBase,
10     mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location},
11 };
12 use rustc_span::source_map::DesugaringKind;
13 use rustc_span::symbol::{kw, Symbol};
14 use rustc_span::{sym, BytePos, Span};
15 use rustc_target::abi::FieldIdx;
16 
17 use crate::diagnostics::BorrowedContentSource;
18 use crate::util::FindAssignments;
19 use crate::MirBorrowckCtxt;
20 
21 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
22 pub(crate) enum AccessKind {
23     MutableBorrow,
24     Mutate,
25 }
26 
27 impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
report_mutability_error( &mut self, access_place: Place<'tcx>, span: Span, the_place_err: PlaceRef<'tcx>, error_access: AccessKind, location: Location, )28     pub(crate) fn report_mutability_error(
29         &mut self,
30         access_place: Place<'tcx>,
31         span: Span,
32         the_place_err: PlaceRef<'tcx>,
33         error_access: AccessKind,
34         location: Location,
35     ) {
36         debug!(
37             "report_mutability_error(\
38                 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
39             )",
40             access_place, span, the_place_err, error_access, location,
41         );
42 
43         let mut err;
44         let item_msg;
45         let reason;
46         let mut opt_source = None;
47         let access_place_desc = self.describe_any_place(access_place.as_ref());
48         debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
49 
50         match the_place_err {
51             PlaceRef { local, projection: [] } => {
52                 item_msg = access_place_desc;
53                 if access_place.as_local().is_some() {
54                     reason = ", as it is not declared as mutable".to_string();
55                 } else {
56                     let name = self.local_names[local].expect("immutable unnamed local");
57                     reason = format!(", as `{name}` is not declared as mutable");
58                 }
59             }
60 
61             PlaceRef {
62                 local,
63                 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
64             } => {
65                 debug_assert!(is_closure_or_generator(
66                     Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
67                 ));
68 
69                 let imm_borrow_derefed = self.upvars[upvar_index.index()]
70                     .place
71                     .place
72                     .deref_tys()
73                     .any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)));
74 
75                 // If the place is immutable then:
76                 //
77                 // - Either we deref an immutable ref to get to our final place.
78                 //    - We don't capture derefs of raw ptrs
79                 // - Or the final place is immut because the root variable of the capture
80                 //   isn't marked mut and we should suggest that to the user.
81                 if imm_borrow_derefed {
82                     // If we deref an immutable ref then the suggestion here doesn't help.
83                     return;
84                 } else {
85                     item_msg = access_place_desc;
86                     if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
87                         reason = ", as it is not declared as mutable".to_string();
88                     } else {
89                         let name = self.upvars[upvar_index.index()].place.to_string(self.infcx.tcx);
90                         reason = format!(", as `{name}` is not declared as mutable");
91                     }
92                 }
93             }
94 
95             PlaceRef { local, projection: [ProjectionElem::Deref] }
96                 if self.body.local_decls[local].is_ref_for_guard() =>
97             {
98                 item_msg = access_place_desc;
99                 reason = ", as it is immutable for the pattern guard".to_string();
100             }
101             PlaceRef { local, projection: [ProjectionElem::Deref] }
102                 if self.body.local_decls[local].is_ref_to_static() =>
103             {
104                 if access_place.projection.len() == 1 {
105                     item_msg = format!("immutable static item {access_place_desc}");
106                     reason = String::new();
107                 } else {
108                     item_msg = access_place_desc;
109                     let local_info = self.body.local_decls[local].local_info();
110                     if let LocalInfo::StaticRef { def_id, .. } = *local_info {
111                         let static_name = &self.infcx.tcx.item_name(def_id);
112                         reason = format!(", as `{static_name}` is an immutable static item");
113                     } else {
114                         bug!("is_ref_to_static return true, but not ref to static?");
115                     }
116                 }
117             }
118             PlaceRef { local: _, projection: [proj_base @ .., ProjectionElem::Deref] } => {
119                 if the_place_err.local == ty::CAPTURE_STRUCT_LOCAL
120                     && proj_base.is_empty()
121                     && !self.upvars.is_empty()
122                 {
123                     item_msg = access_place_desc;
124                     debug_assert!(self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_ref());
125                     debug_assert!(is_closure_or_generator(
126                         the_place_err.ty(self.body, self.infcx.tcx).ty
127                     ));
128 
129                     reason = if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
130                         ", as it is a captured variable in a `Fn` closure".to_string()
131                     } else {
132                         ", as `Fn` closures cannot mutate their captured variables".to_string()
133                     }
134                 } else {
135                     let source = self.borrowed_content_source(PlaceRef {
136                         local: the_place_err.local,
137                         projection: proj_base,
138                     });
139                     let pointer_type = source.describe_for_immutable_place(self.infcx.tcx);
140                     opt_source = Some(source);
141                     if let Some(desc) = self.describe_place(access_place.as_ref()) {
142                         item_msg = format!("`{desc}`");
143                         reason = match error_access {
144                             AccessKind::Mutate => format!(", which is behind {pointer_type}"),
145                             AccessKind::MutableBorrow => {
146                                 format!(", as it is behind {pointer_type}")
147                             }
148                         }
149                     } else {
150                         item_msg = format!("data in {pointer_type}");
151                         reason = String::new();
152                     }
153                 }
154             }
155 
156             PlaceRef {
157                 local: _,
158                 projection:
159                     [
160                         ..,
161                         ProjectionElem::Index(_)
162                         | ProjectionElem::ConstantIndex { .. }
163                         | ProjectionElem::OpaqueCast { .. }
164                         | ProjectionElem::Subslice { .. }
165                         | ProjectionElem::Downcast(..),
166                     ],
167             } => bug!("Unexpected immutable place."),
168         }
169 
170         debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
171 
172         // `act` and `acted_on` are strings that let us abstract over
173         // the verbs used in some diagnostic messages.
174         let act;
175         let acted_on;
176         let mut suggest = true;
177         let mut mut_error = None;
178         let mut count = 1;
179 
180         let span = match error_access {
181             AccessKind::Mutate => {
182                 err = self.cannot_assign(span, &(item_msg + &reason));
183                 act = "assign";
184                 acted_on = "written";
185                 span
186             }
187             AccessKind::MutableBorrow => {
188                 act = "borrow as mutable";
189                 acted_on = "borrowed as mutable";
190 
191                 let borrow_spans = self.borrow_spans(span, location);
192                 let borrow_span = borrow_spans.args_or_use();
193                 match the_place_err {
194                     PlaceRef { local, projection: [] }
195                         if self.body.local_decls[local].can_be_made_mutable() =>
196                     {
197                         let span = self.body.local_decls[local].source_info.span;
198                         mut_error = Some(span);
199                         if let Some((buffer, c)) = self.get_buffered_mut_error(span) {
200                             // We've encountered a second (or more) attempt to mutably borrow an
201                             // immutable binding, so the likely problem is with the binding
202                             // declaration, not the use. We collect these in a single diagnostic
203                             // and make the binding the primary span of the error.
204                             err = buffer;
205                             count = c + 1;
206                             if count == 2 {
207                                 err.replace_span_with(span, false);
208                                 err.span_label(span, "not mutable");
209                             }
210                             suggest = false;
211                         } else {
212                             err = self.cannot_borrow_path_as_mutable_because(
213                                 borrow_span,
214                                 &item_msg,
215                                 &reason,
216                             );
217                         }
218                     }
219                     _ => {
220                         err = self.cannot_borrow_path_as_mutable_because(
221                             borrow_span,
222                             &item_msg,
223                             &reason,
224                         );
225                     }
226                 }
227                 if suggest {
228                     borrow_spans.var_subdiag(
229                     None,
230                     &mut err,
231                     Some(mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }),
232                     |_kind, var_span| {
233                         let place = self.describe_any_place(access_place.as_ref());
234                         crate::session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure {
235                             place,
236                             var_span,
237                         }
238                     },
239                 );
240                 }
241                 borrow_span
242             }
243         };
244 
245         debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
246 
247         match the_place_err {
248             // Suggest making an existing shared borrow in a struct definition a mutable borrow.
249             //
250             // This is applicable when we have a deref of a field access to a deref of a local -
251             // something like `*((*_1).0`. The local that we get will be a reference to the
252             // struct we've got a field access of (it must be a reference since there's a deref
253             // after the field access).
254             PlaceRef {
255                 local,
256                 projection:
257                     [
258                         proj_base @ ..,
259                         ProjectionElem::Deref,
260                         ProjectionElem::Field(field, _),
261                         ProjectionElem::Deref,
262                     ],
263             } => {
264                 err.span_label(span, format!("cannot {act}"));
265 
266                 if let Some(span) = get_mut_span_in_struct_field(
267                     self.infcx.tcx,
268                     Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty,
269                     *field,
270                 ) {
271                     err.span_suggestion_verbose(
272                         span,
273                         "consider changing this to be mutable",
274                         " mut ",
275                         Applicability::MaybeIncorrect,
276                     );
277                 }
278             }
279 
280             // Suggest removing a `&mut` from the use of a mutable reference.
281             PlaceRef { local, projection: [] }
282                 if self
283                     .body
284                     .local_decls
285                     .get(local)
286                     .is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) =>
287             {
288                 let decl = &self.body.local_decls[local];
289                 err.span_label(span, format!("cannot {act}"));
290                 if let Some(mir::Statement {
291                     source_info,
292                     kind:
293                         mir::StatementKind::Assign(box (
294                             _,
295                             mir::Rvalue::Ref(
296                                 _,
297                                 mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
298                                 _,
299                             ),
300                         )),
301                     ..
302                 }) = &self.body[location.block].statements.get(location.statement_index)
303                 {
304                     match *decl.local_info() {
305                         LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
306                             binding_mode: ty::BindingMode::BindByValue(Mutability::Not),
307                             opt_ty_info: Some(sp),
308                             opt_match_place: _,
309                             pat_span: _,
310                         })) => {
311                             if suggest {
312                                 err.span_note(sp, "the binding is already a mutable borrow");
313                             }
314                         }
315                         _ => {
316                             err.span_note(
317                                 decl.source_info.span,
318                                 "the binding is already a mutable borrow",
319                             );
320                         }
321                     }
322                     if let Ok(snippet) =
323                         self.infcx.tcx.sess.source_map().span_to_snippet(source_info.span)
324                     {
325                         if snippet.starts_with("&mut ") {
326                             // We don't have access to the HIR to get accurate spans, but we can
327                             // give a best effort structured suggestion.
328                             err.span_suggestion_verbose(
329                                 source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
330                                 "try removing `&mut` here",
331                                 "",
332                                 Applicability::MachineApplicable,
333                             );
334                         } else {
335                             // This can occur with things like `(&mut self).foo()`.
336                             err.span_help(source_info.span, "try removing `&mut` here");
337                         }
338                     } else {
339                         err.span_help(source_info.span, "try removing `&mut` here");
340                     }
341                 } else if decl.mutability.is_not() {
342                     if matches!(
343                         decl.local_info(),
344                         LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::MutRef))
345                     ) {
346                         err.note(
347                             "as `Self` may be unsized, this call attempts to take `&mut &mut self`",
348                         );
349                         err.note("however, `&mut self` expands to `self: &mut Self`, therefore `self` cannot be borrowed mutably");
350                     } else {
351                         err.span_suggestion_verbose(
352                             decl.source_info.span.shrink_to_lo(),
353                             "consider making the binding mutable",
354                             "mut ",
355                             Applicability::MachineApplicable,
356                         );
357                     };
358                 }
359             }
360 
361             // We want to suggest users use `let mut` for local (user
362             // variable) mutations...
363             PlaceRef { local, projection: [] }
364                 if self.body.local_decls[local].can_be_made_mutable() =>
365             {
366                 // ... but it doesn't make sense to suggest it on
367                 // variables that are `ref x`, `ref mut x`, `&self`,
368                 // or `&mut self` (such variables are simply not
369                 // mutable).
370                 let local_decl = &self.body.local_decls[local];
371                 assert_eq!(local_decl.mutability, Mutability::Not);
372 
373                 if count < 10 {
374                     err.span_label(span, format!("cannot {act}"));
375                 }
376                 if suggest {
377                     err.span_suggestion_verbose(
378                         local_decl.source_info.span.shrink_to_lo(),
379                         "consider changing this to be mutable",
380                         "mut ",
381                         Applicability::MachineApplicable,
382                     );
383                     let tcx = self.infcx.tcx;
384                     if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() {
385                         self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
386                     }
387                 }
388             }
389 
390             // Also suggest adding mut for upvars
391             PlaceRef {
392                 local,
393                 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
394             } => {
395                 debug_assert!(is_closure_or_generator(
396                     Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
397                 ));
398 
399                 let captured_place = &self.upvars[upvar_index.index()].place;
400 
401                 err.span_label(span, format!("cannot {act}"));
402 
403                 let upvar_hir_id = captured_place.get_root_variable();
404 
405                 if let Some(Node::Pat(pat)) = self.infcx.tcx.hir().find(upvar_hir_id)
406                     && let hir::PatKind::Binding(
407                         hir::BindingAnnotation::NONE,
408                         _,
409                         upvar_ident,
410                         _,
411                     ) = pat.kind
412                 {
413                     if upvar_ident.name == kw::SelfLower {
414                         for (_, node) in self.infcx.tcx.hir().parent_iter(upvar_hir_id) {
415                             if let Some(fn_decl) = node.fn_decl() {
416                                 if !matches!(fn_decl.implicit_self, hir::ImplicitSelfKind::ImmRef | hir::ImplicitSelfKind::MutRef) {
417                                     err.span_suggestion(
418                                         upvar_ident.span,
419                                         "consider changing this to be mutable",
420                                         format!("mut {}", upvar_ident.name),
421                                         Applicability::MachineApplicable,
422                                     );
423                                     break;
424                                 }
425                             }
426                         }
427                     } else {
428                         err.span_suggestion(
429                             upvar_ident.span,
430                             "consider changing this to be mutable",
431                             format!("mut {}", upvar_ident.name),
432                             Applicability::MachineApplicable,
433                         );
434                     }
435                 }
436 
437                 let tcx = self.infcx.tcx;
438                 if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
439                     && let ty::Closure(id, _) = *ty.kind()
440                 {
441                     self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
442                 }
443             }
444 
445             // complete hack to approximate old AST-borrowck
446             // diagnostic: if the span starts with a mutable borrow of
447             // a local variable, then just suggest the user remove it.
448             PlaceRef { local: _, projection: [] }
449                 if self
450                     .infcx
451                     .tcx
452                     .sess
453                     .source_map()
454                     .span_to_snippet(span)
455                     .is_ok_and(|snippet| snippet.starts_with("&mut ")) =>
456             {
457                 err.span_label(span, format!("cannot {act}"));
458                 err.span_suggestion(
459                     span,
460                     "try removing `&mut` here",
461                     "",
462                     Applicability::MaybeIncorrect,
463                 );
464             }
465 
466             PlaceRef { local, projection: [ProjectionElem::Deref] }
467                 if self.body.local_decls[local].is_ref_for_guard() =>
468             {
469                 err.span_label(span, format!("cannot {act}"));
470                 err.note(
471                     "variables bound in patterns are immutable until the end of the pattern guard",
472                 );
473             }
474 
475             // We want to point out when a `&` can be readily replaced
476             // with an `&mut`.
477             //
478             // FIXME: can this case be generalized to work for an
479             // arbitrary base for the projection?
480             PlaceRef { local, projection: [ProjectionElem::Deref] }
481                 if self.body.local_decls[local].is_user_variable() =>
482             {
483                 let local_decl = &self.body.local_decls[local];
484 
485                 let (pointer_sigil, pointer_desc) =
486                     if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
487 
488                 match self.local_names[local] {
489                     Some(name) if !local_decl.from_compiler_desugaring() => {
490                         err.span_label(
491                             span,
492                             format!(
493                                 "`{name}` is a `{pointer_sigil}` {pointer_desc}, \
494                                  so the data it refers to cannot be {acted_on}",
495                             ),
496                         );
497 
498                         self.suggest_make_local_mut(&mut err, local, name);
499                     }
500                     _ => {
501                         err.span_label(
502                             span,
503                             format!("cannot {act} through `{pointer_sigil}` {pointer_desc}"),
504                         );
505                     }
506                 }
507             }
508 
509             PlaceRef { local, projection: [ProjectionElem::Deref] }
510                 if local == ty::CAPTURE_STRUCT_LOCAL && !self.upvars.is_empty() =>
511             {
512                 self.expected_fn_found_fn_mut_call(&mut err, span, act);
513             }
514 
515             PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => {
516                 err.span_label(span, format!("cannot {act}"));
517 
518                 match opt_source {
519                     Some(BorrowedContentSource::OverloadedDeref(ty)) => {
520                         err.help(format!(
521                             "trait `DerefMut` is required to modify through a dereference, \
522                              but it is not implemented for `{ty}`",
523                         ));
524                     }
525                     Some(BorrowedContentSource::OverloadedIndex(ty)) => {
526                         err.help(format!(
527                             "trait `IndexMut` is required to modify indexed content, \
528                              but it is not implemented for `{ty}`",
529                         ));
530                         self.suggest_map_index_mut_alternatives(ty, &mut err, span);
531                     }
532                     _ => (),
533                 }
534             }
535 
536             _ => {
537                 err.span_label(span, format!("cannot {act}"));
538             }
539         }
540 
541         if let Some(span) = mut_error {
542             self.buffer_mut_error(span, err, count);
543         } else {
544             self.buffer_error(err);
545         }
546     }
547 
suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diagnostic, span: Span)548     fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diagnostic, span: Span) {
549         let Some(adt) = ty.ty_adt_def() else { return };
550         let did = adt.did();
551         if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did)
552             || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did)
553         {
554             struct V<'a, 'tcx> {
555                 assign_span: Span,
556                 err: &'a mut Diagnostic,
557                 ty: Ty<'tcx>,
558                 suggested: bool,
559             }
560             impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
561                 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
562                     hir::intravisit::walk_stmt(self, stmt);
563                     let expr = match stmt.kind {
564                         hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr,
565                         hir::StmtKind::Local(hir::Local { init: Some(expr), .. }) => expr,
566                         _ => {
567                             return;
568                         }
569                     };
570                     if let hir::ExprKind::Assign(place, rv, _sp) = expr.kind
571                         && let hir::ExprKind::Index(val, index) = place.kind
572                         && (expr.span == self.assign_span || place.span == self.assign_span)
573                     {
574                         // val[index] = rv;
575                         // ---------- place
576                         self.err.multipart_suggestions(
577                             format!(
578                                 "to modify a `{}`, use `.get_mut()`, `.insert()` or the entry API",
579                                 self.ty,
580                             ),
581                             vec![
582                                 vec![ // val.insert(index, rv);
583                                     (
584                                         val.span.shrink_to_hi().with_hi(index.span.lo()),
585                                         ".insert(".to_string(),
586                                     ),
587                                     (
588                                         index.span.shrink_to_hi().with_hi(rv.span.lo()),
589                                         ", ".to_string(),
590                                     ),
591                                     (rv.span.shrink_to_hi(), ")".to_string()),
592                                 ],
593                                 vec![ // val.get_mut(index).map(|v| { *v = rv; });
594                                     (
595                                         val.span.shrink_to_hi().with_hi(index.span.lo()),
596                                         ".get_mut(".to_string(),
597                                     ),
598                                     (
599                                         index.span.shrink_to_hi().with_hi(place.span.hi()),
600                                         ").map(|val| { *val".to_string(),
601                                     ),
602                                     (
603                                         rv.span.shrink_to_hi(),
604                                         "; })".to_string(),
605                                     ),
606                                 ],
607                                 vec![ // let x = val.entry(index).or_insert(rv);
608                                     (val.span.shrink_to_lo(), "let val = ".to_string()),
609                                     (
610                                         val.span.shrink_to_hi().with_hi(index.span.lo()),
611                                         ".entry(".to_string(),
612                                     ),
613                                     (
614                                         index.span.shrink_to_hi().with_hi(rv.span.lo()),
615                                         ").or_insert(".to_string(),
616                                     ),
617                                     (rv.span.shrink_to_hi(), ")".to_string()),
618                                 ],
619                             ],
620                             Applicability::MachineApplicable,
621                         );
622                         self.suggested = true;
623                     } else if let hir::ExprKind::MethodCall(_path, receiver, _, sp) = expr.kind
624                         && let hir::ExprKind::Index(val, index) = receiver.kind
625                         && expr.span == self.assign_span
626                     {
627                         // val[index].path(args..);
628                         self.err.multipart_suggestion(
629                             format!("to modify a `{}` use `.get_mut()`", self.ty),
630                             vec![
631                                 (
632                                     val.span.shrink_to_hi().with_hi(index.span.lo()),
633                                     ".get_mut(".to_string(),
634                                 ),
635                                 (
636                                     index.span.shrink_to_hi().with_hi(receiver.span.hi()),
637                                     ").map(|val| val".to_string(),
638                                 ),
639                                 (sp.shrink_to_hi(), ")".to_string()),
640                             ],
641                             Applicability::MachineApplicable,
642                         );
643                         self.suggested = true;
644                     }
645                 }
646             }
647             let hir_map = self.infcx.tcx.hir();
648             let def_id = self.body.source.def_id();
649             let hir_id = hir_map.local_def_id_to_hir_id(def_id.as_local().unwrap());
650             let node = hir_map.find(hir_id);
651             let Some(hir::Node::Item(item)) = node else { return; };
652             let hir::ItemKind::Fn(.., body_id) = item.kind else { return; };
653             let body = self.infcx.tcx.hir().body(body_id);
654 
655             let mut v = V { assign_span: span, err, ty, suggested: false };
656             v.visit_body(body);
657             if !v.suggested {
658                 err.help(format!(
659                     "to modify a `{ty}`, use `.get_mut()`, `.insert()` or the entry API",
660                 ));
661             }
662         }
663     }
664 
665     /// User cannot make signature of a trait mutable without changing the
666     /// trait. So we find if this error belongs to a trait and if so we move
667     /// suggestion to the trait or disable it if it is out of scope of this crate
is_error_in_trait(&self, local: Local) -> (bool, Option<Span>)668     fn is_error_in_trait(&self, local: Local) -> (bool, Option<Span>) {
669         if self.body.local_kind(local) != LocalKind::Arg {
670             return (false, None);
671         }
672         let hir_map = self.infcx.tcx.hir();
673         let my_def = self.body.source.def_id();
674         let my_hir = hir_map.local_def_id_to_hir_id(my_def.as_local().unwrap());
675         let Some(td) =
676             self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
677         else {
678             return (false, None);
679         };
680         (
681             true,
682             td.as_local().and_then(|tld| match hir_map.find_by_def_id(tld) {
683                 Some(Node::Item(hir::Item {
684                     kind: hir::ItemKind::Trait(_, _, _, _, items),
685                     ..
686                 })) => {
687                     let mut f_in_trait_opt = None;
688                     for hir::TraitItemRef { id: fi, kind: k, .. } in *items {
689                         let hi = fi.hir_id();
690                         if !matches!(k, hir::AssocItemKind::Fn { .. }) {
691                             continue;
692                         }
693                         if hir_map.name(hi) != hir_map.name(my_hir) {
694                             continue;
695                         }
696                         f_in_trait_opt = Some(hi);
697                         break;
698                     }
699                     f_in_trait_opt.and_then(|f_in_trait| match hir_map.find(f_in_trait) {
700                         Some(Node::TraitItem(hir::TraitItem {
701                             kind:
702                                 hir::TraitItemKind::Fn(
703                                     hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. },
704                                     _,
705                                 ),
706                             ..
707                         })) => {
708                             let hir::Ty { span, .. } = inputs[local.index() - 1];
709                             Some(span)
710                         }
711                         _ => None,
712                     })
713                 }
714                 _ => None,
715             }),
716         )
717     }
718 
719     // point to span of upvar making closure call require mutable borrow
show_mutating_upvar( &self, tcx: TyCtxt<'_>, closure_local_def_id: hir::def_id::LocalDefId, the_place_err: PlaceRef<'tcx>, err: &mut Diagnostic, )720     fn show_mutating_upvar(
721         &self,
722         tcx: TyCtxt<'_>,
723         closure_local_def_id: hir::def_id::LocalDefId,
724         the_place_err: PlaceRef<'tcx>,
725         err: &mut Diagnostic,
726     ) {
727         let tables = tcx.typeck(closure_local_def_id);
728         if let Some((span, closure_kind_origin)) = tcx.closure_kind_origin(closure_local_def_id) {
729             let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base {
730                 let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin);
731                 let root_hir_id = upvar_id.var_path.hir_id;
732                 // we have an origin for this closure kind starting at this root variable so it's safe to unwrap here
733                 let captured_places =
734                     tables.closure_min_captures[&closure_local_def_id].get(&root_hir_id).unwrap();
735 
736                 let origin_projection = closure_kind_origin
737                     .projections
738                     .iter()
739                     .map(|proj| proj.kind)
740                     .collect::<Vec<_>>();
741                 let mut capture_reason = String::new();
742                 for captured_place in captured_places {
743                     let captured_place_kinds = captured_place
744                         .place
745                         .projections
746                         .iter()
747                         .map(|proj| proj.kind)
748                         .collect::<Vec<_>>();
749                     if rustc_middle::ty::is_ancestor_or_same_capture(
750                         &captured_place_kinds,
751                         &origin_projection,
752                     ) {
753                         match captured_place.info.capture_kind {
754                             ty::UpvarCapture::ByRef(
755                                 ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
756                             ) => {
757                                 capture_reason = format!("mutable borrow of `{upvar}`");
758                             }
759                             ty::UpvarCapture::ByValue => {
760                                 capture_reason = format!("possible mutation of `{upvar}`");
761                             }
762                             _ => bug!("upvar `{upvar}` borrowed, but not mutably"),
763                         }
764                         break;
765                     }
766                 }
767                 if capture_reason.is_empty() {
768                     bug!("upvar `{upvar}` borrowed, but cannot find reason");
769                 }
770                 capture_reason
771             } else {
772                 bug!("not an upvar")
773             };
774             err.span_label(
775                 *span,
776                 format!(
777                     "calling `{}` requires mutable binding due to {}",
778                     self.describe_place(the_place_err).unwrap(),
779                     reason
780                 ),
781             );
782         }
783     }
784 
785     // Attempt to search similar mutable associated items for suggestion.
786     // In the future, attempt in all path but initially for RHS of for_loop
suggest_similar_mut_method_for_for_loop(&self, err: &mut Diagnostic)787     fn suggest_similar_mut_method_for_for_loop(&self, err: &mut Diagnostic) {
788         use hir::{
789             BodyId, Expr,
790             ExprKind::{Block, Call, DropTemps, Match, MethodCall},
791             HirId, ImplItem, ImplItemKind, Item, ItemKind,
792         };
793 
794         fn maybe_body_id_of_fn(hir_map: Map<'_>, id: HirId) -> Option<BodyId> {
795             match hir_map.find(id) {
796                 Some(Node::Item(Item { kind: ItemKind::Fn(_, _, body_id), .. }))
797                 | Some(Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(_, body_id), .. })) => {
798                     Some(*body_id)
799                 }
800                 _ => None,
801             }
802         }
803         let hir_map = self.infcx.tcx.hir();
804         let mir_body_hir_id = self.mir_hir_id();
805         if let Some(fn_body_id) = maybe_body_id_of_fn(hir_map, mir_body_hir_id) {
806             if let Block(
807                 hir::Block {
808                     expr:
809                         Some(Expr {
810                             kind:
811                                 DropTemps(Expr {
812                                     kind:
813                                         Match(
814                                             Expr {
815                                                 kind:
816                                                     Call(
817                                                         _,
818                                                         [
819                                                             Expr {
820                                                                 kind:
821                                                                     MethodCall(path_segment, _, _, span),
822                                                                 hir_id,
823                                                                 ..
824                                                             },
825                                                             ..,
826                                                         ],
827                                                     ),
828                                                 ..
829                                             },
830                                             ..,
831                                         ),
832                                     ..
833                                 }),
834                             ..
835                         }),
836                     ..
837                 },
838                 _,
839             ) = hir_map.body(fn_body_id).value.kind
840             {
841                 let opt_suggestions = self
842                     .infcx
843                     .tcx
844                     .typeck(path_segment.hir_id.owner.def_id)
845                     .type_dependent_def_id(*hir_id)
846                     .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
847                     .map(|def_id| self.infcx.tcx.associated_items(def_id))
848                     .map(|assoc_items| {
849                         assoc_items
850                             .in_definition_order()
851                             .map(|assoc_item_def| assoc_item_def.ident(self.infcx.tcx))
852                             .filter(|&ident| {
853                                 let original_method_ident = path_segment.ident;
854                                 original_method_ident != ident
855                                     && ident
856                                         .as_str()
857                                         .starts_with(&original_method_ident.name.to_string())
858                             })
859                             .map(|ident| format!("{ident}()"))
860                             .peekable()
861                     });
862 
863                 if let Some(mut suggestions) = opt_suggestions
864                     && suggestions.peek().is_some()
865                 {
866                     err.span_suggestions(
867                         *span,
868                         "use mutable method",
869                         suggestions,
870                         Applicability::MaybeIncorrect,
871                     );
872                 }
873             }
874         };
875     }
876 
877     /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
expected_fn_found_fn_mut_call(&self, err: &mut Diagnostic, sp: Span, act: &str)878     fn expected_fn_found_fn_mut_call(&self, err: &mut Diagnostic, sp: Span, act: &str) {
879         err.span_label(sp, format!("cannot {act}"));
880 
881         let hir = self.infcx.tcx.hir();
882         let closure_id = self.mir_hir_id();
883         let closure_span = self.infcx.tcx.def_span(self.mir_def_id());
884         let fn_call_id = hir.parent_id(closure_id);
885         let node = hir.get(fn_call_id);
886         let def_id = hir.enclosing_body_owner(fn_call_id);
887         let mut look_at_return = true;
888         // If we can detect the expression to be an `fn` call where the closure was an argument,
889         // we point at the `fn` definition argument...
890         if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Call(func, args), .. }) = node {
891             let arg_pos = args
892                 .iter()
893                 .enumerate()
894                 .filter(|(_, arg)| arg.hir_id == closure_id)
895                 .map(|(pos, _)| pos)
896                 .next();
897             let tables = self.infcx.tcx.typeck(def_id);
898             if let Some(ty::FnDef(def_id, _)) =
899                 tables.node_type_opt(func.hir_id).as_ref().map(|ty| ty.kind())
900             {
901                 let arg = match hir.get_if_local(*def_id) {
902                     Some(
903                         hir::Node::Item(hir::Item {
904                             ident, kind: hir::ItemKind::Fn(sig, ..), ..
905                         })
906                         | hir::Node::TraitItem(hir::TraitItem {
907                             ident,
908                             kind: hir::TraitItemKind::Fn(sig, _),
909                             ..
910                         })
911                         | hir::Node::ImplItem(hir::ImplItem {
912                             ident,
913                             kind: hir::ImplItemKind::Fn(sig, _),
914                             ..
915                         }),
916                     ) => Some(
917                         arg_pos
918                             .and_then(|pos| {
919                                 sig.decl.inputs.get(
920                                     pos + if sig.decl.implicit_self.has_implicit_self() {
921                                         1
922                                     } else {
923                                         0
924                                     },
925                                 )
926                             })
927                             .map(|arg| arg.span)
928                             .unwrap_or(ident.span),
929                     ),
930                     _ => None,
931                 };
932                 if let Some(span) = arg {
933                     err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
934                     err.span_label(func.span, "expects `Fn` instead of `FnMut`");
935                     err.span_label(closure_span, "in this closure");
936                     look_at_return = false;
937                 }
938             }
939         }
940 
941         if look_at_return && hir.get_return_block(closure_id).is_some() {
942             // ...otherwise we are probably in the tail expression of the function, point at the
943             // return type.
944             match hir.get_by_def_id(hir.get_parent_item(fn_call_id).def_id) {
945                 hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. })
946                 | hir::Node::TraitItem(hir::TraitItem {
947                     ident,
948                     kind: hir::TraitItemKind::Fn(sig, _),
949                     ..
950                 })
951                 | hir::Node::ImplItem(hir::ImplItem {
952                     ident,
953                     kind: hir::ImplItemKind::Fn(sig, _),
954                     ..
955                 }) => {
956                     err.span_label(ident.span, "");
957                     err.span_label(
958                         sig.decl.output.span(),
959                         "change this to return `FnMut` instead of `Fn`",
960                     );
961                     err.span_label(closure_span, "in this closure");
962                 }
963                 _ => {}
964             }
965         }
966     }
967 
suggest_make_local_mut( &self, err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, local: Local, name: Symbol, )968     fn suggest_make_local_mut(
969         &self,
970         err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
971         local: Local,
972         name: Symbol,
973     ) {
974         let local_decl = &self.body.local_decls[local];
975 
976         let (pointer_sigil, pointer_desc) =
977             if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
978 
979         let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
980         if is_trait_sig && local_trait.is_none() {
981             return;
982         }
983 
984         let decl_span = match local_trait {
985             Some(span) => span,
986             None => local_decl.source_info.span,
987         };
988 
989         let label = match *local_decl.local_info() {
990             LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
991                 let suggestion = suggest_ampmut_self(self.infcx.tcx, decl_span);
992                 Some((true, decl_span, suggestion))
993             }
994 
995             LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
996                 binding_mode: ty::BindingMode::BindByValue(_),
997                 opt_ty_info,
998                 ..
999             })) => {
1000                 // check if the RHS is from desugaring
1001                 let opt_assignment_rhs_span =
1002                     self.body.find_assignments(local).first().map(|&location| {
1003                         if let Some(mir::Statement {
1004                             source_info: _,
1005                             kind:
1006                                 mir::StatementKind::Assign(box (
1007                                     _,
1008                                     mir::Rvalue::Use(mir::Operand::Copy(place)),
1009                                 )),
1010                         }) = self.body[location.block].statements.get(location.statement_index)
1011                         {
1012                             self.body.local_decls[place.local].source_info.span
1013                         } else {
1014                             self.body.source_info(location).span
1015                         }
1016                     });
1017                 match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
1018                     // on for loops, RHS points to the iterator part
1019                     Some(DesugaringKind::ForLoop) => {
1020                         self.suggest_similar_mut_method_for_for_loop(err);
1021                         err.span_label(
1022                             opt_assignment_rhs_span.unwrap(),
1023                             format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",),
1024                         );
1025                         None
1026                     }
1027                     // don't create labels for compiler-generated spans
1028                     Some(_) => None,
1029                     None => {
1030                         let label = if name != kw::SelfLower {
1031                             suggest_ampmut(
1032                                 self.infcx.tcx,
1033                                 local_decl.ty,
1034                                 decl_span,
1035                                 opt_assignment_rhs_span,
1036                                 opt_ty_info,
1037                             )
1038                         } else {
1039                             match local_decl.local_info() {
1040                                 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1041                                     opt_ty_info: None,
1042                                     ..
1043                                 })) => {
1044                                     let sugg = suggest_ampmut_self(self.infcx.tcx, decl_span);
1045                                     (true, decl_span, sugg)
1046                                 }
1047                                 // explicit self (eg `self: &'a Self`)
1048                                 _ => suggest_ampmut(
1049                                     self.infcx.tcx,
1050                                     local_decl.ty,
1051                                     decl_span,
1052                                     opt_assignment_rhs_span,
1053                                     opt_ty_info,
1054                                 ),
1055                             }
1056                         };
1057                         Some(label)
1058                     }
1059                 }
1060             }
1061 
1062             LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1063                 binding_mode: ty::BindingMode::BindByReference(_),
1064                 ..
1065             })) => {
1066                 let pattern_span: Span = local_decl.source_info.span;
1067                 suggest_ref_mut(self.infcx.tcx, pattern_span)
1068                     .map(|span| (true, span, "mut ".to_owned()))
1069             }
1070 
1071             _ => unreachable!(),
1072         };
1073 
1074         match label {
1075             Some((true, err_help_span, suggested_code)) => {
1076                 err.span_suggestion_verbose(
1077                     err_help_span,
1078                     format!("consider changing this to be a mutable {pointer_desc}"),
1079                     suggested_code,
1080                     Applicability::MachineApplicable,
1081                 );
1082             }
1083             Some((false, err_label_span, message)) => {
1084                 struct BindingFinder {
1085                     span: Span,
1086                     hir_id: Option<hir::HirId>,
1087                 }
1088 
1089                 impl<'tcx> Visitor<'tcx> for BindingFinder {
1090                     fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
1091                         if let hir::StmtKind::Local(local) = s.kind {
1092                             if local.pat.span == self.span {
1093                                 self.hir_id = Some(local.hir_id);
1094                             }
1095                         }
1096                         hir::intravisit::walk_stmt(self, s);
1097                     }
1098                 }
1099                 let hir_map = self.infcx.tcx.hir();
1100                 let def_id = self.body.source.def_id();
1101                 let hir_id = hir_map.local_def_id_to_hir_id(def_id.expect_local());
1102                 let node = hir_map.find(hir_id);
1103                 let hir_id = if let Some(hir::Node::Item(item)) = node
1104                 && let hir::ItemKind::Fn(.., body_id) = item.kind
1105             {
1106                 let body = hir_map.body(body_id);
1107                 let mut v = BindingFinder {
1108                     span: err_label_span,
1109                     hir_id: None,
1110                 };
1111                 v.visit_body(body);
1112                 v.hir_id
1113             } else {
1114                 None
1115             };
1116                 if let Some(hir_id) = hir_id
1117                 && let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
1118             {
1119                 let (changing, span, sugg) = match local.ty {
1120                     Some(ty) => ("changing", ty.span, message),
1121                     None => (
1122                         "specifying",
1123                         local.pat.span.shrink_to_hi(),
1124                         format!(": {message}"),
1125                     ),
1126                 };
1127                 err.span_suggestion_verbose(
1128                     span,
1129                     format!("consider {changing} this binding's type"),
1130                     sugg,
1131                     Applicability::HasPlaceholders,
1132                 );
1133             } else {
1134                 err.span_label(
1135                     err_label_span,
1136                     format!(
1137                         "consider changing this binding's type to be: `{message}`"
1138                     ),
1139                 );
1140             }
1141             }
1142             None => {}
1143         }
1144     }
1145 }
1146 
mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool1147 pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
1148     debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
1149 
1150     match *local_decl.local_info() {
1151         // Check if mutably borrowing a mutable reference.
1152         LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1153             binding_mode: ty::BindingMode::BindByValue(Mutability::Not),
1154             ..
1155         })) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)),
1156         LocalInfo::User(mir::BindingForm::ImplicitSelf(kind)) => {
1157             // Check if the user variable is a `&mut self` and we can therefore
1158             // suggest removing the `&mut`.
1159             //
1160             // Deliberately fall into this case for all implicit self types,
1161             // so that we don't fall into the next case with them.
1162             kind == hir::ImplicitSelfKind::MutRef
1163         }
1164         _ if Some(kw::SelfLower) == local_name => {
1165             // Otherwise, check if the name is the `self` keyword - in which case
1166             // we have an explicit self. Do the same thing in this case and check
1167             // for a `self: &mut Self` to suggest removing the `&mut`.
1168             matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut))
1169         }
1170         _ => false,
1171     }
1172 }
1173 
suggest_ampmut_self<'tcx>(tcx: TyCtxt<'tcx>, span: Span) -> String1174 fn suggest_ampmut_self<'tcx>(tcx: TyCtxt<'tcx>, span: Span) -> String {
1175     match tcx.sess.source_map().span_to_snippet(span) {
1176         Ok(snippet) => {
1177             let lt_pos = snippet.find('\'');
1178             if let Some(lt_pos) = lt_pos {
1179                 format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
1180             } else {
1181                 "&mut self".to_string()
1182             }
1183         }
1184         _ => "&mut self".to_string(),
1185     }
1186 }
1187 
1188 // When we want to suggest a user change a local variable to be a `&mut`, there
1189 // are three potential "obvious" things to highlight:
1190 //
1191 // let ident [: Type] [= RightHandSideExpression];
1192 //     ^^^^^    ^^^^     ^^^^^^^^^^^^^^^^^^^^^^^
1193 //     (1.)     (2.)              (3.)
1194 //
1195 // We can always fallback on highlighting the first. But chances are good that
1196 // the user experience will be better if we highlight one of the others if possible;
1197 // for example, if the RHS is present and the Type is not, then the type is going to
1198 // be inferred *from* the RHS, which means we should highlight that (and suggest
1199 // that they borrow the RHS mutably).
1200 //
1201 // This implementation attempts to emulate AST-borrowck prioritization
1202 // by trying (3.), then (2.) and finally falling back on (1.).
suggest_ampmut<'tcx>( tcx: TyCtxt<'tcx>, decl_ty: Ty<'tcx>, decl_span: Span, opt_assignment_rhs_span: Option<Span>, opt_ty_info: Option<Span>, ) -> (bool, Span, String)1203 fn suggest_ampmut<'tcx>(
1204     tcx: TyCtxt<'tcx>,
1205     decl_ty: Ty<'tcx>,
1206     decl_span: Span,
1207     opt_assignment_rhs_span: Option<Span>,
1208     opt_ty_info: Option<Span>,
1209 ) -> (bool, Span, String) {
1210     // if there is a RHS and it starts with a `&` from it, then check if it is
1211     // mutable, and if not, put suggest putting `mut ` to make it mutable.
1212     // we don't have to worry about lifetime annotations here because they are
1213     // not valid when taking a reference. For example, the following is not valid Rust:
1214     //
1215     // let x: &i32 = &'a 5;
1216     //                ^^ lifetime annotation not allowed
1217     //
1218     if let Some(assignment_rhs_span) = opt_assignment_rhs_span
1219         && let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span)
1220         && let Some(stripped) = src.strip_prefix('&')
1221     {
1222         let is_mut = if let Some(rest) = stripped.trim_start().strip_prefix("mut") {
1223             match rest.chars().next() {
1224                 // e.g. `&mut x`
1225                 Some(c) if c.is_whitespace() => true,
1226                 // e.g. `&mut(x)`
1227                 Some('(') => true,
1228                 // e.g. `&mut{x}`
1229                 Some('{') => true,
1230                 // e.g. `&mutablevar`
1231                 _ => false,
1232             }
1233         } else {
1234             false
1235         };
1236         // if the reference is already mutable then there is nothing we can do
1237         // here.
1238         if !is_mut {
1239             let span = assignment_rhs_span;
1240             // shrink the span to just after the `&` in `&variable`
1241             let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
1242 
1243             // FIXME(Ezrashaw): returning is bad because we still might want to
1244             // update the annotated type, see #106857.
1245             return (true, span, "mut ".to_owned());
1246         }
1247     }
1248 
1249     let (binding_exists, span) = match opt_ty_info {
1250         // if this is a variable binding with an explicit type,
1251         // then we will suggest changing it to be mutable.
1252         // this is `Applicability::MachineApplicable`.
1253         Some(ty_span) => (true, ty_span),
1254 
1255         // otherwise, we'll suggest *adding* an annotated type, we'll suggest
1256         // the RHS's type for that.
1257         // this is `Applicability::HasPlaceholders`.
1258         None => (false, decl_span),
1259     };
1260 
1261     // if the binding already exists and is a reference with a explicit
1262     // lifetime, then we can suggest adding ` mut`. this is special-cased from
1263     // the path without a explicit lifetime.
1264     if let Ok(src) = tcx.sess.source_map().span_to_snippet(span)
1265         && src.starts_with("&'")
1266         // note that `&     'a T` is invalid so this is correct.
1267         && let Some(ws_pos) = src.find(char::is_whitespace)
1268     {
1269         let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
1270         (true, span, " mut".to_owned())
1271     // if there is already a binding, we modify it to be `mut`
1272     } else if binding_exists {
1273         // shrink the span to just after the `&` in `&variable`
1274         let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
1275         (true, span, "mut ".to_owned())
1276     } else {
1277         // otherwise, suggest that the user annotates the binding; we provide the
1278         // type of the local.
1279         let ty_mut = decl_ty.builtin_deref(true).unwrap();
1280         assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
1281 
1282         (
1283             false,
1284             span,
1285             format!("{}mut {}", if decl_ty.is_ref() {"&"} else {"*"}, ty_mut.ty)
1286         )
1287     }
1288 }
1289 
is_closure_or_generator(ty: Ty<'_>) -> bool1290 fn is_closure_or_generator(ty: Ty<'_>) -> bool {
1291     ty.is_closure() || ty.is_generator()
1292 }
1293 
1294 /// Given a field that needs to be mutable, returns a span where the " mut " could go.
1295 /// This function expects the local to be a reference to a struct in order to produce a span.
1296 ///
1297 /// ```text
1298 /// LL |     s: &'a   String
1299 ///    |           ^^^ returns a span taking up the space here
1300 /// ```
get_mut_span_in_struct_field<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, field: FieldIdx, ) -> Option<Span>1301 fn get_mut_span_in_struct_field<'tcx>(
1302     tcx: TyCtxt<'tcx>,
1303     ty: Ty<'tcx>,
1304     field: FieldIdx,
1305 ) -> Option<Span> {
1306     // Expect our local to be a reference to a struct of some kind.
1307     if let ty::Ref(_, ty, _) = ty.kind()
1308         && let ty::Adt(def, _) = ty.kind()
1309         && let field = def.all_fields().nth(field.index())?
1310         // Use the HIR types to construct the diagnostic message.
1311         && let node = tcx.hir().find_by_def_id(field.did.as_local()?)?
1312         // Now we're dealing with the actual struct that we're going to suggest a change to,
1313         // we can expect a field that is an immutable reference to a type.
1314         && let hir::Node::Field(field) = node
1315         && let hir::TyKind::Ref(lt, hir::MutTy { mutbl: hir::Mutability::Not, ty }) = field.ty.kind
1316     {
1317         return Some(lt.ident.span.between(ty.span));
1318     }
1319 
1320     None
1321 }
1322 
1323 /// If possible, suggest replacing `ref` with `ref mut`.
suggest_ref_mut(tcx: TyCtxt<'_>, span: Span) -> Option<Span>1324 fn suggest_ref_mut(tcx: TyCtxt<'_>, span: Span) -> Option<Span> {
1325     let pattern_str = tcx.sess.source_map().span_to_snippet(span).ok()?;
1326     if pattern_str.starts_with("ref")
1327         && pattern_str["ref".len()..].starts_with(rustc_lexer::is_whitespace)
1328     {
1329         let span = span.with_lo(span.lo() + BytePos(4)).shrink_to_lo();
1330         Some(span)
1331     } else {
1332         None
1333     }
1334 }
1335