1 //! Borrow checker diagnostics.
2 
3 use rustc_errors::DiagnosticBuilder;
4 use rustc_hir as hir;
5 use rustc_hir::def::Namespace;
6 use rustc_hir::def_id::DefId;
7 use rustc_hir::lang_items::LangItemGroup;
8 use rustc_hir::GeneratorKind;
9 use rustc_middle::mir::{
10     AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand,
11     Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
12 };
13 use rustc_middle::ty::print::Print;
14 use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt};
15 use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
16 use rustc_span::{hygiene::DesugaringKind, symbol::sym, Span};
17 use rustc_target::abi::VariantIdx;
18 
19 use super::borrow_set::BorrowData;
20 use super::MirBorrowckCtxt;
21 
22 mod find_use;
23 mod outlives_suggestion;
24 mod region_name;
25 mod var_name;
26 
27 mod bound_region_errors;
28 mod conflict_errors;
29 mod explain_borrow;
30 mod move_errors;
31 mod mutability_errors;
32 mod region_errors;
33 
34 crate use bound_region_errors::{ToUniverseInfo, UniverseInfo};
35 crate use mutability_errors::AccessKind;
36 crate use outlives_suggestion::OutlivesSuggestionBuilder;
37 crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
38 crate use region_name::{RegionName, RegionNameSource};
39 use rustc_span::symbol::Ident;
40 
41 pub(super) struct IncludingDowncast(pub(super) bool);
42 
43 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
44     /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
45     /// is moved after being invoked.
46     ///
47     /// ```text
48     /// note: closure cannot be invoked more than once because it moves the variable `dict` out of
49     ///       its environment
50     ///   --> $DIR/issue-42065.rs:16:29
51     ///    |
52     /// LL |         for (key, value) in dict {
53     ///    |                             ^^^^
54     /// ```
add_moved_or_invoked_closure_note( &self, location: Location, place: PlaceRef<'tcx>, diag: &mut DiagnosticBuilder<'_>, )55     pub(super) fn add_moved_or_invoked_closure_note(
56         &self,
57         location: Location,
58         place: PlaceRef<'tcx>,
59         diag: &mut DiagnosticBuilder<'_>,
60     ) {
61         debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
62         let mut target = place.local_or_deref_local();
63         for stmt in &self.body[location.block].statements[location.statement_index..] {
64             debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
65             if let StatementKind::Assign(box (into, Rvalue::Use(from))) = &stmt.kind {
66                 debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
67                 match from {
68                     Operand::Copy(ref place) | Operand::Move(ref place)
69                         if target == place.local_or_deref_local() =>
70                     {
71                         target = into.local_or_deref_local()
72                     }
73                     _ => {}
74                 }
75             }
76         }
77 
78         // Check if we are attempting to call a closure after it has been invoked.
79         let terminator = self.body[location.block].terminator();
80         debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
81         if let TerminatorKind::Call {
82             func: Operand::Constant(box Constant { literal, .. }),
83             args,
84             ..
85         } = &terminator.kind
86         {
87             if let ty::FnDef(id, _) = *literal.ty().kind() {
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 did = did.expect_local();
102                         let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
103 
104                         if let Some((span, hir_place)) =
105                             self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id)
106                         {
107                             diag.span_note(
108                                 *span,
109                                 &format!(
110                                     "closure cannot be invoked more than once because it moves the \
111                                     variable `{}` out of its environment",
112                                     ty::place_to_string_for_capture(self.infcx.tcx, hir_place)
113                                 ),
114                             );
115                             return;
116                         }
117                     }
118                 }
119             }
120         }
121 
122         // Check if we are just moving a closure after it has been invoked.
123         if let Some(target) = target {
124             if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() {
125                 let did = did.expect_local();
126                 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
127 
128                 if let Some((span, hir_place)) =
129                     self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id)
130                 {
131                     diag.span_note(
132                         *span,
133                         &format!(
134                             "closure cannot be moved more than once as it is not `Copy` due to \
135                              moving the variable `{}` out of its environment",
136                             ty::place_to_string_for_capture(self.infcx.tcx, hir_place)
137                         ),
138                     );
139                 }
140             }
141         }
142     }
143 
144     /// End-user visible description of `place` if one can be found.
145     /// If the place is a temporary for instance, `"value"` will be returned.
describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String146     pub(super) fn describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String {
147         match self.describe_place(place_ref) {
148             Some(mut descr) => {
149                 // Surround descr with `backticks`.
150                 descr.reserve(2);
151                 descr.insert(0, '`');
152                 descr.push('`');
153                 descr
154             }
155             None => "value".to_string(),
156         }
157     }
158 
159     /// End-user visible description of `place` if one can be found.
160     /// If the place is a temporary for instance, None will be returned.
describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String>161     pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String> {
162         self.describe_place_with_options(place_ref, IncludingDowncast(false))
163     }
164 
165     /// End-user visible description of `place` if one can be found. If the
166     /// place is a temporary for instance, None will be returned.
167     /// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
168     /// `Downcast` and `IncludingDowncast` is true
describe_place_with_options( &self, place: PlaceRef<'tcx>, including_downcast: IncludingDowncast, ) -> Option<String>169     pub(super) fn describe_place_with_options(
170         &self,
171         place: PlaceRef<'tcx>,
172         including_downcast: IncludingDowncast,
173     ) -> Option<String> {
174         let mut buf = String::new();
175         match self.append_place_to_string(place, &mut buf, false, &including_downcast) {
176             Ok(()) => Some(buf),
177             Err(()) => None,
178         }
179     }
180 
181     /// Appends end-user visible description of `place` to `buf`.
append_place_to_string( &self, place: PlaceRef<'tcx>, buf: &mut String, mut autoderef: bool, including_downcast: &IncludingDowncast, ) -> Result<(), ()>182     fn append_place_to_string(
183         &self,
184         place: PlaceRef<'tcx>,
185         buf: &mut String,
186         mut autoderef: bool,
187         including_downcast: &IncludingDowncast,
188     ) -> Result<(), ()> {
189         match place {
190             PlaceRef { local, projection: [] } => {
191                 self.append_local_to_string(local, buf)?;
192             }
193             PlaceRef { local, projection: [ProjectionElem::Deref] }
194                 if self.body.local_decls[local].is_ref_for_guard() =>
195             {
196                 self.append_place_to_string(
197                     PlaceRef { local, projection: &[] },
198                     buf,
199                     autoderef,
200                     &including_downcast,
201                 )?;
202             }
203             PlaceRef { local, projection: [ProjectionElem::Deref] }
204                 if self.body.local_decls[local].is_ref_to_static() =>
205             {
206                 let local_info = &self.body.local_decls[local].local_info;
207                 if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info {
208                     buf.push_str(&self.infcx.tcx.item_name(def_id).as_str());
209                 } else {
210                     unreachable!();
211                 }
212             }
213             PlaceRef { local, projection: [proj_base @ .., elem] } => {
214                 match elem {
215                     ProjectionElem::Deref => {
216                         let upvar_field_projection = self.is_upvar_field_projection(place);
217                         if let Some(field) = upvar_field_projection {
218                             let var_index = field.index();
219                             let name = self.upvars[var_index].place.to_string(self.infcx.tcx);
220                             if self.upvars[var_index].by_ref {
221                                 buf.push_str(&name);
222                             } else {
223                                 buf.push('*');
224                                 buf.push_str(&name);
225                             }
226                         } else {
227                             if autoderef {
228                                 // FIXME turn this recursion into iteration
229                                 self.append_place_to_string(
230                                     PlaceRef { local, projection: proj_base },
231                                     buf,
232                                     autoderef,
233                                     &including_downcast,
234                                 )?;
235                             } else {
236                                 buf.push('*');
237                                 self.append_place_to_string(
238                                     PlaceRef { local, projection: proj_base },
239                                     buf,
240                                     autoderef,
241                                     &including_downcast,
242                                 )?;
243                             }
244                         }
245                     }
246                     ProjectionElem::Downcast(..) => {
247                         self.append_place_to_string(
248                             PlaceRef { local, projection: proj_base },
249                             buf,
250                             autoderef,
251                             &including_downcast,
252                         )?;
253                         if including_downcast.0 {
254                             return Err(());
255                         }
256                     }
257                     ProjectionElem::Field(field, _ty) => {
258                         autoderef = true;
259 
260                         // FIXME(project-rfc_2229#36): print capture precisely here.
261                         let upvar_field_projection = self.is_upvar_field_projection(place);
262                         if let Some(field) = upvar_field_projection {
263                             let var_index = field.index();
264                             let name = self.upvars[var_index].place.to_string(self.infcx.tcx);
265                             buf.push_str(&name);
266                         } else {
267                             let field_name = self
268                                 .describe_field(PlaceRef { local, projection: proj_base }, *field);
269                             self.append_place_to_string(
270                                 PlaceRef { local, projection: proj_base },
271                                 buf,
272                                 autoderef,
273                                 &including_downcast,
274                             )?;
275                             buf.push('.');
276                             buf.push_str(&field_name);
277                         }
278                     }
279                     ProjectionElem::Index(index) => {
280                         autoderef = true;
281 
282                         self.append_place_to_string(
283                             PlaceRef { local, projection: proj_base },
284                             buf,
285                             autoderef,
286                             &including_downcast,
287                         )?;
288                         buf.push('[');
289                         if self.append_local_to_string(*index, buf).is_err() {
290                             buf.push('_');
291                         }
292                         buf.push(']');
293                     }
294                     ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
295                         autoderef = true;
296                         // Since it isn't possible to borrow an element on a particular index and
297                         // then use another while the borrow is held, don't output indices details
298                         // to avoid confusing the end-user
299                         self.append_place_to_string(
300                             PlaceRef { local, projection: proj_base },
301                             buf,
302                             autoderef,
303                             &including_downcast,
304                         )?;
305                         buf.push_str("[..]");
306                     }
307                 };
308             }
309         }
310 
311         Ok(())
312     }
313 
314     /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
315     /// a name, or its name was generated by the compiler, then `Err` is returned
append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()>316     fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> {
317         let decl = &self.body.local_decls[local];
318         match self.local_names[local] {
319             Some(name) if !decl.from_compiler_desugaring() => {
320                 buf.push_str(&name.as_str());
321                 Ok(())
322             }
323             _ => Err(()),
324         }
325     }
326 
327     /// End-user visible description of the `field`nth field of `base`
describe_field(&self, place: PlaceRef<'tcx>, field: Field) -> String328     fn describe_field(&self, place: PlaceRef<'tcx>, field: Field) -> String {
329         // FIXME Place2 Make this work iteratively
330         match place {
331             PlaceRef { local, projection: [] } => {
332                 let local = &self.body.local_decls[local];
333                 self.describe_field_from_ty(&local.ty, field, None)
334             }
335             PlaceRef { local, projection: [proj_base @ .., elem] } => match elem {
336                 ProjectionElem::Deref => {
337                     self.describe_field(PlaceRef { local, projection: proj_base }, field)
338                 }
339                 ProjectionElem::Downcast(_, variant_index) => {
340                     let base_ty = place.ty(self.body, self.infcx.tcx).ty;
341                     self.describe_field_from_ty(&base_ty, field, Some(*variant_index))
342                 }
343                 ProjectionElem::Field(_, field_type) => {
344                     self.describe_field_from_ty(&field_type, field, None)
345                 }
346                 ProjectionElem::Index(..)
347                 | ProjectionElem::ConstantIndex { .. }
348                 | ProjectionElem::Subslice { .. } => {
349                     self.describe_field(PlaceRef { local, projection: proj_base }, field)
350                 }
351             },
352         }
353     }
354 
355     /// End-user visible description of the `field_index`nth field of `ty`
describe_field_from_ty( &self, ty: Ty<'_>, field: Field, variant_index: Option<VariantIdx>, ) -> String356     fn describe_field_from_ty(
357         &self,
358         ty: Ty<'_>,
359         field: Field,
360         variant_index: Option<VariantIdx>,
361     ) -> String {
362         if ty.is_box() {
363             // If the type is a box, the field is described from the boxed type
364             self.describe_field_from_ty(&ty.boxed_ty(), field, variant_index)
365         } else {
366             match *ty.kind() {
367                 ty::Adt(def, _) => {
368                     let variant = if let Some(idx) = variant_index {
369                         assert!(def.is_enum());
370                         &def.variants[idx]
371                     } else {
372                         def.non_enum_variant()
373                     };
374                     variant.fields[field.index()].ident.to_string()
375                 }
376                 ty::Tuple(_) => field.index().to_string(),
377                 ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
378                     self.describe_field_from_ty(&ty, field, variant_index)
379                 }
380                 ty::Array(ty, _) | ty::Slice(ty) => {
381                     self.describe_field_from_ty(&ty, field, variant_index)
382                 }
383                 ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
384                     // We won't be borrowck'ing here if the closure came from another crate,
385                     // so it's safe to call `expect_local`.
386                     //
387                     // We know the field exists so it's safe to call operator[] and `unwrap` here.
388                     let var_id = self
389                         .infcx
390                         .tcx
391                         .typeck(def_id.expect_local())
392                         .closure_min_captures_flattened(def_id)
393                         .nth(field.index())
394                         .unwrap()
395                         .get_root_variable();
396 
397                     self.infcx.tcx.hir().name(var_id).to_string()
398                 }
399                 _ => {
400                     // Might need a revision when the fields in trait RFC is implemented
401                     // (https://github.com/rust-lang/rfcs/pull/1546)
402                     bug!("End-user description not implemented for field access on `{:?}`", ty);
403                 }
404             }
405         }
406     }
407 
408     /// Add a note that a type does not implement `Copy`
note_type_does_not_implement_copy( &self, err: &mut DiagnosticBuilder<'a>, place_desc: &str, ty: Ty<'tcx>, span: Option<Span>, move_prefix: &str, )409     pub(super) fn note_type_does_not_implement_copy(
410         &self,
411         err: &mut DiagnosticBuilder<'a>,
412         place_desc: &str,
413         ty: Ty<'tcx>,
414         span: Option<Span>,
415         move_prefix: &str,
416     ) {
417         let message = format!(
418             "{}move occurs because {} has type `{}`, which does not implement the `Copy` trait",
419             move_prefix, place_desc, ty,
420         );
421         if let Some(span) = span {
422             err.span_label(span, message);
423         } else {
424             err.note(&message);
425         }
426     }
427 
borrowed_content_source( &self, deref_base: PlaceRef<'tcx>, ) -> BorrowedContentSource<'tcx>428     pub(super) fn borrowed_content_source(
429         &self,
430         deref_base: PlaceRef<'tcx>,
431     ) -> BorrowedContentSource<'tcx> {
432         let tcx = self.infcx.tcx;
433 
434         // Look up the provided place and work out the move path index for it,
435         // we'll use this to check whether it was originally from an overloaded
436         // operator.
437         match self.move_data.rev_lookup.find(deref_base) {
438             LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => {
439                 debug!("borrowed_content_source: mpi={:?}", mpi);
440 
441                 for i in &self.move_data.init_path_map[mpi] {
442                     let init = &self.move_data.inits[*i];
443                     debug!("borrowed_content_source: init={:?}", init);
444                     // We're only interested in statements that initialized a value, not the
445                     // initializations from arguments.
446                     let loc = match init.location {
447                         InitLocation::Statement(stmt) => stmt,
448                         _ => continue,
449                     };
450 
451                     let bbd = &self.body[loc.block];
452                     let is_terminator = bbd.statements.len() == loc.statement_index;
453                     debug!(
454                         "borrowed_content_source: loc={:?} is_terminator={:?}",
455                         loc, is_terminator,
456                     );
457                     if !is_terminator {
458                         continue;
459                     } else if let Some(Terminator {
460                         kind: TerminatorKind::Call { ref func, from_hir_call: false, .. },
461                         ..
462                     }) = bbd.terminator
463                     {
464                         if let Some(source) =
465                             BorrowedContentSource::from_call(func.ty(self.body, tcx), tcx)
466                         {
467                             return source;
468                         }
469                     }
470                 }
471             }
472             // Base is a `static` so won't be from an overloaded operator
473             _ => (),
474         };
475 
476         // If we didn't find an overloaded deref or index, then assume it's a
477         // built in deref and check the type of the base.
478         let base_ty = deref_base.ty(self.body, tcx).ty;
479         if base_ty.is_unsafe_ptr() {
480             BorrowedContentSource::DerefRawPointer
481         } else if base_ty.is_mutable_ptr() {
482             BorrowedContentSource::DerefMutableRef
483         } else {
484             BorrowedContentSource::DerefSharedRef
485         }
486     }
487 }
488 
489 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
490     /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
491     /// name where required.
get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String492     pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
493         let mut s = String::new();
494         let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
495 
496         // We need to add synthesized lifetimes where appropriate. We do
497         // this by hooking into the pretty printer and telling it to label the
498         // lifetimes without names with the value `'0`.
499         match ty.kind() {
500             ty::Ref(
501                 ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br, .. })
502                 | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }),
503                 _,
504                 _,
505             ) => printer.region_highlight_mode.highlighting_bound_region(*br, counter),
506             _ => {}
507         }
508 
509         let _ = ty.print(printer);
510         s
511     }
512 
513     /// Returns the name of the provided `Ty` (that must be a reference)'s region with a
514     /// synthesized lifetime name where required.
get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String515     pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
516         let mut s = String::new();
517         let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
518 
519         let region = match ty.kind() {
520             ty::Ref(region, _, _) => {
521                 match region {
522                     ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br, .. })
523                     | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => {
524                         printer.region_highlight_mode.highlighting_bound_region(*br, counter)
525                     }
526                     _ => {}
527                 }
528 
529                 region
530             }
531             _ => bug!("ty for annotation of borrow region is not a reference"),
532         };
533 
534         let _ = region.print(printer);
535         s
536     }
537 }
538 
539 /// The span(s) associated to a use of a place.
540 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
541 pub(super) enum UseSpans<'tcx> {
542     /// The access is caused by capturing a variable for a closure.
543     ClosureUse {
544         /// This is true if the captured variable was from a generator.
545         generator_kind: Option<GeneratorKind>,
546         /// The span of the args of the closure, including the `move` keyword if
547         /// it's present.
548         args_span: Span,
549         /// The span of the use resulting in capture kind
550         /// Check `ty::CaptureInfo` for more details
551         capture_kind_span: Span,
552         /// The span of the use resulting in the captured path
553         /// Check `ty::CaptureInfo` for more details
554         path_span: Span,
555     },
556     /// The access is caused by using a variable as the receiver of a method
557     /// that takes 'self'
558     FnSelfUse {
559         /// The span of the variable being moved
560         var_span: Span,
561         /// The span of the method call on the variable
562         fn_call_span: Span,
563         /// The definition span of the method being called
564         fn_span: Span,
565         kind: FnSelfUseKind<'tcx>,
566     },
567     /// This access is caused by a `match` or `if let` pattern.
568     PatUse(Span),
569     /// This access has a single span associated to it: common case.
570     OtherUse(Span),
571 }
572 
573 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
574 pub(super) enum FnSelfUseKind<'tcx> {
575     /// A normal method call of the form `receiver.foo(a, b, c)`
576     Normal {
577         self_arg: Ident,
578         implicit_into_iter: bool,
579         /// Whether the self type of the method call has an `.as_ref()` method.
580         /// Used for better diagnostics.
581         is_option_or_result: bool,
582     },
583     /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)`
584     FnOnceCall,
585     /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`)
586     Operator { self_arg: Ident },
587     DerefCoercion {
588         /// The `Span` of the `Target` associated type
589         /// in the `Deref` impl we are using.
590         deref_target: Span,
591         /// The type `T::Deref` we are dereferencing to
592         deref_target_ty: Ty<'tcx>,
593     },
594 }
595 
596 impl UseSpans<'_> {
args_or_use(self) -> Span597     pub(super) fn args_or_use(self) -> Span {
598         match self {
599             UseSpans::ClosureUse { args_span: span, .. }
600             | UseSpans::PatUse(span)
601             | UseSpans::OtherUse(span) => span,
602             UseSpans::FnSelfUse {
603                 fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, ..
604             } => fn_call_span,
605             UseSpans::FnSelfUse { var_span, .. } => var_span,
606         }
607     }
608 
609     /// Returns the span of `self`, in the case of a `ClosureUse` returns the `path_span`
var_or_use_path_span(self) -> Span610     pub(super) fn var_or_use_path_span(self) -> Span {
611         match self {
612             UseSpans::ClosureUse { path_span: span, .. }
613             | UseSpans::PatUse(span)
614             | UseSpans::OtherUse(span) => span,
615             UseSpans::FnSelfUse {
616                 fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, ..
617             } => fn_call_span,
618             UseSpans::FnSelfUse { var_span, .. } => var_span,
619         }
620     }
621 
622     /// Returns the span of `self`, in the case of a `ClosureUse` returns the `capture_kind_span`
var_or_use(self) -> Span623     pub(super) fn var_or_use(self) -> Span {
624         match self {
625             UseSpans::ClosureUse { capture_kind_span: span, .. }
626             | UseSpans::PatUse(span)
627             | UseSpans::OtherUse(span) => span,
628             UseSpans::FnSelfUse {
629                 fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, ..
630             } => fn_call_span,
631             UseSpans::FnSelfUse { var_span, .. } => var_span,
632         }
633     }
634 
generator_kind(self) -> Option<GeneratorKind>635     pub(super) fn generator_kind(self) -> Option<GeneratorKind> {
636         match self {
637             UseSpans::ClosureUse { generator_kind, .. } => generator_kind,
638             _ => None,
639         }
640     }
641 
642     // Add a span label to the arguments of the closure, if it exists.
args_span_label( self, err: &mut DiagnosticBuilder<'_>, message: impl Into<String>, )643     pub(super) fn args_span_label(
644         self,
645         err: &mut DiagnosticBuilder<'_>,
646         message: impl Into<String>,
647     ) {
648         if let UseSpans::ClosureUse { args_span, .. } = self {
649             err.span_label(args_span, message);
650         }
651     }
652 
653     // Add a span label to the use of the captured variable, if it exists.
654     // only adds label to the `path_span`
var_span_label_path_only( self, err: &mut DiagnosticBuilder<'_>, message: impl Into<String>, )655     pub(super) fn var_span_label_path_only(
656         self,
657         err: &mut DiagnosticBuilder<'_>,
658         message: impl Into<String>,
659     ) {
660         if let UseSpans::ClosureUse { path_span, .. } = self {
661             err.span_label(path_span, message);
662         }
663     }
664 
665     // Add a span label to the use of the captured variable, if it exists.
var_span_label( self, err: &mut DiagnosticBuilder<'_>, message: impl Into<String>, kind_desc: impl Into<String>, )666     pub(super) fn var_span_label(
667         self,
668         err: &mut DiagnosticBuilder<'_>,
669         message: impl Into<String>,
670         kind_desc: impl Into<String>,
671     ) {
672         if let UseSpans::ClosureUse { capture_kind_span, path_span, .. } = self {
673             if capture_kind_span == path_span {
674                 err.span_label(capture_kind_span, message);
675             } else {
676                 let capture_kind_label =
677                     format!("capture is {} because of use here", kind_desc.into());
678                 let path_label = message;
679                 err.span_label(capture_kind_span, capture_kind_label);
680                 err.span_label(path_span, path_label);
681             }
682         }
683     }
684 
685     /// Returns `false` if this place is not used in a closure.
for_closure(&self) -> bool686     pub(super) fn for_closure(&self) -> bool {
687         match *self {
688             UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_none(),
689             _ => false,
690         }
691     }
692 
693     /// Returns `false` if this place is not used in a generator.
for_generator(&self) -> bool694     pub(super) fn for_generator(&self) -> bool {
695         match *self {
696             UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_some(),
697             _ => false,
698         }
699     }
700 
701     /// Describe the span associated with a use of a place.
describe(&self) -> String702     pub(super) fn describe(&self) -> String {
703         match *self {
704             UseSpans::ClosureUse { generator_kind, .. } => {
705                 if generator_kind.is_some() {
706                     " in generator".to_string()
707                 } else {
708                     " in closure".to_string()
709                 }
710             }
711             _ => String::new(),
712         }
713     }
714 
or_else<F>(self, if_other: F) -> Self where F: FnOnce() -> Self,715     pub(super) fn or_else<F>(self, if_other: F) -> Self
716     where
717         F: FnOnce() -> Self,
718     {
719         match self {
720             closure @ UseSpans::ClosureUse { .. } => closure,
721             UseSpans::PatUse(_) | UseSpans::OtherUse(_) => if_other(),
722             fn_self @ UseSpans::FnSelfUse { .. } => fn_self,
723         }
724     }
725 }
726 
727 pub(super) enum BorrowedContentSource<'tcx> {
728     DerefRawPointer,
729     DerefMutableRef,
730     DerefSharedRef,
731     OverloadedDeref(Ty<'tcx>),
732     OverloadedIndex(Ty<'tcx>),
733 }
734 
735 impl BorrowedContentSource<'tcx> {
describe_for_unnamed_place(&self, tcx: TyCtxt<'_>) -> String736     pub(super) fn describe_for_unnamed_place(&self, tcx: TyCtxt<'_>) -> String {
737         match *self {
738             BorrowedContentSource::DerefRawPointer => "a raw pointer".to_string(),
739             BorrowedContentSource::DerefSharedRef => "a shared reference".to_string(),
740             BorrowedContentSource::DerefMutableRef => "a mutable reference".to_string(),
741             BorrowedContentSource::OverloadedDeref(ty) => match ty.kind() {
742                 ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => {
743                     "an `Rc`".to_string()
744                 }
745                 ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Arc, def.did) => {
746                     "an `Arc`".to_string()
747                 }
748                 _ => format!("dereference of `{}`", ty),
749             },
750             BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{}`", ty),
751         }
752     }
753 
describe_for_named_place(&self) -> Option<&'static str>754     pub(super) fn describe_for_named_place(&self) -> Option<&'static str> {
755         match *self {
756             BorrowedContentSource::DerefRawPointer => Some("raw pointer"),
757             BorrowedContentSource::DerefSharedRef => Some("shared reference"),
758             BorrowedContentSource::DerefMutableRef => Some("mutable reference"),
759             // Overloaded deref and index operators should be evaluated into a
760             // temporary. So we don't need a description here.
761             BorrowedContentSource::OverloadedDeref(_)
762             | BorrowedContentSource::OverloadedIndex(_) => None,
763         }
764     }
765 
describe_for_immutable_place(&self, tcx: TyCtxt<'_>) -> String766     pub(super) fn describe_for_immutable_place(&self, tcx: TyCtxt<'_>) -> String {
767         match *self {
768             BorrowedContentSource::DerefRawPointer => "a `*const` pointer".to_string(),
769             BorrowedContentSource::DerefSharedRef => "a `&` reference".to_string(),
770             BorrowedContentSource::DerefMutableRef => {
771                 bug!("describe_for_immutable_place: DerefMutableRef isn't immutable")
772             }
773             BorrowedContentSource::OverloadedDeref(ty) => match ty.kind() {
774                 ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => {
775                     "an `Rc`".to_string()
776                 }
777                 ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Arc, def.did) => {
778                     "an `Arc`".to_string()
779                 }
780                 _ => format!("a dereference of `{}`", ty),
781             },
782             BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{}`", ty),
783         }
784     }
785 
from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self>786     fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> {
787         match *func.kind() {
788             ty::FnDef(def_id, substs) => {
789                 let trait_id = tcx.trait_of_item(def_id)?;
790 
791                 let lang_items = tcx.lang_items();
792                 if Some(trait_id) == lang_items.deref_trait()
793                     || Some(trait_id) == lang_items.deref_mut_trait()
794                 {
795                     Some(BorrowedContentSource::OverloadedDeref(substs.type_at(0)))
796                 } else if Some(trait_id) == lang_items.index_trait()
797                     || Some(trait_id) == lang_items.index_mut_trait()
798                 {
799                     Some(BorrowedContentSource::OverloadedIndex(substs.type_at(0)))
800                 } else {
801                     None
802                 }
803             }
804             _ => None,
805         }
806     }
807 }
808 
809 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
810     /// Finds the spans associated to a move or copy of move_place at location.
move_spans( &self, moved_place: PlaceRef<'tcx>, location: Location, ) -> UseSpans<'tcx>811     pub(super) fn move_spans(
812         &self,
813         moved_place: PlaceRef<'tcx>, // Could also be an upvar.
814         location: Location,
815     ) -> UseSpans<'tcx> {
816         use self::UseSpans::*;
817 
818         let stmt = match self.body[location.block].statements.get(location.statement_index) {
819             Some(stmt) => stmt,
820             None => return OtherUse(self.body.source_info(location).span),
821         };
822 
823         debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
824         if let StatementKind::Assign(box (_, Rvalue::Aggregate(ref kind, ref places))) = stmt.kind {
825             match kind {
826                 box AggregateKind::Closure(def_id, _)
827                 | box AggregateKind::Generator(def_id, _, _) => {
828                     debug!("move_spans: def_id={:?} places={:?}", def_id, places);
829                     if let Some((args_span, generator_kind, capture_kind_span, path_span)) =
830                         self.closure_span(*def_id, moved_place, places)
831                     {
832                         return ClosureUse {
833                             generator_kind,
834                             args_span,
835                             capture_kind_span,
836                             path_span,
837                         };
838                     }
839                 }
840                 _ => {}
841             }
842         }
843 
844         // StatementKind::FakeRead only contains a def_id if they are introduced as a result
845         // of pattern matching within a closure.
846         if let StatementKind::FakeRead(box (cause, ref place)) = stmt.kind {
847             match cause {
848                 FakeReadCause::ForMatchedPlace(Some(closure_def_id))
849                 | FakeReadCause::ForLet(Some(closure_def_id)) => {
850                     debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place);
851                     let places = &[Operand::Move(*place)];
852                     if let Some((args_span, generator_kind, capture_kind_span, path_span)) =
853                         self.closure_span(closure_def_id, moved_place, places)
854                     {
855                         return ClosureUse {
856                             generator_kind,
857                             args_span,
858                             capture_kind_span,
859                             path_span,
860                         };
861                     }
862                 }
863                 _ => {}
864             }
865         }
866 
867         let normal_ret =
868             if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) {
869                 PatUse(stmt.source_info.span)
870             } else {
871                 OtherUse(stmt.source_info.span)
872             };
873 
874         // We are trying to find MIR of the form:
875         // ```
876         // _temp = _moved_val;
877         // ...
878         // FnSelfCall(_temp, ...)
879         // ```
880         //
881         // where `_moved_val` is the place we generated the move error for,
882         // `_temp` is some other local, and `FnSelfCall` is a function
883         // that has a `self` parameter.
884 
885         let target_temp = match stmt.kind {
886             StatementKind::Assign(box (temp, _)) if temp.as_local().is_some() => {
887                 temp.as_local().unwrap()
888             }
889             _ => return normal_ret,
890         };
891 
892         debug!("move_spans: target_temp = {:?}", target_temp);
893 
894         if let Some(Terminator {
895             kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, ..
896         }) = &self.body[location.block].terminator
897         {
898             let (method_did, method_substs) = if let Some(info) =
899                 rustc_const_eval::util::find_self_call(
900                     self.infcx.tcx,
901                     &self.body,
902                     target_temp,
903                     location.block,
904                 ) {
905                 info
906             } else {
907                 return normal_ret;
908             };
909 
910             let tcx = self.infcx.tcx;
911             let parent = tcx.parent(method_did);
912             let is_fn_once = parent == tcx.lang_items().fn_once_trait();
913             let is_operator = !from_hir_call
914                 && parent.map_or(false, |p| tcx.lang_items().group(LangItemGroup::Op).contains(&p));
915             let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did);
916             let fn_call_span = *fn_span;
917 
918             let self_arg = tcx.fn_arg_names(method_did)[0];
919 
920             debug!(
921                 "terminator = {:?} from_hir_call={:?}",
922                 self.body[location.block].terminator, from_hir_call
923             );
924 
925             // Check for a 'special' use of 'self' -
926             // an FnOnce call, an operator (e.g. `<<`), or a
927             // deref coercion.
928             let kind = if is_fn_once {
929                 Some(FnSelfUseKind::FnOnceCall)
930             } else if is_operator {
931                 Some(FnSelfUseKind::Operator { self_arg })
932             } else if is_deref {
933                 let deref_target =
934                     tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
935                         Instance::resolve(tcx, self.param_env, deref_target, method_substs)
936                             .transpose()
937                     });
938                 if let Some(Ok(instance)) = deref_target {
939                     let deref_target_ty = instance.ty(tcx, self.param_env);
940                     Some(FnSelfUseKind::DerefCoercion {
941                         deref_target: tcx.def_span(instance.def_id()),
942                         deref_target_ty,
943                     })
944                 } else {
945                     None
946                 }
947             } else {
948                 None
949             };
950 
951             let kind = kind.unwrap_or_else(|| {
952                 // This isn't a 'special' use of `self`
953                 debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span);
954                 let implicit_into_iter = Some(method_did) == tcx.lang_items().into_iter_fn()
955                     && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop);
956                 let parent_self_ty = parent
957                     .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl)
958                     .and_then(|did| match tcx.type_of(did).kind() {
959                         ty::Adt(def, ..) => Some(def.did),
960                         _ => None,
961                     });
962                 let is_option_or_result = parent_self_ty.map_or(false, |def_id| {
963                     tcx.is_diagnostic_item(sym::Option, def_id)
964                         || tcx.is_diagnostic_item(sym::Result, def_id)
965                 });
966                 FnSelfUseKind::Normal { self_arg, implicit_into_iter, is_option_or_result }
967             });
968 
969             return FnSelfUse {
970                 var_span: stmt.source_info.span,
971                 fn_call_span,
972                 fn_span: self
973                     .infcx
974                     .tcx
975                     .sess
976                     .source_map()
977                     .guess_head_span(self.infcx.tcx.def_span(method_did)),
978                 kind,
979             };
980         }
981         normal_ret
982     }
983 
984     /// Finds the span of arguments of a closure (within `maybe_closure_span`)
985     /// and its usage of the local assigned at `location`.
986     /// This is done by searching in statements succeeding `location`
987     /// and originating from `maybe_closure_span`.
borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx>988     pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx> {
989         use self::UseSpans::*;
990         debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
991 
992         let target = match self.body[location.block].statements.get(location.statement_index) {
993             Some(&Statement { kind: StatementKind::Assign(box (ref place, _)), .. }) => {
994                 if let Some(local) = place.as_local() {
995                     local
996                 } else {
997                     return OtherUse(use_span);
998                 }
999             }
1000             _ => return OtherUse(use_span),
1001         };
1002 
1003         if self.body.local_kind(target) != LocalKind::Temp {
1004             // operands are always temporaries.
1005             return OtherUse(use_span);
1006         }
1007 
1008         for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
1009             if let StatementKind::Assign(box (_, Rvalue::Aggregate(ref kind, ref places))) =
1010                 stmt.kind
1011             {
1012                 let (def_id, is_generator) = match kind {
1013                     box AggregateKind::Closure(def_id, _) => (def_id, false),
1014                     box AggregateKind::Generator(def_id, _, _) => (def_id, true),
1015                     _ => continue,
1016                 };
1017 
1018                 debug!(
1019                     "borrow_spans: def_id={:?} is_generator={:?} places={:?}",
1020                     def_id, is_generator, places
1021                 );
1022                 if let Some((args_span, generator_kind, capture_kind_span, path_span)) =
1023                     self.closure_span(*def_id, Place::from(target).as_ref(), places)
1024                 {
1025                     return ClosureUse { generator_kind, args_span, capture_kind_span, path_span };
1026                 } else {
1027                     return OtherUse(use_span);
1028                 }
1029             }
1030 
1031             if use_span != stmt.source_info.span {
1032                 break;
1033             }
1034         }
1035 
1036         OtherUse(use_span)
1037     }
1038 
1039     /// Finds the spans of a captured place within a closure or generator.
1040     /// The first span is the location of the use resulting in the capture kind of the capture
1041     /// The second span is the location the use resulting in the captured path of the capture
closure_span( &self, def_id: DefId, target_place: PlaceRef<'tcx>, places: &[Operand<'tcx>], ) -> Option<(Span, Option<GeneratorKind>, Span, Span)>1042     fn closure_span(
1043         &self,
1044         def_id: DefId,
1045         target_place: PlaceRef<'tcx>,
1046         places: &[Operand<'tcx>],
1047     ) -> Option<(Span, Option<GeneratorKind>, Span, Span)> {
1048         debug!(
1049             "closure_span: def_id={:?} target_place={:?} places={:?}",
1050             def_id, target_place, places
1051         );
1052         let local_did = def_id.as_local()?;
1053         let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(local_did);
1054         let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
1055         debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
1056         if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr {
1057             for (captured_place, place) in self
1058                 .infcx
1059                 .tcx
1060                 .typeck(def_id.expect_local())
1061                 .closure_min_captures_flattened(def_id)
1062                 .zip(places)
1063             {
1064                 match place {
1065                     Operand::Copy(place) | Operand::Move(place)
1066                         if target_place == place.as_ref() =>
1067                     {
1068                         debug!("closure_span: found captured local {:?}", place);
1069                         let body = self.infcx.tcx.hir().body(*body_id);
1070                         let generator_kind = body.generator_kind();
1071 
1072                         return Some((
1073                             *args_span,
1074                             generator_kind,
1075                             captured_place.get_capture_kind_span(self.infcx.tcx),
1076                             captured_place.get_path_span(self.infcx.tcx),
1077                         ));
1078                     }
1079                     _ => {}
1080                 }
1081             }
1082         }
1083         None
1084     }
1085 
1086     /// Helper to retrieve span(s) of given borrow from the current MIR
1087     /// representation
retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx>1088     pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx> {
1089         let span = self.body.source_info(borrow.reserve_location).span;
1090         self.borrow_spans(span, borrow.reserve_location)
1091     }
1092 }
1093