]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/mutability_errors.rs
c424c06c41add30a8986b77cab091cbc4ca59082
[rust.git] / src / librustc_mir / borrow_check / mutability_errors.rs
1 use core::unicode::property::Pattern_White_Space;
2 use rustc::hir;
3 use rustc::hir::Node;
4 use rustc::mir::{self, BindingForm, ClearCrossCrate, Local, Location, Body};
5 use rustc::mir::{
6     Mutability, Place, PlaceRef, PlaceBase, Projection, ProjectionElem, Static, StaticKind
7 };
8 use rustc::ty::{self, Ty, TyCtxt};
9 use rustc_data_structures::indexed_vec::Idx;
10 use syntax_pos::Span;
11 use syntax_pos::symbol::kw;
12
13 use crate::borrow_check::MirBorrowckCtxt;
14 use crate::borrow_check::error_reporting::BorrowedContentSource;
15 use crate::util::collect_writes::FindAssignments;
16 use rustc_errors::Applicability;
17
18 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
19 pub(super) enum AccessKind {
20     MutableBorrow,
21     Mutate,
22     Move,
23 }
24
25 impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
26     pub(super) fn report_mutability_error(
27         &mut self,
28         access_place: &Place<'tcx>,
29         span: Span,
30         the_place_err: PlaceRef<'cx, 'tcx>,
31         error_access: AccessKind,
32         location: Location,
33     ) {
34         debug!(
35             "report_mutability_error(\
36                 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
37             )",
38             access_place, span, the_place_err, error_access, location,
39         );
40
41         let mut err;
42         let item_msg;
43         let reason;
44         let mut opt_source = None;
45         let access_place_desc = self.describe_place(access_place.as_place_ref());
46         debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
47
48         match the_place_err {
49             PlaceRef {
50                 base: PlaceBase::Local(local),
51                 projection: None,
52             } => {
53                 item_msg = format!("`{}`", access_place_desc.unwrap());
54                 if let Place {
55                     base: PlaceBase::Local(_),
56                     projection: None,
57                 } = access_place {
58                     reason = ", as it is not declared as mutable".to_string();
59                 } else {
60                     let name = self.body.local_decls[*local]
61                         .name
62                         .expect("immutable unnamed local");
63                     reason = format!(", as `{}` is not declared as mutable", name);
64                 }
65             }
66
67             PlaceRef {
68                 base: _,
69                 projection:
70                     Some(box Projection {
71                         base,
72                         elem: ProjectionElem::Field(upvar_index, _),
73                     }),
74             } => {
75                 debug_assert!(is_closure_or_generator(
76                     Place::ty_from(&the_place_err.base, &base, self.body, self.infcx.tcx).ty
77                 ));
78
79                 item_msg = format!("`{}`", access_place_desc.unwrap());
80                 if self.is_upvar_field_projection(access_place.as_place_ref()).is_some() {
81                     reason = ", as it is not declared as mutable".to_string();
82                 } else {
83                     let name = self.upvars[upvar_index.index()].name;
84                     reason = format!(", as `{}` is not declared as mutable", name);
85                 }
86             }
87
88             PlaceRef {
89                 base: _,
90                 projection:
91                     Some(box Projection {
92                         base,
93                         elem: ProjectionElem::Deref,
94                     }),
95             } => {
96                 if the_place_err.base == &PlaceBase::Local(Local::new(1)) &&
97                     base.is_none() &&
98                     !self.upvars.is_empty() {
99                     item_msg = format!("`{}`", access_place_desc.unwrap());
100                     debug_assert!(self.body.local_decls[Local::new(1)].ty.is_region_ptr());
101                     debug_assert!(is_closure_or_generator(
102                         Place::ty_from(
103                             the_place_err.base,
104                             the_place_err.projection,
105                             self.body,
106                             self.infcx.tcx
107                         )
108                         .ty
109                     ));
110
111                     reason =
112                         if self.is_upvar_field_projection(access_place.as_place_ref()).is_some() {
113                             ", as it is a captured variable in a `Fn` closure".to_string()
114                         } else {
115                             ", as `Fn` closures cannot mutate their captured variables".to_string()
116                         }
117                 } else if {
118                     if let (PlaceBase::Local(local), None) = (&the_place_err.base, base) {
119                         self.body.local_decls[*local].is_ref_for_guard()
120                     } else {
121                         false
122                     }
123                 } {
124                     item_msg = format!("`{}`", access_place_desc.unwrap());
125                     reason = ", as it is immutable for the pattern guard".to_string();
126                 } else {
127                     let source = self.borrowed_content_source(PlaceRef {
128                         base: the_place_err.base,
129                         projection: base,
130                     });
131                     let pointer_type = source.describe_for_immutable_place();
132                     opt_source = Some(source);
133                     if let Some(desc) = access_place_desc {
134                         item_msg = format!("`{}`", desc);
135                         reason = match error_access {
136                             AccessKind::Move |
137                             AccessKind::Mutate => format!(" which is behind {}", pointer_type),
138                             AccessKind::MutableBorrow => {
139                                 format!(", as it is behind {}", pointer_type)
140                             }
141                         }
142                     } else {
143                         item_msg = format!("data in {}", pointer_type);
144                         reason = String::new();
145                     }
146                 }
147             }
148
149             PlaceRef {
150                 base:
151                     PlaceBase::Static(box Static {
152                         kind: StaticKind::Promoted(_),
153                         ..
154                     }),
155                 projection: None,
156             } => unreachable!(),
157
158             PlaceRef {
159                 base:
160                     PlaceBase::Static(box Static {
161                         kind: StaticKind::Static(def_id),
162                         ..
163                     }),
164                 projection: None,
165             } => {
166                 if let Place {
167                     base: PlaceBase::Static(_),
168                     projection: None,
169                 } = access_place {
170                     item_msg = format!("immutable static item `{}`", access_place_desc.unwrap());
171                     reason = String::new();
172                 } else {
173                     item_msg = format!("`{}`", access_place_desc.unwrap());
174                     let static_name = &self.infcx.tcx.item_name(*def_id);
175                     reason = format!(", as `{}` is an immutable static item", static_name);
176                 }
177             }
178
179             PlaceRef {
180                 base: _,
181                 projection:
182                     Some(box Projection {
183                         base: _,
184                         elem: ProjectionElem::Index(_),
185                     }),
186             }
187             | PlaceRef {
188                 base: _,
189                 projection:
190                     Some(box Projection {
191                         base: _,
192                         elem: ProjectionElem::ConstantIndex { .. },
193                     }),
194             }
195             | PlaceRef {
196                 base: _,
197                 projection: Some(box Projection {
198                     base: _,
199                     elem: ProjectionElem::Subslice { .. },
200                 }),
201             }
202             | PlaceRef {
203                 base: _,
204                 projection: Some(box Projection {
205                     base: _,
206                     elem: ProjectionElem::Downcast(..),
207                 }),
208             } => bug!("Unexpected immutable place."),
209         }
210
211         debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
212
213         // `act` and `acted_on` are strings that let us abstract over
214         // the verbs used in some diagnostic messages.
215         let act;
216         let acted_on;
217
218         let span = match error_access {
219             AccessKind::Move => {
220                 err = self.cannot_move_out_of(span, &(item_msg + &reason));
221                 err.span_label(span, "cannot move");
222                 err.buffer(&mut self.errors_buffer);
223                 return;
224             }
225             AccessKind::Mutate => {
226                 err = self.cannot_assign(span, &(item_msg + &reason));
227                 act = "assign";
228                 acted_on = "written";
229                 span
230             }
231             AccessKind::MutableBorrow => {
232                 act = "borrow as mutable";
233                 acted_on = "borrowed as mutable";
234
235                 let borrow_spans = self.borrow_spans(span, location);
236                 let borrow_span = borrow_spans.args_or_use();
237                 err = self.cannot_borrow_path_as_mutable_because(
238                     borrow_span,
239                     &item_msg,
240                     &reason,
241                 );
242                 borrow_spans.var_span_label(
243                     &mut err,
244                     format!(
245                         "mutable borrow occurs due to use of `{}` in closure",
246                         // always Some() if the message is printed.
247                         self.describe_place(access_place.as_place_ref()).unwrap_or_default(),
248                     )
249                 );
250                 borrow_span
251             }
252         };
253
254         debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
255
256         match the_place_err {
257             // Suggest making an existing shared borrow in a struct definition a mutable borrow.
258             //
259             // This is applicable when we have a deref of a field access to a deref of a local -
260             // something like `*((*_1).0`. The local that we get will be a reference to the
261             // struct we've got a field access of (it must be a reference since there's a deref
262             // after the field access).
263             PlaceRef {
264                 base,
265                 projection: Some(box Projection {
266                     base: Some(box Projection {
267                         base: Some(box Projection {
268                             base: base_proj,
269                             elem: ProjectionElem::Deref,
270                         }),
271                         elem: ProjectionElem::Field(field, _),
272                     }),
273                     elem: ProjectionElem::Deref,
274                 }),
275             } => {
276                 err.span_label(span, format!("cannot {ACT}", ACT = act));
277
278                 if let Some((span, message)) = annotate_struct_field(
279                     self.infcx.tcx,
280                     Place::ty_from(&base, &base_proj, self.body, self.infcx.tcx).ty,
281                     field,
282                 ) {
283                     err.span_suggestion(
284                         span,
285                         "consider changing this to be mutable",
286                         message,
287                         Applicability::MaybeIncorrect,
288                     );
289                 }
290             },
291
292             // Suggest removing a `&mut` from the use of a mutable reference.
293             PlaceRef {
294                 base: PlaceBase::Local(local),
295                 projection: None,
296             } if {
297                 self.body.local_decls.get(*local).map(|local_decl| {
298                     if let ClearCrossCrate::Set(
299                         mir::BindingForm::ImplicitSelf(kind)
300                     ) = local_decl.is_user_variable.as_ref().unwrap() {
301                         // Check if the user variable is a `&mut self` and we can therefore
302                         // suggest removing the `&mut`.
303                         //
304                         // Deliberately fall into this case for all implicit self types,
305                         // so that we don't fall in to the next case with them.
306                         *kind == mir::ImplicitSelfKind::MutRef
307                     } else if Some(kw::SelfLower) == local_decl.name {
308                         // Otherwise, check if the name is the self kewyord - in which case
309                         // we have an explicit self. Do the same thing in this case and check
310                         // for a `self: &mut Self` to suggest removing the `&mut`.
311                         if let ty::Ref(
312                             _, _, hir::Mutability::MutMutable
313                         ) = local_decl.ty.sty {
314                             true
315                         } else {
316                             false
317                         }
318                     } else {
319                         false
320                     }
321                 }).unwrap_or(false)
322             } => {
323                 err.span_label(span, format!("cannot {ACT}", ACT = act));
324                 err.span_label(span, "try removing `&mut` here");
325             },
326
327             // We want to suggest users use `let mut` for local (user
328             // variable) mutations...
329             PlaceRef {
330                 base: PlaceBase::Local(local),
331                 projection: None,
332             } if self.body.local_decls[*local].can_be_made_mutable() => {
333                 // ... but it doesn't make sense to suggest it on
334                 // variables that are `ref x`, `ref mut x`, `&self`,
335                 // or `&mut self` (such variables are simply not
336                 // mutable).
337                 let local_decl = &self.body.local_decls[*local];
338                 assert_eq!(local_decl.mutability, Mutability::Not);
339
340                 err.span_label(span, format!("cannot {ACT}", ACT = act));
341                 err.span_suggestion(
342                     local_decl.source_info.span,
343                     "consider changing this to be mutable",
344                     format!("mut {}", local_decl.name.unwrap()),
345                     Applicability::MachineApplicable,
346                 );
347             }
348
349             // Also suggest adding mut for upvars
350             PlaceRef {
351                 base,
352                 projection: Some(box Projection {
353                     base: proj_base,
354                     elem: ProjectionElem::Field(upvar_index, _),
355                 }),
356             } => {
357                 debug_assert!(is_closure_or_generator(
358                     Place::ty_from(&base, &proj_base, self.body, self.infcx.tcx).ty
359                 ));
360
361                 err.span_label(span, format!("cannot {ACT}", ACT = act));
362
363                 let upvar_hir_id = self.upvars[upvar_index.index()].var_hir_id;
364                 if let Some(Node::Binding(pat)) = self.infcx.tcx.hir().find(upvar_hir_id)
365                 {
366                     if let hir::PatKind::Binding(
367                         hir::BindingAnnotation::Unannotated,
368                         _,
369                         upvar_ident,
370                         _,
371                     ) = pat.node
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
383             // complete hack to approximate old AST-borrowck
384             // diagnostic: if the span starts with a mutable borrow of
385             // a local variable, then just suggest the user remove it.
386             PlaceRef {
387                 base: PlaceBase::Local(_),
388                 projection: None,
389             } if {
390                     if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
391                         snippet.starts_with("&mut ")
392                     } else {
393                         false
394                     }
395                 } =>
396             {
397                 err.span_label(span, format!("cannot {ACT}", ACT = act));
398                 err.span_label(span, "try removing `&mut` here");
399             }
400
401             PlaceRef {
402                 base: PlaceBase::Local(local),
403                 projection: Some(box Projection {
404                     base: None,
405                     elem: ProjectionElem::Deref,
406                 }),
407             } if {
408                 if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) =
409                     self.body.local_decls[*local].is_user_variable
410                 {
411                     true
412                 } else {
413                     false
414                 }
415             } =>
416             {
417                 err.span_label(span, format!("cannot {ACT}", ACT = act));
418                 err.note(
419                     "variables bound in patterns are immutable until the end of the pattern guard",
420                 );
421             }
422
423             // We want to point out when a `&` can be readily replaced
424             // with an `&mut`.
425             //
426             // FIXME: can this case be generalized to work for an
427             // arbitrary base for the projection?
428             PlaceRef {
429                 base: PlaceBase::Local(local),
430                 projection: Some(box Projection {
431                     base: None,
432                     elem: ProjectionElem::Deref,
433                 }),
434             } if self.body.local_decls[*local].is_user_variable.is_some() =>
435             {
436                 let local_decl = &self.body.local_decls[*local];
437                 let suggestion = match local_decl.is_user_variable.as_ref().unwrap() {
438                     ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf(_)) => {
439                         Some(suggest_ampmut_self(self.infcx.tcx, local_decl))
440                     }
441
442                     ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
443                         binding_mode: ty::BindingMode::BindByValue(_),
444                         opt_ty_info,
445                         ..
446                     })) => Some(suggest_ampmut(
447                         self.infcx.tcx,
448                         self.body,
449                         *local,
450                         local_decl,
451                         *opt_ty_info,
452                     )),
453
454                     ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
455                         binding_mode: ty::BindingMode::BindByReference(_),
456                         ..
457                     })) => {
458                         let pattern_span = local_decl.source_info.span;
459                         suggest_ref_mut(self.infcx.tcx, pattern_span)
460                             .map(|replacement| (pattern_span, replacement))
461                     }
462
463                     ClearCrossCrate::Set(mir::BindingForm::RefForGuard) => unreachable!(),
464
465                     ClearCrossCrate::Clear => bug!("saw cleared local state"),
466                 };
467
468                 let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() {
469                     ("&", "reference")
470                 } else {
471                     ("*const", "pointer")
472                 };
473
474                 if let Some((err_help_span, suggested_code)) = suggestion {
475                     err.span_suggestion(
476                         err_help_span,
477                         &format!("consider changing this to be a mutable {}", pointer_desc),
478                         suggested_code,
479                         Applicability::MachineApplicable,
480                     );
481                 }
482
483                 match local_decl.name {
484                     Some(name) if !local_decl.from_compiler_desugaring() => {
485                         err.span_label(
486                             span,
487                             format!(
488                                 "`{NAME}` is a `{SIGIL}` {DESC}, \
489                                 so the data it refers to cannot be {ACTED_ON}",
490                                 NAME = name,
491                                 SIGIL = pointer_sigil,
492                                 DESC = pointer_desc,
493                                 ACTED_ON = acted_on
494                             ),
495                         );
496                     }
497                     _ => {
498                         err.span_label(
499                             span,
500                             format!(
501                                 "cannot {ACT} through `{SIGIL}` {DESC}",
502                                 ACT = act,
503                                 SIGIL = pointer_sigil,
504                                 DESC = pointer_desc
505                             ),
506                         );
507                     }
508                 }
509             }
510
511             PlaceRef {
512                 base,
513                 projection: Some(box Projection {
514                     base: None,
515                     elem: ProjectionElem::Deref,
516                 }),
517             // FIXME document what is this 1 magic number about
518             } if *base == PlaceBase::Local(Local::new(1)) &&
519                   !self.upvars.is_empty() =>
520             {
521                 err.span_label(span, format!("cannot {ACT}", ACT = act));
522                 err.span_help(
523                     self.body.span,
524                     "consider changing this to accept closures that implement `FnMut`"
525                 );
526             }
527
528             PlaceRef {
529                 base: _,
530                 projection: Some(box Projection {
531                     base: _,
532                     elem: ProjectionElem::Deref,
533                 }),
534             } => {
535                 err.span_label(span, format!("cannot {ACT}", ACT = act));
536
537                 match opt_source {
538                     Some(BorrowedContentSource::OverloadedDeref(ty)) => {
539                         err.help(
540                             &format!(
541                                 "trait `DerefMut` is required to modify through a dereference, \
542                                 but it is not implemented for `{}`",
543                                 ty,
544                             ),
545                         );
546                     },
547                     Some(BorrowedContentSource::OverloadedIndex(ty)) => {
548                         err.help(
549                             &format!(
550                                 "trait `IndexMut` is required to modify indexed content, \
551                                 but it is not implemented for `{}`",
552                                 ty,
553                             ),
554                         );
555                     }
556                     _ => (),
557                 }
558             }
559
560             _ => {
561                 err.span_label(span, format!("cannot {ACT}", ACT = act));
562             }
563         }
564
565         err.buffer(&mut self.errors_buffer);
566     }
567 }
568
569 fn suggest_ampmut_self<'tcx>(
570     tcx: TyCtxt<'tcx>,
571     local_decl: &mir::LocalDecl<'tcx>,
572 ) -> (Span, String) {
573     let sp = local_decl.source_info.span;
574     (sp, match tcx.sess.source_map().span_to_snippet(sp) {
575         Ok(snippet) => {
576             let lt_pos = snippet.find('\'');
577             if let Some(lt_pos) = lt_pos {
578                 format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
579             } else {
580                 "&mut self".to_string()
581             }
582         }
583         _ => "&mut self".to_string()
584     })
585 }
586
587 // When we want to suggest a user change a local variable to be a `&mut`, there
588 // are three potential "obvious" things to highlight:
589 //
590 // let ident [: Type] [= RightHandSideExpression];
591 //     ^^^^^    ^^^^     ^^^^^^^^^^^^^^^^^^^^^^^
592 //     (1.)     (2.)              (3.)
593 //
594 // We can always fallback on highlighting the first. But chances are good that
595 // the user experience will be better if we highlight one of the others if possible;
596 // for example, if the RHS is present and the Type is not, then the type is going to
597 // be inferred *from* the RHS, which means we should highlight that (and suggest
598 // that they borrow the RHS mutably).
599 //
600 // This implementation attempts to emulate AST-borrowck prioritization
601 // by trying (3.), then (2.) and finally falling back on (1.).
602 fn suggest_ampmut<'tcx>(
603     tcx: TyCtxt<'tcx>,
604     body: &Body<'tcx>,
605     local: Local,
606     local_decl: &mir::LocalDecl<'tcx>,
607     opt_ty_info: Option<Span>,
608 ) -> (Span, String) {
609     let locations = body.find_assignments(local);
610     if !locations.is_empty() {
611         let assignment_rhs_span = body.source_info(locations[0]).span;
612         if let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span) {
613             if let (true, Some(ws_pos)) = (
614                 src.starts_with("&'"),
615                 src.find(|c: char| -> bool { c.is_whitespace() }),
616             ) {
617                 let lt_name = &src[1..ws_pos];
618                 let ty = &src[ws_pos..];
619                 return (assignment_rhs_span, format!("&{} mut {}", lt_name, ty));
620             } else if src.starts_with('&') {
621                 let borrowed_expr = &src[1..];
622                 return (assignment_rhs_span, format!("&mut {}", borrowed_expr));
623             }
624         }
625     }
626
627     let highlight_span = match opt_ty_info {
628         // if this is a variable binding with an explicit type,
629         // try to highlight that for the suggestion.
630         Some(ty_span) => ty_span,
631
632         // otherwise, just highlight the span associated with
633         // the (MIR) LocalDecl.
634         None => local_decl.source_info.span,
635     };
636
637     if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span) {
638         if let (true, Some(ws_pos)) = (
639             src.starts_with("&'"),
640             src.find(|c: char| -> bool { c.is_whitespace() }),
641         ) {
642             let lt_name = &src[1..ws_pos];
643             let ty = &src[ws_pos..];
644             return (highlight_span, format!("&{} mut{}", lt_name, ty));
645         }
646     }
647
648     let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
649     assert_eq!(ty_mut.mutbl, hir::MutImmutable);
650     (highlight_span,
651      if local_decl.ty.is_region_ptr() {
652          format!("&mut {}", ty_mut.ty)
653      } else {
654          format!("*mut {}", ty_mut.ty)
655      })
656 }
657
658 fn is_closure_or_generator(ty: Ty<'_>) -> bool {
659     ty.is_closure() || ty.is_generator()
660 }
661
662 /// Adds a suggestion to a struct definition given a field access to a local.
663 /// This function expects the local to be a reference to a struct in order to produce a suggestion.
664 ///
665 /// ```text
666 /// LL |     s: &'a String
667 ///    |        ---------- use `&'a mut String` here to make mutable
668 /// ```
669 fn annotate_struct_field(
670     tcx: TyCtxt<'tcx>,
671     ty: Ty<'tcx>,
672     field: &mir::Field,
673 ) -> Option<(Span, String)> {
674     // Expect our local to be a reference to a struct of some kind.
675     if let ty::Ref(_, ty, _) = ty.sty {
676         if let ty::Adt(def, _) = ty.sty {
677             let field = def.all_fields().nth(field.index())?;
678             // Use the HIR types to construct the diagnostic message.
679             let hir_id = tcx.hir().as_local_hir_id(field.did)?;
680             let node = tcx.hir().find(hir_id)?;
681             // Now we're dealing with the actual struct that we're going to suggest a change to,
682             // we can expect a field that is an immutable reference to a type.
683             if let hir::Node::Field(field) = node {
684                 if let hir::TyKind::Rptr(lifetime, hir::MutTy {
685                     mutbl: hir::Mutability::MutImmutable,
686                     ref ty
687                 }) = field.ty.node {
688                     // Get the snippets in two parts - the named lifetime (if there is one) and
689                     // type being referenced, that way we can reconstruct the snippet without loss
690                     // of detail.
691                     let type_snippet = tcx.sess.source_map().span_to_snippet(ty.span).ok()?;
692                     let lifetime_snippet = if !lifetime.is_elided() {
693                         format!("{} ", tcx.sess.source_map().span_to_snippet(lifetime.span).ok()?)
694                     } else {
695                         String::new()
696                     };
697
698                     return Some((
699                         field.ty.span,
700                         format!(
701                             "&{}mut {}",
702                             lifetime_snippet, &*type_snippet,
703                         ),
704                     ));
705                 }
706             }
707         }
708     }
709
710     None
711 }
712
713 /// If possible, suggest replacing `ref` with `ref mut`.
714 fn suggest_ref_mut(tcx: TyCtxt<'_>, binding_span: Span) -> Option<(String)> {
715     let hi_src = tcx.sess.source_map().span_to_snippet(binding_span).unwrap();
716     if hi_src.starts_with("ref")
717         && hi_src["ref".len()..].starts_with(Pattern_White_Space)
718     {
719         let replacement = format!("ref mut{}", &hi_src["ref".len()..]);
720         Some(replacement)
721     } else {
722         None
723     }
724 }