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