]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/error_reporting.rs
3f8cd03660c43ab6708420f79eb03509076453cf
[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::VarBindingForm;
14 use rustc::mir::{BindingForm, BorrowKind, ClearCrossCrate, Field, Local};
15 use rustc::mir::{LocalDecl, LocalKind, Location, Operand, Place};
16 use rustc::mir::{ProjectionElem, Rvalue, Statement, StatementKind};
17 use rustc::ty;
18 use rustc_data_structures::fx::FxHashSet;
19 use rustc_data_structures::sync::Lrc;
20 use rustc_errors::DiagnosticBuilder;
21 use syntax_pos::Span;
22
23 use super::borrow_set::BorrowData;
24 use super::{Context, MirBorrowckCtxt};
25 use super::{InitializationRequiringAction, PrefixSet};
26
27 use dataflow::drop_flag_effects;
28 use dataflow::move_paths::indexes::MoveOutIndex;
29 use dataflow::move_paths::MovePathIndex;
30 use util::borrowck_errors::{BorrowckErrors, Origin};
31
32 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
33     pub(super) fn report_use_of_moved_or_uninitialized(
34         &mut self,
35         context: Context,
36         desired_action: InitializationRequiringAction,
37         (place, span): (&Place<'tcx>, Span),
38         mpi: MovePathIndex,
39     ) {
40         let use_spans = self
41             .move_spans(place, context.loc)
42             .or_else(|| self.borrow_spans(span, context.loc));
43         let span = use_spans.args_or_use();
44
45         let mois = self.get_moved_indexes(context, mpi);
46         debug!("report_use_of_moved_or_uninitialized: mois={:?}", mois);
47
48         if mois.is_empty() {
49             let root_place = self.prefixes(&place, PrefixSet::All).last().unwrap();
50
51             if self.moved_error_reported.contains(&root_place.clone()) {
52                 debug!(
53                     "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
54                     root_place
55                 );
56                 return;
57             }
58
59             self.moved_error_reported.insert(root_place.clone());
60
61             let item_msg = match self.describe_place_with_options(place, IncludingDowncast(true)) {
62                 Some(name) => format!("`{}`", name),
63                 None => "value".to_owned(),
64             };
65             let mut err = self.tcx.cannot_act_on_uninitialized_variable(
66                 span,
67                 desired_action.as_noun(),
68                 &self
69                     .describe_place_with_options(place, IncludingDowncast(true))
70                     .unwrap_or("_".to_owned()),
71                 Origin::Mir,
72             );
73             err.span_label(span, format!("use of possibly uninitialized {}", item_msg));
74
75             use_spans.var_span_label(
76                 &mut err,
77                 format!("{} occurs due to use in closure", desired_action.as_noun()),
78             );
79
80             err.buffer(&mut self.errors_buffer);
81         } else {
82             let msg = ""; //FIXME: add "partially " or "collaterally "
83
84             let mut err = self.tcx.cannot_act_on_moved_value(
85                 span,
86                 desired_action.as_noun(),
87                 msg,
88                 self.describe_place_with_options(&place, IncludingDowncast(true)),
89                 Origin::Mir,
90             );
91
92             let mut is_loop_move = false;
93             for moi in &mois {
94                 let move_out = self.move_data.moves[*moi];
95                 let moved_place = &self.move_data.move_paths[move_out.path].place;
96
97                 let move_spans = self.move_spans(moved_place, move_out.source);
98                 let move_span = move_spans.args_or_use();
99
100                 let move_msg = if move_spans.for_closure() {
101                     " into closure"
102                 } else {
103                     ""
104                 };
105
106                 if span == move_span {
107                     err.span_label(
108                         span,
109                         format!("value moved{} here in previous iteration of loop", move_msg),
110                     );
111                     is_loop_move = true;
112                 } else {
113                     err.span_label(move_span, format!("value moved{} here", move_msg));
114                     move_spans.var_span_label(&mut err, "variable moved due to use in closure");
115                 };
116             }
117
118             use_spans.var_span_label(
119                 &mut err,
120                 format!("{} occurs due to use in closure", desired_action.as_noun()),
121             );
122
123             if !is_loop_move {
124                 err.span_label(
125                     span,
126                     format!(
127                         "value {} here after move",
128                         desired_action.as_verb_in_past_tense()
129                     ),
130                 );
131             }
132
133             if let Some(ty) = self.retrieve_type_for_place(place) {
134                 let needs_note = match ty.sty {
135                     ty::Closure(id, _) => {
136                         let tables = self.tcx.typeck_tables_of(id);
137                         let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
138                         let hir_id = self.tcx.hir.node_to_hir_id(node_id);
139                         if tables.closure_kind_origins().get(hir_id).is_some() {
140                             false
141                         } else {
142                             true
143                         }
144                     }
145                     _ => true,
146                 };
147
148                 if needs_note {
149                     let mpi = self.move_data.moves[mois[0]].path;
150                     let place = &self.move_data.move_paths[mpi].place;
151
152                     if let Some(ty) = self.retrieve_type_for_place(place) {
153                         let note_msg = match self
154                             .describe_place_with_options(place, IncludingDowncast(true))
155                         {
156                             Some(name) => format!("`{}`", name),
157                             None => "value".to_owned(),
158                         };
159
160                         err.note(&format!(
161                             "move occurs because {} has type `{}`, \
162                              which does not implement the `Copy` trait",
163                             note_msg, ty
164                         ));
165                     }
166                 }
167             }
168
169             err.buffer(&mut self.errors_buffer);
170         }
171     }
172
173     pub(super) fn report_move_out_while_borrowed(
174         &mut self,
175         context: Context,
176         (place, _span): (&Place<'tcx>, Span),
177         borrow: &BorrowData<'tcx>,
178     ) {
179         let tcx = self.tcx;
180         let value_msg = match self.describe_place(place) {
181             Some(name) => format!("`{}`", name),
182             None => "value".to_owned(),
183         };
184         let borrow_msg = match self.describe_place(&borrow.borrowed_place) {
185             Some(name) => format!("`{}`", name),
186             None => "value".to_owned(),
187         };
188
189         let borrow_spans = self.retrieve_borrow_spans(borrow);
190         let borrow_span = borrow_spans.args_or_use();
191
192         let move_spans = self.move_spans(place, context.loc);
193         let span = move_spans.args_or_use();
194
195         let mut err = tcx.cannot_move_when_borrowed(
196             span,
197             &self.describe_place(place).unwrap_or("_".to_owned()),
198             Origin::Mir,
199         );
200         err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
201         err.span_label(span, format!("move out of {} occurs here", value_msg));
202
203         borrow_spans.var_span_label(&mut err, "borrow occurs due to use in closure");
204
205         move_spans.var_span_label(&mut err, "move occurs due to use in closure");
206
207         self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
208         err.buffer(&mut self.errors_buffer);
209     }
210
211     pub(super) fn report_use_while_mutably_borrowed(
212         &mut self,
213         context: Context,
214         (place, _span): (&Place<'tcx>, Span),
215         borrow: &BorrowData<'tcx>,
216     ) {
217         let tcx = self.tcx;
218
219         let borrow_spans = self.retrieve_borrow_spans(borrow);
220         let borrow_span = borrow_spans.args_or_use();
221
222         // Conflicting borrows are reported separately, so only check for move
223         // captures.
224         let use_spans = self.move_spans(place, context.loc);
225         let span = use_spans.var_or_use();
226
227         let mut err = tcx.cannot_use_when_mutably_borrowed(
228             span,
229             &self.describe_place(place).unwrap_or("_".to_owned()),
230             borrow_span,
231             &self
232                 .describe_place(&borrow.borrowed_place)
233                 .unwrap_or("_".to_owned()),
234             Origin::Mir,
235         );
236
237         borrow_spans.var_span_label(&mut err, {
238             let place = &borrow.borrowed_place;
239             let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
240
241             format!("borrow occurs due to use of `{}` in closure", desc_place)
242         });
243
244         self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
245         err.buffer(&mut self.errors_buffer);
246     }
247
248     pub(super) fn report_conflicting_borrow(
249         &mut self,
250         context: Context,
251         (place, span): (&Place<'tcx>, Span),
252         gen_borrow_kind: BorrowKind,
253         issued_borrow: &BorrowData<'tcx>,
254     ) {
255         let issued_spans = self.retrieve_borrow_spans(issued_borrow);
256         let issued_span = issued_spans.args_or_use();
257
258         let borrow_spans = self.borrow_spans(span, context.loc);
259         let span = borrow_spans.args_or_use();
260
261         let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
262         let tcx = self.tcx;
263
264         // FIXME: supply non-"" `opt_via` when appropriate
265         let mut err = match (
266             gen_borrow_kind,
267             "immutable",
268             "mutable",
269             issued_borrow.kind,
270             "immutable",
271             "mutable",
272         ) {
273             (BorrowKind::Shared, lft, _, BorrowKind::Mut { .. }, _, rgt)
274             | (BorrowKind::Mut { .. }, _, lft, BorrowKind::Shared, rgt, _) => tcx
275                 .cannot_reborrow_already_borrowed(
276                     span,
277                     &desc_place,
278                     "",
279                     lft,
280                     issued_span,
281                     "it",
282                     rgt,
283                     "",
284                     None,
285                     Origin::Mir,
286                 ),
287
288             (BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => tcx
289                 .cannot_mutably_borrow_multiply(
290                     span,
291                     &desc_place,
292                     "",
293                     issued_span,
294                     "",
295                     None,
296                     Origin::Mir,
297                 ),
298
299             (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => tcx
300                 .cannot_uniquely_borrow_by_two_closures(
301                     span,
302                     &desc_place,
303                     issued_span,
304                     None,
305                     Origin::Mir,
306                 ),
307
308             (BorrowKind::Unique, _, _, _, _, _) => tcx.cannot_uniquely_borrow_by_one_closure(
309                 span,
310                 &desc_place,
311                 "",
312                 issued_span,
313                 "it",
314                 "",
315                 None,
316                 Origin::Mir,
317             ),
318
319             (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => tcx
320                 .cannot_reborrow_already_uniquely_borrowed(
321                     span,
322                     &desc_place,
323                     "",
324                     lft,
325                     issued_span,
326                     "",
327                     None,
328                     Origin::Mir,
329                 ),
330
331             (BorrowKind::Mut { .. }, _, lft, BorrowKind::Unique, _, _) => tcx
332                 .cannot_reborrow_already_uniquely_borrowed(
333                     span,
334                     &desc_place,
335                     "",
336                     lft,
337                     issued_span,
338                     "",
339                     None,
340                     Origin::Mir,
341                 ),
342
343             (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(),
344         };
345
346         if issued_spans == borrow_spans {
347             borrow_spans.var_span_label(
348                 &mut err,
349                 format!("borrows occur due to use of `{}` in closure", desc_place),
350             );
351         } else {
352             let borrow_place = &issued_borrow.borrowed_place;
353             let borrow_place_desc = self.describe_place(borrow_place).unwrap_or("_".to_owned());
354             issued_spans.var_span_label(
355                 &mut err,
356                 format!(
357                     "first borrow occurs due to use of `{}` in closure",
358                     borrow_place_desc
359                 ),
360             );
361
362             borrow_spans.var_span_label(
363                 &mut err,
364                 format!(
365                     "second borrow occurs due to use of `{}` in closure",
366                     desc_place
367                 ),
368             );
369         }
370
371         self.explain_why_borrow_contains_point(context, issued_borrow, None, &mut err);
372
373         err.buffer(&mut self.errors_buffer);
374     }
375
376     pub(super) fn report_borrowed_value_does_not_live_long_enough(
377         &mut self,
378         context: Context,
379         borrow: &BorrowData<'tcx>,
380         place_span: (&Place<'tcx>, Span),
381         kind: Option<WriteKind>,
382     ) {
383         let drop_span = place_span.1;
384         let scope_tree = self.tcx.region_scope_tree(self.mir_def_id);
385         let root_place = self
386             .prefixes(&borrow.borrowed_place, PrefixSet::All)
387             .last()
388             .unwrap();
389
390         let borrow_spans = self.retrieve_borrow_spans(borrow);
391         let borrow_span = borrow_spans.var_or_use();
392
393         let proper_span = match *root_place {
394             Place::Local(local) => self.mir.local_decls[local].source_info.span,
395             _ => drop_span,
396         };
397
398         if self
399             .access_place_error_reported
400             .contains(&(root_place.clone(), borrow_span))
401         {
402             debug!(
403                 "suppressing access_place error when borrow doesn't live long enough for {:?}",
404                 borrow_span
405             );
406             return;
407         }
408
409         self.access_place_error_reported
410             .insert((root_place.clone(), borrow_span));
411
412         let mut err = match &self.describe_place(&borrow.borrowed_place) {
413             Some(_) if self.is_place_thread_local(root_place) => {
414                 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span)
415             }
416             Some(name) => self.report_local_value_does_not_live_long_enough(
417                 context,
418                 name,
419                 &scope_tree,
420                 &borrow,
421                 drop_span,
422                 borrow_span,
423                 proper_span,
424                 kind.map(|k| (k, place_span.0)),
425             ),
426             None => self.report_temporary_value_does_not_live_long_enough(
427                 context,
428                 &scope_tree,
429                 &borrow,
430                 drop_span,
431                 borrow_span,
432                 proper_span,
433             ),
434         };
435
436         borrow_spans.args_span_label(&mut err, "value captured here");
437
438         err.buffer(&mut self.errors_buffer);
439     }
440
441     fn report_local_value_does_not_live_long_enough(
442         &mut self,
443         context: Context,
444         name: &String,
445         scope_tree: &Lrc<ScopeTree>,
446         borrow: &BorrowData<'tcx>,
447         drop_span: Span,
448         borrow_span: Span,
449         _proper_span: Span,
450         kind_place: Option<(WriteKind, &Place<'tcx>)>,
451     ) -> DiagnosticBuilder<'cx> {
452         debug!(
453             "report_local_value_does_not_live_long_enough(\
454              {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\
455              )",
456             context, name, scope_tree, borrow, drop_span, borrow_span
457         );
458
459         let mut err = self.tcx.path_does_not_live_long_enough(
460             borrow_span,
461             &format!("`{}`", name),
462             Origin::Mir,
463         );
464
465         err.span_label(borrow_span, "borrowed value does not live long enough");
466         err.span_label(
467             drop_span,
468             format!("`{}` dropped here while still borrowed", name),
469         );
470
471         self.explain_why_borrow_contains_point(context, borrow, kind_place, &mut err);
472         err
473     }
474
475     fn report_thread_local_value_does_not_live_long_enough(
476         &mut self,
477         drop_span: Span,
478         borrow_span: Span,
479     ) -> DiagnosticBuilder<'cx> {
480         debug!(
481             "report_thread_local_value_does_not_live_long_enough(\
482              {:?}, {:?}\
483              )",
484             drop_span, borrow_span
485         );
486
487         let mut err = self
488             .tcx
489             .thread_local_value_does_not_live_long_enough(borrow_span, Origin::Mir);
490
491         err.span_label(
492             borrow_span,
493             "thread-local variables cannot be borrowed beyond the end of the function",
494         );
495         err.span_label(drop_span, "end of enclosing function is here");
496         err
497     }
498
499     fn report_temporary_value_does_not_live_long_enough(
500         &mut self,
501         context: Context,
502         scope_tree: &Lrc<ScopeTree>,
503         borrow: &BorrowData<'tcx>,
504         drop_span: Span,
505         _borrow_span: Span,
506         proper_span: Span,
507     ) -> DiagnosticBuilder<'cx> {
508         debug!(
509             "report_temporary_value_does_not_live_long_enough(\
510              {:?}, {:?}, {:?}, {:?}, {:?}\
511              )",
512             context, scope_tree, borrow, drop_span, proper_span
513         );
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(drop_span, "temporary value only lives until here");
520
521         self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
522         err
523     }
524
525     fn get_moved_indexes(&mut self, context: Context, mpi: MovePathIndex) -> Vec<MoveOutIndex> {
526         let mir = self.mir;
527
528         let mut stack = Vec::new();
529         stack.extend(mir.predecessor_locations(context.loc));
530
531         let mut visited = FxHashSet();
532         let mut result = vec![];
533
534         'dfs: while let Some(l) = stack.pop() {
535             debug!(
536                 "report_use_of_moved_or_uninitialized: current_location={:?}",
537                 l
538             );
539
540             if !visited.insert(l) {
541                 continue;
542             }
543
544             // check for moves
545             let stmt_kind = mir[l.block]
546                 .statements
547                 .get(l.statement_index)
548                 .map(|s| &s.kind);
549             if let Some(StatementKind::StorageDead(..)) = stmt_kind {
550                 // this analysis only tries to find moves explicitly
551                 // written by the user, so we ignore the move-outs
552                 // created by `StorageDead` and at the beginning
553                 // of a function.
554             } else {
555                 for moi in &self.move_data.loc_map[l] {
556                     debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
557                     if self.move_data.moves[*moi].path == mpi {
558                         debug!("report_use_of_moved_or_uninitialized: found");
559                         result.push(*moi);
560
561                         // Strictly speaking, we could continue our DFS here. There may be
562                         // other moves that can reach the point of error. But it is kind of
563                         // confusing to highlight them.
564                         //
565                         // Example:
566                         //
567                         // ```
568                         // let a = vec![];
569                         // let b = a;
570                         // let c = a;
571                         // drop(a); // <-- current point of error
572                         // ```
573                         //
574                         // Because we stop the DFS here, we only highlight `let c = a`,
575                         // and not `let b = a`. We will of course also report an error at
576                         // `let c = a` which highlights `let b = a` as the move.
577                         continue 'dfs;
578                     }
579                 }
580             }
581
582             // check for inits
583             let mut any_match = false;
584             drop_flag_effects::for_location_inits(self.tcx, self.mir, self.move_data, l, |m| {
585                 if m == mpi {
586                     any_match = true;
587                 }
588             });
589             if any_match {
590                 continue 'dfs;
591             }
592
593             stack.extend(mir.predecessor_locations(l));
594         }
595
596         result
597     }
598
599     pub(super) fn report_illegal_mutation_of_borrowed(
600         &mut self,
601         context: Context,
602         (place, span): (&Place<'tcx>, Span),
603         loan: &BorrowData<'tcx>,
604     ) {
605         let loan_spans = self.retrieve_borrow_spans(loan);
606         let loan_span = loan_spans.args_or_use();
607
608         let tcx = self.tcx;
609         let mut err = tcx.cannot_assign_to_borrowed(
610             span,
611             loan_span,
612             &self.describe_place(place).unwrap_or("_".to_owned()),
613             Origin::Mir,
614         );
615
616         loan_spans.var_span_label(&mut err, "borrow occurs due to use in closure");
617
618         self.explain_why_borrow_contains_point(context, loan, None, &mut err);
619
620         err.buffer(&mut self.errors_buffer);
621     }
622
623     /// Reports an illegal reassignment; for example, an assignment to
624     /// (part of) a non-`mut` local that occurs potentially after that
625     /// local has already been initialized. `place` is the path being
626     /// assigned; `err_place` is a place providing a reason why
627     /// `place` is not mutable (e.g. the non-`mut` local `x` in an
628     /// assignment to `x.f`).
629     pub(super) fn report_illegal_reassignment(
630         &mut self,
631         _context: Context,
632         (place, span): (&Place<'tcx>, Span),
633         assigned_span: Span,
634         err_place: &Place<'tcx>,
635     ) {
636         let (from_arg, local_decl) = if let Place::Local(local) = *err_place {
637             if let LocalKind::Arg = self.mir.local_kind(local) {
638                 (true, Some(&self.mir.local_decls[local]))
639             } else {
640                 (false, Some(&self.mir.local_decls[local]))
641             }
642         } else {
643             (false, None)
644         };
645
646         // If root local is initialized immediately (everything apart from let
647         // PATTERN;) then make the error refer to that local, rather than the
648         // place being assigned later.
649         let (place_description, assigned_span) = match local_decl {
650             Some(LocalDecl {
651                 is_user_variable: Some(ClearCrossCrate::Clear),
652                 ..
653             })
654             | Some(LocalDecl {
655                 is_user_variable:
656                     Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
657                         opt_match_place: None,
658                         ..
659                     }))),
660                 ..
661             })
662             | Some(LocalDecl {
663                 is_user_variable: None,
664                 ..
665             })
666             | None => (self.describe_place(place), assigned_span),
667             Some(decl) => (self.describe_place(err_place), decl.source_info.span),
668         };
669
670         let mut err = self.tcx.cannot_reassign_immutable(
671             span,
672             place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"),
673             from_arg,
674             Origin::Mir,
675         );
676         let msg = if from_arg {
677             "cannot assign to immutable argument"
678         } else {
679             "cannot assign twice to immutable variable"
680         };
681         if span != assigned_span {
682             if !from_arg {
683                 let value_msg = match place_description {
684                     Some(name) => format!("`{}`", name),
685                     None => "value".to_owned(),
686                 };
687                 err.span_label(assigned_span, format!("first assignment to {}", value_msg));
688             }
689         }
690         if let Some(decl) = local_decl {
691             if let Some(name) = decl.name {
692                 if decl.can_be_made_mutable() {
693                     err.span_label(
694                         decl.source_info.span,
695                         format!("consider changing this to `mut {}`", name),
696                     );
697                 }
698             }
699         }
700         err.span_label(span, msg);
701         err.buffer(&mut self.errors_buffer);
702     }
703 }
704
705 pub(super) struct IncludingDowncast(bool);
706
707 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
708     // End-user visible description of `place` if one can be found. If the
709     // place is a temporary for instance, None will be returned.
710     pub(super) fn describe_place(&self, place: &Place<'tcx>) -> Option<String> {
711         self.describe_place_with_options(place, IncludingDowncast(false))
712     }
713
714     // End-user visible description of `place` if one can be found. If the
715     // place is a temporary for instance, None will be returned.
716     // `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
717     // `Downcast` and `IncludingDowncast` is true
718     pub(super) fn describe_place_with_options(
719         &self,
720         place: &Place<'tcx>,
721         including_downcast: IncludingDowncast,
722     ) -> Option<String> {
723         let mut buf = String::new();
724         match self.append_place_to_string(place, &mut buf, false, &including_downcast) {
725             Ok(()) => Some(buf),
726             Err(()) => None,
727         }
728     }
729
730     // Appends end-user visible description of `place` to `buf`.
731     fn append_place_to_string(
732         &self,
733         place: &Place<'tcx>,
734         buf: &mut String,
735         mut autoderef: bool,
736         including_downcast: &IncludingDowncast,
737     ) -> Result<(), ()> {
738         match *place {
739             Place::Promoted(_) => {
740                 buf.push_str("promoted");
741             }
742             Place::Local(local) => {
743                 self.append_local_to_string(local, buf)?;
744             }
745             Place::Static(ref static_) => {
746                 buf.push_str(&self.tcx.item_name(static_.def_id).to_string());
747             }
748             Place::Projection(ref proj) => {
749                 match proj.elem {
750                     ProjectionElem::Deref => {
751                         let upvar_field_projection =
752                             place.is_upvar_field_projection(self.mir, &self.tcx);
753                         if let Some(field) = upvar_field_projection {
754                             let var_index = field.index();
755                             let name = self.mir.upvar_decls[var_index].debug_name.to_string();
756                             if self.mir.upvar_decls[var_index].by_ref {
757                                 buf.push_str(&name);
758                             } else {
759                                 buf.push_str(&format!("*{}", &name));
760                             }
761                         } else {
762                             if autoderef {
763                                 self.append_place_to_string(
764                                     &proj.base,
765                                     buf,
766                                     autoderef,
767                                     &including_downcast,
768                                 )?;
769                             } else if let Place::Local(local) = proj.base {
770                                 if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) =
771                                     self.mir.local_decls[local].is_user_variable
772                                 {
773                                     self.append_place_to_string(
774                                         &proj.base,
775                                         buf,
776                                         autoderef,
777                                         &including_downcast,
778                                     )?;
779                                 } else {
780                                     buf.push_str(&"*");
781                                     self.append_place_to_string(
782                                         &proj.base,
783                                         buf,
784                                         autoderef,
785                                         &including_downcast,
786                                     )?;
787                                 }
788                             } else {
789                                 buf.push_str(&"*");
790                                 self.append_place_to_string(
791                                     &proj.base,
792                                     buf,
793                                     autoderef,
794                                     &including_downcast,
795                                 )?;
796                             }
797                         }
798                     }
799                     ProjectionElem::Downcast(..) => {
800                         self.append_place_to_string(
801                             &proj.base,
802                             buf,
803                             autoderef,
804                             &including_downcast,
805                         )?;
806                         if including_downcast.0 {
807                             return Err(());
808                         }
809                     }
810                     ProjectionElem::Field(field, _ty) => {
811                         autoderef = true;
812
813                         let upvar_field_projection =
814                             place.is_upvar_field_projection(self.mir, &self.tcx);
815                         if let Some(field) = upvar_field_projection {
816                             let var_index = field.index();
817                             let name = self.mir.upvar_decls[var_index].debug_name.to_string();
818                             buf.push_str(&name);
819                         } else {
820                             let field_name = self.describe_field(&proj.base, field);
821                             self.append_place_to_string(
822                                 &proj.base,
823                                 buf,
824                                 autoderef,
825                                 &including_downcast,
826                             )?;
827                             buf.push_str(&format!(".{}", field_name));
828                         }
829                     }
830                     ProjectionElem::Index(index) => {
831                         autoderef = true;
832
833                         self.append_place_to_string(
834                             &proj.base,
835                             buf,
836                             autoderef,
837                             &including_downcast,
838                         )?;
839                         buf.push_str("[");
840                         if self.append_local_to_string(index, buf).is_err() {
841                             buf.push_str("..");
842                         }
843                         buf.push_str("]");
844                     }
845                     ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
846                         autoderef = true;
847                         // Since it isn't possible to borrow an element on a particular index and
848                         // then use another while the borrow is held, don't output indices details
849                         // to avoid confusing the end-user
850                         self.append_place_to_string(
851                             &proj.base,
852                             buf,
853                             autoderef,
854                             &including_downcast,
855                         )?;
856                         buf.push_str(&"[..]");
857                     }
858                 };
859             }
860         }
861
862         Ok(())
863     }
864
865     // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
866     // a name, then `Err` is returned
867     fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
868         let local = &self.mir.local_decls[local_index];
869         match local.name {
870             Some(name) => {
871                 buf.push_str(&name.to_string());
872                 Ok(())
873             }
874             None => Err(()),
875         }
876     }
877
878     // End-user visible description of the `field`nth field of `base`
879     fn describe_field(&self, base: &Place, field: Field) -> String {
880         match *base {
881             Place::Local(local) => {
882                 let local = &self.mir.local_decls[local];
883                 self.describe_field_from_ty(&local.ty, field)
884             }
885             Place::Promoted(ref prom) => self.describe_field_from_ty(&prom.1, field),
886             Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field),
887             Place::Projection(ref proj) => match proj.elem {
888                 ProjectionElem::Deref => self.describe_field(&proj.base, field),
889                 ProjectionElem::Downcast(def, variant_index) => format!(
890                     "{}",
891                     def.variants[variant_index].fields[field.index()].ident
892                 ),
893                 ProjectionElem::Field(_, field_type) => {
894                     self.describe_field_from_ty(&field_type, field)
895                 }
896                 ProjectionElem::Index(..)
897                 | ProjectionElem::ConstantIndex { .. }
898                 | ProjectionElem::Subslice { .. } => {
899                     self.describe_field(&proj.base, field).to_string()
900                 }
901             },
902         }
903     }
904
905     // End-user visible description of the `field_index`nth field of `ty`
906     fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String {
907         if ty.is_box() {
908             // If the type is a box, the field is described from the boxed type
909             self.describe_field_from_ty(&ty.boxed_ty(), field)
910         } else {
911             match ty.sty {
912                 ty::Adt(def, _) => if def.is_enum() {
913                     field.index().to_string()
914                 } else {
915                     def.non_enum_variant().fields[field.index()]
916                         .ident
917                         .to_string()
918                 },
919                 ty::Tuple(_) => field.index().to_string(),
920                 ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
921                     self.describe_field_from_ty(&ty, field)
922                 }
923                 ty::Array(ty, _) | ty::Slice(ty) => self.describe_field_from_ty(&ty, field),
924                 ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
925                     // Convert the def-id into a node-id. node-ids are only valid for
926                     // the local code in the current crate, so this returns an `Option` in case
927                     // the closure comes from another crate. But in that case we wouldn't
928                     // be borrowck'ing it, so we can just unwrap:
929                     let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
930                     let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]);
931
932                     self.tcx.hir.name(freevar.var_id()).to_string()
933                 }
934                 _ => {
935                     // Might need a revision when the fields in trait RFC is implemented
936                     // (https://github.com/rust-lang/rfcs/pull/1546)
937                     bug!(
938                         "End-user description not implemented for field access on `{:?}`",
939                         ty.sty
940                     );
941                 }
942             }
943         }
944     }
945
946     // Retrieve type of a place for the current MIR representation
947     fn retrieve_type_for_place(&self, place: &Place<'tcx>) -> Option<ty::Ty> {
948         match place {
949             Place::Local(local) => {
950                 let local = &self.mir.local_decls[*local];
951                 Some(local.ty)
952             }
953             Place::Promoted(ref prom) => Some(prom.1),
954             Place::Static(ref st) => Some(st.ty),
955             Place::Projection(ref proj) => match proj.elem {
956                 ProjectionElem::Field(_, ty) => Some(ty),
957                 _ => None,
958             },
959         }
960     }
961
962     /// Check if a place is a thread-local static.
963     pub fn is_place_thread_local(&self, place: &Place<'tcx>) -> bool {
964         if let Place::Static(statik) = place {
965             let attrs = self.tcx.get_attrs(statik.def_id);
966             let is_thread_local = attrs.iter().any(|attr| attr.check_name("thread_local"));
967
968             debug!(
969                 "is_place_thread_local: attrs={:?} is_thread_local={:?}",
970                 attrs, is_thread_local
971             );
972             is_thread_local
973         } else {
974             debug!("is_place_thread_local: no");
975             false
976         }
977     }
978 }
979
980 // The span(s) associated to a use of a place.
981 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
982 pub(super) enum UseSpans {
983     // The access is caused by capturing a variable for a closure.
984     ClosureUse {
985         // The span of the args of the closure, including the `move` keyword if
986         // it's present.
987         args_span: Span,
988         // The span of the first use of the captured variable inside the closure.
989         var_span: Span,
990     },
991     // This access has a single span associated to it: common case.
992     OtherUse(Span),
993 }
994
995 impl UseSpans {
996     pub(super) fn args_or_use(self) -> Span {
997         match self {
998             UseSpans::ClosureUse {
999                 args_span: span, ..
1000             }
1001             | UseSpans::OtherUse(span) => span,
1002         }
1003     }
1004
1005     pub(super) fn var_or_use(self) -> Span {
1006         match self {
1007             UseSpans::ClosureUse { var_span: span, .. } | UseSpans::OtherUse(span) => span,
1008         }
1009     }
1010
1011     // Add a span label to the arguments of the closure, if it exists.
1012     pub(super) fn args_span_label(self, err: &mut DiagnosticBuilder, message: impl Into<String>) {
1013         if let UseSpans::ClosureUse { args_span, .. } = self {
1014             err.span_label(args_span, message);
1015         }
1016     }
1017
1018     // Add a span label to the use of the captured variable, if it exists.
1019     pub(super) fn var_span_label(self, err: &mut DiagnosticBuilder, message: impl Into<String>) {
1020         if let UseSpans::ClosureUse { var_span, .. } = self {
1021             err.span_label(var_span, message);
1022         }
1023     }
1024
1025     pub(super) fn for_closure(self) -> bool {
1026         match self {
1027             UseSpans::ClosureUse { .. } => true,
1028             UseSpans::OtherUse(_) => false,
1029         }
1030     }
1031
1032     pub(super) fn or_else<F>(self, if_other: F) -> Self
1033     where
1034         F: FnOnce() -> Self,
1035     {
1036         match self {
1037             closure @ UseSpans::ClosureUse { .. } => closure,
1038             UseSpans::OtherUse(_) => if_other(),
1039         }
1040     }
1041 }
1042
1043 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1044     /// Finds the spans associated to a move or copy of move_place at location.
1045     pub(super) fn move_spans(
1046         &self,
1047         moved_place: &Place<'tcx>, // Could also be an upvar.
1048         location: Location,
1049     ) -> UseSpans {
1050         use self::UseSpans::*;
1051         use rustc::hir::ExprKind::Closure;
1052         use rustc::mir::AggregateKind;
1053
1054         let stmt = match self.mir[location.block]
1055             .statements
1056             .get(location.statement_index)
1057         {
1058             Some(stmt) => stmt,
1059             None => return OtherUse(self.mir.source_info(location).span),
1060         };
1061
1062         if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
1063             if let AggregateKind::Closure(def_id, _) = **kind {
1064                 debug!("find_closure_move_span: found closure {:?}", places);
1065
1066                 if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
1067                     if let Closure(_, _, _, args_span, _) = self.tcx.hir.expect_expr(node_id).node {
1068                         if let Some(var_span) = self.tcx.with_freevars(node_id, |freevars| {
1069                             for (v, place) in freevars.iter().zip(places) {
1070                                 match place {
1071                                     Operand::Copy(place) | Operand::Move(place)
1072                                         if moved_place == place =>
1073                                     {
1074                                         debug!(
1075                                             "find_closure_move_span: found captured local {:?}",
1076                                             place
1077                                         );
1078                                         return Some(v.span);
1079                                     }
1080                                     _ => {}
1081                                 }
1082                             }
1083                             None
1084                         }) {
1085                             return ClosureUse {
1086                                 args_span,
1087                                 var_span,
1088                             };
1089                         }
1090                     }
1091                 }
1092             }
1093         }
1094
1095         return OtherUse(stmt.source_info.span);
1096     }
1097
1098     /// Finds the span of arguments of a closure (within `maybe_closure_span`)
1099     /// and its usage of the local assigned at `location`.
1100     /// This is done by searching in statements succeeding `location`
1101     /// and originating from `maybe_closure_span`.
1102     pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans {
1103         use self::UseSpans::*;
1104         use rustc::hir::ExprKind::Closure;
1105         use rustc::mir::AggregateKind;
1106
1107         let local = match self.mir[location.block]
1108             .statements
1109             .get(location.statement_index)
1110         {
1111             Some(&Statement {
1112                 kind: StatementKind::Assign(Place::Local(local), _),
1113                 ..
1114             }) => local,
1115             _ => return OtherUse(use_span),
1116         };
1117
1118         if self.mir.local_kind(local) != LocalKind::Temp {
1119             // operands are always temporaries.
1120             return OtherUse(use_span);
1121         }
1122
1123         for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
1124             if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
1125                 if let AggregateKind::Closure(def_id, _) = **kind {
1126                     debug!("find_closure_borrow_span: found closure {:?}", places);
1127
1128                     return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
1129                         let args_span = if let Closure(_, _, _, span, _) =
1130                             self.tcx.hir.expect_expr(node_id).node
1131                         {
1132                             span
1133                         } else {
1134                             return OtherUse(use_span);
1135                         };
1136
1137                         self.tcx
1138                             .with_freevars(node_id, |freevars| {
1139                                 for (v, place) in freevars.iter().zip(places) {
1140                                     match *place {
1141                                         Operand::Copy(Place::Local(l))
1142                                         | Operand::Move(Place::Local(l))
1143                                             if local == l =>
1144                                         {
1145                                             debug!(
1146                                                 "find_closure_borrow_span: found captured local \
1147                                                  {:?}",
1148                                                 l
1149                                             );
1150                                             return Some(v.span);
1151                                         }
1152                                         _ => {}
1153                                     }
1154                                 }
1155                                 None
1156                             }).map(|var_span| ClosureUse {
1157                                 args_span,
1158                                 var_span,
1159                             }).unwrap_or(OtherUse(use_span))
1160                     } else {
1161                         OtherUse(use_span)
1162                     };
1163                 }
1164             }
1165
1166             if use_span != stmt.source_info.span {
1167                 break;
1168             }
1169         }
1170
1171         OtherUse(use_span)
1172     }
1173
1174     /// Helper to retrieve span(s) of given borrow from the current MIR
1175     /// representation
1176     pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData) -> UseSpans {
1177         let span = self.mir.source_info(borrow.reserve_location).span;
1178         self.borrow_spans(span, borrow.reserve_location)
1179     }
1180 }