1use std::collections::BTreeMap;
4
5use rustc_abi::{FieldIdx, VariantIdx};
6use rustc_data_structures::fx::FxIndexMap;
7use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify};
8use rustc_hir::def::{CtorKind, Namespace};
9use rustc_hir::{self as hir, CoroutineKind, LangItem};
10use rustc_index::IndexSlice;
11use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin};
12use rustc_infer::traits::SelectionError;
13use rustc_middle::bug;
14use rustc_middle::mir::{
15 AggregateKind, CallSource, ConstOperand, ConstraintCategory, FakeReadCause, Local, LocalInfo,
16 LocalKind, Location, Operand, Place, PlaceRef, PlaceTy, ProjectionElem, Rvalue, Statement,
17 StatementKind, Terminator, TerminatorKind, find_self_call,
18};
19use rustc_middle::ty::print::Print;
20use rustc_middle::ty::{self, Ty, TyCtxt};
21use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveOutIndex};
22use rustc_span::def_id::LocalDefId;
23use rustc_span::source_map::Spanned;
24use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
25use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
26use rustc_trait_selection::error_reporting::traits::call_kind::{CallDesugaringKind, call_kind};
27use rustc_trait_selection::infer::InferCtxtExt;
28use rustc_trait_selection::traits::{
29 FulfillmentError, FulfillmentErrorCode, type_known_to_meet_bound_modulo_regions,
30};
31use tracing::debug;
32
33use super::MirBorrowckCtxt;
34use super::borrow_set::BorrowData;
35use crate::constraints::OutlivesConstraint;
36use crate::fluent_generated as fluent;
37use crate::nll::ConstraintDescription;
38use crate::session_diagnostics::{
39 CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause,
40 CaptureVarKind, CaptureVarPathUseCause, OnClosureNote,
41};
42
43mod find_all_local_uses;
44mod find_use;
45mod outlives_suggestion;
46mod region_name;
47mod var_name;
48
49mod bound_region_errors;
50mod conflict_errors;
51mod explain_borrow;
52mod move_errors;
53mod mutability_errors;
54mod opaque_suggestions;
55mod region_errors;
56
57pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo};
58pub(crate) use move_errors::{IllegalMoveOriginKind, MoveError};
59pub(crate) use mutability_errors::AccessKind;
60pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder;
61pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
62pub(crate) use region_name::{RegionName, RegionNameSource};
63pub(crate) use rustc_trait_selection::error_reporting::traits::call_kind::CallKind;
64
65pub(super) struct DescribePlaceOpt {
66 including_downcast: bool,
67
68 including_tuple_field: bool,
71}
72
73pub(super) struct IncludingTupleField(pub(super) bool);
74
75enum BufferedDiag<'infcx> {
76 Error(Diag<'infcx>),
77 NonError(Diag<'infcx, ()>),
78}
79
80impl<'infcx> BufferedDiag<'infcx> {
81 fn sort_span(&self) -> Span {
82 match self {
83 BufferedDiag::Error(diag) => diag.sort_span,
84 BufferedDiag::NonError(diag) => diag.sort_span,
85 }
86 }
87}
88
89#[derive(Default)]
90pub(crate) struct BorrowckDiagnosticsBuffer<'infcx, 'tcx> {
91 buffered_move_errors: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, Diag<'infcx>)>,
106
107 buffered_mut_errors: FxIndexMap<Span, (Diag<'infcx>, usize)>,
108
109 buffered_diags: Vec<BufferedDiag<'infcx>>,
111}
112
113impl<'infcx, 'tcx> BorrowckDiagnosticsBuffer<'infcx, 'tcx> {
114 pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
115 self.buffered_diags.push(BufferedDiag::NonError(diag));
116 }
117}
118
119impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
120 pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) {
121 self.diags_buffer.buffered_diags.push(BufferedDiag::Error(diag));
122 }
123
124 pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
125 self.diags_buffer.buffer_non_error(diag);
126 }
127
128 pub(crate) fn buffer_move_error(
129 &mut self,
130 move_out_indices: Vec<MoveOutIndex>,
131 place_and_err: (PlaceRef<'tcx>, Diag<'infcx>),
132 ) -> bool {
133 if let Some((_, diag)) =
134 self.diags_buffer.buffered_move_errors.insert(move_out_indices, place_and_err)
135 {
136 diag.cancel();
138 false
139 } else {
140 true
141 }
142 }
143
144 pub(crate) fn get_buffered_mut_error(&mut self, span: Span) -> Option<(Diag<'infcx>, usize)> {
145 self.diags_buffer.buffered_mut_errors.swap_remove(&span)
147 }
148
149 pub(crate) fn buffer_mut_error(&mut self, span: Span, diag: Diag<'infcx>, count: usize) {
150 self.diags_buffer.buffered_mut_errors.insert(span, (diag, count));
151 }
152
153 pub(crate) fn emit_errors(&mut self) -> Option<ErrorGuaranteed> {
154 let mut res = self.infcx.tainted_by_errors();
155
156 for (_, (_, diag)) in std::mem::take(&mut self.diags_buffer.buffered_move_errors) {
158 self.buffer_error(diag);
160 }
161 for (_, (mut diag, count)) in std::mem::take(&mut self.diags_buffer.buffered_mut_errors) {
162 if count > 10 {
163 #[allow(rustc::diagnostic_outside_of_impl)]
164 #[allow(rustc::untranslatable_diagnostic)]
165 diag.note(format!("...and {} other attempted mutable borrows", count - 10));
166 }
167 self.buffer_error(diag);
168 }
169
170 if !self.diags_buffer.buffered_diags.is_empty() {
171 self.diags_buffer.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span());
172 for buffered_diag in self.diags_buffer.buffered_diags.drain(..) {
173 match buffered_diag {
174 BufferedDiag::Error(diag) => res = Some(diag.emit()),
175 BufferedDiag::NonError(diag) => diag.emit(),
176 }
177 }
178 }
179
180 res
181 }
182
183 pub(crate) fn has_buffered_diags(&self) -> bool {
184 self.diags_buffer.buffered_diags.is_empty()
185 }
186
187 pub(crate) fn has_move_error(
188 &self,
189 move_out_indices: &[MoveOutIndex],
190 ) -> Option<&(PlaceRef<'tcx>, Diag<'infcx>)> {
191 self.diags_buffer.buffered_move_errors.get(move_out_indices)
192 }
193}
194
195impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
196 #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn add_moved_or_invoked_closure_note(
209 &self,
210 location: Location,
211 place: PlaceRef<'tcx>,
212 diag: &mut Diag<'infcx>,
213 ) -> bool {
214 debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
215 let mut target = place.local_or_deref_local();
216 for stmt in &self.body[location.block].statements[location.statement_index..] {
217 debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
218 if let StatementKind::Assign(box (into, Rvalue::Use(from))) = &stmt.kind {
219 debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
220 match from {
221 Operand::Copy(place) | Operand::Move(place)
222 if target == place.local_or_deref_local() =>
223 {
224 target = into.local_or_deref_local()
225 }
226 _ => {}
227 }
228 }
229 }
230
231 let terminator = self.body[location.block].terminator();
233 debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
234 if let TerminatorKind::Call {
235 func: Operand::Constant(box ConstOperand { const_, .. }),
236 args,
237 ..
238 } = &terminator.kind
239 {
240 if let ty::FnDef(id, _) = *const_.ty().kind() {
241 debug!("add_moved_or_invoked_closure_note: id={:?}", id);
242 if self.infcx.tcx.is_lang_item(self.infcx.tcx.parent(id), LangItem::FnOnce) {
243 let closure = match args.first() {
244 Some(Spanned {
245 node: Operand::Copy(place) | Operand::Move(place), ..
246 }) if target == place.local_or_deref_local() => {
247 place.local_or_deref_local().unwrap()
248 }
249 _ => return false,
250 };
251
252 debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
253 if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() {
254 let did = did.expect_local();
255 if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
256 diag.subdiagnostic(OnClosureNote::InvokedTwice {
257 place_name: &ty::place_to_string_for_capture(
258 self.infcx.tcx,
259 hir_place,
260 ),
261 span: *span,
262 });
263 return true;
264 }
265 }
266 }
267 }
268 }
269
270 if let Some(target) = target {
272 if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() {
273 let did = did.expect_local();
274 if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
275 diag.subdiagnostic(OnClosureNote::MovedTwice {
276 place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
277 span: *span,
278 });
279 return true;
280 }
281 }
282 }
283 false
284 }
285
286 pub(super) fn describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String {
289 match self.describe_place(place_ref) {
290 Some(mut descr) => {
291 descr.reserve(2);
293 descr.insert(0, '`');
294 descr.push('`');
295 descr
296 }
297 None => "value".to_string(),
298 }
299 }
300
301 pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String> {
304 self.describe_place_with_options(
305 place_ref,
306 DescribePlaceOpt { including_downcast: false, including_tuple_field: true },
307 )
308 }
309
310 pub(super) fn describe_place_with_options(
315 &self,
316 place: PlaceRef<'tcx>,
317 opt: DescribePlaceOpt,
318 ) -> Option<String> {
319 let local = place.local;
320 if self.body.local_decls[local]
321 .source_info
322 .span
323 .in_external_macro(self.infcx.tcx.sess.source_map())
324 {
325 return None;
326 }
327
328 let mut autoderef_index = None;
329 let mut buf = String::new();
330 let mut ok = self.append_local_to_string(local, &mut buf);
331
332 for (index, elem) in place.projection.into_iter().enumerate() {
333 match elem {
334 ProjectionElem::Deref => {
335 if index == 0 {
336 if self.body.local_decls[local].is_ref_for_guard() {
337 continue;
338 }
339 if let LocalInfo::StaticRef { def_id, .. } =
340 *self.body.local_decls[local].local_info()
341 {
342 buf.push_str(self.infcx.tcx.item_name(def_id).as_str());
343 ok = Ok(());
344 continue;
345 }
346 }
347 if let Some(field) = self.is_upvar_field_projection(PlaceRef {
348 local,
349 projection: place.projection.split_at(index + 1).0,
350 }) {
351 let var_index = field.index();
352 buf = self.upvars[var_index].to_string(self.infcx.tcx);
353 ok = Ok(());
354 if !self.upvars[var_index].is_by_ref() {
355 buf.insert(0, '*');
356 }
357 } else {
358 if autoderef_index.is_none() {
359 autoderef_index = match place.projection.iter().rposition(|elem| {
360 !matches!(
361 elem,
362 ProjectionElem::Deref | ProjectionElem::Downcast(..)
363 )
364 }) {
365 Some(index) => Some(index + 1),
366 None => Some(0),
367 };
368 }
369 if index >= autoderef_index.unwrap() {
370 buf.insert(0, '*');
371 }
372 }
373 }
374 ProjectionElem::Downcast(..) if opt.including_downcast => return None,
375 ProjectionElem::Downcast(..) => (),
376 ProjectionElem::OpaqueCast(..) => (),
377 ProjectionElem::Subtype(..) => (),
378 ProjectionElem::UnwrapUnsafeBinder(_) => (),
379 ProjectionElem::Field(field, _ty) => {
380 if let Some(field) = self.is_upvar_field_projection(PlaceRef {
382 local,
383 projection: place.projection.split_at(index + 1).0,
384 }) {
385 buf = self.upvars[field.index()].to_string(self.infcx.tcx);
386 ok = Ok(());
387 } else {
388 let field_name = self.describe_field(
389 PlaceRef { local, projection: place.projection.split_at(index).0 },
390 *field,
391 IncludingTupleField(opt.including_tuple_field),
392 );
393 if let Some(field_name_str) = field_name {
394 buf.push('.');
395 buf.push_str(&field_name_str);
396 }
397 }
398 }
399 ProjectionElem::Index(index) => {
400 buf.push('[');
401 if self.append_local_to_string(*index, &mut buf).is_err() {
402 buf.push('_');
403 }
404 buf.push(']');
405 }
406 ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
407 buf.push_str("[..]");
411 }
412 }
413 }
414 ok.ok().map(|_| buf)
415 }
416
417 fn describe_name(&self, place: PlaceRef<'tcx>) -> Option<Symbol> {
418 for elem in place.projection.into_iter() {
419 match elem {
420 ProjectionElem::Downcast(Some(name), _) => {
421 return Some(*name);
422 }
423 _ => {}
424 }
425 }
426 None
427 }
428
429 fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> {
432 let decl = &self.body.local_decls[local];
433 match self.local_names[local] {
434 Some(name) if !decl.from_compiler_desugaring() => {
435 buf.push_str(name.as_str());
436 Ok(())
437 }
438 _ => Err(()),
439 }
440 }
441
442 fn describe_field(
444 &self,
445 place: PlaceRef<'tcx>,
446 field: FieldIdx,
447 including_tuple_field: IncludingTupleField,
448 ) -> Option<String> {
449 let place_ty = match place {
450 PlaceRef { local, projection: [] } => PlaceTy::from_ty(self.body.local_decls[local].ty),
451 PlaceRef { local, projection: [proj_base @ .., elem] } => match elem {
452 ProjectionElem::Deref
453 | ProjectionElem::Index(..)
454 | ProjectionElem::ConstantIndex { .. }
455 | ProjectionElem::Subslice { .. } => {
456 PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
457 }
458 ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
459 ProjectionElem::Subtype(ty)
460 | ProjectionElem::OpaqueCast(ty)
461 | ProjectionElem::UnwrapUnsafeBinder(ty) => PlaceTy::from_ty(*ty),
462 ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
463 },
464 };
465 self.describe_field_from_ty(
466 place_ty.ty,
467 field,
468 place_ty.variant_index,
469 including_tuple_field,
470 )
471 }
472
473 fn describe_field_from_ty(
475 &self,
476 ty: Ty<'_>,
477 field: FieldIdx,
478 variant_index: Option<VariantIdx>,
479 including_tuple_field: IncludingTupleField,
480 ) -> Option<String> {
481 if let Some(boxed_ty) = ty.boxed_ty() {
482 self.describe_field_from_ty(boxed_ty, field, variant_index, including_tuple_field)
484 } else {
485 match *ty.kind() {
486 ty::Adt(def, _) => {
487 let variant = if let Some(idx) = variant_index {
488 assert!(def.is_enum());
489 def.variant(idx)
490 } else {
491 def.non_enum_variant()
492 };
493 if !including_tuple_field.0 && variant.ctor_kind() == Some(CtorKind::Fn) {
494 return None;
495 }
496 Some(variant.fields[field].name.to_string())
497 }
498 ty::Tuple(_) => Some(field.index().to_string()),
499 ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => {
500 self.describe_field_from_ty(ty, field, variant_index, including_tuple_field)
501 }
502 ty::Array(ty, _) | ty::Slice(ty) => {
503 self.describe_field_from_ty(ty, field, variant_index, including_tuple_field)
504 }
505 ty::Closure(def_id, _) | ty::Coroutine(def_id, _) => {
506 let def_id = def_id.expect_local();
511 let var_id =
512 self.infcx.tcx.closure_captures(def_id)[field.index()].get_root_variable();
513
514 Some(self.infcx.tcx.hir_name(var_id).to_string())
515 }
516 _ => {
517 bug!("End-user description not implemented for field access on `{:?}`", ty);
520 }
521 }
522 }
523 }
524
525 pub(super) fn borrowed_content_source(
526 &self,
527 deref_base: PlaceRef<'tcx>,
528 ) -> BorrowedContentSource<'tcx> {
529 let tcx = self.infcx.tcx;
530
531 match self.move_data.rev_lookup.find(deref_base) {
535 LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => {
536 debug!("borrowed_content_source: mpi={:?}", mpi);
537
538 for i in &self.move_data.init_path_map[mpi] {
539 let init = &self.move_data.inits[*i];
540 debug!("borrowed_content_source: init={:?}", init);
541 let InitLocation::Statement(loc) = init.location else { continue };
544
545 let bbd = &self.body[loc.block];
546 let is_terminator = bbd.statements.len() == loc.statement_index;
547 debug!(
548 "borrowed_content_source: loc={:?} is_terminator={:?}",
549 loc, is_terminator,
550 );
551 if !is_terminator {
552 continue;
553 } else if let Some(Terminator {
554 kind:
555 TerminatorKind::Call {
556 func,
557 call_source: CallSource::OverloadedOperator,
558 ..
559 },
560 ..
561 }) = &bbd.terminator
562 {
563 if let Some(source) =
564 BorrowedContentSource::from_call(func.ty(self.body, tcx), tcx)
565 {
566 return source;
567 }
568 }
569 }
570 }
571 _ => (),
573 };
574
575 let base_ty = deref_base.ty(self.body, tcx).ty;
578 if base_ty.is_raw_ptr() {
579 BorrowedContentSource::DerefRawPointer
580 } else if base_ty.is_mutable_ptr() {
581 BorrowedContentSource::DerefMutableRef
582 } else {
583 BorrowedContentSource::DerefSharedRef
584 }
585 }
586
587 pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
590 let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
591
592 if let ty::Ref(region, ..) = ty.kind() {
596 match region.kind() {
597 ty::ReBound(_, ty::BoundRegion { kind: br, .. })
598 | ty::RePlaceholder(ty::PlaceholderRegion {
599 bound: ty::BoundRegion { kind: br, .. },
600 ..
601 }) => printer.region_highlight_mode.highlighting_bound_region(br, counter),
602 _ => {}
603 }
604 }
605
606 ty.print(&mut printer).unwrap();
607 printer.into_buffer()
608 }
609
610 pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
613 let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
614
615 let region = if let ty::Ref(region, ..) = ty.kind() {
616 match region.kind() {
617 ty::ReBound(_, ty::BoundRegion { kind: br, .. })
618 | ty::RePlaceholder(ty::PlaceholderRegion {
619 bound: ty::BoundRegion { kind: br, .. },
620 ..
621 }) => printer.region_highlight_mode.highlighting_bound_region(br, counter),
622 _ => {}
623 }
624 region
625 } else {
626 bug!("ty for annotation of borrow region is not a reference");
627 };
628
629 region.print(&mut printer).unwrap();
630 printer.into_buffer()
631 }
632
633 fn add_placeholder_from_predicate_note<G: EmissionGuarantee>(
636 &self,
637 err: &mut Diag<'_, G>,
638 path: &[OutlivesConstraint<'tcx>],
639 ) {
640 let predicate_span = path.iter().find_map(|constraint| {
641 let outlived = constraint.sub;
642 if let Some(origin) = self.regioncx.definitions.get(outlived)
643 && let NllRegionVariableOrigin::Placeholder(_) = origin.origin
644 && let ConstraintCategory::Predicate(span) = constraint.category
645 {
646 Some(span)
647 } else {
648 None
649 }
650 });
651
652 if let Some(span) = predicate_span {
653 err.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
654 }
655 }
656
657 fn add_sized_or_copy_bound_info<G: EmissionGuarantee>(
660 &self,
661 err: &mut Diag<'_, G>,
662 blamed_category: ConstraintCategory<'tcx>,
663 path: &[OutlivesConstraint<'tcx>],
664 ) {
665 for sought_category in [ConstraintCategory::SizedBound, ConstraintCategory::CopyBound] {
666 if sought_category != blamed_category
667 && let Some(sought_constraint) = path.iter().find(|c| c.category == sought_category)
668 {
669 let label = format!(
670 "requirement occurs due to {}",
671 sought_category.description().trim_end()
672 );
673 err.span_label(sought_constraint.span, label);
674 }
675 }
676 }
677}
678
679#[derive(Copy, Clone, PartialEq, Eq, Debug)]
681pub(super) enum UseSpans<'tcx> {
682 ClosureUse {
684 closure_kind: hir::ClosureKind,
686 args_span: Span,
689 capture_kind_span: Span,
692 path_span: Span,
695 },
696 FnSelfUse {
699 var_span: Span,
701 fn_call_span: Span,
703 fn_span: Span,
705 kind: CallKind<'tcx>,
706 },
707 PatUse(Span),
709 OtherUse(Span),
711}
712
713impl UseSpans<'_> {
714 pub(super) fn args_or_use(self) -> Span {
715 match self {
716 UseSpans::ClosureUse { args_span: span, .. }
717 | UseSpans::PatUse(span)
718 | UseSpans::OtherUse(span) => span,
719 UseSpans::FnSelfUse { var_span, .. } => var_span,
720 }
721 }
722
723 pub(super) fn var_or_use_path_span(self) -> Span {
725 match self {
726 UseSpans::ClosureUse { path_span: span, .. }
727 | UseSpans::PatUse(span)
728 | UseSpans::OtherUse(span) => span,
729 UseSpans::FnSelfUse { var_span, .. } => var_span,
730 }
731 }
732
733 pub(super) fn var_or_use(self) -> Span {
735 match self {
736 UseSpans::ClosureUse { capture_kind_span: span, .. }
737 | UseSpans::PatUse(span)
738 | UseSpans::OtherUse(span) => span,
739 UseSpans::FnSelfUse { var_span, .. } => var_span,
740 }
741 }
742
743 pub(super) fn coroutine_kind(self) -> Option<CoroutineKind> {
745 match self {
746 UseSpans::ClosureUse {
747 closure_kind: hir::ClosureKind::Coroutine(coroutine_kind),
748 ..
749 } => Some(coroutine_kind),
750 _ => None,
751 }
752 }
753
754 #[allow(rustc::diagnostic_outside_of_impl)]
756 pub(super) fn args_subdiag(self, err: &mut Diag<'_>, f: impl FnOnce(Span) -> CaptureArgLabel) {
757 if let UseSpans::ClosureUse { args_span, .. } = self {
758 err.subdiagnostic(f(args_span));
759 }
760 }
761
762 #[allow(rustc::diagnostic_outside_of_impl)]
765 pub(super) fn var_path_only_subdiag(
766 self,
767 err: &mut Diag<'_>,
768 action: crate::InitializationRequiringAction,
769 ) {
770 use CaptureVarPathUseCause::*;
771
772 use crate::InitializationRequiringAction::*;
773 if let UseSpans::ClosureUse { closure_kind, path_span, .. } = self {
774 match closure_kind {
775 hir::ClosureKind::Coroutine(_) => {
776 err.subdiagnostic(match action {
777 Borrow => BorrowInCoroutine { path_span },
778 MatchOn | Use => UseInCoroutine { path_span },
779 Assignment => AssignInCoroutine { path_span },
780 PartialAssignment => AssignPartInCoroutine { path_span },
781 });
782 }
783 hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
784 err.subdiagnostic(match action {
785 Borrow => BorrowInClosure { path_span },
786 MatchOn | Use => UseInClosure { path_span },
787 Assignment => AssignInClosure { path_span },
788 PartialAssignment => AssignPartInClosure { path_span },
789 });
790 }
791 }
792 }
793 }
794
795 #[allow(rustc::diagnostic_outside_of_impl)]
797 pub(super) fn var_subdiag(
798 self,
799 err: &mut Diag<'_>,
800 kind: Option<rustc_middle::mir::BorrowKind>,
801 f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause,
802 ) {
803 if let UseSpans::ClosureUse { closure_kind, capture_kind_span, path_span, .. } = self {
804 if capture_kind_span != path_span {
805 err.subdiagnostic(match kind {
806 Some(kd) => match kd {
807 rustc_middle::mir::BorrowKind::Shared
808 | rustc_middle::mir::BorrowKind::Fake(_) => {
809 CaptureVarKind::Immut { kind_span: capture_kind_span }
810 }
811
812 rustc_middle::mir::BorrowKind::Mut { .. } => {
813 CaptureVarKind::Mut { kind_span: capture_kind_span }
814 }
815 },
816 None => CaptureVarKind::Move { kind_span: capture_kind_span },
817 });
818 };
819 let diag = f(closure_kind, path_span);
820 err.subdiagnostic(diag);
821 }
822 }
823
824 pub(super) fn for_closure(&self) -> bool {
826 match *self {
827 UseSpans::ClosureUse { closure_kind, .. } => {
828 matches!(closure_kind, hir::ClosureKind::Closure)
829 }
830 _ => false,
831 }
832 }
833
834 pub(super) fn for_coroutine(&self) -> bool {
836 match *self {
837 UseSpans::ClosureUse { closure_kind, .. } => {
839 matches!(closure_kind, hir::ClosureKind::Coroutine(..))
840 }
841 _ => false,
842 }
843 }
844
845 pub(super) fn or_else<F>(self, if_other: F) -> Self
846 where
847 F: FnOnce() -> Self,
848 {
849 match self {
850 closure @ UseSpans::ClosureUse { .. } => closure,
851 UseSpans::PatUse(_) | UseSpans::OtherUse(_) => if_other(),
852 fn_self @ UseSpans::FnSelfUse { .. } => fn_self,
853 }
854 }
855}
856
857pub(super) enum BorrowedContentSource<'tcx> {
858 DerefRawPointer,
859 DerefMutableRef,
860 DerefSharedRef,
861 OverloadedDeref(Ty<'tcx>),
862 OverloadedIndex(Ty<'tcx>),
863}
864
865impl<'tcx> BorrowedContentSource<'tcx> {
866 pub(super) fn describe_for_unnamed_place(&self, tcx: TyCtxt<'_>) -> String {
867 match *self {
868 BorrowedContentSource::DerefRawPointer => "a raw pointer".to_string(),
869 BorrowedContentSource::DerefSharedRef => "a shared reference".to_string(),
870 BorrowedContentSource::DerefMutableRef => "a mutable reference".to_string(),
871 BorrowedContentSource::OverloadedDeref(ty) => ty
872 .ty_adt_def()
873 .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? {
874 name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")),
875 _ => None,
876 })
877 .unwrap_or_else(|| format!("dereference of `{ty}`")),
878 BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{ty}`"),
879 }
880 }
881
882 pub(super) fn describe_for_named_place(&self) -> Option<&'static str> {
883 match *self {
884 BorrowedContentSource::DerefRawPointer => Some("raw pointer"),
885 BorrowedContentSource::DerefSharedRef => Some("shared reference"),
886 BorrowedContentSource::DerefMutableRef => Some("mutable reference"),
887 BorrowedContentSource::OverloadedDeref(_)
890 | BorrowedContentSource::OverloadedIndex(_) => None,
891 }
892 }
893
894 pub(super) fn describe_for_immutable_place(&self, tcx: TyCtxt<'_>) -> String {
895 match *self {
896 BorrowedContentSource::DerefRawPointer => "a `*const` pointer".to_string(),
897 BorrowedContentSource::DerefSharedRef => "a `&` reference".to_string(),
898 BorrowedContentSource::DerefMutableRef => {
899 bug!("describe_for_immutable_place: DerefMutableRef isn't immutable")
900 }
901 BorrowedContentSource::OverloadedDeref(ty) => ty
902 .ty_adt_def()
903 .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? {
904 name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")),
905 _ => None,
906 })
907 .unwrap_or_else(|| format!("dereference of `{ty}`")),
908 BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{ty}`"),
909 }
910 }
911
912 fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> {
913 match *func.kind() {
914 ty::FnDef(def_id, args) => {
915 let trait_id = tcx.trait_of_item(def_id)?;
916
917 if tcx.is_lang_item(trait_id, LangItem::Deref)
918 || tcx.is_lang_item(trait_id, LangItem::DerefMut)
919 {
920 Some(BorrowedContentSource::OverloadedDeref(args.type_at(0)))
921 } else if tcx.is_lang_item(trait_id, LangItem::Index)
922 || tcx.is_lang_item(trait_id, LangItem::IndexMut)
923 {
924 Some(BorrowedContentSource::OverloadedIndex(args.type_at(0)))
925 } else {
926 None
927 }
928 }
929 _ => None,
930 }
931 }
932}
933
934struct CapturedMessageOpt {
936 is_partial_move: bool,
937 is_loop_message: bool,
938 is_move_msg: bool,
939 is_loop_move: bool,
940 has_suggest_reborrow: bool,
941 maybe_reinitialized_locations_is_empty: bool,
942}
943
944impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
945 pub(super) fn move_spans(
947 &self,
948 moved_place: PlaceRef<'tcx>, location: Location,
950 ) -> UseSpans<'tcx> {
951 use self::UseSpans::*;
952
953 let Some(stmt) = self.body[location.block].statements.get(location.statement_index) else {
954 return OtherUse(self.body.source_info(location).span);
955 };
956
957 debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
958 if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind
959 && let AggregateKind::Closure(def_id, _) | AggregateKind::Coroutine(def_id, _) = **kind
960 {
961 debug!("move_spans: def_id={:?} places={:?}", def_id, places);
962 let def_id = def_id.expect_local();
963 if let Some((args_span, closure_kind, capture_kind_span, path_span)) =
964 self.closure_span(def_id, moved_place, places)
965 {
966 return ClosureUse { closure_kind, args_span, capture_kind_span, path_span };
967 }
968 }
969
970 if let StatementKind::FakeRead(box (cause, place)) = stmt.kind {
973 match cause {
974 FakeReadCause::ForMatchedPlace(Some(closure_def_id))
975 | FakeReadCause::ForLet(Some(closure_def_id)) => {
976 debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place);
977 let places = &[Operand::Move(place)];
978 if let Some((args_span, closure_kind, capture_kind_span, path_span)) =
979 self.closure_span(closure_def_id, moved_place, IndexSlice::from_raw(places))
980 {
981 return ClosureUse {
982 closure_kind,
983 args_span,
984 capture_kind_span,
985 path_span,
986 };
987 }
988 }
989 _ => {}
990 }
991 }
992
993 let normal_ret =
994 if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) {
995 PatUse(stmt.source_info.span)
996 } else {
997 OtherUse(stmt.source_info.span)
998 };
999
1000 let target_temp = match stmt.kind {
1012 StatementKind::Assign(box (temp, _)) if temp.as_local().is_some() => {
1013 temp.as_local().unwrap()
1014 }
1015 _ => return normal_ret,
1016 };
1017
1018 debug!("move_spans: target_temp = {:?}", target_temp);
1019
1020 if let Some(Terminator {
1021 kind: TerminatorKind::Call { fn_span, call_source, .. }, ..
1022 }) = &self.body[location.block].terminator
1023 {
1024 let Some((method_did, method_args)) =
1025 find_self_call(self.infcx.tcx, self.body, target_temp, location.block)
1026 else {
1027 return normal_ret;
1028 };
1029
1030 let kind = call_kind(
1031 self.infcx.tcx,
1032 self.infcx.typing_env(self.infcx.param_env),
1033 method_did,
1034 method_args,
1035 *fn_span,
1036 call_source.from_hir_call(),
1037 self.infcx.tcx.fn_arg_idents(method_did)[0],
1038 );
1039
1040 return FnSelfUse {
1041 var_span: stmt.source_info.span,
1042 fn_call_span: *fn_span,
1043 fn_span: self.infcx.tcx.def_span(method_did),
1044 kind,
1045 };
1046 }
1047
1048 normal_ret
1049 }
1050
1051 pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx> {
1056 use self::UseSpans::*;
1057 debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
1058
1059 let target = match self.body[location.block].statements.get(location.statement_index) {
1060 Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) => {
1061 if let Some(local) = place.as_local() {
1062 local
1063 } else {
1064 return OtherUse(use_span);
1065 }
1066 }
1067 _ => return OtherUse(use_span),
1068 };
1069
1070 if self.body.local_kind(target) != LocalKind::Temp {
1071 return OtherUse(use_span);
1073 }
1074
1075 let maybe_additional_statement =
1077 if let TerminatorKind::Drop { target: drop_target, .. } =
1078 self.body[location.block].terminator().kind
1079 {
1080 self.body[drop_target].statements.first()
1081 } else {
1082 None
1083 };
1084
1085 let statements =
1086 self.body[location.block].statements[location.statement_index + 1..].iter();
1087
1088 for stmt in statements.chain(maybe_additional_statement) {
1089 if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind {
1090 let (&def_id, is_coroutine) = match kind {
1091 box AggregateKind::Closure(def_id, _) => (def_id, false),
1092 box AggregateKind::Coroutine(def_id, _) => (def_id, true),
1093 _ => continue,
1094 };
1095 let def_id = def_id.expect_local();
1096
1097 debug!(
1098 "borrow_spans: def_id={:?} is_coroutine={:?} places={:?}",
1099 def_id, is_coroutine, places
1100 );
1101 if let Some((args_span, closure_kind, capture_kind_span, path_span)) =
1102 self.closure_span(def_id, Place::from(target).as_ref(), places)
1103 {
1104 return ClosureUse { closure_kind, args_span, capture_kind_span, path_span };
1105 } else {
1106 return OtherUse(use_span);
1107 }
1108 }
1109
1110 if use_span != stmt.source_info.span {
1111 break;
1112 }
1113 }
1114
1115 OtherUse(use_span)
1116 }
1117
1118 fn closure_span(
1122 &self,
1123 def_id: LocalDefId,
1124 target_place: PlaceRef<'tcx>,
1125 places: &IndexSlice<FieldIdx, Operand<'tcx>>,
1126 ) -> Option<(Span, hir::ClosureKind, Span, Span)> {
1127 debug!(
1128 "closure_span: def_id={:?} target_place={:?} places={:?}",
1129 def_id, target_place, places
1130 );
1131 let hir_id = self.infcx.tcx.local_def_id_to_hir_id(def_id);
1132 let expr = &self.infcx.tcx.hir_expect_expr(hir_id).kind;
1133 debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
1134 if let &hir::ExprKind::Closure(&hir::Closure { kind, fn_decl_span, .. }) = expr {
1135 for (captured_place, place) in
1136 self.infcx.tcx.closure_captures(def_id).iter().zip(places)
1137 {
1138 match place {
1139 Operand::Copy(place) | Operand::Move(place)
1140 if target_place == place.as_ref() =>
1141 {
1142 debug!("closure_span: found captured local {:?}", place);
1143 return Some((
1144 fn_decl_span,
1145 kind,
1146 captured_place.get_capture_kind_span(self.infcx.tcx),
1147 captured_place.get_path_span(self.infcx.tcx),
1148 ));
1149 }
1150 _ => {}
1151 }
1152 }
1153 }
1154 None
1155 }
1156
1157 pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx> {
1160 let span = self.body.source_info(borrow.reserve_location).span;
1161 self.borrow_spans(span, borrow.reserve_location)
1162 }
1163
1164 #[allow(rustc::diagnostic_outside_of_impl)]
1165 #[allow(rustc::untranslatable_diagnostic)] fn explain_captures(
1167 &mut self,
1168 err: &mut Diag<'infcx>,
1169 span: Span,
1170 move_span: Span,
1171 move_spans: UseSpans<'tcx>,
1172 moved_place: Place<'tcx>,
1173 msg_opt: CapturedMessageOpt,
1174 ) {
1175 let CapturedMessageOpt {
1176 is_partial_move: is_partial,
1177 is_loop_message,
1178 is_move_msg,
1179 is_loop_move,
1180 has_suggest_reborrow,
1181 maybe_reinitialized_locations_is_empty,
1182 } = msg_opt;
1183 if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
1184 let place_name = self
1185 .describe_place(moved_place.as_ref())
1186 .map(|n| format!("`{n}`"))
1187 .unwrap_or_else(|| "value".to_owned());
1188 match kind {
1189 CallKind::FnCall { fn_trait_id, self_ty }
1190 if self.infcx.tcx.is_lang_item(fn_trait_id, LangItem::FnOnce) =>
1191 {
1192 err.subdiagnostic(CaptureReasonLabel::Call {
1193 fn_call_span,
1194 place_name: &place_name,
1195 is_partial,
1196 is_loop_message,
1197 });
1198 if let ty::Param(param_ty) = *self_ty.kind()
1220 && let generics = self.infcx.tcx.generics_of(self.mir_def_id())
1221 && let param = generics.type_param(param_ty, self.infcx.tcx)
1222 && let Some(hir_generics) = self
1223 .infcx
1224 .tcx
1225 .typeck_root_def_id(self.mir_def_id().to_def_id())
1226 .as_local()
1227 .and_then(|def_id| self.infcx.tcx.hir_get_generics(def_id))
1228 && let spans = hir_generics
1229 .predicates
1230 .iter()
1231 .filter_map(|pred| match pred.kind {
1232 hir::WherePredicateKind::BoundPredicate(pred) => Some(pred),
1233 _ => None,
1234 })
1235 .filter(|pred| {
1236 if let Some((id, _)) = pred.bounded_ty.as_generic_param() {
1237 id == param.def_id
1238 } else {
1239 false
1240 }
1241 })
1242 .flat_map(|pred| pred.bounds)
1243 .filter_map(|bound| {
1244 if let Some(trait_ref) = bound.trait_ref()
1245 && let Some(trait_def_id) = trait_ref.trait_def_id()
1246 && trait_def_id == fn_trait_id
1247 {
1248 Some(bound.span())
1249 } else {
1250 None
1251 }
1252 })
1253 .collect::<Vec<Span>>()
1254 && !spans.is_empty()
1255 {
1256 let mut span: MultiSpan = spans.clone().into();
1257 for sp in spans {
1258 span.push_span_label(sp, fluent::borrowck_moved_a_fn_once_in_call_def);
1259 }
1260 span.push_span_label(
1261 fn_call_span,
1262 fluent::borrowck_moved_a_fn_once_in_call,
1263 );
1264 err.span_note(span, fluent::borrowck_moved_a_fn_once_in_call_call);
1265 } else {
1266 err.subdiagnostic(CaptureReasonNote::FnOnceMoveInCall { var_span });
1267 }
1268 }
1269 CallKind::Operator { self_arg, trait_id, .. } => {
1270 let self_arg = self_arg.unwrap();
1271 err.subdiagnostic(CaptureReasonLabel::OperatorUse {
1272 fn_call_span,
1273 place_name: &place_name,
1274 is_partial,
1275 is_loop_message,
1276 });
1277 if self.fn_self_span_reported.insert(fn_span) {
1278 let lang = self.infcx.tcx.lang_items();
1279 err.subdiagnostic(
1280 if [lang.not_trait(), lang.deref_trait(), lang.neg_trait()]
1281 .contains(&Some(trait_id))
1282 {
1283 CaptureReasonNote::UnOpMoveByOperator { span: self_arg.span }
1284 } else {
1285 CaptureReasonNote::LhsMoveByOperator { span: self_arg.span }
1286 },
1287 );
1288 }
1289 }
1290 CallKind::Normal { self_arg, desugaring, method_did, method_args } => {
1291 let self_arg = self_arg.unwrap();
1292 let mut has_sugg = false;
1293 let tcx = self.infcx.tcx;
1294 if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
1297 self.explain_iterator_advancement_in_for_loop_if_applicable(
1298 err,
1299 span,
1300 &move_spans,
1301 );
1302
1303 let func = tcx.def_path_str(method_did);
1304 err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
1305 func,
1306 place_name: place_name.clone(),
1307 span: self_arg.span,
1308 });
1309 }
1310 let parent_did = tcx.parent(method_did);
1311 let parent_self_ty =
1312 matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. })
1313 .then_some(parent_did)
1314 .and_then(|did| match tcx.type_of(did).instantiate_identity().kind() {
1315 ty::Adt(def, ..) => Some(def.did()),
1316 _ => None,
1317 });
1318 let is_option_or_result = parent_self_ty.is_some_and(|def_id| {
1319 matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
1320 });
1321 if is_option_or_result && maybe_reinitialized_locations_is_empty {
1322 err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
1323 }
1324 if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
1325 let ty = moved_place.ty(self.body, tcx).ty;
1326 let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) {
1327 Some(def_id) => type_known_to_meet_bound_modulo_regions(
1328 self.infcx,
1329 self.infcx.param_env,
1330 Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty),
1331 def_id,
1332 ),
1333 _ => false,
1334 };
1335 if suggest {
1336 err.subdiagnostic(CaptureReasonSuggest::IterateSlice {
1337 ty,
1338 span: move_span.shrink_to_lo(),
1339 });
1340 }
1341
1342 err.subdiagnostic(CaptureReasonLabel::ImplicitCall {
1343 fn_call_span,
1344 place_name: &place_name,
1345 is_partial,
1346 is_loop_message,
1347 });
1348 if let ty::Ref(_, _, hir::Mutability::Mut) =
1352 moved_place.ty(self.body, self.infcx.tcx).ty.kind()
1353 {
1354 if !is_loop_move && !has_suggest_reborrow {
1359 self.suggest_reborrow(
1360 err,
1361 move_span.shrink_to_lo(),
1362 moved_place.as_ref(),
1363 );
1364 }
1365 }
1366 } else {
1367 if let Some((CallDesugaringKind::Await, _)) = desugaring {
1368 err.subdiagnostic(CaptureReasonLabel::Await {
1369 fn_call_span,
1370 place_name: &place_name,
1371 is_partial,
1372 is_loop_message,
1373 });
1374 } else {
1375 err.subdiagnostic(CaptureReasonLabel::MethodCall {
1376 fn_call_span,
1377 place_name: &place_name,
1378 is_partial,
1379 is_loop_message,
1380 });
1381 }
1382 let ty = moved_place.ty(self.body, tcx).ty;
1384
1385 if let ty::Adt(def, args) = ty.peel_refs().kind()
1386 && tcx.is_lang_item(def.did(), LangItem::Pin)
1387 && let ty::Ref(_, _, hir::Mutability::Mut) = args.type_at(0).kind()
1388 && let self_ty = self.infcx.instantiate_binder_with_fresh_vars(
1389 fn_call_span,
1390 BoundRegionConversionTime::FnCall,
1391 tcx.fn_sig(method_did).instantiate(tcx, method_args).input(0),
1392 )
1393 && self.infcx.can_eq(self.infcx.param_env, ty, self_ty)
1394 {
1395 err.subdiagnostic(CaptureReasonSuggest::FreshReborrow {
1396 span: move_span.shrink_to_hi(),
1397 });
1398 has_sugg = true;
1399 }
1400 if let Some(clone_trait) = tcx.lang_items().clone_trait() {
1401 let sugg = if moved_place
1402 .iter_projections()
1403 .any(|(_, elem)| matches!(elem, ProjectionElem::Deref))
1404 {
1405 let (start, end) = if let Some(expr) = self.find_expr(move_span)
1406 && let Some(_) = self.clone_on_reference(expr)
1407 && let hir::ExprKind::MethodCall(_, rcvr, _, _) = expr.kind
1408 {
1409 (move_span.shrink_to_lo(), move_span.with_lo(rcvr.span.hi()))
1410 } else {
1411 (move_span.shrink_to_lo(), move_span.shrink_to_hi())
1412 };
1413 vec![
1414 (start, format!("<{ty} as Clone>::clone(&")),
1419 (end, ")".to_string()),
1420 ]
1421 } else {
1422 vec![(move_span.shrink_to_hi(), ".clone()".to_string())]
1423 };
1424 if let Some(errors) = self.infcx.type_implements_trait_shallow(
1425 clone_trait,
1426 ty,
1427 self.infcx.param_env,
1428 ) && !has_sugg
1429 {
1430 let msg = match &errors[..] {
1431 [] => "you can `clone` the value and consume it, but this \
1432 might not be your desired behavior"
1433 .to_string(),
1434 [error] => {
1435 format!(
1436 "you could `clone` the value and consume it, if the \
1437 `{}` trait bound could be satisfied",
1438 error.obligation.predicate,
1439 )
1440 }
1441 _ => {
1442 format!(
1443 "you could `clone` the value and consume it, if the \
1444 following trait bounds could be satisfied: {}",
1445 listify(&errors, |e: &FulfillmentError<'tcx>| format!(
1446 "`{}`",
1447 e.obligation.predicate
1448 ))
1449 .unwrap(),
1450 )
1451 }
1452 };
1453 err.multipart_suggestion_verbose(
1454 msg,
1455 sugg,
1456 Applicability::MaybeIncorrect,
1457 );
1458 for error in errors {
1459 if let FulfillmentErrorCode::Select(
1460 SelectionError::Unimplemented,
1461 ) = error.code
1462 && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
1463 pred,
1464 )) = error.obligation.predicate.kind().skip_binder()
1465 {
1466 self.infcx.err_ctxt().suggest_derive(
1467 &error.obligation,
1468 err,
1469 error.obligation.predicate.kind().rebind(pred),
1470 );
1471 }
1472 }
1473 }
1474 }
1475 }
1476 }
1477 _ => {}
1479 }
1480 } else {
1481 if move_span != span || is_loop_message {
1482 err.subdiagnostic(CaptureReasonLabel::MovedHere {
1483 move_span,
1484 is_partial,
1485 is_move_msg,
1486 is_loop_message,
1487 });
1488 }
1489 if !is_loop_message {
1492 move_spans.var_subdiag(err, None, |kind, var_span| match kind {
1493 hir::ClosureKind::Coroutine(_) => {
1494 CaptureVarCause::PartialMoveUseInCoroutine { var_span, is_partial }
1495 }
1496 hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
1497 CaptureVarCause::PartialMoveUseInClosure { var_span, is_partial }
1498 }
1499 })
1500 }
1501 }
1502 }
1503}