• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Borrow checker diagnostics.
2 
3 use crate::session_diagnostics::{
4     CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause,
5     CaptureVarKind, CaptureVarPathUseCause, OnClosureNote,
6 };
7 use itertools::Itertools;
8 use rustc_errors::{Applicability, Diagnostic};
9 use rustc_hir as hir;
10 use rustc_hir::def::{CtorKind, Namespace};
11 use rustc_hir::GeneratorKind;
12 use rustc_index::IndexSlice;
13 use rustc_infer::infer::LateBoundRegionConversionTime;
14 use rustc_middle::mir::tcx::PlaceTy;
15 use rustc_middle::mir::{
16     AggregateKind, CallSource, Constant, FakeReadCause, Local, LocalInfo, LocalKind, Location,
17     Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
18     TerminatorKind,
19 };
20 use rustc_middle::ty::print::Print;
21 use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
22 use rustc_middle::util::{call_kind, CallDesugaringKind};
23 use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
24 use rustc_span::def_id::LocalDefId;
25 use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
26 use rustc_target::abi::{FieldIdx, VariantIdx};
27 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
28 use rustc_trait_selection::traits::{
29     type_known_to_meet_bound_modulo_regions, Obligation, ObligationCause,
30 };
31 
32 use super::borrow_set::BorrowData;
33 use super::MirBorrowckCtxt;
34 
35 mod find_all_local_uses;
36 mod find_use;
37 mod outlives_suggestion;
38 mod region_name;
39 mod var_name;
40 
41 mod bound_region_errors;
42 mod conflict_errors;
43 mod explain_borrow;
44 mod move_errors;
45 mod mutability_errors;
46 mod region_errors;
47 
48 pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo};
49 pub(crate) use mutability_errors::AccessKind;
50 pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder;
51 pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
52 pub(crate) use region_name::{RegionName, RegionNameSource};
53 pub(crate) use rustc_middle::util::CallKind;
54 
55 pub(super) struct DescribePlaceOpt {
56     pub including_downcast: bool,
57 
58     /// Enable/Disable tuple fields.
59     /// For example `x` tuple. if it's `true` `x.0`. Otherwise `x`
60     pub including_tuple_field: bool,
61 }
62 
63 pub(super) struct IncludingTupleField(pub(super) bool);
64 
65 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
66     /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
67     /// is moved after being invoked.
68     ///
69     /// ```text
70     /// note: closure cannot be invoked more than once because it moves the variable `dict` out of
71     ///       its environment
72     ///   --> $DIR/issue-42065.rs:16:29
73     ///    |
74     /// LL |         for (key, value) in dict {
75     ///    |                             ^^^^
76     /// ```
add_moved_or_invoked_closure_note( &self, location: Location, place: PlaceRef<'tcx>, diag: &mut Diagnostic, ) -> bool77     pub(super) fn add_moved_or_invoked_closure_note(
78         &self,
79         location: Location,
80         place: PlaceRef<'tcx>,
81         diag: &mut Diagnostic,
82     ) -> bool {
83         debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
84         let mut target = place.local_or_deref_local();
85         for stmt in &self.body[location.block].statements[location.statement_index..] {
86             debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
87             if let StatementKind::Assign(box (into, Rvalue::Use(from))) = &stmt.kind {
88                 debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
89                 match from {
90                     Operand::Copy(place) | Operand::Move(place)
91                         if target == place.local_or_deref_local() =>
92                     {
93                         target = into.local_or_deref_local()
94                     }
95                     _ => {}
96                 }
97             }
98         }
99 
100         // Check if we are attempting to call a closure after it has been invoked.
101         let terminator = self.body[location.block].terminator();
102         debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
103         if let TerminatorKind::Call {
104             func: Operand::Constant(box Constant { literal, .. }),
105             args,
106             ..
107         } = &terminator.kind
108         {
109             if let ty::FnDef(id, _) = *literal.ty().kind() {
110                 debug!("add_moved_or_invoked_closure_note: id={:?}", id);
111                 if Some(self.infcx.tcx.parent(id)) == self.infcx.tcx.lang_items().fn_once_trait() {
112                     let closure = match args.first() {
113                         Some(Operand::Copy(place) | Operand::Move(place))
114                             if target == place.local_or_deref_local() =>
115                         {
116                             place.local_or_deref_local().unwrap()
117                         }
118                         _ => return false,
119                     };
120 
121                     debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
122                     if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() {
123                         let did = did.expect_local();
124                         if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
125                             diag.eager_subdiagnostic(
126                                 &self.infcx.tcx.sess.parse_sess.span_diagnostic,
127                                 OnClosureNote::InvokedTwice {
128                                     place_name: &ty::place_to_string_for_capture(
129                                         self.infcx.tcx,
130                                         hir_place,
131                                     ),
132                                     span: *span,
133                                 },
134                             );
135                             return true;
136                         }
137                     }
138                 }
139             }
140         }
141 
142         // Check if we are just moving a closure after it has been invoked.
143         if let Some(target) = target {
144             if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() {
145                 let did = did.expect_local();
146                 if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
147                     diag.eager_subdiagnostic(
148                         &self.infcx.tcx.sess.parse_sess.span_diagnostic,
149                         OnClosureNote::MovedTwice {
150                             place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
151                             span: *span,
152                         },
153                     );
154                     return true;
155                 }
156             }
157         }
158         false
159     }
160 
161     /// End-user visible description of `place` if one can be found.
162     /// If the place is a temporary for instance, `"value"` will be returned.
describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String163     pub(super) fn describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String {
164         match self.describe_place(place_ref) {
165             Some(mut descr) => {
166                 // Surround descr with `backticks`.
167                 descr.reserve(2);
168                 descr.insert(0, '`');
169                 descr.push('`');
170                 descr
171             }
172             None => "value".to_string(),
173         }
174     }
175 
176     /// End-user visible description of `place` if one can be found.
177     /// If the place is a temporary for instance, `None` will be returned.
describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String>178     pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String> {
179         self.describe_place_with_options(
180             place_ref,
181             DescribePlaceOpt { including_downcast: false, including_tuple_field: true },
182         )
183     }
184 
185     /// End-user visible description of `place` if one can be found. If the place is a temporary
186     /// for instance, `None` will be returned.
187     /// `IncludingDowncast` parameter makes the function return `None` if `ProjectionElem` is
188     /// `Downcast` and `IncludingDowncast` is true
describe_place_with_options( &self, place: PlaceRef<'tcx>, opt: DescribePlaceOpt, ) -> Option<String>189     pub(super) fn describe_place_with_options(
190         &self,
191         place: PlaceRef<'tcx>,
192         opt: DescribePlaceOpt,
193     ) -> Option<String> {
194         let local = place.local;
195         let mut autoderef_index = None;
196         let mut buf = String::new();
197         let mut ok = self.append_local_to_string(local, &mut buf);
198 
199         for (index, elem) in place.projection.into_iter().enumerate() {
200             match elem {
201                 ProjectionElem::Deref => {
202                     if index == 0 {
203                         if self.body.local_decls[local].is_ref_for_guard() {
204                             continue;
205                         }
206                         if let LocalInfo::StaticRef { def_id, .. } =
207                             *self.body.local_decls[local].local_info()
208                         {
209                             buf.push_str(self.infcx.tcx.item_name(def_id).as_str());
210                             ok = Ok(());
211                             continue;
212                         }
213                     }
214                     if let Some(field) = self.is_upvar_field_projection(PlaceRef {
215                         local,
216                         projection: place.projection.split_at(index + 1).0,
217                     }) {
218                         let var_index = field.index();
219                         buf = self.upvars[var_index].place.to_string(self.infcx.tcx);
220                         ok = Ok(());
221                         if !self.upvars[var_index].by_ref {
222                             buf.insert(0, '*');
223                         }
224                     } else {
225                         if autoderef_index.is_none() {
226                             autoderef_index =
227                                 match place.projection.into_iter().rev().find_position(|elem| {
228                                     !matches!(
229                                         elem,
230                                         ProjectionElem::Deref | ProjectionElem::Downcast(..)
231                                     )
232                                 }) {
233                                     Some((index, _)) => Some(place.projection.len() - index),
234                                     None => Some(0),
235                                 };
236                         }
237                         if index >= autoderef_index.unwrap() {
238                             buf.insert(0, '*');
239                         }
240                     }
241                 }
242                 ProjectionElem::Downcast(..) if opt.including_downcast => return None,
243                 ProjectionElem::Downcast(..) => (),
244                 ProjectionElem::OpaqueCast(..) => (),
245                 ProjectionElem::Field(field, _ty) => {
246                     // FIXME(project-rfc_2229#36): print capture precisely here.
247                     if let Some(field) = self.is_upvar_field_projection(PlaceRef {
248                         local,
249                         projection: place.projection.split_at(index + 1).0,
250                     }) {
251                         buf = self.upvars[field.index()].place.to_string(self.infcx.tcx);
252                         ok = Ok(());
253                     } else {
254                         let field_name = self.describe_field(
255                             PlaceRef { local, projection: place.projection.split_at(index).0 },
256                             *field,
257                             IncludingTupleField(opt.including_tuple_field),
258                         );
259                         if let Some(field_name_str) = field_name {
260                             buf.push('.');
261                             buf.push_str(&field_name_str);
262                         }
263                     }
264                 }
265                 ProjectionElem::Index(index) => {
266                     buf.push('[');
267                     if self.append_local_to_string(*index, &mut buf).is_err() {
268                         buf.push('_');
269                     }
270                     buf.push(']');
271                 }
272                 ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
273                     // Since it isn't possible to borrow an element on a particular index and
274                     // then use another while the borrow is held, don't output indices details
275                     // to avoid confusing the end-user
276                     buf.push_str("[..]");
277                 }
278             }
279         }
280         ok.ok().map(|_| buf)
281     }
282 
describe_name(&self, place: PlaceRef<'tcx>) -> Option<Symbol>283     fn describe_name(&self, place: PlaceRef<'tcx>) -> Option<Symbol> {
284         for elem in place.projection.into_iter() {
285             match elem {
286                 ProjectionElem::Downcast(Some(name), _) => {
287                     return Some(*name);
288                 }
289                 _ => {}
290             }
291         }
292         None
293     }
294 
295     /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
296     /// 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<(), ()>297     fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> {
298         let decl = &self.body.local_decls[local];
299         match self.local_names[local] {
300             Some(name) if !decl.from_compiler_desugaring() => {
301                 buf.push_str(name.as_str());
302                 Ok(())
303             }
304             _ => Err(()),
305         }
306     }
307 
308     /// End-user visible description of the `field`nth field of `base`
describe_field( &self, place: PlaceRef<'tcx>, field: FieldIdx, including_tuple_field: IncludingTupleField, ) -> Option<String>309     fn describe_field(
310         &self,
311         place: PlaceRef<'tcx>,
312         field: FieldIdx,
313         including_tuple_field: IncludingTupleField,
314     ) -> Option<String> {
315         let place_ty = match place {
316             PlaceRef { local, projection: [] } => PlaceTy::from_ty(self.body.local_decls[local].ty),
317             PlaceRef { local, projection: [proj_base @ .., elem] } => match elem {
318                 ProjectionElem::Deref
319                 | ProjectionElem::Index(..)
320                 | ProjectionElem::ConstantIndex { .. }
321                 | ProjectionElem::Subslice { .. } => {
322                     PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
323                 }
324                 ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
325                 ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(*ty),
326                 ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
327             },
328         };
329         self.describe_field_from_ty(
330             place_ty.ty,
331             field,
332             place_ty.variant_index,
333             including_tuple_field,
334         )
335     }
336 
337     /// End-user visible description of the `field_index`nth field of `ty`
describe_field_from_ty( &self, ty: Ty<'_>, field: FieldIdx, variant_index: Option<VariantIdx>, including_tuple_field: IncludingTupleField, ) -> Option<String>338     fn describe_field_from_ty(
339         &self,
340         ty: Ty<'_>,
341         field: FieldIdx,
342         variant_index: Option<VariantIdx>,
343         including_tuple_field: IncludingTupleField,
344     ) -> Option<String> {
345         if ty.is_box() {
346             // If the type is a box, the field is described from the boxed type
347             self.describe_field_from_ty(ty.boxed_ty(), field, variant_index, including_tuple_field)
348         } else {
349             match *ty.kind() {
350                 ty::Adt(def, _) => {
351                     let variant = if let Some(idx) = variant_index {
352                         assert!(def.is_enum());
353                         &def.variant(idx)
354                     } else {
355                         def.non_enum_variant()
356                     };
357                     if !including_tuple_field.0 && variant.ctor_kind() == Some(CtorKind::Fn) {
358                         return None;
359                     }
360                     Some(variant.fields[field].name.to_string())
361                 }
362                 ty::Tuple(_) => Some(field.index().to_string()),
363                 ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
364                     self.describe_field_from_ty(ty, field, variant_index, including_tuple_field)
365                 }
366                 ty::Array(ty, _) | ty::Slice(ty) => {
367                     self.describe_field_from_ty(ty, field, variant_index, including_tuple_field)
368                 }
369                 ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
370                     // We won't be borrowck'ing here if the closure came from another crate,
371                     // so it's safe to call `expect_local`.
372                     //
373                     // We know the field exists so it's safe to call operator[] and `unwrap` here.
374                     let def_id = def_id.expect_local();
375                     let var_id =
376                         self.infcx.tcx.closure_captures(def_id)[field.index()].get_root_variable();
377 
378                     Some(self.infcx.tcx.hir().name(var_id).to_string())
379                 }
380                 _ => {
381                     // Might need a revision when the fields in trait RFC is implemented
382                     // (https://github.com/rust-lang/rfcs/pull/1546)
383                     bug!("End-user description not implemented for field access on `{:?}`", ty);
384                 }
385             }
386         }
387     }
388 
borrowed_content_source( &self, deref_base: PlaceRef<'tcx>, ) -> BorrowedContentSource<'tcx>389     pub(super) fn borrowed_content_source(
390         &self,
391         deref_base: PlaceRef<'tcx>,
392     ) -> BorrowedContentSource<'tcx> {
393         let tcx = self.infcx.tcx;
394 
395         // Look up the provided place and work out the move path index for it,
396         // we'll use this to check whether it was originally from an overloaded
397         // operator.
398         match self.move_data.rev_lookup.find(deref_base) {
399             LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => {
400                 debug!("borrowed_content_source: mpi={:?}", mpi);
401 
402                 for i in &self.move_data.init_path_map[mpi] {
403                     let init = &self.move_data.inits[*i];
404                     debug!("borrowed_content_source: init={:?}", init);
405                     // We're only interested in statements that initialized a value, not the
406                     // initializations from arguments.
407                     let InitLocation::Statement(loc) = init.location else { continue };
408 
409                     let bbd = &self.body[loc.block];
410                     let is_terminator = bbd.statements.len() == loc.statement_index;
411                     debug!(
412                         "borrowed_content_source: loc={:?} is_terminator={:?}",
413                         loc, is_terminator,
414                     );
415                     if !is_terminator {
416                         continue;
417                     } else if let Some(Terminator {
418                         kind:
419                             TerminatorKind::Call {
420                                 func,
421                                 call_source: CallSource::OverloadedOperator,
422                                 ..
423                             },
424                         ..
425                     }) = &bbd.terminator
426                     {
427                         if let Some(source) =
428                             BorrowedContentSource::from_call(func.ty(self.body, tcx), tcx)
429                         {
430                             return source;
431                         }
432                     }
433                 }
434             }
435             // Base is a `static` so won't be from an overloaded operator
436             _ => (),
437         };
438 
439         // If we didn't find an overloaded deref or index, then assume it's a
440         // built in deref and check the type of the base.
441         let base_ty = deref_base.ty(self.body, tcx).ty;
442         if base_ty.is_unsafe_ptr() {
443             BorrowedContentSource::DerefRawPointer
444         } else if base_ty.is_mutable_ptr() {
445             BorrowedContentSource::DerefMutableRef
446         } else {
447             BorrowedContentSource::DerefSharedRef
448         }
449     }
450 
451     /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
452     /// name where required.
get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String453     pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
454         let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
455 
456         // We need to add synthesized lifetimes where appropriate. We do
457         // this by hooking into the pretty printer and telling it to label the
458         // lifetimes without names with the value `'0`.
459         if let ty::Ref(region, ..) = ty.kind() {
460             match **region {
461                 ty::ReLateBound(_, ty::BoundRegion { kind: br, .. })
462                 | ty::RePlaceholder(ty::PlaceholderRegion {
463                     bound: ty::BoundRegion { kind: br, .. },
464                     ..
465                 }) => printer.region_highlight_mode.highlighting_bound_region(br, counter),
466                 _ => {}
467             }
468         }
469 
470         ty.print(printer).unwrap().into_buffer()
471     }
472 
473     /// Returns the name of the provided `Ty` (that must be a reference)'s region with a
474     /// synthesized lifetime name where required.
get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String475     pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
476         let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
477 
478         let region = if let ty::Ref(region, ..) = ty.kind() {
479             match **region {
480                 ty::ReLateBound(_, ty::BoundRegion { kind: br, .. })
481                 | ty::RePlaceholder(ty::PlaceholderRegion {
482                     bound: ty::BoundRegion { kind: br, .. },
483                     ..
484                 }) => printer.region_highlight_mode.highlighting_bound_region(br, counter),
485                 _ => {}
486             }
487             region
488         } else {
489             bug!("ty for annotation of borrow region is not a reference");
490         };
491 
492         region.print(printer).unwrap().into_buffer()
493     }
494 }
495 
496 /// The span(s) associated to a use of a place.
497 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
498 pub(super) enum UseSpans<'tcx> {
499     /// The access is caused by capturing a variable for a closure.
500     ClosureUse {
501         /// This is true if the captured variable was from a generator.
502         generator_kind: Option<GeneratorKind>,
503         /// The span of the args of the closure, including the `move` keyword if
504         /// it's present.
505         args_span: Span,
506         /// The span of the use resulting in capture kind
507         /// Check `ty::CaptureInfo` for more details
508         capture_kind_span: Span,
509         /// The span of the use resulting in the captured path
510         /// Check `ty::CaptureInfo` for more details
511         path_span: Span,
512     },
513     /// The access is caused by using a variable as the receiver of a method
514     /// that takes 'self'
515     FnSelfUse {
516         /// The span of the variable being moved
517         var_span: Span,
518         /// The span of the method call on the variable
519         fn_call_span: Span,
520         /// The definition span of the method being called
521         fn_span: Span,
522         kind: CallKind<'tcx>,
523     },
524     /// This access is caused by a `match` or `if let` pattern.
525     PatUse(Span),
526     /// This access has a single span associated to it: common case.
527     OtherUse(Span),
528 }
529 
530 impl UseSpans<'_> {
args_or_use(self) -> Span531     pub(super) fn args_or_use(self) -> Span {
532         match self {
533             UseSpans::ClosureUse { args_span: span, .. }
534             | UseSpans::PatUse(span)
535             | UseSpans::OtherUse(span) => span,
536             UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => {
537                 fn_call_span
538             }
539             UseSpans::FnSelfUse { var_span, .. } => var_span,
540         }
541     }
542 
543     /// Returns the span of `self`, in the case of a `ClosureUse` returns the `path_span`
var_or_use_path_span(self) -> Span544     pub(super) fn var_or_use_path_span(self) -> Span {
545         match self {
546             UseSpans::ClosureUse { path_span: span, .. }
547             | UseSpans::PatUse(span)
548             | UseSpans::OtherUse(span) => span,
549             UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => {
550                 fn_call_span
551             }
552             UseSpans::FnSelfUse { var_span, .. } => var_span,
553         }
554     }
555 
556     /// Returns the span of `self`, in the case of a `ClosureUse` returns the `capture_kind_span`
var_or_use(self) -> Span557     pub(super) fn var_or_use(self) -> Span {
558         match self {
559             UseSpans::ClosureUse { capture_kind_span: span, .. }
560             | UseSpans::PatUse(span)
561             | UseSpans::OtherUse(span) => span,
562             UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => {
563                 fn_call_span
564             }
565             UseSpans::FnSelfUse { var_span, .. } => var_span,
566         }
567     }
568 
generator_kind(self) -> Option<GeneratorKind>569     pub(super) fn generator_kind(self) -> Option<GeneratorKind> {
570         match self {
571             UseSpans::ClosureUse { generator_kind, .. } => generator_kind,
572             _ => None,
573         }
574     }
575 
576     /// Add a span label to the arguments of the closure, if it exists.
args_subdiag( self, err: &mut Diagnostic, f: impl FnOnce(Span) -> CaptureArgLabel, )577     pub(super) fn args_subdiag(
578         self,
579         err: &mut Diagnostic,
580         f: impl FnOnce(Span) -> CaptureArgLabel,
581     ) {
582         if let UseSpans::ClosureUse { args_span, .. } = self {
583             err.subdiagnostic(f(args_span));
584         }
585     }
586 
587     /// Add a span label to the use of the captured variable, if it exists.
588     /// only adds label to the `path_span`
var_path_only_subdiag( self, err: &mut Diagnostic, action: crate::InitializationRequiringAction, )589     pub(super) fn var_path_only_subdiag(
590         self,
591         err: &mut Diagnostic,
592         action: crate::InitializationRequiringAction,
593     ) {
594         use crate::InitializationRequiringAction::*;
595         use CaptureVarPathUseCause::*;
596         if let UseSpans::ClosureUse { generator_kind, path_span, .. } = self {
597             match generator_kind {
598                 Some(_) => {
599                     err.subdiagnostic(match action {
600                         Borrow => BorrowInGenerator { path_span },
601                         MatchOn | Use => UseInGenerator { path_span },
602                         Assignment => AssignInGenerator { path_span },
603                         PartialAssignment => AssignPartInGenerator { path_span },
604                     });
605                 }
606                 None => {
607                     err.subdiagnostic(match action {
608                         Borrow => BorrowInClosure { path_span },
609                         MatchOn | Use => UseInClosure { path_span },
610                         Assignment => AssignInClosure { path_span },
611                         PartialAssignment => AssignPartInClosure { path_span },
612                     });
613                 }
614             }
615         }
616     }
617 
618     /// Add a subdiagnostic to the use of the captured variable, if it exists.
var_subdiag( self, handler: Option<&rustc_errors::Handler>, err: &mut Diagnostic, kind: Option<rustc_middle::mir::BorrowKind>, f: impl FnOnce(Option<GeneratorKind>, Span) -> CaptureVarCause, )619     pub(super) fn var_subdiag(
620         self,
621         handler: Option<&rustc_errors::Handler>,
622         err: &mut Diagnostic,
623         kind: Option<rustc_middle::mir::BorrowKind>,
624         f: impl FnOnce(Option<GeneratorKind>, Span) -> CaptureVarCause,
625     ) {
626         if let UseSpans::ClosureUse { generator_kind, capture_kind_span, path_span, .. } = self {
627             if capture_kind_span != path_span {
628                 err.subdiagnostic(match kind {
629                     Some(kd) => match kd {
630                         rustc_middle::mir::BorrowKind::Shared
631                         | rustc_middle::mir::BorrowKind::Shallow => {
632                             CaptureVarKind::Immut { kind_span: capture_kind_span }
633                         }
634 
635                         rustc_middle::mir::BorrowKind::Mut { .. } => {
636                             CaptureVarKind::Mut { kind_span: capture_kind_span }
637                         }
638                     },
639                     None => CaptureVarKind::Move { kind_span: capture_kind_span },
640                 });
641             };
642             let diag = f(generator_kind, path_span);
643             match handler {
644                 Some(hd) => err.eager_subdiagnostic(hd, diag),
645                 None => err.subdiagnostic(diag),
646             };
647         }
648     }
649 
650     /// Returns `false` if this place is not used in a closure.
for_closure(&self) -> bool651     pub(super) fn for_closure(&self) -> bool {
652         match *self {
653             UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_none(),
654             _ => false,
655         }
656     }
657 
658     /// Returns `false` if this place is not used in a generator.
for_generator(&self) -> bool659     pub(super) fn for_generator(&self) -> bool {
660         match *self {
661             UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_some(),
662             _ => false,
663         }
664     }
665 
or_else<F>(self, if_other: F) -> Self where F: FnOnce() -> Self,666     pub(super) fn or_else<F>(self, if_other: F) -> Self
667     where
668         F: FnOnce() -> Self,
669     {
670         match self {
671             closure @ UseSpans::ClosureUse { .. } => closure,
672             UseSpans::PatUse(_) | UseSpans::OtherUse(_) => if_other(),
673             fn_self @ UseSpans::FnSelfUse { .. } => fn_self,
674         }
675     }
676 }
677 
678 pub(super) enum BorrowedContentSource<'tcx> {
679     DerefRawPointer,
680     DerefMutableRef,
681     DerefSharedRef,
682     OverloadedDeref(Ty<'tcx>),
683     OverloadedIndex(Ty<'tcx>),
684 }
685 
686 impl<'tcx> BorrowedContentSource<'tcx> {
describe_for_unnamed_place(&self, tcx: TyCtxt<'_>) -> String687     pub(super) fn describe_for_unnamed_place(&self, tcx: TyCtxt<'_>) -> String {
688         match *self {
689             BorrowedContentSource::DerefRawPointer => "a raw pointer".to_string(),
690             BorrowedContentSource::DerefSharedRef => "a shared reference".to_string(),
691             BorrowedContentSource::DerefMutableRef => "a mutable reference".to_string(),
692             BorrowedContentSource::OverloadedDeref(ty) => ty
693                 .ty_adt_def()
694                 .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? {
695                     name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")),
696                     _ => None,
697                 })
698                 .unwrap_or_else(|| format!("dereference of `{ty}`")),
699             BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{ty}`"),
700         }
701     }
702 
describe_for_named_place(&self) -> Option<&'static str>703     pub(super) fn describe_for_named_place(&self) -> Option<&'static str> {
704         match *self {
705             BorrowedContentSource::DerefRawPointer => Some("raw pointer"),
706             BorrowedContentSource::DerefSharedRef => Some("shared reference"),
707             BorrowedContentSource::DerefMutableRef => Some("mutable reference"),
708             // Overloaded deref and index operators should be evaluated into a
709             // temporary. So we don't need a description here.
710             BorrowedContentSource::OverloadedDeref(_)
711             | BorrowedContentSource::OverloadedIndex(_) => None,
712         }
713     }
714 
describe_for_immutable_place(&self, tcx: TyCtxt<'_>) -> String715     pub(super) fn describe_for_immutable_place(&self, tcx: TyCtxt<'_>) -> String {
716         match *self {
717             BorrowedContentSource::DerefRawPointer => "a `*const` pointer".to_string(),
718             BorrowedContentSource::DerefSharedRef => "a `&` reference".to_string(),
719             BorrowedContentSource::DerefMutableRef => {
720                 bug!("describe_for_immutable_place: DerefMutableRef isn't immutable")
721             }
722             BorrowedContentSource::OverloadedDeref(ty) => ty
723                 .ty_adt_def()
724                 .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? {
725                     name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")),
726                     _ => None,
727                 })
728                 .unwrap_or_else(|| format!("dereference of `{ty}`")),
729             BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{ty}`"),
730         }
731     }
732 
from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self>733     fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> {
734         match *func.kind() {
735             ty::FnDef(def_id, substs) => {
736                 let trait_id = tcx.trait_of_item(def_id)?;
737 
738                 let lang_items = tcx.lang_items();
739                 if Some(trait_id) == lang_items.deref_trait()
740                     || Some(trait_id) == lang_items.deref_mut_trait()
741                 {
742                     Some(BorrowedContentSource::OverloadedDeref(substs.type_at(0)))
743                 } else if Some(trait_id) == lang_items.index_trait()
744                     || Some(trait_id) == lang_items.index_mut_trait()
745                 {
746                     Some(BorrowedContentSource::OverloadedIndex(substs.type_at(0)))
747                 } else {
748                     None
749                 }
750             }
751             _ => None,
752         }
753     }
754 }
755 
756 ///helper struct for explain_captures()
757 struct CapturedMessageOpt {
758     is_partial_move: bool,
759     is_loop_message: bool,
760     is_move_msg: bool,
761     is_loop_move: bool,
762     maybe_reinitialized_locations_is_empty: bool,
763 }
764 
765 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
766     /// 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>767     pub(super) fn move_spans(
768         &self,
769         moved_place: PlaceRef<'tcx>, // Could also be an upvar.
770         location: Location,
771     ) -> UseSpans<'tcx> {
772         use self::UseSpans::*;
773 
774         let Some(stmt) = self.body[location.block].statements.get(location.statement_index) else {
775             return OtherUse(self.body.source_info(location).span);
776         };
777 
778         debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
779         if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind
780             && let AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) = **kind
781         {
782             debug!("move_spans: def_id={:?} places={:?}", def_id, places);
783             let def_id = def_id.expect_local();
784             if let Some((args_span, generator_kind, capture_kind_span, path_span)) =
785                 self.closure_span(def_id, moved_place, places)
786             {
787                 return ClosureUse {
788                     generator_kind,
789                     args_span,
790                     capture_kind_span,
791                     path_span,
792                 };
793             }
794         }
795 
796         // StatementKind::FakeRead only contains a def_id if they are introduced as a result
797         // of pattern matching within a closure.
798         if let StatementKind::FakeRead(box (cause, place)) = stmt.kind {
799             match cause {
800                 FakeReadCause::ForMatchedPlace(Some(closure_def_id))
801                 | FakeReadCause::ForLet(Some(closure_def_id)) => {
802                     debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place);
803                     let places = &[Operand::Move(place)];
804                     if let Some((args_span, generator_kind, capture_kind_span, path_span)) =
805                         self.closure_span(closure_def_id, moved_place, IndexSlice::from_raw(places))
806                     {
807                         return ClosureUse {
808                             generator_kind,
809                             args_span,
810                             capture_kind_span,
811                             path_span,
812                         };
813                     }
814                 }
815                 _ => {}
816             }
817         }
818 
819         let normal_ret =
820             if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) {
821                 PatUse(stmt.source_info.span)
822             } else {
823                 OtherUse(stmt.source_info.span)
824             };
825 
826         // We are trying to find MIR of the form:
827         // ```
828         // _temp = _moved_val;
829         // ...
830         // FnSelfCall(_temp, ...)
831         // ```
832         //
833         // where `_moved_val` is the place we generated the move error for,
834         // `_temp` is some other local, and `FnSelfCall` is a function
835         // that has a `self` parameter.
836 
837         let target_temp = match stmt.kind {
838             StatementKind::Assign(box (temp, _)) if temp.as_local().is_some() => {
839                 temp.as_local().unwrap()
840             }
841             _ => return normal_ret,
842         };
843 
844         debug!("move_spans: target_temp = {:?}", target_temp);
845 
846         if let Some(Terminator {
847             kind: TerminatorKind::Call { fn_span, call_source, .. }, ..
848         }) = &self.body[location.block].terminator
849         {
850             let Some((method_did, method_substs)) =
851             rustc_middle::util::find_self_call(
852                     self.infcx.tcx,
853                     &self.body,
854                     target_temp,
855                     location.block,
856                 )
857             else {
858                 return normal_ret;
859             };
860 
861             let kind = call_kind(
862                 self.infcx.tcx,
863                 self.param_env,
864                 method_did,
865                 method_substs,
866                 *fn_span,
867                 call_source.from_hir_call(),
868                 Some(self.infcx.tcx.fn_arg_names(method_did)[0]),
869             );
870 
871             return FnSelfUse {
872                 var_span: stmt.source_info.span,
873                 fn_call_span: *fn_span,
874                 fn_span: self.infcx.tcx.def_span(method_did),
875                 kind,
876             };
877         }
878         normal_ret
879     }
880 
881     /// Finds the span of arguments of a closure (within `maybe_closure_span`)
882     /// and its usage of the local assigned at `location`.
883     /// This is done by searching in statements succeeding `location`
884     /// and originating from `maybe_closure_span`.
borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx>885     pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx> {
886         use self::UseSpans::*;
887         debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
888 
889         let target = match self.body[location.block].statements.get(location.statement_index) {
890             Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) => {
891                 if let Some(local) = place.as_local() {
892                     local
893                 } else {
894                     return OtherUse(use_span);
895                 }
896             }
897             _ => return OtherUse(use_span),
898         };
899 
900         if self.body.local_kind(target) != LocalKind::Temp {
901             // operands are always temporaries.
902             return OtherUse(use_span);
903         }
904 
905         // drop and replace might have moved the assignment to the next block
906         let maybe_additional_statement =
907             if let TerminatorKind::Drop { target: drop_target, .. } =
908                 self.body[location.block].terminator().kind
909             {
910                 self.body[drop_target].statements.first()
911             } else {
912                 None
913             };
914 
915         let statements =
916             self.body[location.block].statements[location.statement_index + 1..].iter();
917 
918         for stmt in statements.chain(maybe_additional_statement) {
919             if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind {
920                 let (&def_id, is_generator) = match kind {
921                     box AggregateKind::Closure(def_id, _) => (def_id, false),
922                     box AggregateKind::Generator(def_id, _, _) => (def_id, true),
923                     _ => continue,
924                 };
925                 let def_id = def_id.expect_local();
926 
927                 debug!(
928                     "borrow_spans: def_id={:?} is_generator={:?} places={:?}",
929                     def_id, is_generator, places
930                 );
931                 if let Some((args_span, generator_kind, capture_kind_span, path_span)) =
932                     self.closure_span(def_id, Place::from(target).as_ref(), places)
933                 {
934                     return ClosureUse { generator_kind, args_span, capture_kind_span, path_span };
935                 } else {
936                     return OtherUse(use_span);
937                 }
938             }
939 
940             if use_span != stmt.source_info.span {
941                 break;
942             }
943         }
944 
945         OtherUse(use_span)
946     }
947 
948     /// Finds the spans of a captured place within a closure or generator.
949     /// The first span is the location of the use resulting in the capture kind of the capture
950     /// The second span is the location the use resulting in the captured path of the capture
closure_span( &self, def_id: LocalDefId, target_place: PlaceRef<'tcx>, places: &IndexSlice<FieldIdx, Operand<'tcx>>, ) -> Option<(Span, Option<GeneratorKind>, Span, Span)>951     fn closure_span(
952         &self,
953         def_id: LocalDefId,
954         target_place: PlaceRef<'tcx>,
955         places: &IndexSlice<FieldIdx, Operand<'tcx>>,
956     ) -> Option<(Span, Option<GeneratorKind>, Span, Span)> {
957         debug!(
958             "closure_span: def_id={:?} target_place={:?} places={:?}",
959             def_id, target_place, places
960         );
961         let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(def_id);
962         let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
963         debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
964         if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = expr {
965             for (captured_place, place) in
966                 self.infcx.tcx.closure_captures(def_id).iter().zip(places)
967             {
968                 match place {
969                     Operand::Copy(place) | Operand::Move(place)
970                         if target_place == place.as_ref() =>
971                     {
972                         debug!("closure_span: found captured local {:?}", place);
973                         let body = self.infcx.tcx.hir().body(body);
974                         let generator_kind = body.generator_kind();
975 
976                         return Some((
977                             fn_decl_span,
978                             generator_kind,
979                             captured_place.get_capture_kind_span(self.infcx.tcx),
980                             captured_place.get_path_span(self.infcx.tcx),
981                         ));
982                     }
983                     _ => {}
984                 }
985             }
986         }
987         None
988     }
989 
990     /// Helper to retrieve span(s) of given borrow from the current MIR
991     /// representation
retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx>992     pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx> {
993         let span = self.body.source_info(borrow.reserve_location).span;
994         self.borrow_spans(span, borrow.reserve_location)
995     }
996 
explain_captures( &mut self, err: &mut Diagnostic, span: Span, move_span: Span, move_spans: UseSpans<'tcx>, moved_place: Place<'tcx>, msg_opt: CapturedMessageOpt, )997     fn explain_captures(
998         &mut self,
999         err: &mut Diagnostic,
1000         span: Span,
1001         move_span: Span,
1002         move_spans: UseSpans<'tcx>,
1003         moved_place: Place<'tcx>,
1004         msg_opt: CapturedMessageOpt,
1005     ) {
1006         let CapturedMessageOpt {
1007             is_partial_move: is_partial,
1008             is_loop_message,
1009             is_move_msg,
1010             is_loop_move,
1011             maybe_reinitialized_locations_is_empty,
1012         } = msg_opt;
1013         if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
1014             let place_name = self
1015                 .describe_place(moved_place.as_ref())
1016                 .map(|n| format!("`{n}`"))
1017                 .unwrap_or_else(|| "value".to_owned());
1018             match kind {
1019                 CallKind::FnCall { fn_trait_id, .. }
1020                     if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() =>
1021                 {
1022                     err.subdiagnostic(CaptureReasonLabel::Call {
1023                         fn_call_span,
1024                         place_name: &place_name,
1025                         is_partial,
1026                         is_loop_message,
1027                     });
1028                     err.subdiagnostic(CaptureReasonNote::FnOnceMoveInCall { var_span });
1029                 }
1030                 CallKind::Operator { self_arg, .. } => {
1031                     let self_arg = self_arg.unwrap();
1032                     err.subdiagnostic(CaptureReasonLabel::OperatorUse {
1033                         fn_call_span,
1034                         place_name: &place_name,
1035                         is_partial,
1036                         is_loop_message,
1037                     });
1038                     if self.fn_self_span_reported.insert(fn_span) {
1039                         err.subdiagnostic(CaptureReasonNote::LhsMoveByOperator {
1040                             span: self_arg.span,
1041                         });
1042                     }
1043                 }
1044                 CallKind::Normal { self_arg, desugaring, method_did, method_substs } => {
1045                     let self_arg = self_arg.unwrap();
1046                     let tcx = self.infcx.tcx;
1047                     if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
1048                         let ty = moved_place.ty(self.body, tcx).ty;
1049                         let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) {
1050                             Some(def_id) => type_known_to_meet_bound_modulo_regions(
1051                                 &self.infcx,
1052                                 self.param_env,
1053                                 Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty),
1054                                 def_id,
1055                             ),
1056                             _ => false,
1057                         };
1058                         if suggest {
1059                             err.subdiagnostic(CaptureReasonSuggest::IterateSlice {
1060                                 ty,
1061                                 span: move_span.shrink_to_lo(),
1062                             });
1063                         }
1064 
1065                         err.subdiagnostic(CaptureReasonLabel::ImplicitCall {
1066                             fn_call_span,
1067                             place_name: &place_name,
1068                             is_partial,
1069                             is_loop_message,
1070                         });
1071                         // If the moved place was a `&mut` ref, then we can
1072                         // suggest to reborrow it where it was moved, so it
1073                         // will still be valid by the time we get to the usage.
1074                         if let ty::Ref(_, _, hir::Mutability::Mut) =
1075                             moved_place.ty(self.body, self.infcx.tcx).ty.kind()
1076                         {
1077                             // If we are in a loop this will be suggested later.
1078                             if !is_loop_move {
1079                                 err.span_suggestion_verbose(
1080                                     move_span.shrink_to_lo(),
1081                                     format!(
1082                                         "consider creating a fresh reborrow of {} here",
1083                                         self.describe_place(moved_place.as_ref())
1084                                             .map(|n| format!("`{n}`"))
1085                                             .unwrap_or_else(|| "the mutable reference".to_string()),
1086                                     ),
1087                                     "&mut *",
1088                                     Applicability::MachineApplicable,
1089                                 );
1090                             }
1091                         }
1092                     } else {
1093                         if let Some((CallDesugaringKind::Await, _)) = desugaring {
1094                             err.subdiagnostic(CaptureReasonLabel::Await {
1095                                 fn_call_span,
1096                                 place_name: &place_name,
1097                                 is_partial,
1098                                 is_loop_message,
1099                             });
1100                         } else {
1101                             err.subdiagnostic(CaptureReasonLabel::MethodCall {
1102                                 fn_call_span,
1103                                 place_name: &place_name,
1104                                 is_partial,
1105                                 is_loop_message,
1106                             });
1107                         }
1108                         // Erase and shadow everything that could be passed to the new infcx.
1109                         let ty = moved_place.ty(self.body, tcx).ty;
1110 
1111                         if let ty::Adt(def, substs) = ty.kind()
1112                             && Some(def.did()) == tcx.lang_items().pin_type()
1113                             && let ty::Ref(_, _, hir::Mutability::Mut) = substs.type_at(0).kind()
1114                             && let self_ty = self.infcx.instantiate_binder_with_fresh_vars(
1115                                 fn_call_span,
1116                                 LateBoundRegionConversionTime::FnCall,
1117                                 tcx.fn_sig(method_did).subst(tcx, method_substs).input(0),
1118                             )
1119                             && self.infcx.can_eq(self.param_env, ty, self_ty)
1120                         {
1121                             err.eager_subdiagnostic(
1122                                 &self.infcx.tcx.sess.parse_sess.span_diagnostic,
1123                                 CaptureReasonSuggest::FreshReborrow {
1124                                     span: fn_call_span.shrink_to_lo(),
1125                                 });
1126                         }
1127                         if let Some(clone_trait) = tcx.lang_items().clone_trait()
1128                             && let trait_ref = ty::TraitRef::new(tcx, clone_trait, [ty])
1129                             && let o = Obligation::new(
1130                                 tcx,
1131                                 ObligationCause::dummy(),
1132                                 self.param_env,
1133                                 ty::Binder::dummy(trait_ref),
1134                             )
1135                             && self.infcx.predicate_must_hold_modulo_regions(&o)
1136                         {
1137                             err.span_suggestion_verbose(
1138                                 fn_call_span.shrink_to_lo(),
1139                                 "you can `clone` the value and consume it, but this might not be \
1140                                  your desired behavior",
1141                                 "clone().".to_string(),
1142                                 Applicability::MaybeIncorrect,
1143                             );
1144                         }
1145                     }
1146                     // Avoid pointing to the same function in multiple different
1147                     // error messages.
1148                     if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
1149                         self.explain_iterator_advancement_in_for_loop_if_applicable(
1150                             err,
1151                             span,
1152                             &move_spans,
1153                         );
1154 
1155                         let func = tcx.def_path_str(method_did);
1156                         err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
1157                             func,
1158                             place_name,
1159                             span: self_arg.span,
1160                         });
1161                     }
1162                     let parent_did = tcx.parent(method_did);
1163                     let parent_self_ty =
1164                         matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. })
1165                             .then_some(parent_did)
1166                             .and_then(|did| match tcx.type_of(did).subst_identity().kind() {
1167                                 ty::Adt(def, ..) => Some(def.did()),
1168                                 _ => None,
1169                             });
1170                     let is_option_or_result = parent_self_ty.is_some_and(|def_id| {
1171                         matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
1172                     });
1173                     if is_option_or_result && maybe_reinitialized_locations_is_empty {
1174                         err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
1175                     }
1176                 }
1177                 // Other desugarings takes &self, which cannot cause a move
1178                 _ => {}
1179             }
1180         } else {
1181             if move_span != span || is_loop_message {
1182                 err.subdiagnostic(CaptureReasonLabel::MovedHere {
1183                     move_span,
1184                     is_partial,
1185                     is_move_msg,
1186                     is_loop_message,
1187                 });
1188             }
1189             // If the move error occurs due to a loop, don't show
1190             // another message for the same span
1191             if !is_loop_message {
1192                 move_spans.var_subdiag(None, err, None, |kind, var_span| match kind {
1193                     Some(_) => CaptureVarCause::PartialMoveUseInGenerator { var_span, is_partial },
1194                     None => CaptureVarCause::PartialMoveUseInClosure { var_span, is_partial },
1195                 })
1196             }
1197         }
1198     }
1199 }
1200