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