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