]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/error_reporting.rs
Suggest that values are dropped in the opposite order they are defined
[rust.git] / src / librustc_mir / borrow_check / error_reporting.rs
1 // Copyright 2017 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 borrow_check::WriteKind;
12 use syntax_pos::Span;
13 use rustc::middle::region::ScopeTree;
14 use rustc::mir::{BorrowKind, Field, Local, LocalKind, Location, Operand};
15 use rustc::mir::{Place, ProjectionElem, Rvalue, Statement, StatementKind};
16 use rustc::ty::{self, RegionKind};
17 use rustc_data_structures::indexed_vec::Idx;
18 use rustc_data_structures::sync::Lrc;
19
20 use super::{Context, MirBorrowckCtxt};
21 use super::{InitializationRequiringAction, PrefixSet};
22 use super::borrow_set::BorrowData;
23
24 use dataflow::{FlowAtLocation, MovingOutStatements};
25 use dataflow::move_paths::MovePathIndex;
26 use util::borrowck_errors::{BorrowckErrors, Origin};
27
28 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
29     pub(super) fn report_use_of_moved_or_uninitialized(
30         &mut self,
31         _context: Context,
32         desired_action: InitializationRequiringAction,
33         (place, span): (&Place<'tcx>, Span),
34         mpi: MovePathIndex,
35         curr_move_out: &FlowAtLocation<MovingOutStatements<'_, 'gcx, 'tcx>>,
36     ) {
37         let mois = self.move_data.path_map[mpi]
38             .iter()
39             .filter(|moi| curr_move_out.contains(moi))
40             .collect::<Vec<_>>();
41
42         if mois.is_empty() {
43             let root_place = self.prefixes(&place, PrefixSet::All)
44                 .last()
45                 .unwrap();
46
47             if self.moved_error_reported
48                 .contains(&root_place.clone())
49             {
50                 debug!(
51                     "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
52                     root_place
53                 );
54                 return;
55             }
56
57             self.moved_error_reported
58                 .insert(root_place.clone());
59
60             let item_msg = match self.describe_place(place) {
61                 Some(name) => format!("`{}`", name),
62                 None => "value".to_owned(),
63             };
64             self.tcx
65                 .cannot_act_on_uninitialized_variable(
66                     span,
67                     desired_action.as_noun(),
68                     &self.describe_place(place).unwrap_or("_".to_owned()),
69                     Origin::Mir,
70                 )
71                 .span_label(span, format!("use of possibly uninitialized {}", item_msg))
72                 .emit();
73         } else {
74             let msg = ""; //FIXME: add "partially " or "collaterally "
75
76             let mut err = self.tcx.cannot_act_on_moved_value(
77                 span,
78                 desired_action.as_noun(),
79                 msg,
80                 &self.describe_place(place).unwrap_or("_".to_owned()),
81                 Origin::Mir,
82             );
83
84             let mut is_loop_move = false;
85             for moi in mois {
86                 let move_msg = ""; //FIXME: add " (into closure)"
87                 let move_span = self.mir.source_info(self.move_data.moves[*moi].source).span;
88                 if span == move_span {
89                     err.span_label(
90                         span,
91                         format!("value moved{} here in previous iteration of loop", move_msg),
92                     );
93                     is_loop_move = true;
94                 } else {
95                     err.span_label(move_span, format!("value moved{} here", move_msg));
96                 };
97             }
98             if !is_loop_move {
99                 err.span_label(
100                     span,
101                     format!(
102                         "value {} here after move",
103                         desired_action.as_verb_in_past_tense()
104                     ),
105                 );
106             }
107
108             if let Some(ty) = self.retrieve_type_for_place(place) {
109                 let needs_note = match ty.sty {
110                     ty::TypeVariants::TyClosure(id, _) => {
111                         let tables = self.tcx.typeck_tables_of(id);
112                         let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
113                         let hir_id = self.tcx.hir.node_to_hir_id(node_id);
114                         if let Some(_) = tables.closure_kind_origins().get(hir_id) {
115                             false
116                         } else {
117                             true
118                         }
119                     }
120                     _ => true,
121                 };
122
123                 if needs_note {
124                     let note_msg = match self.describe_place(place) {
125                         Some(name) => format!("`{}`", name),
126                         None => "value".to_owned(),
127                     };
128
129                     err.note(&format!(
130                         "move occurs because {} has type `{}`, \
131                          which does not implement the `Copy` trait",
132                         note_msg, ty
133                     ));
134                 }
135             }
136
137             err.emit();
138         }
139     }
140
141     pub(super) fn report_move_out_while_borrowed(
142         &mut self,
143         context: Context,
144         (place, span): (&Place<'tcx>, Span),
145         borrow: &BorrowData<'tcx>,
146     ) {
147         let tcx = self.tcx;
148         let value_msg = match self.describe_place(place) {
149             Some(name) => format!("`{}`", name),
150             None => "value".to_owned(),
151         };
152         let borrow_msg = match self.describe_place(&borrow.borrowed_place) {
153             Some(name) => format!("`{}`", name),
154             None => "value".to_owned(),
155         };
156         let mut err = tcx.cannot_move_when_borrowed(
157             span,
158             &self.describe_place(place).unwrap_or("_".to_owned()),
159             Origin::Mir,
160         );
161         err.span_label(
162             self.retrieve_borrow_span(borrow),
163             format!("borrow of {} occurs here", borrow_msg),
164         );
165         err.span_label(span, format!("move out of {} occurs here", value_msg));
166         self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
167         err.emit();
168     }
169
170     pub(super) fn report_use_while_mutably_borrowed(
171         &mut self,
172         context: Context,
173         (place, span): (&Place<'tcx>, Span),
174         borrow: &BorrowData<'tcx>,
175     ) {
176         let tcx = self.tcx;
177         let mut err = tcx.cannot_use_when_mutably_borrowed(
178             span,
179             &self.describe_place(place).unwrap_or("_".to_owned()),
180             self.retrieve_borrow_span(borrow),
181             &self.describe_place(&borrow.borrowed_place)
182                 .unwrap_or("_".to_owned()),
183             Origin::Mir,
184         );
185
186         self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
187
188         err.emit();
189     }
190
191     /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of
192     /// the local assigned at `location`.
193     /// This is done by searching in statements succeeding `location`
194     /// and originating from `maybe_closure_span`.
195     fn find_closure_span(
196         &self,
197         maybe_closure_span: Span,
198         location: Location,
199     ) -> Option<(Span, Span)> {
200         use rustc::hir::ExprClosure;
201         use rustc::mir::AggregateKind;
202
203         let local = match self.mir[location.block]
204             .statements
205             .get(location.statement_index)
206         {
207             Some(&Statement {
208                 kind: StatementKind::Assign(Place::Local(local), _),
209                 ..
210             }) => local,
211             _ => return None,
212         };
213
214         for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
215             if maybe_closure_span != stmt.source_info.span {
216                 break;
217             }
218
219             if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
220                 if let AggregateKind::Closure(def_id, _) = **kind {
221                     debug!("find_closure_span: found closure {:?}", places);
222
223                     return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
224                         let args_span = if let ExprClosure(_, _, _, span, _) =
225                             self.tcx.hir.expect_expr(node_id).node
226                         {
227                             span
228                         } else {
229                             return None;
230                         };
231
232                         self.tcx
233                             .with_freevars(node_id, |freevars| {
234                                 for (v, place) in freevars.iter().zip(places) {
235                                     match *place {
236                                         Operand::Copy(Place::Local(l))
237                                         | Operand::Move(Place::Local(l)) if local == l =>
238                                         {
239                                             debug!(
240                                                 "find_closure_span: found captured local {:?}",
241                                                 l
242                                             );
243                                             return Some(v.span);
244                                         }
245                                         _ => {}
246                                     }
247                                 }
248                                 None
249                             })
250                             .map(|var_span| (args_span, var_span))
251                     } else {
252                         None
253                     };
254                 }
255             }
256         }
257
258         None
259     }
260
261     pub(super) fn report_conflicting_borrow(
262         &mut self,
263         context: Context,
264         (place, span): (&Place<'tcx>, Span),
265         gen_borrow_kind: BorrowKind,
266         issued_borrow: &BorrowData<'tcx>,
267     ) {
268         let issued_span = self.retrieve_borrow_span(issued_borrow);
269
270         let new_closure_span = self.find_closure_span(span, context.loc);
271         let span = new_closure_span.map(|(args, _)| args).unwrap_or(span);
272         let old_closure_span = self.find_closure_span(issued_span, issued_borrow.reserve_location);
273         let issued_span = old_closure_span
274             .map(|(args, _)| args)
275             .unwrap_or(issued_span);
276
277         let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
278         let tcx = self.tcx;
279
280         // FIXME: supply non-"" `opt_via` when appropriate
281         let mut err = match (
282             gen_borrow_kind,
283             "immutable",
284             "mutable",
285             issued_borrow.kind,
286             "immutable",
287             "mutable",
288         ) {
289             (BorrowKind::Shared, lft, _, BorrowKind::Mut { .. }, _, rgt)
290             | (BorrowKind::Mut { .. }, _, lft, BorrowKind::Shared, rgt, _) => {
291                 tcx.cannot_reborrow_already_borrowed(
292                     span,
293                     &desc_place,
294                     "",
295                     lft,
296                     issued_span,
297                     "it",
298                     rgt,
299                     "",
300                     None,
301                     Origin::Mir,
302                 )
303             }
304
305             (BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => {
306                 tcx.cannot_mutably_borrow_multiply(
307                     span,
308                     &desc_place,
309                     "",
310                     issued_span,
311                     "",
312                     None,
313                     Origin::Mir,
314                 )
315             }
316
317             (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => {
318                 tcx.cannot_uniquely_borrow_by_two_closures(
319                     span,
320                     &desc_place,
321                     issued_span,
322                     None,
323                     Origin::Mir,
324                 )
325             }
326
327             (BorrowKind::Unique, _, _, _, _, _) => tcx.cannot_uniquely_borrow_by_one_closure(
328                 span,
329                 &desc_place,
330                 "",
331                 issued_span,
332                 "it",
333                 "",
334                 None,
335                 Origin::Mir,
336             ),
337
338             (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => {
339                 tcx.cannot_reborrow_already_uniquely_borrowed(
340                     span,
341                     &desc_place,
342                     "",
343                     lft,
344                     issued_span,
345                     "",
346                     None,
347                     Origin::Mir,
348                 )
349             }
350
351             (BorrowKind::Mut { .. }, _, lft, BorrowKind::Unique, _, _) => {
352                 tcx.cannot_reborrow_already_uniquely_borrowed(
353                     span,
354                     &desc_place,
355                     "",
356                     lft,
357                     issued_span,
358                     "",
359                     None,
360                     Origin::Mir,
361                 )
362             }
363
364             (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(),
365         };
366
367         if let Some((_, var_span)) = old_closure_span {
368             err.span_label(
369                 var_span,
370                 format!(
371                     "previous borrow occurs due to use of `{}` in closure",
372                     desc_place
373                 ),
374             );
375         }
376
377         if let Some((_, var_span)) = new_closure_span {
378             err.span_label(
379                 var_span,
380                 format!("borrow occurs due to use of `{}` in closure", desc_place),
381             );
382         }
383
384         self.explain_why_borrow_contains_point(context, issued_borrow, None, &mut err);
385
386         err.emit();
387     }
388
389     pub(super) fn report_borrowed_value_does_not_live_long_enough(
390         &mut self,
391         context: Context,
392         borrow: &BorrowData<'tcx>,
393         place_span: (&Place<'tcx>, Span),
394         kind: Option<WriteKind>,
395     ) {
396         let drop_span = place_span.1;
397         let scope_tree = self.tcx.region_scope_tree(self.mir_def_id);
398         let root_place = self.prefixes(&borrow.borrowed_place, PrefixSet::All)
399             .last()
400             .unwrap();
401
402         let borrow_span = self.mir.source_info(borrow.reserve_location).span;
403         let proper_span = match *root_place {
404             Place::Local(local) => self.mir.local_decls[local].source_info.span,
405             _ => drop_span,
406         };
407
408         if self.access_place_error_reported
409             .contains(&(root_place.clone(), borrow_span))
410         {
411             debug!(
412                 "suppressing access_place error when borrow doesn't live long enough for {:?}",
413                 borrow_span
414             );
415             return;
416         }
417
418         self.access_place_error_reported
419             .insert((root_place.clone(), borrow_span));
420
421         match (borrow.region, &self.describe_place(&borrow.borrowed_place)) {
422             (RegionKind::ReScope(_), Some(name)) => {
423                 self.report_scoped_local_value_does_not_live_long_enough(
424                     context,
425                     name,
426                     &scope_tree,
427                     &borrow,
428                     drop_span,
429                     borrow_span,
430                     proper_span,
431                 );
432             }
433             (RegionKind::ReScope(_), None) => {
434                 self.report_scoped_temporary_value_does_not_live_long_enough(
435                     context,
436                     &scope_tree,
437                     &borrow,
438                     drop_span,
439                     borrow_span,
440                     proper_span,
441                 );
442             }
443             (RegionKind::ReEarlyBound(_), Some(name))
444             | (RegionKind::ReFree(_), Some(name))
445             | (RegionKind::ReStatic, Some(name))
446             | (RegionKind::ReEmpty, Some(name))
447             | (RegionKind::ReVar(_), Some(name)) => {
448                 self.report_unscoped_local_value_does_not_live_long_enough(
449                     context,
450                     name,
451                     &scope_tree,
452                     &borrow,
453                     drop_span,
454                     borrow_span,
455                     proper_span,
456                     kind.map(|k| (k, place_span.0)),
457                 );
458             }
459             (RegionKind::ReEarlyBound(_), None)
460             | (RegionKind::ReFree(_), None)
461             | (RegionKind::ReStatic, None)
462             | (RegionKind::ReEmpty, None)
463             | (RegionKind::ReVar(_), None) => {
464                 self.report_unscoped_temporary_value_does_not_live_long_enough(
465                     context,
466                     &scope_tree,
467                     &borrow,
468                     drop_span,
469                     borrow_span,
470                     proper_span,
471                 );
472             }
473             (RegionKind::ReLateBound(_, _), _)
474             | (RegionKind::ReSkolemized(_, _), _)
475             | (RegionKind::ReClosureBound(_), _)
476             | (RegionKind::ReCanonical(_), _)
477             | (RegionKind::ReErased, _) => {
478                 span_bug!(drop_span, "region {:?} does not make sense in this context",
479                           borrow.region);
480             }
481         }
482     }
483
484     fn report_scoped_local_value_does_not_live_long_enough(
485         &mut self,
486         context: Context,
487         name: &String,
488         _scope_tree: &Lrc<ScopeTree>,
489         borrow: &BorrowData<'tcx>,
490         drop_span: Span,
491         borrow_span: Span,
492         _proper_span: Span,
493     ) {
494         let tcx = self.tcx;
495         let mut err =
496             tcx.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name), Origin::Mir);
497         err.span_label(borrow_span, "borrowed value does not live long enough");
498         err.span_label(
499             drop_span,
500             format!("`{}` dropped here while still borrowed", name),
501         );
502         self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
503         err.emit();
504     }
505
506     fn report_scoped_temporary_value_does_not_live_long_enough(
507         &mut self,
508         context: Context,
509         _scope_tree: &Lrc<ScopeTree>,
510         borrow: &BorrowData<'tcx>,
511         drop_span: Span,
512         _borrow_span: Span,
513         proper_span: Span,
514     ) {
515         let tcx = self.tcx;
516         let mut err =
517             tcx.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir);
518         err.span_label(proper_span, "temporary value does not live long enough");
519         err.span_label(
520             drop_span,
521             "temporary value dropped here while still borrowed",
522         );
523         err.note("consider using a `let` binding to increase its lifetime");
524         self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
525         err.emit();
526     }
527
528     fn report_unscoped_local_value_does_not_live_long_enough(
529         &mut self,
530         context: Context,
531         name: &String,
532         scope_tree: &Lrc<ScopeTree>,
533         borrow: &BorrowData<'tcx>,
534         drop_span: Span,
535         borrow_span: Span,
536         _proper_span: Span,
537         kind_place: Option<(WriteKind, &Place<'tcx>)>,
538     ) {
539         debug!(
540             "report_unscoped_local_value_does_not_live_long_enough(\
541              {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\
542              )",
543             context, name, scope_tree, borrow, drop_span, borrow_span
544         );
545
546         let tcx = self.tcx;
547         let mut err =
548             tcx.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name), Origin::Mir);
549         err.span_label(borrow_span, "borrowed value does not live long enough");
550         err.span_label(drop_span, "borrowed value only lives until here");
551
552         self.explain_why_borrow_contains_point(context, borrow, kind_place, &mut err);
553         err.emit();
554     }
555
556     fn report_unscoped_temporary_value_does_not_live_long_enough(
557         &mut self,
558         context: Context,
559         scope_tree: &Lrc<ScopeTree>,
560         borrow: &BorrowData<'tcx>,
561         drop_span: Span,
562         _borrow_span: Span,
563         proper_span: Span,
564     ) {
565         debug!(
566             "report_unscoped_temporary_value_does_not_live_long_enough(\
567              {:?}, {:?}, {:?}, {:?}, {:?}\
568              )",
569             context, scope_tree, borrow, drop_span, proper_span
570         );
571
572         let tcx = self.tcx;
573         let mut err =
574             tcx.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir);
575         err.span_label(proper_span, "temporary value does not live long enough");
576         err.span_label(drop_span, "temporary value only lives until here");
577
578         self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
579         err.emit();
580     }
581
582     pub(super) fn report_illegal_mutation_of_borrowed(
583         &mut self,
584         context: Context,
585         (place, span): (&Place<'tcx>, Span),
586         loan: &BorrowData<'tcx>,
587     ) {
588         let tcx = self.tcx;
589         let mut err = tcx.cannot_assign_to_borrowed(
590             span,
591             self.retrieve_borrow_span(loan),
592             &self.describe_place(place).unwrap_or("_".to_owned()),
593             Origin::Mir,
594         );
595
596         self.explain_why_borrow_contains_point(context, loan, None, &mut err);
597
598         err.emit();
599     }
600
601     /// Reports an illegal reassignment; for example, an assignment to
602     /// (part of) a non-`mut` local that occurs potentially after that
603     /// local has already been initialized. `place` is the path being
604     /// assigned; `err_place` is a place providing a reason why
605     /// `place` is not mutable (e.g. the non-`mut` local `x` in an
606     /// assignment to `x.f`).
607     pub(super) fn report_illegal_reassignment(
608         &mut self,
609         _context: Context,
610         (place, span): (&Place<'tcx>, Span),
611         assigned_span: Span,
612         err_place: &Place<'tcx>,
613     ) {
614         let is_arg = if let Place::Local(local) = place {
615             if let LocalKind::Arg = self.mir.local_kind(*local) {
616                 true
617             } else {
618                 false
619             }
620         } else {
621             false
622         };
623
624         let mut err = self.tcx.cannot_reassign_immutable(
625             span,
626             &self.describe_place(place).unwrap_or("_".to_owned()),
627             is_arg,
628             Origin::Mir,
629         );
630         let msg = if is_arg {
631             "cannot assign to immutable argument"
632         } else {
633             "cannot assign twice to immutable variable"
634         };
635         if span != assigned_span {
636             if !is_arg {
637                 let value_msg = match self.describe_place(place) {
638                     Some(name) => format!("`{}`", name),
639                     None => "value".to_owned(),
640                 };
641                 err.span_label(assigned_span, format!("first assignment to {}", value_msg));
642             }
643         }
644         if let Place::Local(local) = err_place {
645             let local_decl = &self.mir.local_decls[*local];
646             if let Some(name) = local_decl.name {
647                 if local_decl.can_be_made_mutable() {
648                     err.span_label(local_decl.source_info.span,
649                                    format!("consider changing this to `mut {}`", name));
650                 }
651             }
652         }
653         err.span_label(span, msg);
654         err.emit();
655     }
656 }
657
658 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
659     // End-user visible description of `place` if one can be found. If the
660     // place is a temporary for instance, None will be returned.
661     pub(super) fn describe_place(&self, place: &Place<'tcx>) -> Option<String> {
662         let mut buf = String::new();
663         match self.append_place_to_string(place, &mut buf, false) {
664             Ok(()) => Some(buf),
665             Err(()) => None,
666         }
667     }
668
669     // Appends end-user visible description of `place` to `buf`.
670     fn append_place_to_string(
671         &self,
672         place: &Place<'tcx>,
673         buf: &mut String,
674         mut autoderef: bool,
675     ) -> Result<(), ()> {
676         match *place {
677             Place::Local(local) => {
678                 self.append_local_to_string(local, buf)?;
679             }
680             Place::Static(ref static_) => {
681                 buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id)));
682             }
683             Place::Projection(ref proj) => {
684                 match proj.elem {
685                     ProjectionElem::Deref => {
686                         if let Some(field) = self.is_upvar_field_projection(&proj.base) {
687                             let var_index = field.index();
688                             let name = self.mir.upvar_decls[var_index].debug_name.to_string();
689                             if self.mir.upvar_decls[var_index].by_ref {
690                                 buf.push_str(&name);
691                             } else {
692                                 buf.push_str(&format!("*{}", &name));
693                             }
694                         } else {
695                             if autoderef {
696                                 self.append_place_to_string(&proj.base, buf, autoderef)?;
697                             } else {
698                                 buf.push_str(&"*");
699                                 self.append_place_to_string(&proj.base, buf, autoderef)?;
700                             }
701                         }
702                     }
703                     ProjectionElem::Downcast(..) => {
704                         self.append_place_to_string(&proj.base, buf, autoderef)?;
705                     }
706                     ProjectionElem::Field(field, _ty) => {
707                         autoderef = true;
708
709                         if let Some(field) = self.is_upvar_field_projection(place) {
710                             let var_index = field.index();
711                             let name = self.mir.upvar_decls[var_index].debug_name.to_string();
712                             buf.push_str(&name);
713                         } else {
714                             let field_name = self.describe_field(&proj.base, field);
715                             self.append_place_to_string(&proj.base, buf, autoderef)?;
716                             buf.push_str(&format!(".{}", field_name));
717                         }
718                     }
719                     ProjectionElem::Index(index) => {
720                         autoderef = true;
721
722                         self.append_place_to_string(&proj.base, buf, autoderef)?;
723                         buf.push_str("[");
724                         if let Err(_) = self.append_local_to_string(index, buf) {
725                             buf.push_str("..");
726                         }
727                         buf.push_str("]");
728                     }
729                     ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
730                         autoderef = true;
731                         // Since it isn't possible to borrow an element on a particular index and
732                         // then use another while the borrow is held, don't output indices details
733                         // to avoid confusing the end-user
734                         self.append_place_to_string(&proj.base, buf, autoderef)?;
735                         buf.push_str(&"[..]");
736                     }
737                 };
738             }
739         }
740
741         Ok(())
742     }
743
744     // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
745     // a name, then `Err` is returned
746     fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
747         let local = &self.mir.local_decls[local_index];
748         match local.name {
749             Some(name) => {
750                 buf.push_str(&format!("{}", name));
751                 Ok(())
752             }
753             None => Err(()),
754         }
755     }
756
757     // End-user visible description of the `field`nth field of `base`
758     fn describe_field(&self, base: &Place, field: Field) -> String {
759         match *base {
760             Place::Local(local) => {
761                 let local = &self.mir.local_decls[local];
762                 self.describe_field_from_ty(&local.ty, field)
763             }
764             Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field),
765             Place::Projection(ref proj) => match proj.elem {
766                 ProjectionElem::Deref => self.describe_field(&proj.base, field),
767                 ProjectionElem::Downcast(def, variant_index) => {
768                     format!("{}", def.variants[variant_index].fields[field.index()].ident)
769                 }
770                 ProjectionElem::Field(_, field_type) => {
771                     self.describe_field_from_ty(&field_type, field)
772                 }
773                 ProjectionElem::Index(..)
774                 | ProjectionElem::ConstantIndex { .. }
775                 | ProjectionElem::Subslice { .. } => {
776                     format!("{}", self.describe_field(&proj.base, field))
777                 }
778             },
779         }
780     }
781
782     // End-user visible description of the `field_index`nth field of `ty`
783     fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String {
784         if ty.is_box() {
785             // If the type is a box, the field is described from the boxed type
786             self.describe_field_from_ty(&ty.boxed_ty(), field)
787         } else {
788             match ty.sty {
789                 ty::TyAdt(def, _) => if def.is_enum() {
790                     format!("{}", field.index())
791                 } else {
792                     format!("{}", def.non_enum_variant().fields[field.index()].ident)
793                 },
794                 ty::TyTuple(_) => format!("{}", field.index()),
795                 ty::TyRef(_, ty, _) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => {
796                     self.describe_field_from_ty(&ty, field)
797                 }
798                 ty::TyArray(ty, _) | ty::TySlice(ty) => self.describe_field_from_ty(&ty, field),
799                 ty::TyClosure(def_id, _) | ty::TyGenerator(def_id, _, _) => {
800                     // Convert the def-id into a node-id. node-ids are only valid for
801                     // the local code in the current crate, so this returns an `Option` in case
802                     // the closure comes from another crate. But in that case we wouldn't
803                     // be borrowck'ing it, so we can just unwrap:
804                     let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
805                     let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]);
806
807                     self.tcx.hir.name(freevar.var_id()).to_string()
808                 }
809                 _ => {
810                     // Might need a revision when the fields in trait RFC is implemented
811                     // (https://github.com/rust-lang/rfcs/pull/1546)
812                     bug!(
813                         "End-user description not implemented for field access on `{:?}`",
814                         ty.sty
815                     );
816                 }
817             }
818         }
819     }
820
821     // Retrieve span of given borrow from the current MIR representation
822     crate fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span {
823         self.mir.source_info(borrow.reserve_location).span
824     }
825
826     // Retrieve type of a place for the current MIR representation
827     fn retrieve_type_for_place(&self, place: &Place<'tcx>) -> Option<ty::Ty> {
828         match place {
829             Place::Local(local) => {
830                 let local = &self.mir.local_decls[*local];
831                 Some(local.ty)
832             }
833             Place::Static(ref st) => Some(st.ty),
834             Place::Projection(ref proj) => match proj.elem {
835                 ProjectionElem::Field(_, ty) => Some(ty),
836                 _ => None,
837             },
838         }
839     }
840 }