]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/diagnostics/mod.rs
38c479c72c1e782a35ad6f53ae6412c356a8a6e7
[rust.git] / src / librustc_mir / borrow_check / diagnostics / mod.rs
1 //! Borrow checker diagnostics.
2
3 use rustc::mir::{
4     AggregateKind, Constant, Field, Local, LocalInfo, LocalKind, Location, Operand, Place,
5     PlaceBase, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
6     TerminatorKind,
7 };
8 use rustc::ty::layout::VariantIdx;
9 use rustc::ty::print::Print;
10 use rustc::ty::{self, DefIdTree, Ty, TyCtxt};
11 use rustc_errors::DiagnosticBuilder;
12 use rustc_hir as hir;
13 use rustc_hir::def::Namespace;
14 use rustc_hir::def_id::DefId;
15 use rustc_hir::GeneratorKind;
16 use rustc_span::Span;
17
18 use super::borrow_set::BorrowData;
19 use super::MirBorrowckCtxt;
20 use crate::dataflow::move_paths::{InitLocation, LookupResult};
21
22 mod find_use;
23 mod outlives_suggestion;
24 mod region_name;
25 mod var_name;
26
27 mod conflict_errors;
28 mod explain_borrow;
29 mod move_errors;
30 mod mutability_errors;
31 mod region_errors;
32
33 crate use mutability_errors::AccessKind;
34 crate use outlives_suggestion::OutlivesSuggestionBuilder;
35 crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
36 crate use region_name::{RegionErrorNamingCtx, RegionName, RegionNameSource};
37
38 pub(super) struct IncludingDowncast(pub(super) bool);
39
40 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
41     /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
42     /// is moved after being invoked.
43     ///
44     /// ```text
45     /// note: closure cannot be invoked more than once because it moves the variable `dict` out of
46     ///       its environment
47     ///   --> $DIR/issue-42065.rs:16:29
48     ///    |
49     /// LL |         for (key, value) in dict {
50     ///    |                             ^^^^
51     /// ```
52     pub(super) fn add_moved_or_invoked_closure_note(
53         &self,
54         location: Location,
55         place: PlaceRef<'cx, 'tcx>,
56         diag: &mut DiagnosticBuilder<'_>,
57     ) {
58         debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
59         let mut target = place.local_or_deref_local();
60         for stmt in &self.body[location.block].statements[location.statement_index..] {
61             debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
62             if let StatementKind::Assign(box (into, Rvalue::Use(from))) = &stmt.kind {
63                 debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
64                 match from {
65                     Operand::Copy(ref place) | Operand::Move(ref place)
66                         if target == place.local_or_deref_local() =>
67                     {
68                         target = into.local_or_deref_local()
69                     }
70                     _ => {}
71                 }
72             }
73         }
74
75         // Check if we are attempting to call a closure after it has been invoked.
76         let terminator = self.body[location.block].terminator();
77         debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
78         if let TerminatorKind::Call {
79             func:
80                 Operand::Constant(box Constant {
81                     literal: ty::Const { ty: &ty::TyS { kind: ty::FnDef(id, _), .. }, .. },
82                     ..
83                 }),
84             args,
85             ..
86         } = &terminator.kind
87         {
88             debug!("add_moved_or_invoked_closure_note: id={:?}", id);
89             if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() {
90                 let closure = match args.first() {
91                     Some(Operand::Copy(ref place)) | Some(Operand::Move(ref place))
92                         if target == place.local_or_deref_local() =>
93                     {
94                         place.local_or_deref_local().unwrap()
95                     }
96                     _ => return,
97                 };
98
99                 debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
100                 if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind {
101                     let hir_id = self.infcx.tcx.hir().as_local_hir_id(did).unwrap();
102
103                     if let Some((span, name)) =
104                         self.infcx.tcx.typeck_tables_of(did).closure_kind_origins().get(hir_id)
105                     {
106                         diag.span_note(
107                             *span,
108                             &format!(
109                                 "closure cannot be invoked more than once because it moves the \
110                                  variable `{}` out of its environment",
111                                 name,
112                             ),
113                         );
114                         return;
115                     }
116                 }
117             }
118         }
119
120         // Check if we are just moving a closure after it has been invoked.
121         if let Some(target) = target {
122             if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind {
123                 let hir_id = self.infcx.tcx.hir().as_local_hir_id(did).unwrap();
124
125                 if let Some((span, name)) =
126                     self.infcx.tcx.typeck_tables_of(did).closure_kind_origins().get(hir_id)
127                 {
128                     diag.span_note(
129                         *span,
130                         &format!(
131                             "closure cannot be moved more than once as it is not `Copy` due to \
132                              moving the variable `{}` out of its environment",
133                             name
134                         ),
135                     );
136                 }
137             }
138         }
139     }
140
141     /// End-user visible description of `place` if one can be found. If the
142     /// place is a temporary for instance, None will be returned.
143     pub(super) fn describe_place(&self, place_ref: PlaceRef<'cx, 'tcx>) -> Option<String> {
144         self.describe_place_with_options(place_ref, IncludingDowncast(false))
145     }
146
147     /// End-user visible description of `place` if one can be found. If the
148     /// place is a temporary for instance, None will be returned.
149     /// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
150     /// `Downcast` and `IncludingDowncast` is true
151     pub(super) fn describe_place_with_options(
152         &self,
153         place: PlaceRef<'cx, 'tcx>,
154         including_downcast: IncludingDowncast,
155     ) -> Option<String> {
156         let mut buf = String::new();
157         match self.append_place_to_string(place, &mut buf, false, &including_downcast) {
158             Ok(()) => Some(buf),
159             Err(()) => None,
160         }
161     }
162
163     /// Appends end-user visible description of `place` to `buf`.
164     fn append_place_to_string(
165         &self,
166         place: PlaceRef<'cx, 'tcx>,
167         buf: &mut String,
168         mut autoderef: bool,
169         including_downcast: &IncludingDowncast,
170     ) -> Result<(), ()> {
171         match place {
172             PlaceRef { base: PlaceBase::Local(local), projection: [] } => {
173                 self.append_local_to_string(*local, buf)?;
174             }
175             PlaceRef { base: &PlaceBase::Local(local), projection: [ProjectionElem::Deref] }
176                 if self.body.local_decls[local].is_ref_for_guard() =>
177             {
178                 self.append_place_to_string(
179                     PlaceRef { base: &PlaceBase::Local(local), projection: &[] },
180                     buf,
181                     autoderef,
182                     &including_downcast,
183                 )?;
184             }
185             PlaceRef { base: &PlaceBase::Local(local), projection: [ProjectionElem::Deref] }
186                 if self.body.local_decls[local].is_ref_to_static() =>
187             {
188                 let local_info = &self.body.local_decls[local].local_info;
189                 if let LocalInfo::StaticRef { def_id, .. } = *local_info {
190                     buf.push_str(&self.infcx.tcx.item_name(def_id).as_str());
191                 } else {
192                     unreachable!();
193                 }
194             }
195             PlaceRef { base, projection: [proj_base @ .., elem] } => {
196                 match elem {
197                     ProjectionElem::Deref => {
198                         let upvar_field_projection = self.is_upvar_field_projection(place);
199                         if let Some(field) = upvar_field_projection {
200                             let var_index = field.index();
201                             let name = self.upvars[var_index].name.to_string();
202                             if self.upvars[var_index].by_ref {
203                                 buf.push_str(&name);
204                             } else {
205                                 buf.push_str(&format!("*{}", &name));
206                             }
207                         } else {
208                             if autoderef {
209                                 // FIXME turn this recursion into iteration
210                                 self.append_place_to_string(
211                                     PlaceRef { base, projection: proj_base },
212                                     buf,
213                                     autoderef,
214                                     &including_downcast,
215                                 )?;
216                             } else {
217                                 match (proj_base, base) {
218                                     _ => {
219                                         buf.push_str(&"*");
220                                         self.append_place_to_string(
221                                             PlaceRef { base, projection: proj_base },
222                                             buf,
223                                             autoderef,
224                                             &including_downcast,
225                                         )?;
226                                     }
227                                 }
228                             }
229                         }
230                     }
231                     ProjectionElem::Downcast(..) => {
232                         self.append_place_to_string(
233                             PlaceRef { base, projection: proj_base },
234                             buf,
235                             autoderef,
236                             &including_downcast,
237                         )?;
238                         if including_downcast.0 {
239                             return Err(());
240                         }
241                     }
242                     ProjectionElem::Field(field, _ty) => {
243                         autoderef = true;
244
245                         let upvar_field_projection = self.is_upvar_field_projection(place);
246                         if let Some(field) = upvar_field_projection {
247                             let var_index = field.index();
248                             let name = self.upvars[var_index].name.to_string();
249                             buf.push_str(&name);
250                         } else {
251                             let field_name = self
252                                 .describe_field(PlaceRef { base, projection: proj_base }, *field);
253                             self.append_place_to_string(
254                                 PlaceRef { base, projection: proj_base },
255                                 buf,
256                                 autoderef,
257                                 &including_downcast,
258                             )?;
259                             buf.push_str(&format!(".{}", field_name));
260                         }
261                     }
262                     ProjectionElem::Index(index) => {
263                         autoderef = true;
264
265                         self.append_place_to_string(
266                             PlaceRef { base, projection: proj_base },
267                             buf,
268                             autoderef,
269                             &including_downcast,
270                         )?;
271                         buf.push_str("[");
272                         if self.append_local_to_string(*index, buf).is_err() {
273                             buf.push_str("_");
274                         }
275                         buf.push_str("]");
276                     }
277                     ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
278                         autoderef = true;
279                         // Since it isn't possible to borrow an element on a particular index and
280                         // then use another while the borrow is held, don't output indices details
281                         // to avoid confusing the end-user
282                         self.append_place_to_string(
283                             PlaceRef { base, projection: proj_base },
284                             buf,
285                             autoderef,
286                             &including_downcast,
287                         )?;
288                         buf.push_str(&"[..]");
289                     }
290                 };
291             }
292         }
293
294         Ok(())
295     }
296
297     /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
298     /// a name, or its name was generated by the compiler, then `Err` is returned
299     fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> {
300         let decl = &self.body.local_decls[local];
301         match self.local_names[local] {
302             Some(name) if !decl.from_compiler_desugaring() => {
303                 buf.push_str(&name.as_str());
304                 Ok(())
305             }
306             _ => Err(()),
307         }
308     }
309
310     /// End-user visible description of the `field`nth field of `base`
311     fn describe_field(&self, place: PlaceRef<'cx, 'tcx>, field: Field) -> String {
312         // FIXME Place2 Make this work iteratively
313         match place {
314             PlaceRef { base: PlaceBase::Local(local), projection: [] } => {
315                 let local = &self.body.local_decls[*local];
316                 self.describe_field_from_ty(&local.ty, field, None)
317             }
318             PlaceRef { base, projection: [proj_base @ .., elem] } => match elem {
319                 ProjectionElem::Deref => {
320                     self.describe_field(PlaceRef { base, projection: proj_base }, field)
321                 }
322                 ProjectionElem::Downcast(_, variant_index) => {
323                     let base_ty =
324                         Place::ty_from(place.base, place.projection, *self.body, self.infcx.tcx).ty;
325                     self.describe_field_from_ty(&base_ty, field, Some(*variant_index))
326                 }
327                 ProjectionElem::Field(_, field_type) => {
328                     self.describe_field_from_ty(&field_type, field, None)
329                 }
330                 ProjectionElem::Index(..)
331                 | ProjectionElem::ConstantIndex { .. }
332                 | ProjectionElem::Subslice { .. } => {
333                     self.describe_field(PlaceRef { base, projection: proj_base }, field)
334                 }
335             },
336         }
337     }
338
339     /// End-user visible description of the `field_index`nth field of `ty`
340     fn describe_field_from_ty(
341         &self,
342         ty: Ty<'_>,
343         field: Field,
344         variant_index: Option<VariantIdx>,
345     ) -> String {
346         if ty.is_box() {
347             // If the type is a box, the field is described from the boxed type
348             self.describe_field_from_ty(&ty.boxed_ty(), field, variant_index)
349         } else {
350             match ty.kind {
351                 ty::Adt(def, _) => {
352                     let variant = if let Some(idx) = variant_index {
353                         assert!(def.is_enum());
354                         &def.variants[idx]
355                     } else {
356                         def.non_enum_variant()
357                     };
358                     variant.fields[field.index()].ident.to_string()
359                 }
360                 ty::Tuple(_) => field.index().to_string(),
361                 ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
362                     self.describe_field_from_ty(&ty, field, variant_index)
363                 }
364                 ty::Array(ty, _) | ty::Slice(ty) => {
365                     self.describe_field_from_ty(&ty, field, variant_index)
366                 }
367                 ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
368                     // `tcx.upvars(def_id)` returns an `Option`, which is `None` in case
369                     // the closure comes from another crate. But in that case we wouldn't
370                     // be borrowck'ing it, so we can just unwrap:
371                     let (&var_id, _) =
372                         self.infcx.tcx.upvars(def_id).unwrap().get_index(field.index()).unwrap();
373
374                     self.infcx.tcx.hir().name(var_id).to_string()
375                 }
376                 _ => {
377                     // Might need a revision when the fields in trait RFC is implemented
378                     // (https://github.com/rust-lang/rfcs/pull/1546)
379                     bug!("End-user description not implemented for field access on `{:?}`", ty);
380                 }
381             }
382         }
383     }
384
385     /// Add a note that a type does not implement `Copy`
386     pub(super) fn note_type_does_not_implement_copy(
387         &self,
388         err: &mut DiagnosticBuilder<'a>,
389         place_desc: &str,
390         ty: Ty<'tcx>,
391         span: Option<Span>,
392     ) {
393         let message = format!(
394             "move occurs because {} has type `{}`, which does not implement the `Copy` trait",
395             place_desc, ty,
396         );
397         if let Some(span) = span {
398             err.span_label(span, message);
399         } else {
400             err.note(&message);
401         }
402     }
403
404     pub(super) fn borrowed_content_source(
405         &self,
406         deref_base: PlaceRef<'cx, 'tcx>,
407     ) -> BorrowedContentSource<'tcx> {
408         let tcx = self.infcx.tcx;
409
410         // Look up the provided place and work out the move path index for it,
411         // we'll use this to check whether it was originally from an overloaded
412         // operator.
413         match self.move_data.rev_lookup.find(deref_base) {
414             LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => {
415                 debug!("borrowed_content_source: mpi={:?}", mpi);
416
417                 for i in &self.move_data.init_path_map[mpi] {
418                     let init = &self.move_data.inits[*i];
419                     debug!("borrowed_content_source: init={:?}", init);
420                     // We're only interested in statements that initialized a value, not the
421                     // initializations from arguments.
422                     let loc = match init.location {
423                         InitLocation::Statement(stmt) => stmt,
424                         _ => continue,
425                     };
426
427                     let bbd = &self.body[loc.block];
428                     let is_terminator = bbd.statements.len() == loc.statement_index;
429                     debug!(
430                         "borrowed_content_source: loc={:?} is_terminator={:?}",
431                         loc, is_terminator,
432                     );
433                     if !is_terminator {
434                         continue;
435                     } else if let Some(Terminator {
436                         kind: TerminatorKind::Call { ref func, from_hir_call: false, .. },
437                         ..
438                     }) = bbd.terminator
439                     {
440                         if let Some(source) =
441                             BorrowedContentSource::from_call(func.ty(*self.body, tcx), tcx)
442                         {
443                             return source;
444                         }
445                     }
446                 }
447             }
448             // Base is a `static` so won't be from an overloaded operator
449             _ => (),
450         };
451
452         // If we didn't find an overloaded deref or index, then assume it's a
453         // built in deref and check the type of the base.
454         let base_ty = Place::ty_from(deref_base.base, deref_base.projection, *self.body, tcx).ty;
455         if base_ty.is_unsafe_ptr() {
456             BorrowedContentSource::DerefRawPointer
457         } else if base_ty.is_mutable_ptr() {
458             BorrowedContentSource::DerefMutableRef
459         } else {
460             BorrowedContentSource::DerefSharedRef
461         }
462     }
463 }
464
465 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
466     /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
467     /// name where required.
468     pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
469         let mut s = String::new();
470         let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
471
472         // We need to add synthesized lifetimes where appropriate. We do
473         // this by hooking into the pretty printer and telling it to label the
474         // lifetimes without names with the value `'0`.
475         match ty.kind {
476             ty::Ref(ty::RegionKind::ReLateBound(_, br), _, _)
477             | ty::Ref(
478                 ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }),
479                 _,
480                 _,
481             ) => printer.region_highlight_mode.highlighting_bound_region(*br, counter),
482             _ => {}
483         }
484
485         let _ = ty.print(printer);
486         s
487     }
488
489     /// Returns the name of the provided `Ty` (that must be a reference)'s region with a
490     /// synthesized lifetime name where required.
491     pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
492         let mut s = String::new();
493         let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
494
495         let region = match ty.kind {
496             ty::Ref(region, _, _) => {
497                 match region {
498                     ty::RegionKind::ReLateBound(_, br)
499                     | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => {
500                         printer.region_highlight_mode.highlighting_bound_region(*br, counter)
501                     }
502                     _ => {}
503                 }
504
505                 region
506             }
507             _ => bug!("ty for annotation of borrow region is not a reference"),
508         };
509
510         let _ = region.print(printer);
511         s
512     }
513 }
514
515 // The span(s) associated to a use of a place.
516 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
517 pub(super) enum UseSpans {
518     // The access is caused by capturing a variable for a closure.
519     ClosureUse {
520         // This is true if the captured variable was from a generator.
521         generator_kind: Option<GeneratorKind>,
522         // The span of the args of the closure, including the `move` keyword if
523         // it's present.
524         args_span: Span,
525         // The span of the first use of the captured variable inside the closure.
526         var_span: Span,
527     },
528     // This access has a single span associated to it: common case.
529     OtherUse(Span),
530 }
531
532 impl UseSpans {
533     pub(super) fn args_or_use(self) -> Span {
534         match self {
535             UseSpans::ClosureUse { args_span: span, .. } | UseSpans::OtherUse(span) => span,
536         }
537     }
538
539     pub(super) fn var_or_use(self) -> Span {
540         match self {
541             UseSpans::ClosureUse { var_span: span, .. } | UseSpans::OtherUse(span) => span,
542         }
543     }
544
545     pub(super) fn generator_kind(self) -> Option<GeneratorKind> {
546         match self {
547             UseSpans::ClosureUse { generator_kind, .. } => generator_kind,
548             _ => None,
549         }
550     }
551
552     // Add a span label to the arguments of the closure, if it exists.
553     pub(super) fn args_span_label(
554         self,
555         err: &mut DiagnosticBuilder<'_>,
556         message: impl Into<String>,
557     ) {
558         if let UseSpans::ClosureUse { args_span, .. } = self {
559             err.span_label(args_span, message);
560         }
561     }
562
563     // Add a span label to the use of the captured variable, if it exists.
564     pub(super) fn var_span_label(
565         self,
566         err: &mut DiagnosticBuilder<'_>,
567         message: impl Into<String>,
568     ) {
569         if let UseSpans::ClosureUse { var_span, .. } = self {
570             err.span_label(var_span, message);
571         }
572     }
573
574     /// Returns `false` if this place is not used in a closure.
575     pub(super) fn for_closure(&self) -> bool {
576         match *self {
577             UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_none(),
578             _ => false,
579         }
580     }
581
582     /// Returns `false` if this place is not used in a generator.
583     pub(super) fn for_generator(&self) -> bool {
584         match *self {
585             UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_some(),
586             _ => false,
587         }
588     }
589
590     /// Describe the span associated with a use of a place.
591     pub(super) fn describe(&self) -> String {
592         match *self {
593             UseSpans::ClosureUse { generator_kind, .. } => {
594                 if generator_kind.is_some() {
595                     " in generator".to_string()
596                 } else {
597                     " in closure".to_string()
598                 }
599             }
600             _ => "".to_string(),
601         }
602     }
603
604     pub(super) fn or_else<F>(self, if_other: F) -> Self
605     where
606         F: FnOnce() -> Self,
607     {
608         match self {
609             closure @ UseSpans::ClosureUse { .. } => closure,
610             UseSpans::OtherUse(_) => if_other(),
611         }
612     }
613 }
614
615 pub(super) enum BorrowedContentSource<'tcx> {
616     DerefRawPointer,
617     DerefMutableRef,
618     DerefSharedRef,
619     OverloadedDeref(Ty<'tcx>),
620     OverloadedIndex(Ty<'tcx>),
621 }
622
623 impl BorrowedContentSource<'tcx> {
624     pub(super) fn describe_for_unnamed_place(&self) -> String {
625         match *self {
626             BorrowedContentSource::DerefRawPointer => format!("a raw pointer"),
627             BorrowedContentSource::DerefSharedRef => format!("a shared reference"),
628             BorrowedContentSource::DerefMutableRef => format!("a mutable reference"),
629             BorrowedContentSource::OverloadedDeref(ty) => {
630                 if ty.is_rc() {
631                     format!("an `Rc`")
632                 } else if ty.is_arc() {
633                     format!("an `Arc`")
634                 } else {
635                     format!("dereference of `{}`", ty)
636                 }
637             }
638             BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{}`", ty),
639         }
640     }
641
642     pub(super) fn describe_for_named_place(&self) -> Option<&'static str> {
643         match *self {
644             BorrowedContentSource::DerefRawPointer => Some("raw pointer"),
645             BorrowedContentSource::DerefSharedRef => Some("shared reference"),
646             BorrowedContentSource::DerefMutableRef => Some("mutable reference"),
647             // Overloaded deref and index operators should be evaluated into a
648             // temporary. So we don't need a description here.
649             BorrowedContentSource::OverloadedDeref(_)
650             | BorrowedContentSource::OverloadedIndex(_) => None,
651         }
652     }
653
654     pub(super) fn describe_for_immutable_place(&self) -> String {
655         match *self {
656             BorrowedContentSource::DerefRawPointer => format!("a `*const` pointer"),
657             BorrowedContentSource::DerefSharedRef => format!("a `&` reference"),
658             BorrowedContentSource::DerefMutableRef => {
659                 bug!("describe_for_immutable_place: DerefMutableRef isn't immutable")
660             }
661             BorrowedContentSource::OverloadedDeref(ty) => {
662                 if ty.is_rc() {
663                     format!("an `Rc`")
664                 } else if ty.is_arc() {
665                     format!("an `Arc`")
666                 } else {
667                     format!("a dereference of `{}`", ty)
668                 }
669             }
670             BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{}`", ty),
671         }
672     }
673
674     fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> {
675         match func.kind {
676             ty::FnDef(def_id, substs) => {
677                 let trait_id = tcx.trait_of_item(def_id)?;
678
679                 let lang_items = tcx.lang_items();
680                 if Some(trait_id) == lang_items.deref_trait()
681                     || Some(trait_id) == lang_items.deref_mut_trait()
682                 {
683                     Some(BorrowedContentSource::OverloadedDeref(substs.type_at(0)))
684                 } else if Some(trait_id) == lang_items.index_trait()
685                     || Some(trait_id) == lang_items.index_mut_trait()
686                 {
687                     Some(BorrowedContentSource::OverloadedIndex(substs.type_at(0)))
688                 } else {
689                     None
690                 }
691             }
692             _ => None,
693         }
694     }
695 }
696
697 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
698     /// Finds the spans associated to a move or copy of move_place at location.
699     pub(super) fn move_spans(
700         &self,
701         moved_place: PlaceRef<'cx, 'tcx>, // Could also be an upvar.
702         location: Location,
703     ) -> UseSpans {
704         use self::UseSpans::*;
705
706         let stmt = match self.body[location.block].statements.get(location.statement_index) {
707             Some(stmt) => stmt,
708             None => return OtherUse(self.body.source_info(location).span),
709         };
710
711         debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
712         if let StatementKind::Assign(box (_, Rvalue::Aggregate(ref kind, ref places))) = stmt.kind {
713             let def_id = match kind {
714                 box AggregateKind::Closure(def_id, _)
715                 | box AggregateKind::Generator(def_id, _, _) => def_id,
716                 _ => return OtherUse(stmt.source_info.span),
717             };
718
719             debug!("move_spans: def_id={:?} places={:?}", def_id, places);
720             if let Some((args_span, generator_kind, var_span)) =
721                 self.closure_span(*def_id, moved_place, places)
722             {
723                 return ClosureUse { generator_kind, args_span, var_span };
724             }
725         }
726
727         OtherUse(stmt.source_info.span)
728     }
729
730     /// Finds the span of arguments of a closure (within `maybe_closure_span`)
731     /// and its usage of the local assigned at `location`.
732     /// This is done by searching in statements succeeding `location`
733     /// and originating from `maybe_closure_span`.
734     pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans {
735         use self::UseSpans::*;
736         debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
737
738         let target = match self.body[location.block].statements.get(location.statement_index) {
739             Some(&Statement { kind: StatementKind::Assign(box (ref place, _)), .. }) => {
740                 if let Some(local) = place.as_local() {
741                     local
742                 } else {
743                     return OtherUse(use_span);
744                 }
745             }
746             _ => return OtherUse(use_span),
747         };
748
749         if self.body.local_kind(target) != LocalKind::Temp {
750             // operands are always temporaries.
751             return OtherUse(use_span);
752         }
753
754         for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
755             if let StatementKind::Assign(box (_, Rvalue::Aggregate(ref kind, ref places))) =
756                 stmt.kind
757             {
758                 let (def_id, is_generator) = match kind {
759                     box AggregateKind::Closure(def_id, _) => (def_id, false),
760                     box AggregateKind::Generator(def_id, _, _) => (def_id, true),
761                     _ => continue,
762                 };
763
764                 debug!(
765                     "borrow_spans: def_id={:?} is_generator={:?} places={:?}",
766                     def_id, is_generator, places
767                 );
768                 if let Some((args_span, generator_kind, var_span)) =
769                     self.closure_span(*def_id, Place::from(target).as_ref(), places)
770                 {
771                     return ClosureUse { generator_kind, args_span, var_span };
772                 } else {
773                     return OtherUse(use_span);
774                 }
775             }
776
777             if use_span != stmt.source_info.span {
778                 break;
779             }
780         }
781
782         OtherUse(use_span)
783     }
784
785     /// Finds the span of a captured variable within a closure or generator.
786     fn closure_span(
787         &self,
788         def_id: DefId,
789         target_place: PlaceRef<'cx, 'tcx>,
790         places: &Vec<Operand<'tcx>>,
791     ) -> Option<(Span, Option<GeneratorKind>, Span)> {
792         debug!(
793             "closure_span: def_id={:?} target_place={:?} places={:?}",
794             def_id, target_place, places
795         );
796         let hir_id = self.infcx.tcx.hir().as_local_hir_id(def_id)?;
797         let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
798         debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
799         if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr {
800             for (upvar, place) in self.infcx.tcx.upvars(def_id)?.values().zip(places) {
801                 match place {
802                     Operand::Copy(place) | Operand::Move(place)
803                         if target_place == place.as_ref() =>
804                     {
805                         debug!("closure_span: found captured local {:?}", place);
806                         let body = self.infcx.tcx.hir().body(*body_id);
807                         let generator_kind = body.generator_kind();
808                         return Some((*args_span, generator_kind, upvar.span));
809                     }
810                     _ => {}
811                 }
812             }
813         }
814         None
815     }
816
817     /// Helper to retrieve span(s) of given borrow from the current MIR
818     /// representation
819     pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans {
820         let span = self.body.source_info(borrow.reserve_location).span;
821         self.borrow_spans(span, borrow.reserve_location)
822     }
823 }