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