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, IndexVec};
11use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin};
12use rustc_infer::traits::SelectionError;
13use rustc_middle::mir::{
14 AggregateKind, CallSource, ConstOperand, ConstraintCategory, FakeReadCause, Local, LocalInfo,
15 LocalKind, Location, Operand, Place, PlaceRef, PlaceTy, ProjectionElem, Rvalue, Statement,
16 StatementKind, Terminator, TerminatorKind, VarDebugInfoContents, find_self_call,
17};
18use rustc_middle::ty::print::Print;
19use rustc_middle::ty::{self, Ty, TyCtxt};
20use rustc_middle::{bug, span_bug};
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 fn local_name(&self, index: Local) -> Option<Symbol> {
196 *self.local_names().get(index)?
197 }
198
199 fn local_names(&self) -> &IndexSlice<Local, Option<Symbol>> {
200 self.local_names.get_or_init(|| {
201 let mut local_names = IndexVec::from_elem(None, &self.body.local_decls);
202 for var_debug_info in &self.body.var_debug_info {
203 if let VarDebugInfoContents::Place(place) = var_debug_info.value {
204 if let Some(local) = place.as_local() {
205 if let Some(prev_name) = local_names[local]
206 && var_debug_info.name != prev_name
207 {
208 span_bug!(
209 var_debug_info.source_info.span,
210 "local {:?} has many names (`{}` vs `{}`)",
211 local,
212 prev_name,
213 var_debug_info.name
214 );
215 }
216 local_names[local] = Some(var_debug_info.name);
217 }
218 }
219 }
220 local_names
221 })
222 }
223}
224
225impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
226 #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn add_moved_or_invoked_closure_note(
239 &self,
240 location: Location,
241 place: PlaceRef<'tcx>,
242 diag: &mut Diag<'infcx>,
243 ) -> bool {
244 debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
245 let mut target = place.local_or_deref_local();
246 for stmt in &self.body[location.block].statements[location.statement_index..] {
247 debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
248 if let StatementKind::Assign(box (into, Rvalue::Use(from))) = &stmt.kind {
249 debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
250 match from {
251 Operand::Copy(place) | Operand::Move(place)
252 if target == place.local_or_deref_local() =>
253 {
254 target = into.local_or_deref_local()
255 }
256 _ => {}
257 }
258 }
259 }
260
261 let terminator = self.body[location.block].terminator();
263 debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
264 if let TerminatorKind::Call {
265 func: Operand::Constant(box ConstOperand { const_, .. }),
266 args,
267 ..
268 } = &terminator.kind
269 && let ty::FnDef(id, _) = *const_.ty().kind()
270 {
271 debug!("add_moved_or_invoked_closure_note: id={:?}", id);
272 if self.infcx.tcx.is_lang_item(self.infcx.tcx.parent(id), LangItem::FnOnce) {
273 let closure = match args.first() {
274 Some(Spanned { node: Operand::Copy(place) | Operand::Move(place), .. })
275 if target == place.local_or_deref_local() =>
276 {
277 place.local_or_deref_local().unwrap()
278 }
279 _ => return false,
280 };
281
282 debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
283 if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() {
284 let did = did.expect_local();
285 if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
286 diag.subdiagnostic(OnClosureNote::InvokedTwice {
287 place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
288 span: *span,
289 });
290 return true;
291 }
292 }
293 }
294 }
295
296 if let Some(target) = target
298 && let ty::Closure(did, _) = self.body.local_decls[target].ty.kind()
299 {
300 let did = did.expect_local();
301 if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
302 diag.subdiagnostic(OnClosureNote::MovedTwice {
303 place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
304 span: *span,
305 });
306 return true;
307 }
308 }
309 false
310 }
311
312 pub(super) fn describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String {
315 match self.describe_place(place_ref) {
316 Some(mut descr) => {
317 descr.reserve(2);
319 descr.insert(0, '`');
320 descr.push('`');
321 descr
322 }
323 None => "value".to_string(),
324 }
325 }
326
327 pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String> {
330 self.describe_place_with_options(
331 place_ref,
332 DescribePlaceOpt { including_downcast: false, including_tuple_field: true },
333 )
334 }
335
336 pub(super) fn describe_place_with_options(
341 &self,
342 place: PlaceRef<'tcx>,
343 opt: DescribePlaceOpt,
344 ) -> Option<String> {
345 let local = place.local;
346 if self.body.local_decls[local]
347 .source_info
348 .span
349 .in_external_macro(self.infcx.tcx.sess.source_map())
350 {
351 return None;
352 }
353
354 let mut autoderef_index = None;
355 let mut buf = String::new();
356 let mut ok = self.append_local_to_string(local, &mut buf);
357
358 for (index, elem) in place.projection.into_iter().enumerate() {
359 match elem {
360 ProjectionElem::Deref => {
361 if index == 0 {
362 if self.body.local_decls[local].is_ref_for_guard() {
363 continue;
364 }
365 if let LocalInfo::StaticRef { def_id, .. } =
366 *self.body.local_decls[local].local_info()
367 {
368 buf.push_str(self.infcx.tcx.item_name(def_id).as_str());
369 ok = Ok(());
370 continue;
371 }
372 }
373 if let Some(field) = self.is_upvar_field_projection(PlaceRef {
374 local,
375 projection: place.projection.split_at(index + 1).0,
376 }) {
377 let var_index = field.index();
378 buf = self.upvars[var_index].to_string(self.infcx.tcx);
379 ok = Ok(());
380 if !self.upvars[var_index].is_by_ref() {
381 buf.insert(0, '*');
382 }
383 } else {
384 if autoderef_index.is_none() {
385 autoderef_index = match place.projection.iter().rposition(|elem| {
386 !matches!(
387 elem,
388 ProjectionElem::Deref | ProjectionElem::Downcast(..)
389 )
390 }) {
391 Some(index) => Some(index + 1),
392 None => Some(0),
393 };
394 }
395 if index >= autoderef_index.unwrap() {
396 buf.insert(0, '*');
397 }
398 }
399 }
400 ProjectionElem::Downcast(..) if opt.including_downcast => return None,
401 ProjectionElem::Downcast(..) => (),
402 ProjectionElem::OpaqueCast(..) => (),
403 ProjectionElem::Subtype(..) => (),
404 ProjectionElem::UnwrapUnsafeBinder(_) => (),
405 ProjectionElem::Field(field, _ty) => {
406 if let Some(field) = self.is_upvar_field_projection(PlaceRef {
408 local,
409 projection: place.projection.split_at(index + 1).0,
410 }) {
411 buf = self.upvars[field.index()].to_string(self.infcx.tcx);
412 ok = Ok(());
413 } else {
414 let field_name = self.describe_field(
415 PlaceRef { local, projection: place.projection.split_at(index).0 },
416 *field,
417 IncludingTupleField(opt.including_tuple_field),
418 );
419 if let Some(field_name_str) = field_name {
420 buf.push('.');
421 buf.push_str(&field_name_str);
422 }
423 }
424 }
425 ProjectionElem::Index(index) => {
426 buf.push('[');
427 if self.append_local_to_string(*index, &mut buf).is_err() {
428 buf.push('_');
429 }
430 buf.push(']');
431 }
432 ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
433 buf.push_str("[..]");
437 }
438 }
439 }
440 ok.ok().map(|_| buf)
441 }
442
443 fn describe_name(&self, place: PlaceRef<'tcx>) -> Option<Symbol> {
444 for elem in place.projection.into_iter() {
445 match elem {
446 ProjectionElem::Downcast(Some(name), _) => {
447 return Some(*name);
448 }
449 _ => {}
450 }
451 }
452 None
453 }
454
455 fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> {
458 let decl = &self.body.local_decls[local];
459 match self.local_name(local) {
460 Some(name) if !decl.from_compiler_desugaring() => {
461 buf.push_str(name.as_str());
462 Ok(())
463 }
464 _ => Err(()),
465 }
466 }
467
468 fn describe_field(
470 &self,
471 place: PlaceRef<'tcx>,
472 field: FieldIdx,
473 including_tuple_field: IncludingTupleField,
474 ) -> Option<String> {
475 let place_ty = match place {
476 PlaceRef { local, projection: [] } => PlaceTy::from_ty(self.body.local_decls[local].ty),
477 PlaceRef { local, projection: [proj_base @ .., elem] } => match elem {
478 ProjectionElem::Deref
479 | ProjectionElem::Index(..)
480 | ProjectionElem::ConstantIndex { .. }
481 | ProjectionElem::Subslice { .. } => {
482 PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
483 }
484 ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
485 ProjectionElem::Subtype(ty)
486 | ProjectionElem::OpaqueCast(ty)
487 | ProjectionElem::UnwrapUnsafeBinder(ty) => PlaceTy::from_ty(*ty),
488 ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
489 },
490 };
491 self.describe_field_from_ty(
492 place_ty.ty,
493 field,
494 place_ty.variant_index,
495 including_tuple_field,
496 )
497 }
498
499 fn describe_field_from_ty(
501 &self,
502 ty: Ty<'_>,
503 field: FieldIdx,
504 variant_index: Option<VariantIdx>,
505 including_tuple_field: IncludingTupleField,
506 ) -> Option<String> {
507 if let Some(boxed_ty) = ty.boxed_ty() {
508 self.describe_field_from_ty(boxed_ty, field, variant_index, including_tuple_field)
510 } else {
511 match *ty.kind() {
512 ty::Adt(def, _) => {
513 let variant = if let Some(idx) = variant_index {
514 assert!(def.is_enum());
515 def.variant(idx)
516 } else {
517 def.non_enum_variant()
518 };
519 if !including_tuple_field.0 && variant.ctor_kind() == Some(CtorKind::Fn) {
520 return None;
521 }
522 Some(variant.fields[field].name.to_string())
523 }
524 ty::Tuple(_) => Some(field.index().to_string()),
525 ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => {
526 self.describe_field_from_ty(ty, field, variant_index, including_tuple_field)
527 }
528 ty::Array(ty, _) | ty::Slice(ty) => {
529 self.describe_field_from_ty(ty, field, variant_index, including_tuple_field)
530 }
531 ty::Closure(def_id, _) | ty::Coroutine(def_id, _) => {
532 let def_id = def_id.expect_local();
537 let var_id =
538 self.infcx.tcx.closure_captures(def_id)[field.index()].get_root_variable();
539
540 Some(self.infcx.tcx.hir_name(var_id).to_string())
541 }
542 _ => {
543 bug!("End-user description not implemented for field access on `{:?}`", ty);
546 }
547 }
548 }
549 }
550
551 pub(super) fn borrowed_content_source(
552 &self,
553 deref_base: PlaceRef<'tcx>,
554 ) -> BorrowedContentSource<'tcx> {
555 let tcx = self.infcx.tcx;
556
557 match self.move_data.rev_lookup.find(deref_base) {
561 LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => {
562 debug!("borrowed_content_source: mpi={:?}", mpi);
563
564 for i in &self.move_data.init_path_map[mpi] {
565 let init = &self.move_data.inits[*i];
566 debug!("borrowed_content_source: init={:?}", init);
567 let InitLocation::Statement(loc) = init.location else { continue };
570
571 let bbd = &self.body[loc.block];
572 let is_terminator = bbd.statements.len() == loc.statement_index;
573 debug!(
574 "borrowed_content_source: loc={:?} is_terminator={:?}",
575 loc, is_terminator,
576 );
577 if !is_terminator {
578 continue;
579 } else if let Some(Terminator {
580 kind:
581 TerminatorKind::Call {
582 func,
583 call_source: CallSource::OverloadedOperator,
584 ..
585 },
586 ..
587 }) = &bbd.terminator
588 {
589 if let Some(source) =
590 BorrowedContentSource::from_call(func.ty(self.body, tcx), tcx)
591 {
592 return source;
593 }
594 }
595 }
596 }
597 _ => (),
599 };
600
601 let base_ty = deref_base.ty(self.body, tcx).ty;
604 if base_ty.is_raw_ptr() {
605 BorrowedContentSource::DerefRawPointer
606 } else if base_ty.is_mutable_ptr() {
607 BorrowedContentSource::DerefMutableRef
608 } else {
609 BorrowedContentSource::DerefSharedRef
610 }
611 }
612
613 pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
616 let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
617
618 if let ty::Ref(region, ..) = ty.kind() {
622 match region.kind() {
623 ty::ReBound(_, ty::BoundRegion { kind: br, .. })
624 | ty::RePlaceholder(ty::PlaceholderRegion {
625 bound: ty::BoundRegion { kind: br, .. },
626 ..
627 }) => printer.region_highlight_mode.highlighting_bound_region(br, counter),
628 _ => {}
629 }
630 }
631
632 ty.print(&mut printer).unwrap();
633 printer.into_buffer()
634 }
635
636 pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
639 let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
640
641 let region = if let ty::Ref(region, ..) = ty.kind() {
642 match region.kind() {
643 ty::ReBound(_, ty::BoundRegion { kind: br, .. })
644 | ty::RePlaceholder(ty::PlaceholderRegion {
645 bound: ty::BoundRegion { kind: br, .. },
646 ..
647 }) => printer.region_highlight_mode.highlighting_bound_region(br, counter),
648 _ => {}
649 }
650 region
651 } else {
652 bug!("ty for annotation of borrow region is not a reference");
653 };
654
655 region.print(&mut printer).unwrap();
656 printer.into_buffer()
657 }
658
659 fn add_placeholder_from_predicate_note<G: EmissionGuarantee>(
662 &self,
663 err: &mut Diag<'_, G>,
664 path: &[OutlivesConstraint<'tcx>],
665 ) {
666 let predicate_span = path.iter().find_map(|constraint| {
667 let outlived = constraint.sub;
668 if let Some(origin) = self.regioncx.definitions.get(outlived)
669 && let NllRegionVariableOrigin::Placeholder(_) = origin.origin
670 && let ConstraintCategory::Predicate(span) = constraint.category
671 {
672 Some(span)
673 } else {
674 None
675 }
676 });
677
678 if let Some(span) = predicate_span {
679 err.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
680 }
681 }
682
683 fn add_sized_or_copy_bound_info<G: EmissionGuarantee>(
686 &self,
687 err: &mut Diag<'_, G>,
688 blamed_category: ConstraintCategory<'tcx>,
689 path: &[OutlivesConstraint<'tcx>],
690 ) {
691 for sought_category in [ConstraintCategory::SizedBound, ConstraintCategory::CopyBound] {
692 if sought_category != blamed_category
693 && let Some(sought_constraint) = path.iter().find(|c| c.category == sought_category)
694 {
695 let label = format!(
696 "requirement occurs due to {}",
697 sought_category.description().trim_end()
698 );
699 err.span_label(sought_constraint.span, label);
700 }
701 }
702 }
703}
704
705#[derive(Copy, Clone, PartialEq, Eq, Debug)]
707pub(super) enum UseSpans<'tcx> {
708 ClosureUse {
710 closure_kind: hir::ClosureKind,
712 args_span: Span,
715 capture_kind_span: Span,
718 path_span: Span,
721 },
722 FnSelfUse {
725 var_span: Span,
727 fn_call_span: Span,
729 fn_span: Span,
731 kind: CallKind<'tcx>,
732 },
733 PatUse(Span),
735 OtherUse(Span),
737}
738
739impl UseSpans<'_> {
740 pub(super) fn args_or_use(self) -> Span {
741 match self {
742 UseSpans::ClosureUse { args_span: span, .. }
743 | UseSpans::PatUse(span)
744 | UseSpans::OtherUse(span) => span,
745 UseSpans::FnSelfUse { var_span, .. } => var_span,
746 }
747 }
748
749 pub(super) fn var_or_use_path_span(self) -> Span {
751 match self {
752 UseSpans::ClosureUse { path_span: span, .. }
753 | UseSpans::PatUse(span)
754 | UseSpans::OtherUse(span) => span,
755 UseSpans::FnSelfUse { var_span, .. } => var_span,
756 }
757 }
758
759 pub(super) fn var_or_use(self) -> Span {
761 match self {
762 UseSpans::ClosureUse { capture_kind_span: span, .. }
763 | UseSpans::PatUse(span)
764 | UseSpans::OtherUse(span) => span,
765 UseSpans::FnSelfUse { var_span, .. } => var_span,
766 }
767 }
768
769 pub(super) fn coroutine_kind(self) -> Option<CoroutineKind> {
771 match self {
772 UseSpans::ClosureUse {
773 closure_kind: hir::ClosureKind::Coroutine(coroutine_kind),
774 ..
775 } => Some(coroutine_kind),
776 _ => None,
777 }
778 }
779
780 #[allow(rustc::diagnostic_outside_of_impl)]
782 pub(super) fn args_subdiag(self, err: &mut Diag<'_>, f: impl FnOnce(Span) -> CaptureArgLabel) {
783 if let UseSpans::ClosureUse { args_span, .. } = self {
784 err.subdiagnostic(f(args_span));
785 }
786 }
787
788 #[allow(rustc::diagnostic_outside_of_impl)]
791 pub(super) fn var_path_only_subdiag(
792 self,
793 err: &mut Diag<'_>,
794 action: crate::InitializationRequiringAction,
795 ) {
796 use CaptureVarPathUseCause::*;
797
798 use crate::InitializationRequiringAction::*;
799 if let UseSpans::ClosureUse { closure_kind, path_span, .. } = self {
800 match closure_kind {
801 hir::ClosureKind::Coroutine(_) => {
802 err.subdiagnostic(match action {
803 Borrow => BorrowInCoroutine { path_span },
804 MatchOn | Use => UseInCoroutine { path_span },
805 Assignment => AssignInCoroutine { path_span },
806 PartialAssignment => AssignPartInCoroutine { path_span },
807 });
808 }
809 hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
810 err.subdiagnostic(match action {
811 Borrow => BorrowInClosure { path_span },
812 MatchOn | Use => UseInClosure { path_span },
813 Assignment => AssignInClosure { path_span },
814 PartialAssignment => AssignPartInClosure { path_span },
815 });
816 }
817 }
818 }
819 }
820
821 #[allow(rustc::diagnostic_outside_of_impl)]
823 pub(super) fn var_subdiag(
824 self,
825 err: &mut Diag<'_>,
826 kind: Option<rustc_middle::mir::BorrowKind>,
827 f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause,
828 ) {
829 if let UseSpans::ClosureUse { closure_kind, capture_kind_span, path_span, .. } = self {
830 if capture_kind_span != path_span {
831 err.subdiagnostic(match kind {
832 Some(kd) => match kd {
833 rustc_middle::mir::BorrowKind::Shared
834 | rustc_middle::mir::BorrowKind::Fake(_) => {
835 CaptureVarKind::Immut { kind_span: capture_kind_span }
836 }
837
838 rustc_middle::mir::BorrowKind::Mut { .. } => {
839 CaptureVarKind::Mut { kind_span: capture_kind_span }
840 }
841 },
842 None => CaptureVarKind::Move { kind_span: capture_kind_span },
843 });
844 };
845 let diag = f(closure_kind, path_span);
846 err.subdiagnostic(diag);
847 }
848 }
849
850 pub(super) fn for_closure(&self) -> bool {
852 match *self {
853 UseSpans::ClosureUse { closure_kind, .. } => {
854 matches!(closure_kind, hir::ClosureKind::Closure)
855 }
856 _ => false,
857 }
858 }
859
860 pub(super) fn for_coroutine(&self) -> bool {
862 match *self {
863 UseSpans::ClosureUse { closure_kind, .. } => {
865 matches!(closure_kind, hir::ClosureKind::Coroutine(..))
866 }
867 _ => false,
868 }
869 }
870
871 pub(super) fn or_else<F>(self, if_other: F) -> Self
872 where
873 F: FnOnce() -> Self,
874 {
875 match self {
876 closure @ UseSpans::ClosureUse { .. } => closure,
877 UseSpans::PatUse(_) | UseSpans::OtherUse(_) => if_other(),
878 fn_self @ UseSpans::FnSelfUse { .. } => fn_self,
879 }
880 }
881}
882
883pub(super) enum BorrowedContentSource<'tcx> {
884 DerefRawPointer,
885 DerefMutableRef,
886 DerefSharedRef,
887 OverloadedDeref(Ty<'tcx>),
888 OverloadedIndex(Ty<'tcx>),
889}
890
891impl<'tcx> BorrowedContentSource<'tcx> {
892 pub(super) fn describe_for_unnamed_place(&self, tcx: TyCtxt<'_>) -> String {
893 match *self {
894 BorrowedContentSource::DerefRawPointer => "a raw pointer".to_string(),
895 BorrowedContentSource::DerefSharedRef => "a shared reference".to_string(),
896 BorrowedContentSource::DerefMutableRef => "a mutable reference".to_string(),
897 BorrowedContentSource::OverloadedDeref(ty) => ty
898 .ty_adt_def()
899 .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? {
900 name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")),
901 _ => None,
902 })
903 .unwrap_or_else(|| format!("dereference of `{ty}`")),
904 BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{ty}`"),
905 }
906 }
907
908 pub(super) fn describe_for_named_place(&self) -> Option<&'static str> {
909 match *self {
910 BorrowedContentSource::DerefRawPointer => Some("raw pointer"),
911 BorrowedContentSource::DerefSharedRef => Some("shared reference"),
912 BorrowedContentSource::DerefMutableRef => Some("mutable reference"),
913 BorrowedContentSource::OverloadedDeref(_)
916 | BorrowedContentSource::OverloadedIndex(_) => None,
917 }
918 }
919
920 pub(super) fn describe_for_immutable_place(&self, tcx: TyCtxt<'_>) -> String {
921 match *self {
922 BorrowedContentSource::DerefRawPointer => "a `*const` pointer".to_string(),
923 BorrowedContentSource::DerefSharedRef => "a `&` reference".to_string(),
924 BorrowedContentSource::DerefMutableRef => {
925 bug!("describe_for_immutable_place: DerefMutableRef isn't immutable")
926 }
927 BorrowedContentSource::OverloadedDeref(ty) => ty
928 .ty_adt_def()
929 .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? {
930 name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")),
931 _ => None,
932 })
933 .unwrap_or_else(|| format!("dereference of `{ty}`")),
934 BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{ty}`"),
935 }
936 }
937
938 fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> {
939 match *func.kind() {
940 ty::FnDef(def_id, args) => {
941 let trait_id = tcx.trait_of_assoc(def_id)?;
942
943 if tcx.is_lang_item(trait_id, LangItem::Deref)
944 || tcx.is_lang_item(trait_id, LangItem::DerefMut)
945 {
946 Some(BorrowedContentSource::OverloadedDeref(args.type_at(0)))
947 } else if tcx.is_lang_item(trait_id, LangItem::Index)
948 || tcx.is_lang_item(trait_id, LangItem::IndexMut)
949 {
950 Some(BorrowedContentSource::OverloadedIndex(args.type_at(0)))
951 } else {
952 None
953 }
954 }
955 _ => None,
956 }
957 }
958}
959
960struct CapturedMessageOpt {
962 is_partial_move: bool,
963 is_loop_message: bool,
964 is_move_msg: bool,
965 is_loop_move: bool,
966 has_suggest_reborrow: bool,
967 maybe_reinitialized_locations_is_empty: bool,
968}
969
970impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
971 pub(super) fn move_spans(
973 &self,
974 moved_place: PlaceRef<'tcx>, location: Location,
976 ) -> UseSpans<'tcx> {
977 use self::UseSpans::*;
978
979 let Some(stmt) = self.body[location.block].statements.get(location.statement_index) else {
980 return OtherUse(self.body.source_info(location).span);
981 };
982
983 debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
984 if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind
985 && let AggregateKind::Closure(def_id, _) | AggregateKind::Coroutine(def_id, _) = **kind
986 {
987 debug!("move_spans: def_id={:?} places={:?}", def_id, places);
988 let def_id = def_id.expect_local();
989 if let Some((args_span, closure_kind, capture_kind_span, path_span)) =
990 self.closure_span(def_id, moved_place, places)
991 {
992 return ClosureUse { closure_kind, args_span, capture_kind_span, path_span };
993 }
994 }
995
996 if let StatementKind::FakeRead(box (cause, place)) = stmt.kind {
999 match cause {
1000 FakeReadCause::ForMatchedPlace(Some(closure_def_id))
1001 | FakeReadCause::ForLet(Some(closure_def_id)) => {
1002 debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place);
1003 let places = &[Operand::Move(place)];
1004 if let Some((args_span, closure_kind, capture_kind_span, path_span)) =
1005 self.closure_span(closure_def_id, moved_place, IndexSlice::from_raw(places))
1006 {
1007 return ClosureUse {
1008 closure_kind,
1009 args_span,
1010 capture_kind_span,
1011 path_span,
1012 };
1013 }
1014 }
1015 _ => {}
1016 }
1017 }
1018
1019 let normal_ret =
1020 if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) {
1021 PatUse(stmt.source_info.span)
1022 } else {
1023 OtherUse(stmt.source_info.span)
1024 };
1025
1026 let target_temp = match stmt.kind {
1038 StatementKind::Assign(box (temp, _)) if temp.as_local().is_some() => {
1039 temp.as_local().unwrap()
1040 }
1041 _ => return normal_ret,
1042 };
1043
1044 debug!("move_spans: target_temp = {:?}", target_temp);
1045
1046 if let Some(Terminator {
1047 kind: TerminatorKind::Call { fn_span, call_source, .. }, ..
1048 }) = &self.body[location.block].terminator
1049 {
1050 let Some((method_did, method_args)) =
1051 find_self_call(self.infcx.tcx, self.body, target_temp, location.block)
1052 else {
1053 return normal_ret;
1054 };
1055
1056 let kind = call_kind(
1057 self.infcx.tcx,
1058 self.infcx.typing_env(self.infcx.param_env),
1059 method_did,
1060 method_args,
1061 *fn_span,
1062 call_source.from_hir_call(),
1063 self.infcx.tcx.fn_arg_idents(method_did)[0],
1064 );
1065
1066 return FnSelfUse {
1067 var_span: stmt.source_info.span,
1068 fn_call_span: *fn_span,
1069 fn_span: self.infcx.tcx.def_span(method_did),
1070 kind,
1071 };
1072 }
1073
1074 normal_ret
1075 }
1076
1077 pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx> {
1082 use self::UseSpans::*;
1083 debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
1084
1085 let target = match self.body[location.block].statements.get(location.statement_index) {
1086 Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) => {
1087 if let Some(local) = place.as_local() {
1088 local
1089 } else {
1090 return OtherUse(use_span);
1091 }
1092 }
1093 _ => return OtherUse(use_span),
1094 };
1095
1096 if self.body.local_kind(target) != LocalKind::Temp {
1097 return OtherUse(use_span);
1099 }
1100
1101 let maybe_additional_statement =
1103 if let TerminatorKind::Drop { target: drop_target, .. } =
1104 self.body[location.block].terminator().kind
1105 {
1106 self.body[drop_target].statements.first()
1107 } else {
1108 None
1109 };
1110
1111 let statements =
1112 self.body[location.block].statements[location.statement_index + 1..].iter();
1113
1114 for stmt in statements.chain(maybe_additional_statement) {
1115 if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind {
1116 let (&def_id, is_coroutine) = match kind {
1117 box AggregateKind::Closure(def_id, _) => (def_id, false),
1118 box AggregateKind::Coroutine(def_id, _) => (def_id, true),
1119 _ => continue,
1120 };
1121 let def_id = def_id.expect_local();
1122
1123 debug!(
1124 "borrow_spans: def_id={:?} is_coroutine={:?} places={:?}",
1125 def_id, is_coroutine, places
1126 );
1127 if let Some((args_span, closure_kind, capture_kind_span, path_span)) =
1128 self.closure_span(def_id, Place::from(target).as_ref(), places)
1129 {
1130 return ClosureUse { closure_kind, args_span, capture_kind_span, path_span };
1131 } else {
1132 return OtherUse(use_span);
1133 }
1134 }
1135
1136 if use_span != stmt.source_info.span {
1137 break;
1138 }
1139 }
1140
1141 OtherUse(use_span)
1142 }
1143
1144 fn closure_span(
1148 &self,
1149 def_id: LocalDefId,
1150 target_place: PlaceRef<'tcx>,
1151 places: &IndexSlice<FieldIdx, Operand<'tcx>>,
1152 ) -> Option<(Span, hir::ClosureKind, Span, Span)> {
1153 debug!(
1154 "closure_span: def_id={:?} target_place={:?} places={:?}",
1155 def_id, target_place, places
1156 );
1157 let hir_id = self.infcx.tcx.local_def_id_to_hir_id(def_id);
1158 let expr = &self.infcx.tcx.hir_expect_expr(hir_id).kind;
1159 debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
1160 if let &hir::ExprKind::Closure(&hir::Closure { kind, fn_decl_span, .. }) = expr {
1161 for (captured_place, place) in
1162 self.infcx.tcx.closure_captures(def_id).iter().zip(places)
1163 {
1164 match place {
1165 Operand::Copy(place) | Operand::Move(place)
1166 if target_place == place.as_ref() =>
1167 {
1168 debug!("closure_span: found captured local {:?}", place);
1169 return Some((
1170 fn_decl_span,
1171 kind,
1172 captured_place.get_capture_kind_span(self.infcx.tcx),
1173 captured_place.get_path_span(self.infcx.tcx),
1174 ));
1175 }
1176 _ => {}
1177 }
1178 }
1179 }
1180 None
1181 }
1182
1183 pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx> {
1186 let span = self.body.source_info(borrow.reserve_location).span;
1187 self.borrow_spans(span, borrow.reserve_location)
1188 }
1189
1190 #[allow(rustc::diagnostic_outside_of_impl)]
1191 #[allow(rustc::untranslatable_diagnostic)] fn explain_captures(
1193 &mut self,
1194 err: &mut Diag<'infcx>,
1195 span: Span,
1196 move_span: Span,
1197 move_spans: UseSpans<'tcx>,
1198 moved_place: Place<'tcx>,
1199 msg_opt: CapturedMessageOpt,
1200 ) {
1201 let CapturedMessageOpt {
1202 is_partial_move: is_partial,
1203 is_loop_message,
1204 is_move_msg,
1205 is_loop_move,
1206 has_suggest_reborrow,
1207 maybe_reinitialized_locations_is_empty,
1208 } = msg_opt;
1209 if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
1210 let place_name = self
1211 .describe_place(moved_place.as_ref())
1212 .map(|n| format!("`{n}`"))
1213 .unwrap_or_else(|| "value".to_owned());
1214 match kind {
1215 CallKind::FnCall { fn_trait_id, self_ty }
1216 if self.infcx.tcx.is_lang_item(fn_trait_id, LangItem::FnOnce) =>
1217 {
1218 err.subdiagnostic(CaptureReasonLabel::Call {
1219 fn_call_span,
1220 place_name: &place_name,
1221 is_partial,
1222 is_loop_message,
1223 });
1224 if let ty::Param(param_ty) = *self_ty.kind()
1246 && let generics = self.infcx.tcx.generics_of(self.mir_def_id())
1247 && let param = generics.type_param(param_ty, self.infcx.tcx)
1248 && let Some(hir_generics) = self
1249 .infcx
1250 .tcx
1251 .typeck_root_def_id(self.mir_def_id().to_def_id())
1252 .as_local()
1253 .and_then(|def_id| self.infcx.tcx.hir_get_generics(def_id))
1254 && let spans = hir_generics
1255 .predicates
1256 .iter()
1257 .filter_map(|pred| match pred.kind {
1258 hir::WherePredicateKind::BoundPredicate(pred) => Some(pred),
1259 _ => None,
1260 })
1261 .filter(|pred| {
1262 if let Some((id, _)) = pred.bounded_ty.as_generic_param() {
1263 id == param.def_id
1264 } else {
1265 false
1266 }
1267 })
1268 .flat_map(|pred| pred.bounds)
1269 .filter_map(|bound| {
1270 if let Some(trait_ref) = bound.trait_ref()
1271 && let Some(trait_def_id) = trait_ref.trait_def_id()
1272 && trait_def_id == fn_trait_id
1273 {
1274 Some(bound.span())
1275 } else {
1276 None
1277 }
1278 })
1279 .collect::<Vec<Span>>()
1280 && !spans.is_empty()
1281 {
1282 let mut span: MultiSpan = spans.clone().into();
1283 err.arg("ty", param_ty.to_string());
1284 let msg = err.dcx.eagerly_translate_to_string(
1285 fluent::borrowck_moved_a_fn_once_in_call_def,
1286 err.args.iter(),
1287 );
1288 err.remove_arg("ty");
1289 for sp in spans {
1290 span.push_span_label(sp, msg.clone());
1291 }
1292 span.push_span_label(
1293 fn_call_span,
1294 fluent::borrowck_moved_a_fn_once_in_call,
1295 );
1296 err.span_note(span, fluent::borrowck_moved_a_fn_once_in_call_call);
1297 } else {
1298 err.subdiagnostic(CaptureReasonNote::FnOnceMoveInCall { var_span });
1299 }
1300 }
1301 CallKind::Operator { self_arg, trait_id, .. } => {
1302 let self_arg = self_arg.unwrap();
1303 err.subdiagnostic(CaptureReasonLabel::OperatorUse {
1304 fn_call_span,
1305 place_name: &place_name,
1306 is_partial,
1307 is_loop_message,
1308 });
1309 if self.fn_self_span_reported.insert(fn_span) {
1310 let lang = self.infcx.tcx.lang_items();
1311 err.subdiagnostic(
1312 if [lang.not_trait(), lang.deref_trait(), lang.neg_trait()]
1313 .contains(&Some(trait_id))
1314 {
1315 CaptureReasonNote::UnOpMoveByOperator { span: self_arg.span }
1316 } else {
1317 CaptureReasonNote::LhsMoveByOperator { span: self_arg.span }
1318 },
1319 );
1320 }
1321 }
1322 CallKind::Normal { self_arg, desugaring, method_did, method_args } => {
1323 let self_arg = self_arg.unwrap();
1324 let mut has_sugg = false;
1325 let tcx = self.infcx.tcx;
1326 if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
1329 self.explain_iterator_advancement_in_for_loop_if_applicable(
1330 err,
1331 span,
1332 &move_spans,
1333 );
1334
1335 let func = tcx.def_path_str(method_did);
1336 err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
1337 func,
1338 place_name: place_name.clone(),
1339 span: self_arg.span,
1340 });
1341 }
1342 let parent_did = tcx.parent(method_did);
1343 let parent_self_ty =
1344 matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. })
1345 .then_some(parent_did)
1346 .and_then(|did| match tcx.type_of(did).instantiate_identity().kind() {
1347 ty::Adt(def, ..) => Some(def.did()),
1348 _ => None,
1349 });
1350 let is_option_or_result = parent_self_ty.is_some_and(|def_id| {
1351 matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
1352 });
1353 if is_option_or_result && maybe_reinitialized_locations_is_empty {
1354 err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
1355 }
1356 if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
1357 let ty = moved_place.ty(self.body, tcx).ty;
1358 let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) {
1359 Some(def_id) => type_known_to_meet_bound_modulo_regions(
1360 self.infcx,
1361 self.infcx.param_env,
1362 Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty),
1363 def_id,
1364 ),
1365 _ => false,
1366 };
1367 if suggest {
1368 err.subdiagnostic(CaptureReasonSuggest::IterateSlice {
1369 ty,
1370 span: move_span.shrink_to_lo(),
1371 });
1372 }
1373
1374 err.subdiagnostic(CaptureReasonLabel::ImplicitCall {
1375 fn_call_span,
1376 place_name: &place_name,
1377 is_partial,
1378 is_loop_message,
1379 });
1380 if let ty::Ref(_, _, hir::Mutability::Mut) =
1384 moved_place.ty(self.body, self.infcx.tcx).ty.kind()
1385 {
1386 if !is_loop_move && !has_suggest_reborrow {
1391 self.suggest_reborrow(
1392 err,
1393 move_span.shrink_to_lo(),
1394 moved_place.as_ref(),
1395 );
1396 }
1397 }
1398 } else {
1399 if let Some((CallDesugaringKind::Await, _)) = desugaring {
1400 err.subdiagnostic(CaptureReasonLabel::Await {
1401 fn_call_span,
1402 place_name: &place_name,
1403 is_partial,
1404 is_loop_message,
1405 });
1406 } else {
1407 err.subdiagnostic(CaptureReasonLabel::MethodCall {
1408 fn_call_span,
1409 place_name: &place_name,
1410 is_partial,
1411 is_loop_message,
1412 });
1413 }
1414 let ty = moved_place.ty(self.body, tcx).ty;
1416
1417 if let ty::Adt(def, args) = ty.peel_refs().kind()
1418 && tcx.is_lang_item(def.did(), LangItem::Pin)
1419 && let ty::Ref(_, _, hir::Mutability::Mut) = args.type_at(0).kind()
1420 && let self_ty = self.infcx.instantiate_binder_with_fresh_vars(
1421 fn_call_span,
1422 BoundRegionConversionTime::FnCall,
1423 tcx.fn_sig(method_did).instantiate(tcx, method_args).input(0),
1424 )
1425 && self.infcx.can_eq(self.infcx.param_env, ty, self_ty)
1426 {
1427 err.subdiagnostic(CaptureReasonSuggest::FreshReborrow {
1428 span: move_span.shrink_to_hi(),
1429 });
1430 has_sugg = true;
1431 }
1432 if let Some(clone_trait) = tcx.lang_items().clone_trait() {
1433 let sugg = if moved_place
1434 .iter_projections()
1435 .any(|(_, elem)| matches!(elem, ProjectionElem::Deref))
1436 {
1437 let (start, end) = if let Some(expr) = self.find_expr(move_span)
1438 && let Some(_) = self.clone_on_reference(expr)
1439 && let hir::ExprKind::MethodCall(_, rcvr, _, _) = expr.kind
1440 {
1441 (move_span.shrink_to_lo(), move_span.with_lo(rcvr.span.hi()))
1442 } else {
1443 (move_span.shrink_to_lo(), move_span.shrink_to_hi())
1444 };
1445 vec![
1446 (start, format!("<{ty} as Clone>::clone(&")),
1451 (end, ")".to_string()),
1452 ]
1453 } else {
1454 vec![(move_span.shrink_to_hi(), ".clone()".to_string())]
1455 };
1456 if let Some(errors) = self.infcx.type_implements_trait_shallow(
1457 clone_trait,
1458 ty,
1459 self.infcx.param_env,
1460 ) && !has_sugg
1461 {
1462 let msg = match &errors[..] {
1463 [] => "you can `clone` the value and consume it, but this \
1464 might not be your desired behavior"
1465 .to_string(),
1466 [error] => {
1467 format!(
1468 "you could `clone` the value and consume it, if the \
1469 `{}` trait bound could be satisfied",
1470 error.obligation.predicate,
1471 )
1472 }
1473 _ => {
1474 format!(
1475 "you could `clone` the value and consume it, if the \
1476 following trait bounds could be satisfied: {}",
1477 listify(&errors, |e: &FulfillmentError<'tcx>| format!(
1478 "`{}`",
1479 e.obligation.predicate
1480 ))
1481 .unwrap(),
1482 )
1483 }
1484 };
1485 err.multipart_suggestion_verbose(
1486 msg,
1487 sugg,
1488 Applicability::MaybeIncorrect,
1489 );
1490 for error in errors {
1491 if let FulfillmentErrorCode::Select(
1492 SelectionError::Unimplemented,
1493 ) = error.code
1494 && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
1495 pred,
1496 )) = error.obligation.predicate.kind().skip_binder()
1497 {
1498 self.infcx.err_ctxt().suggest_derive(
1499 &error.obligation,
1500 err,
1501 error.obligation.predicate.kind().rebind(pred),
1502 );
1503 }
1504 }
1505 }
1506 }
1507 }
1508 }
1509 _ => {}
1511 }
1512 } else {
1513 if move_span != span || is_loop_message {
1514 err.subdiagnostic(CaptureReasonLabel::MovedHere {
1515 move_span,
1516 is_partial,
1517 is_move_msg,
1518 is_loop_message,
1519 });
1520 }
1521 if !is_loop_message {
1524 move_spans.var_subdiag(err, None, |kind, var_span| match kind {
1525 hir::ClosureKind::Coroutine(_) => {
1526 CaptureVarCause::PartialMoveUseInCoroutine { var_span, is_partial }
1527 }
1528 hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
1529 CaptureVarCause::PartialMoveUseInClosure { var_span, is_partial }
1530 }
1531 })
1532 }
1533 }
1534 }
1535
1536 pub(crate) fn local_excluded_from_unused_mut_lint(&self, index: Local) -> bool {
1538 self.local_name(index).is_none_or(|name| name.as_str().starts_with('_'))
1539 }
1540}