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