]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
Remove in_band_lifetimes from borrowck
[rust.git] / compiler / rustc_borrowck / src / diagnostics / mutability_errors.rs
1 use rustc_hir as hir;
2 use rustc_hir::Node;
3 use rustc_middle::hir::map::Map;
4 use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
5 use rustc_middle::ty::{self, Ty, TyCtxt};
6 use rustc_middle::{
7     hir::place::PlaceBase,
8     mir::{
9         self, BindingForm, ClearCrossCrate, ImplicitSelfKind, Local, LocalDecl, LocalInfo,
10         LocalKind, Location,
11     },
12 };
13 use rustc_span::source_map::DesugaringKind;
14 use rustc_span::symbol::{kw, Symbol};
15 use rustc_span::{BytePos, Span};
16
17 use crate::diagnostics::BorrowedContentSource;
18 use crate::MirBorrowckCtxt;
19 use rustc_const_eval::util::collect_writes::FindAssignments;
20 use rustc_errors::{Applicability, DiagnosticBuilder};
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 `{}` is not declared as mutable", name);
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 `{}` is not declared as mutable", name);
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 `{}` is an immutable static item", static_name);
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                         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, message)) = annotate_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(
238                         span,
239                         "consider changing this to be mutable",
240                         message,
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                                 String::new(),
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                             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 ".to_string(),
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, 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::Binding(pat)) = self.infcx.tcx.hir().find(upvar_hir_id) {
366                     if let hir::PatKind::Binding(
367                         hir::BindingAnnotation::Unannotated,
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
382                 let tcx = self.infcx.tcx;
383                 if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
384                 {
385                     if let ty::Closure(id, _) = ty.kind() {
386                         self.show_mutating_upvar(tcx, id, the_place_err, &mut err);
387                     }
388                 }
389             }
390
391             // complete hack to approximate old AST-borrowck
392             // diagnostic: if the span starts with a mutable borrow of
393             // a local variable, then just suggest the user remove it.
394             PlaceRef { local: _, projection: [] }
395                 if {
396                     if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
397                         snippet.starts_with("&mut ")
398                     } else {
399                         false
400                     }
401                 } =>
402             {
403                 err.span_label(span, format!("cannot {ACT}", ACT = act));
404                 err.span_suggestion(
405                     span,
406                     "try removing `&mut` here",
407                     String::new(),
408                     Applicability::MaybeIncorrect,
409                 );
410             }
411
412             PlaceRef { local, projection: [ProjectionElem::Deref] }
413                 if self.body.local_decls[local].is_ref_for_guard() =>
414             {
415                 err.span_label(span, format!("cannot {ACT}", ACT = act));
416                 err.note(
417                     "variables bound in patterns are immutable until the end of the pattern guard",
418                 );
419             }
420
421             // We want to point out when a `&` can be readily replaced
422             // with an `&mut`.
423             //
424             // FIXME: can this case be generalized to work for an
425             // arbitrary base for the projection?
426             PlaceRef { local, projection: [ProjectionElem::Deref] }
427                 if self.body.local_decls[local].is_user_variable() =>
428             {
429                 let local_decl = &self.body.local_decls[local];
430
431                 let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() {
432                     ("&", "reference")
433                 } else {
434                     ("*const", "pointer")
435                 };
436
437                 match self.local_names[local] {
438                     Some(name) if !local_decl.from_compiler_desugaring() => {
439                         let label = match local_decl.local_info.as_ref().unwrap() {
440                             box LocalInfo::User(ClearCrossCrate::Set(
441                                 mir::BindingForm::ImplicitSelf(_),
442                             )) => {
443                                 let (span, suggestion) =
444                                     suggest_ampmut_self(self.infcx.tcx, local_decl);
445                                 Some((true, span, suggestion))
446                             }
447
448                             box LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
449                                 mir::VarBindingForm {
450                                     binding_mode: ty::BindingMode::BindByValue(_),
451                                     opt_ty_info,
452                                     ..
453                                 },
454                             ))) => {
455                                 // check if the RHS is from desugaring
456                                 let opt_assignment_rhs_span =
457                                     self.body.find_assignments(local).first().map(|&location| {
458                                         if let Some(mir::Statement {
459                                             source_info: _,
460                                             kind:
461                                                 mir::StatementKind::Assign(box (
462                                                     _,
463                                                     mir::Rvalue::Use(mir::Operand::Copy(place)),
464                                                 )),
465                                         }) = self.body[location.block]
466                                             .statements
467                                             .get(location.statement_index)
468                                         {
469                                             self.body.local_decls[place.local].source_info.span
470                                         } else {
471                                             self.body.source_info(location).span
472                                         }
473                                     });
474                                 match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
475                                     // on for loops, RHS points to the iterator part
476                                     Some(DesugaringKind::ForLoop) => {
477                                         self.suggest_similar_mut_method_for_for_loop(&mut err);
478                                         Some((
479                                             false,
480                                             opt_assignment_rhs_span.unwrap(),
481                                             format!(
482                                                 "this iterator yields `{SIGIL}` {DESC}s",
483                                                 SIGIL = pointer_sigil,
484                                                 DESC = pointer_desc
485                                             ),
486                                         ))
487                                     }
488                                     // don't create labels for compiler-generated spans
489                                     Some(_) => None,
490                                     None => {
491                                         let (span, suggestion) = suggest_ampmut(
492                                             self.infcx.tcx,
493                                             local_decl,
494                                             opt_assignment_rhs_span,
495                                             *opt_ty_info,
496                                         );
497                                         Some((true, span, suggestion))
498                                     }
499                                 }
500                             }
501
502                             box LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
503                                 mir::VarBindingForm {
504                                     binding_mode: ty::BindingMode::BindByReference(_),
505                                     ..
506                                 },
507                             ))) => {
508                                 let pattern_span = local_decl.source_info.span;
509                                 suggest_ref_mut(self.infcx.tcx, pattern_span)
510                                     .map(|replacement| (true, pattern_span, replacement))
511                             }
512
513                             box LocalInfo::User(ClearCrossCrate::Clear) => {
514                                 bug!("saw cleared local state")
515                             }
516
517                             _ => unreachable!(),
518                         };
519
520                         match label {
521                             Some((true, err_help_span, suggested_code)) => {
522                                 let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
523                                 if !is_trait_sig {
524                                     err.span_suggestion(
525                                         err_help_span,
526                                         &format!(
527                                             "consider changing this to be a mutable {}",
528                                             pointer_desc
529                                         ),
530                                         suggested_code,
531                                         Applicability::MachineApplicable,
532                                     );
533                                 } else if let Some(x) = local_trait {
534                                     err.span_suggestion(
535                                         x,
536                                         &format!(
537                                             "consider changing that to be a mutable {}",
538                                             pointer_desc
539                                         ),
540                                         suggested_code,
541                                         Applicability::MachineApplicable,
542                                     );
543                                 }
544                             }
545                             Some((false, err_label_span, message)) => {
546                                 err.span_label(err_label_span, &message);
547                             }
548                             None => {}
549                         }
550                         err.span_label(
551                             span,
552                             format!(
553                                 "`{NAME}` is a `{SIGIL}` {DESC}, \
554                                 so the data it refers to cannot be {ACTED_ON}",
555                                 NAME = name,
556                                 SIGIL = pointer_sigil,
557                                 DESC = pointer_desc,
558                                 ACTED_ON = acted_on
559                             ),
560                         );
561                     }
562                     _ => {
563                         err.span_label(
564                             span,
565                             format!(
566                                 "cannot {ACT} through `{SIGIL}` {DESC}",
567                                 ACT = act,
568                                 SIGIL = pointer_sigil,
569                                 DESC = pointer_desc
570                             ),
571                         );
572                     }
573                 }
574             }
575
576             PlaceRef { local, projection: [ProjectionElem::Deref] }
577                 if local == ty::CAPTURE_STRUCT_LOCAL && !self.upvars.is_empty() =>
578             {
579                 self.expected_fn_found_fn_mut_call(&mut err, span, act);
580             }
581
582             PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => {
583                 err.span_label(span, format!("cannot {ACT}", ACT = act));
584
585                 match opt_source {
586                     Some(BorrowedContentSource::OverloadedDeref(ty)) => {
587                         err.help(&format!(
588                             "trait `DerefMut` is required to modify through a dereference, \
589                                 but it is not implemented for `{}`",
590                             ty,
591                         ));
592                     }
593                     Some(BorrowedContentSource::OverloadedIndex(ty)) => {
594                         err.help(&format!(
595                             "trait `IndexMut` is required to modify indexed content, \
596                                 but it is not implemented for `{}`",
597                             ty,
598                         ));
599                     }
600                     _ => (),
601                 }
602             }
603
604             _ => {
605                 err.span_label(span, format!("cannot {ACT}", ACT = act));
606             }
607         }
608
609         err.buffer(&mut self.errors_buffer);
610     }
611
612     /// User cannot make signature of a trait mutable without changing the
613     /// trait. So we find if this error belongs to a trait and if so we move
614     /// suggestion to the trait or disable it if it is out of scope of this crate
615     fn is_error_in_trait(&self, local: Local) -> (bool, Option<Span>) {
616         if self.body.local_kind(local) != LocalKind::Arg {
617             return (false, None);
618         }
619         let hir_map = self.infcx.tcx.hir();
620         let my_def = self.body.source.def_id();
621         let my_hir = hir_map.local_def_id_to_hir_id(my_def.as_local().unwrap());
622         let td = if let Some(a) =
623             self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
624         {
625             a
626         } else {
627             return (false, None);
628         };
629         (
630             true,
631             td.as_local().and_then(|tld| {
632                 let h = hir_map.local_def_id_to_hir_id(tld);
633                 match hir_map.find(h) {
634                     Some(Node::Item(hir::Item {
635                         kind: hir::ItemKind::Trait(_, _, _, _, items),
636                         ..
637                     })) => {
638                         let mut f_in_trait_opt = None;
639                         for hir::TraitItemRef { id: fi, kind: k, .. } in *items {
640                             let hi = fi.hir_id();
641                             if !matches!(k, hir::AssocItemKind::Fn { .. }) {
642                                 continue;
643                             }
644                             if hir_map.name(hi) != hir_map.name(my_hir) {
645                                 continue;
646                             }
647                             f_in_trait_opt = Some(hi);
648                             break;
649                         }
650                         f_in_trait_opt.and_then(|f_in_trait| match hir_map.find(f_in_trait) {
651                             Some(Node::TraitItem(hir::TraitItem {
652                                 kind:
653                                     hir::TraitItemKind::Fn(
654                                         hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. },
655                                         _,
656                                     ),
657                                 ..
658                             })) => {
659                                 let hir::Ty { span, .. } = inputs[local.index() - 1];
660                                 Some(span)
661                             }
662                             _ => None,
663                         })
664                     }
665                     _ => None,
666                 }
667             }),
668         )
669     }
670
671     // point to span of upvar making closure call require mutable borrow
672     fn show_mutating_upvar(
673         &self,
674         tcx: TyCtxt<'_>,
675         id: &hir::def_id::DefId,
676         the_place_err: PlaceRef<'tcx>,
677         err: &mut DiagnosticBuilder<'_>,
678     ) {
679         let closure_local_def_id = id.expect_local();
680         let tables = tcx.typeck(closure_local_def_id);
681         let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_local_def_id);
682         if let Some((span, closure_kind_origin)) =
683             &tables.closure_kind_origins().get(closure_hir_id)
684         {
685             let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base {
686                 let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin);
687                 let root_hir_id = upvar_id.var_path.hir_id;
688                 // we have an origin for this closure kind starting at this root variable so it's safe to unwrap here
689                 let captured_places = tables.closure_min_captures[id].get(&root_hir_id).unwrap();
690
691                 let origin_projection = closure_kind_origin
692                     .projections
693                     .iter()
694                     .map(|proj| proj.kind)
695                     .collect::<Vec<_>>();
696                 let mut capture_reason = String::new();
697                 for captured_place in captured_places {
698                     let captured_place_kinds = captured_place
699                         .place
700                         .projections
701                         .iter()
702                         .map(|proj| proj.kind)
703                         .collect::<Vec<_>>();
704                     if rustc_middle::ty::is_ancestor_or_same_capture(
705                         &captured_place_kinds,
706                         &origin_projection,
707                     ) {
708                         match captured_place.info.capture_kind {
709                             ty::UpvarCapture::ByRef(ty::UpvarBorrow {
710                                 kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
711                                 ..
712                             }) => {
713                                 capture_reason = format!("mutable borrow of `{}`", upvar);
714                             }
715                             ty::UpvarCapture::ByValue(_) => {
716                                 capture_reason = format!("possible mutation of `{}`", upvar);
717                             }
718                             _ => bug!("upvar `{}` borrowed, but not mutably", upvar),
719                         }
720                         break;
721                     }
722                 }
723                 if capture_reason.is_empty() {
724                     bug!("upvar `{}` borrowed, but cannot find reason", upvar);
725                 }
726                 capture_reason
727             } else {
728                 bug!("not an upvar")
729             };
730             err.span_label(
731                 *span,
732                 format!(
733                     "calling `{}` requires mutable binding due to {}",
734                     self.describe_place(the_place_err).unwrap(),
735                     reason
736                 ),
737             );
738         }
739     }
740
741     // Attempt to search similar mutable associated items for suggestion.
742     // In the future, attempt in all path but initially for RHS of for_loop
743     fn suggest_similar_mut_method_for_for_loop(&self, err: &mut DiagnosticBuilder<'_>) {
744         use hir::{
745             BodyId, Expr,
746             ExprKind::{Block, Call, DropTemps, Match, MethodCall},
747             HirId, ImplItem, ImplItemKind, Item, ItemKind,
748         };
749
750         fn maybe_body_id_of_fn(hir_map: &Map<'_>, id: HirId) -> Option<BodyId> {
751             match hir_map.find(id) {
752                 Some(Node::Item(Item { kind: ItemKind::Fn(_, _, body_id), .. }))
753                 | Some(Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(_, body_id), .. })) => {
754                     Some(*body_id)
755                 }
756                 _ => None,
757             }
758         }
759         let hir_map = self.infcx.tcx.hir();
760         let mir_body_hir_id = self.mir_hir_id();
761         if let Some(fn_body_id) = maybe_body_id_of_fn(&hir_map, mir_body_hir_id) {
762             if let Block(
763                 hir::Block {
764                     expr:
765                         Some(Expr {
766                             kind:
767                                 DropTemps(Expr {
768                                     kind:
769                                         Match(
770                                             Expr {
771                                                 kind:
772                                                     Call(
773                                                         _,
774                                                         [
775                                                             Expr {
776                                                                 kind: MethodCall(path_segment, ..),
777                                                                 hir_id,
778                                                                 ..
779                                                             },
780                                                             ..,
781                                                         ],
782                                                     ),
783                                                 ..
784                                             },
785                                             ..,
786                                         ),
787                                     ..
788                                 }),
789                             ..
790                         }),
791                     ..
792                 },
793                 _,
794             ) = hir_map.body(fn_body_id).value.kind
795             {
796                 let opt_suggestions = path_segment
797                     .hir_id
798                     .map(|path_hir_id| self.infcx.tcx.typeck(path_hir_id.owner))
799                     .and_then(|typeck| typeck.type_dependent_def_id(*hir_id))
800                     .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
801                     .map(|def_id| self.infcx.tcx.associated_items(def_id))
802                     .map(|assoc_items| {
803                         assoc_items
804                             .in_definition_order()
805                             .map(|assoc_item_def| assoc_item_def.ident)
806                             .filter(|&ident| {
807                                 let original_method_ident = path_segment.ident;
808                                 original_method_ident != ident
809                                     && ident
810                                         .as_str()
811                                         .starts_with(&original_method_ident.name.to_string())
812                             })
813                             .map(|ident| format!("{}()", ident))
814                             .peekable()
815                     });
816
817                 if let Some(mut suggestions) = opt_suggestions {
818                     if suggestions.peek().is_some() {
819                         err.span_suggestions(
820                             path_segment.ident.span,
821                             "use mutable method",
822                             suggestions,
823                             Applicability::MaybeIncorrect,
824                         );
825                     }
826                 }
827             }
828         };
829     }
830
831     /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
832     fn expected_fn_found_fn_mut_call(&self, err: &mut DiagnosticBuilder<'_>, sp: Span, act: &str) {
833         err.span_label(sp, format!("cannot {}", act));
834
835         let hir = self.infcx.tcx.hir();
836         let closure_id = self.mir_hir_id();
837         let fn_call_id = hir.get_parent_node(closure_id);
838         let node = hir.get(fn_call_id);
839         let item_id = hir.enclosing_body_owner(fn_call_id);
840         let mut look_at_return = true;
841         // If we can detect the expression to be an `fn` call where the closure was an argument,
842         // we point at the `fn` definition argument...
843         if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Call(func, args), .. }) = node {
844             let arg_pos = args
845                 .iter()
846                 .enumerate()
847                 .filter(|(_, arg)| arg.span == self.body.span)
848                 .map(|(pos, _)| pos)
849                 .next();
850             let def_id = hir.local_def_id(item_id);
851             let tables = self.infcx.tcx.typeck(def_id);
852             if let Some(ty::FnDef(def_id, _)) =
853                 tables.node_type_opt(func.hir_id).as_ref().map(|ty| ty.kind())
854             {
855                 let arg = match hir.get_if_local(*def_id) {
856                     Some(
857                         hir::Node::Item(hir::Item {
858                             ident, kind: hir::ItemKind::Fn(sig, ..), ..
859                         })
860                         | hir::Node::TraitItem(hir::TraitItem {
861                             ident,
862                             kind: hir::TraitItemKind::Fn(sig, _),
863                             ..
864                         })
865                         | hir::Node::ImplItem(hir::ImplItem {
866                             ident,
867                             kind: hir::ImplItemKind::Fn(sig, _),
868                             ..
869                         }),
870                     ) => Some(
871                         arg_pos
872                             .and_then(|pos| {
873                                 sig.decl.inputs.get(
874                                     pos + if sig.decl.implicit_self.has_implicit_self() {
875                                         1
876                                     } else {
877                                         0
878                                     },
879                                 )
880                             })
881                             .map(|arg| arg.span)
882                             .unwrap_or(ident.span),
883                     ),
884                     _ => None,
885                 };
886                 if let Some(span) = arg {
887                     err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
888                     err.span_label(func.span, "expects `Fn` instead of `FnMut`");
889                     if self.infcx.tcx.sess.source_map().is_multiline(self.body.span) {
890                         err.span_label(self.body.span, "in this closure");
891                     }
892                     look_at_return = false;
893                 }
894             }
895         }
896
897         if look_at_return && hir.get_return_block(closure_id).is_some() {
898             // ...otherwise we are probably in the tail expression of the function, point at the
899             // return type.
900             match hir.get(hir.get_parent_item(fn_call_id)) {
901                 hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. })
902                 | hir::Node::TraitItem(hir::TraitItem {
903                     ident,
904                     kind: hir::TraitItemKind::Fn(sig, _),
905                     ..
906                 })
907                 | hir::Node::ImplItem(hir::ImplItem {
908                     ident,
909                     kind: hir::ImplItemKind::Fn(sig, _),
910                     ..
911                 }) => {
912                     err.span_label(ident.span, "");
913                     err.span_label(
914                         sig.decl.output.span(),
915                         "change this to return `FnMut` instead of `Fn`",
916                     );
917                     err.span_label(self.body.span, "in this closure");
918                 }
919                 _ => {}
920             }
921         }
922     }
923 }
924
925 fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
926     debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
927
928     match local_decl.local_info.as_deref() {
929         // Check if mutably borrowing a mutable reference.
930         Some(LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
931             mir::VarBindingForm {
932                 binding_mode: ty::BindingMode::BindByValue(Mutability::Not), ..
933             },
934         )))) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)),
935         Some(LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf(kind)))) => {
936             // Check if the user variable is a `&mut self` and we can therefore
937             // suggest removing the `&mut`.
938             //
939             // Deliberately fall into this case for all implicit self types,
940             // so that we don't fall in to the next case with them.
941             *kind == mir::ImplicitSelfKind::MutRef
942         }
943         _ if Some(kw::SelfLower) == local_name => {
944             // Otherwise, check if the name is the `self` keyword - in which case
945             // we have an explicit self. Do the same thing in this case and check
946             // for a `self: &mut Self` to suggest removing the `&mut`.
947             matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut))
948         }
949         _ => false,
950     }
951 }
952
953 fn suggest_ampmut_self<'tcx>(
954     tcx: TyCtxt<'tcx>,
955     local_decl: &mir::LocalDecl<'tcx>,
956 ) -> (Span, String) {
957     let sp = local_decl.source_info.span;
958     (
959         sp,
960         match tcx.sess.source_map().span_to_snippet(sp) {
961             Ok(snippet) => {
962                 let lt_pos = snippet.find('\'');
963                 if let Some(lt_pos) = lt_pos {
964                     format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
965                 } else {
966                     "&mut self".to_string()
967                 }
968             }
969             _ => "&mut self".to_string(),
970         },
971     )
972 }
973
974 // When we want to suggest a user change a local variable to be a `&mut`, there
975 // are three potential "obvious" things to highlight:
976 //
977 // let ident [: Type] [= RightHandSideExpression];
978 //     ^^^^^    ^^^^     ^^^^^^^^^^^^^^^^^^^^^^^
979 //     (1.)     (2.)              (3.)
980 //
981 // We can always fallback on highlighting the first. But chances are good that
982 // the user experience will be better if we highlight one of the others if possible;
983 // for example, if the RHS is present and the Type is not, then the type is going to
984 // be inferred *from* the RHS, which means we should highlight that (and suggest
985 // that they borrow the RHS mutably).
986 //
987 // This implementation attempts to emulate AST-borrowck prioritization
988 // by trying (3.), then (2.) and finally falling back on (1.).
989 fn suggest_ampmut<'tcx>(
990     tcx: TyCtxt<'tcx>,
991     local_decl: &mir::LocalDecl<'tcx>,
992     opt_assignment_rhs_span: Option<Span>,
993     opt_ty_info: Option<Span>,
994 ) -> (Span, String) {
995     if let Some(assignment_rhs_span) = opt_assignment_rhs_span {
996         if let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span) {
997             let is_mutbl = |ty: &str| -> bool {
998                 if let Some(rest) = ty.strip_prefix("mut") {
999                     match rest.chars().next() {
1000                         // e.g. `&mut x`
1001                         Some(c) if c.is_whitespace() => true,
1002                         // e.g. `&mut(x)`
1003                         Some('(') => true,
1004                         // e.g. `&mut{x}`
1005                         Some('{') => true,
1006                         // e.g. `&mutablevar`
1007                         _ => false,
1008                     }
1009                 } else {
1010                     false
1011                 }
1012             };
1013             if let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace)) {
1014                 let lt_name = &src[1..ws_pos];
1015                 let ty = src[ws_pos..].trim_start();
1016                 if !is_mutbl(ty) {
1017                     return (assignment_rhs_span, format!("&{} mut {}", lt_name, ty));
1018                 }
1019             } else if let Some(stripped) = src.strip_prefix('&') {
1020                 let stripped = stripped.trim_start();
1021                 if !is_mutbl(stripped) {
1022                     return (assignment_rhs_span, format!("&mut {}", stripped));
1023                 }
1024             }
1025         }
1026     }
1027
1028     let highlight_span = match opt_ty_info {
1029         // if this is a variable binding with an explicit type,
1030         // try to highlight that for the suggestion.
1031         Some(ty_span) => ty_span,
1032
1033         // otherwise, just highlight the span associated with
1034         // the (MIR) LocalDecl.
1035         None => local_decl.source_info.span,
1036     };
1037
1038     if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span) {
1039         if let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace)) {
1040             let lt_name = &src[1..ws_pos];
1041             let ty = &src[ws_pos..];
1042             return (highlight_span, format!("&{} mut{}", lt_name, ty));
1043         }
1044     }
1045
1046     let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
1047     assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
1048     (
1049         highlight_span,
1050         if local_decl.ty.is_region_ptr() {
1051             format!("&mut {}", ty_mut.ty)
1052         } else {
1053             format!("*mut {}", ty_mut.ty)
1054         },
1055     )
1056 }
1057
1058 fn is_closure_or_generator(ty: Ty<'_>) -> bool {
1059     ty.is_closure() || ty.is_generator()
1060 }
1061
1062 /// Adds a suggestion to a struct definition given a field access to a local.
1063 /// This function expects the local to be a reference to a struct in order to produce a suggestion.
1064 ///
1065 /// ```text
1066 /// LL |     s: &'a String
1067 ///    |        ---------- use `&'a mut String` here to make mutable
1068 /// ```
1069 fn annotate_struct_field<'tcx>(
1070     tcx: TyCtxt<'tcx>,
1071     ty: Ty<'tcx>,
1072     field: &mir::Field,
1073 ) -> Option<(Span, String)> {
1074     // Expect our local to be a reference to a struct of some kind.
1075     if let ty::Ref(_, ty, _) = ty.kind() {
1076         if let ty::Adt(def, _) = ty.kind() {
1077             let field = def.all_fields().nth(field.index())?;
1078             // Use the HIR types to construct the diagnostic message.
1079             let hir_id = tcx.hir().local_def_id_to_hir_id(field.did.as_local()?);
1080             let node = tcx.hir().find(hir_id)?;
1081             // Now we're dealing with the actual struct that we're going to suggest a change to,
1082             // we can expect a field that is an immutable reference to a type.
1083             if let hir::Node::Field(field) = node {
1084                 if let hir::TyKind::Rptr(
1085                     lifetime,
1086                     hir::MutTy { mutbl: hir::Mutability::Not, ref ty },
1087                 ) = field.ty.kind
1088                 {
1089                     // Get the snippets in two parts - the named lifetime (if there is one) and
1090                     // type being referenced, that way we can reconstruct the snippet without loss
1091                     // of detail.
1092                     let type_snippet = tcx.sess.source_map().span_to_snippet(ty.span).ok()?;
1093                     let lifetime_snippet = if !lifetime.is_elided() {
1094                         format!("{} ", tcx.sess.source_map().span_to_snippet(lifetime.span).ok()?)
1095                     } else {
1096                         String::new()
1097                     };
1098
1099                     return Some((
1100                         field.ty.span,
1101                         format!("&{}mut {}", lifetime_snippet, &*type_snippet,),
1102                     ));
1103                 }
1104             }
1105         }
1106     }
1107
1108     None
1109 }
1110
1111 /// If possible, suggest replacing `ref` with `ref mut`.
1112 fn suggest_ref_mut(tcx: TyCtxt<'_>, binding_span: Span) -> Option<String> {
1113     let hi_src = tcx.sess.source_map().span_to_snippet(binding_span).ok()?;
1114     if hi_src.starts_with("ref") && hi_src["ref".len()..].starts_with(rustc_lexer::is_whitespace) {
1115         let replacement = format!("ref mut{}", &hi_src["ref".len()..]);
1116         Some(replacement)
1117     } else {
1118         None
1119     }
1120 }