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