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