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