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