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