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::{
10 self as hir, CoroutineKind, GenericBound, LangItem, WhereBoundPredicate, WherePredicateKind,
11};
12use rustc_index::{IndexSlice, IndexVec};
13use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin};
14use rustc_infer::traits::SelectionError;
15use rustc_middle::mir::{
16 AggregateKind, CallSource, ConstOperand, ConstraintCategory, FakeReadCause, Local, LocalInfo,
17 LocalKind, Location, Operand, Place, PlaceRef, PlaceTy, ProjectionElem, Rvalue, Statement,
18 StatementKind, Terminator, TerminatorKind, VarDebugInfoContents, find_self_call,
19};
20use rustc_middle::ty::print::Print;
21use rustc_middle::ty::{self, Ty, TyCtxt};
22use rustc_middle::{bug, span_bug};
23use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveOutIndex};
24use rustc_session::lint::builtin::MACRO_EXTENDED_TEMPORARY_SCOPES;
25use rustc_span::def_id::LocalDefId;
26use rustc_span::source_map::Spanned;
27use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
28use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
29use rustc_trait_selection::error_reporting::traits::call_kind::{CallDesugaringKind, call_kind};
30use rustc_trait_selection::infer::InferCtxtExt;
31use rustc_trait_selection::traits::{
32 FulfillmentError, FulfillmentErrorCode, type_known_to_meet_bound_modulo_regions,
33};
34use tracing::debug;
35
36use super::MirBorrowckCtxt;
37use super::borrow_set::BorrowData;
38use crate::constraints::OutlivesConstraint;
39use crate::fluent_generated as fluent;
40use crate::nll::ConstraintDescription;
41use crate::session_diagnostics::{
42 CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause,
43 CaptureVarKind, CaptureVarPathUseCause, OnClosureNote,
44};
45
46mod find_all_local_uses;
47mod find_use;
48mod outlives_suggestion;
49mod region_name;
50mod var_name;
51
52mod bound_region_errors;
53mod conflict_errors;
54mod explain_borrow;
55mod move_errors;
56mod mutability_errors;
57mod opaque_types;
58mod region_errors;
59
60pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo};
61pub(crate) use move_errors::{IllegalMoveOriginKind, MoveError};
62pub(crate) use mutability_errors::AccessKind;
63pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder;
64pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
65pub(crate) use region_name::{RegionName, RegionNameSource};
66pub(crate) use rustc_trait_selection::error_reporting::traits::call_kind::CallKind;
67
68pub(super) struct DescribePlaceOpt {
69 including_downcast: bool,
70
71 including_tuple_field: bool,
74}
75
76pub(super) struct IncludingTupleField(pub(super) bool);
77
78enum BufferedDiag<'infcx> {
79 Error(Diag<'infcx>),
80 NonError(Diag<'infcx, ()>),
81}
82
83impl<'infcx> BufferedDiag<'infcx> {
84 fn sort_span(&self) -> Span {
85 match self {
86 BufferedDiag::Error(diag) => diag.sort_span,
87 BufferedDiag::NonError(diag) => diag.sort_span,
88 }
89 }
90}
91
92#[derive(Default)]
93pub(crate) struct BorrowckDiagnosticsBuffer<'infcx, 'tcx> {
94 buffered_move_errors: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, Diag<'infcx>)>,
109
110 buffered_mut_errors: FxIndexMap<Span, (Diag<'infcx>, usize)>,
111
112 buffered_diags: Vec<BufferedDiag<'infcx>>,
114}
115
116impl<'infcx, 'tcx> BorrowckDiagnosticsBuffer<'infcx, 'tcx> {
117 pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
118 self.buffered_diags.push(BufferedDiag::NonError(diag));
119 }
120}
121
122impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
123 pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) {
124 self.diags_buffer.buffered_diags.push(BufferedDiag::Error(diag));
125 }
126
127 pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
128 self.diags_buffer.buffer_non_error(diag);
129 }
130
131 pub(crate) fn buffer_move_error(
132 &mut self,
133 move_out_indices: Vec<MoveOutIndex>,
134 place_and_err: (PlaceRef<'tcx>, Diag<'infcx>),
135 ) -> bool {
136 if let Some((_, diag)) =
137 self.diags_buffer.buffered_move_errors.insert(move_out_indices, place_and_err)
138 {
139 diag.cancel();
141 false
142 } else {
143 true
144 }
145 }
146
147 pub(crate) fn get_buffered_mut_error(&mut self, span: Span) -> Option<(Diag<'infcx>, usize)> {
148 self.diags_buffer.buffered_mut_errors.swap_remove(&span)
150 }
151
152 pub(crate) fn buffer_mut_error(&mut self, span: Span, diag: Diag<'infcx>, count: usize) {
153 self.diags_buffer.buffered_mut_errors.insert(span, (diag, count));
154 }
155
156 pub(crate) fn emit_errors(&mut self) -> Option<ErrorGuaranteed> {
157 let mut res = self.infcx.tainted_by_errors();
158
159 for (_, (_, diag)) in std::mem::take(&mut self.diags_buffer.buffered_move_errors) {
161 self.buffer_error(diag);
163 }
164 for (_, (mut diag, count)) in std::mem::take(&mut self.diags_buffer.buffered_mut_errors) {
165 if count > 10 {
166 #[allow(rustc::diagnostic_outside_of_impl)]
167 #[allow(rustc::untranslatable_diagnostic)]
168 diag.note(format!("...and {} other attempted mutable borrows", count - 10));
169 }
170 self.buffer_error(diag);
171 }
172
173 if !self.diags_buffer.buffered_diags.is_empty() {
174 self.diags_buffer.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span());
175 for buffered_diag in self.diags_buffer.buffered_diags.drain(..) {
176 match buffered_diag {
177 BufferedDiag::Error(diag) => res = Some(diag.emit()),
178 BufferedDiag::NonError(diag) => diag.emit(),
179 }
180 }
181 }
182
183 res
184 }
185
186 pub(crate) fn has_buffered_diags(&self) -> bool {
187 self.diags_buffer.buffered_diags.is_empty()
188 }
189
190 pub(crate) fn has_move_error(
191 &self,
192 move_out_indices: &[MoveOutIndex],
193 ) -> Option<&(PlaceRef<'tcx>, Diag<'infcx>)> {
194 self.diags_buffer.buffered_move_errors.get(move_out_indices)
195 }
196
197 fn local_name(&self, index: Local) -> Option<Symbol> {
199 *self.local_names().get(index)?
200 }
201
202 fn local_names(&self) -> &IndexSlice<Local, Option<Symbol>> {
203 self.local_names.get_or_init(|| {
204 let mut local_names = IndexVec::from_elem(None, &self.body.local_decls);
205 for var_debug_info in &self.body.var_debug_info {
206 if let VarDebugInfoContents::Place(place) = var_debug_info.value {
207 if let Some(local) = place.as_local() {
208 if let Some(prev_name) = local_names[local]
209 && var_debug_info.name != prev_name
210 {
211 span_bug!(
212 var_debug_info.source_info.span,
213 "local {:?} has many names (`{}` vs `{}`)",
214 local,
215 prev_name,
216 var_debug_info.name
217 );
218 }
219 local_names[local] = Some(var_debug_info.name);
220 }
221 }
222 }
223 local_names
224 })
225 }
226}
227
228impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
229 #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn add_moved_or_invoked_closure_note(
242 &self,
243 location: Location,
244 place: PlaceRef<'tcx>,
245 diag: &mut Diag<'infcx>,
246 ) -> bool {
247 debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
248 let mut target = place.local_or_deref_local();
249 for stmt in &self.body[location.block].statements[location.statement_index..] {
250 debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
251 if let StatementKind::Assign(box (into, Rvalue::Use(from))) = &stmt.kind {
252 debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
253 match from {
254 Operand::Copy(place) | Operand::Move(place)
255 if target == place.local_or_deref_local() =>
256 {
257 target = into.local_or_deref_local()
258 }
259 _ => {}
260 }
261 }
262 }
263
264 let terminator = self.body[location.block].terminator();
266 debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
267 if let TerminatorKind::Call {
268 func: Operand::Constant(box ConstOperand { const_, .. }),
269 args,
270 ..
271 } = &terminator.kind
272 && let ty::FnDef(id, _) = *const_.ty().kind()
273 {
274 debug!("add_moved_or_invoked_closure_note: id={:?}", id);
275 if self.infcx.tcx.is_lang_item(self.infcx.tcx.parent(id), LangItem::FnOnce) {
276 let closure = match args.first() {
277 Some(Spanned { node: Operand::Copy(place) | Operand::Move(place), .. })
278 if target == place.local_or_deref_local() =>
279 {
280 place.local_or_deref_local().unwrap()
281 }
282 _ => return false,
283 };
284
285 debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
286 if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() {
287 let did = did.expect_local();
288 if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
289 diag.subdiagnostic(OnClosureNote::InvokedTwice {
290 place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
291 span: *span,
292 });
293 return true;
294 }
295 }
296 }
297 }
298
299 if let Some(target) = target
301 && let ty::Closure(did, _) = self.body.local_decls[target].ty.kind()
302 {
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 false
313 }
314
315 pub(super) fn describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String {
318 match self.describe_place(place_ref) {
319 Some(mut descr) => {
320 descr.reserve(2);
322 descr.insert(0, '`');
323 descr.push('`');
324 descr
325 }
326 None => "value".to_string(),
327 }
328 }
329
330 pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String> {
333 self.describe_place_with_options(
334 place_ref,
335 DescribePlaceOpt { including_downcast: false, including_tuple_field: true },
336 )
337 }
338
339 pub(super) fn describe_place_with_options(
344 &self,
345 place: PlaceRef<'tcx>,
346 opt: DescribePlaceOpt,
347 ) -> Option<String> {
348 let local = place.local;
349 if self.body.local_decls[local]
350 .source_info
351 .span
352 .in_external_macro(self.infcx.tcx.sess.source_map())
353 {
354 return None;
355 }
356
357 let mut autoderef_index = None;
358 let mut buf = String::new();
359 let mut ok = self.append_local_to_string(local, &mut buf);
360
361 for (index, elem) in place.projection.into_iter().enumerate() {
362 match elem {
363 ProjectionElem::Deref => {
364 if index == 0 {
365 if self.body.local_decls[local].is_ref_for_guard() {
366 continue;
367 }
368 if let LocalInfo::StaticRef { def_id, .. } =
369 *self.body.local_decls[local].local_info()
370 {
371 buf.push_str(self.infcx.tcx.item_name(def_id).as_str());
372 ok = Ok(());
373 continue;
374 }
375 }
376 if let Some(field) = self.is_upvar_field_projection(PlaceRef {
377 local,
378 projection: place.projection.split_at(index + 1).0,
379 }) {
380 let var_index = field.index();
381 buf = self.upvars[var_index].to_string(self.infcx.tcx);
382 ok = Ok(());
383 if !self.upvars[var_index].is_by_ref() {
384 buf.insert(0, '*');
385 }
386 } else {
387 if autoderef_index.is_none() {
388 autoderef_index = match place.projection.iter().rposition(|elem| {
389 !matches!(
390 elem,
391 ProjectionElem::Deref | ProjectionElem::Downcast(..)
392 )
393 }) {
394 Some(index) => Some(index + 1),
395 None => Some(0),
396 };
397 }
398 if index >= autoderef_index.unwrap() {
399 buf.insert(0, '*');
400 }
401 }
402 }
403 ProjectionElem::Downcast(..) if opt.including_downcast => return None,
404 ProjectionElem::Downcast(..) => (),
405 ProjectionElem::OpaqueCast(..) => (),
406 ProjectionElem::Subtype(..) => (),
407 ProjectionElem::UnwrapUnsafeBinder(_) => (),
408 ProjectionElem::Field(field, _ty) => {
409 if let Some(field) = self.is_upvar_field_projection(PlaceRef {
411 local,
412 projection: place.projection.split_at(index + 1).0,
413 }) {
414 buf = self.upvars[field.index()].to_string(self.infcx.tcx);
415 ok = Ok(());
416 } else {
417 let field_name = self.describe_field(
418 PlaceRef { local, projection: place.projection.split_at(index).0 },
419 *field,
420 IncludingTupleField(opt.including_tuple_field),
421 );
422 if let Some(field_name_str) = field_name {
423 buf.push('.');
424 buf.push_str(&field_name_str);
425 }
426 }
427 }
428 ProjectionElem::Index(index) => {
429 buf.push('[');
430 if self.append_local_to_string(*index, &mut buf).is_err() {
431 buf.push('_');
432 }
433 buf.push(']');
434 }
435 ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
436 buf.push_str("[..]");
440 }
441 }
442 }
443 ok.ok().map(|_| buf)
444 }
445
446 fn describe_name(&self, place: PlaceRef<'tcx>) -> Option<Symbol> {
447 for elem in place.projection.into_iter() {
448 match elem {
449 ProjectionElem::Downcast(Some(name), _) => {
450 return Some(*name);
451 }
452 _ => {}
453 }
454 }
455 None
456 }
457
458 fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> {
461 let decl = &self.body.local_decls[local];
462 match self.local_name(local) {
463 Some(name) if !decl.from_compiler_desugaring() => {
464 buf.push_str(name.as_str());
465 Ok(())
466 }
467 _ => Err(()),
468 }
469 }
470
471 fn describe_field(
473 &self,
474 place: PlaceRef<'tcx>,
475 field: FieldIdx,
476 including_tuple_field: IncludingTupleField,
477 ) -> Option<String> {
478 let place_ty = match place {
479 PlaceRef { local, projection: [] } => PlaceTy::from_ty(self.body.local_decls[local].ty),
480 PlaceRef { local, projection: [proj_base @ .., elem] } => match elem {
481 ProjectionElem::Deref
482 | ProjectionElem::Index(..)
483 | ProjectionElem::ConstantIndex { .. }
484 | ProjectionElem::Subslice { .. } => {
485 PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
486 }
487 ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
488 ProjectionElem::Subtype(ty)
489 | ProjectionElem::OpaqueCast(ty)
490 | ProjectionElem::UnwrapUnsafeBinder(ty) => PlaceTy::from_ty(*ty),
491 ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
492 },
493 };
494 self.describe_field_from_ty(
495 place_ty.ty,
496 field,
497 place_ty.variant_index,
498 including_tuple_field,
499 )
500 }
501
502 fn describe_field_from_ty(
504 &self,
505 ty: Ty<'_>,
506 field: FieldIdx,
507 variant_index: Option<VariantIdx>,
508 including_tuple_field: IncludingTupleField,
509 ) -> Option<String> {
510 if let Some(boxed_ty) = ty.boxed_ty() {
511 self.describe_field_from_ty(boxed_ty, field, variant_index, including_tuple_field)
513 } else {
514 match *ty.kind() {
515 ty::Adt(def, _) => {
516 let variant = if let Some(idx) = variant_index {
517 assert!(def.is_enum());
518 def.variant(idx)
519 } else {
520 def.non_enum_variant()
521 };
522 if !including_tuple_field.0 && variant.ctor_kind() == Some(CtorKind::Fn) {
523 return None;
524 }
525 Some(variant.fields[field].name.to_string())
526 }
527 ty::Tuple(_) => Some(field.index().to_string()),
528 ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => {
529 self.describe_field_from_ty(ty, field, variant_index, including_tuple_field)
530 }
531 ty::Array(ty, _) | ty::Slice(ty) => {
532 self.describe_field_from_ty(ty, field, variant_index, including_tuple_field)
533 }
534 ty::Closure(def_id, _) | ty::Coroutine(def_id, _) => {
535 let def_id = def_id.expect_local();
540 let var_id =
541 self.infcx.tcx.closure_captures(def_id)[field.index()].get_root_variable();
542
543 Some(self.infcx.tcx.hir_name(var_id).to_string())
544 }
545 _ => {
546 bug!("End-user description not implemented for field access on `{:?}`", ty);
549 }
550 }
551 }
552 }
553
554 pub(super) fn borrowed_content_source(
555 &self,
556 deref_base: PlaceRef<'tcx>,
557 ) -> BorrowedContentSource<'tcx> {
558 let tcx = self.infcx.tcx;
559
560 match self.move_data.rev_lookup.find(deref_base) {
564 LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => {
565 debug!("borrowed_content_source: mpi={:?}", mpi);
566
567 for i in &self.move_data.init_path_map[mpi] {
568 let init = &self.move_data.inits[*i];
569 debug!("borrowed_content_source: init={:?}", init);
570 let InitLocation::Statement(loc) = init.location else { continue };
573
574 let bbd = &self.body[loc.block];
575 let is_terminator = bbd.statements.len() == loc.statement_index;
576 debug!(
577 "borrowed_content_source: loc={:?} is_terminator={:?}",
578 loc, is_terminator,
579 );
580 if !is_terminator {
581 continue;
582 } else if let Some(Terminator {
583 kind:
584 TerminatorKind::Call {
585 func,
586 call_source: CallSource::OverloadedOperator,
587 ..
588 },
589 ..
590 }) = &bbd.terminator
591 {
592 if let Some(source) =
593 BorrowedContentSource::from_call(func.ty(self.body, tcx), tcx)
594 {
595 return source;
596 }
597 }
598 }
599 }
600 _ => (),
602 };
603
604 let base_ty = deref_base.ty(self.body, tcx).ty;
607 if base_ty.is_raw_ptr() {
608 BorrowedContentSource::DerefRawPointer
609 } else if base_ty.is_mutable_ptr() {
610 BorrowedContentSource::DerefMutableRef
611 } else {
612 BorrowedContentSource::DerefSharedRef
613 }
614 }
615
616 pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
619 let mut p = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
620
621 if let ty::Ref(region, ..) = ty.kind() {
625 match region.kind() {
626 ty::ReBound(_, ty::BoundRegion { kind: br, .. })
627 | ty::RePlaceholder(ty::PlaceholderRegion {
628 bound: ty::BoundRegion { kind: br, .. },
629 ..
630 }) => p.region_highlight_mode.highlighting_bound_region(br, counter),
631 _ => {}
632 }
633 }
634
635 ty.print(&mut p).unwrap();
636 p.into_buffer()
637 }
638
639 pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
642 let mut p = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
643
644 let region = if let ty::Ref(region, ..) = ty.kind() {
645 match region.kind() {
646 ty::ReBound(_, ty::BoundRegion { kind: br, .. })
647 | ty::RePlaceholder(ty::PlaceholderRegion {
648 bound: ty::BoundRegion { kind: br, .. },
649 ..
650 }) => p.region_highlight_mode.highlighting_bound_region(br, counter),
651 _ => {}
652 }
653 region
654 } else {
655 bug!("ty for annotation of borrow region is not a reference");
656 };
657
658 region.print(&mut p).unwrap();
659 p.into_buffer()
660 }
661
662 fn add_placeholder_from_predicate_note<G: EmissionGuarantee>(
668 &self,
669 diag: &mut Diag<'_, G>,
670 path: &[OutlivesConstraint<'tcx>],
671 ) {
672 let tcx = self.infcx.tcx;
673 let Some((gat_hir_id, generics)) = path.iter().find_map(|constraint| {
674 let outlived = constraint.sub;
675 if let Some(origin) = self.regioncx.definitions.get(outlived)
676 && let NllRegionVariableOrigin::Placeholder(placeholder) = origin.origin
677 && let Some(id) = placeholder.bound.kind.get_id()
678 && let Some(placeholder_id) = id.as_local()
679 && let gat_hir_id = tcx.local_def_id_to_hir_id(placeholder_id)
680 && let Some(generics_impl) =
681 tcx.parent_hir_node(tcx.parent_hir_id(gat_hir_id)).generics()
682 {
683 Some((gat_hir_id, generics_impl))
684 } else {
685 None
686 }
687 }) else {
688 return;
689 };
690
691 for pred in generics.predicates {
695 let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
696 bound_generic_params,
697 bounds,
698 ..
699 }) = pred.kind
700 else {
701 continue;
702 };
703 if bound_generic_params
704 .iter()
705 .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
706 .is_some()
707 {
708 diag.span_note(pred.span, fluent::borrowck_limitations_implies_static);
709 return;
710 }
711 for bound in bounds.iter() {
712 if let GenericBound::Trait(bound) = bound {
713 if bound
714 .bound_generic_params
715 .iter()
716 .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
717 .is_some()
718 {
719 diag.span_note(bound.span, fluent::borrowck_limitations_implies_static);
720 return;
721 }
722 }
723 }
724 }
725 }
726
727 fn add_sized_or_copy_bound_info<G: EmissionGuarantee>(
730 &self,
731 err: &mut Diag<'_, G>,
732 blamed_category: ConstraintCategory<'tcx>,
733 path: &[OutlivesConstraint<'tcx>],
734 ) {
735 for sought_category in [ConstraintCategory::SizedBound, ConstraintCategory::CopyBound] {
736 if sought_category != blamed_category
737 && let Some(sought_constraint) = path.iter().find(|c| c.category == sought_category)
738 {
739 let label = format!(
740 "requirement occurs due to {}",
741 sought_category.description().trim_end()
742 );
743 err.span_label(sought_constraint.span, label);
744 }
745 }
746 }
747}
748
749#[derive(Copy, Clone, PartialEq, Eq, Debug)]
751pub(super) enum UseSpans<'tcx> {
752 ClosureUse {
754 closure_kind: hir::ClosureKind,
756 args_span: Span,
759 capture_kind_span: Span,
762 path_span: Span,
765 },
766 FnSelfUse {
769 var_span: Span,
771 fn_call_span: Span,
773 fn_span: Span,
775 kind: CallKind<'tcx>,
776 },
777 PatUse(Span),
779 OtherUse(Span),
781}
782
783impl UseSpans<'_> {
784 pub(super) fn args_or_use(self) -> Span {
785 match self {
786 UseSpans::ClosureUse { args_span: span, .. }
787 | UseSpans::PatUse(span)
788 | UseSpans::OtherUse(span) => span,
789 UseSpans::FnSelfUse { var_span, .. } => var_span,
790 }
791 }
792
793 pub(super) fn var_or_use_path_span(self) -> Span {
795 match self {
796 UseSpans::ClosureUse { path_span: span, .. }
797 | UseSpans::PatUse(span)
798 | UseSpans::OtherUse(span) => span,
799 UseSpans::FnSelfUse { var_span, .. } => var_span,
800 }
801 }
802
803 pub(super) fn var_or_use(self) -> Span {
805 match self {
806 UseSpans::ClosureUse { capture_kind_span: span, .. }
807 | UseSpans::PatUse(span)
808 | UseSpans::OtherUse(span) => span,
809 UseSpans::FnSelfUse { var_span, .. } => var_span,
810 }
811 }
812
813 pub(super) fn coroutine_kind(self) -> Option<CoroutineKind> {
815 match self {
816 UseSpans::ClosureUse {
817 closure_kind: hir::ClosureKind::Coroutine(coroutine_kind),
818 ..
819 } => Some(coroutine_kind),
820 _ => None,
821 }
822 }
823
824 #[allow(rustc::diagnostic_outside_of_impl)]
826 pub(super) fn args_subdiag(self, err: &mut Diag<'_>, f: impl FnOnce(Span) -> CaptureArgLabel) {
827 if let UseSpans::ClosureUse { args_span, .. } = self {
828 err.subdiagnostic(f(args_span));
829 }
830 }
831
832 #[allow(rustc::diagnostic_outside_of_impl)]
835 pub(super) fn var_path_only_subdiag(
836 self,
837 err: &mut Diag<'_>,
838 action: crate::InitializationRequiringAction,
839 ) {
840 use CaptureVarPathUseCause::*;
841
842 use crate::InitializationRequiringAction::*;
843 if let UseSpans::ClosureUse { closure_kind, path_span, .. } = self {
844 match closure_kind {
845 hir::ClosureKind::Coroutine(_) => {
846 err.subdiagnostic(match action {
847 Borrow => BorrowInCoroutine { path_span },
848 MatchOn | Use => UseInCoroutine { path_span },
849 Assignment => AssignInCoroutine { path_span },
850 PartialAssignment => AssignPartInCoroutine { path_span },
851 });
852 }
853 hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
854 err.subdiagnostic(match action {
855 Borrow => BorrowInClosure { path_span },
856 MatchOn | Use => UseInClosure { path_span },
857 Assignment => AssignInClosure { path_span },
858 PartialAssignment => AssignPartInClosure { path_span },
859 });
860 }
861 }
862 }
863 }
864
865 #[allow(rustc::diagnostic_outside_of_impl)]
867 pub(super) fn var_subdiag(
868 self,
869 err: &mut Diag<'_>,
870 kind: Option<rustc_middle::mir::BorrowKind>,
871 f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause,
872 ) {
873 if let UseSpans::ClosureUse { closure_kind, capture_kind_span, path_span, .. } = self {
874 if capture_kind_span != path_span {
875 err.subdiagnostic(match kind {
876 Some(kd) => match kd {
877 rustc_middle::mir::BorrowKind::Shared
878 | rustc_middle::mir::BorrowKind::Fake(_) => {
879 CaptureVarKind::Immut { kind_span: capture_kind_span }
880 }
881
882 rustc_middle::mir::BorrowKind::Mut { .. } => {
883 CaptureVarKind::Mut { kind_span: capture_kind_span }
884 }
885 },
886 None => CaptureVarKind::Move { kind_span: capture_kind_span },
887 });
888 };
889 let diag = f(closure_kind, path_span);
890 err.subdiagnostic(diag);
891 }
892 }
893
894 pub(super) fn for_closure(&self) -> bool {
896 match *self {
897 UseSpans::ClosureUse { closure_kind, .. } => {
898 matches!(closure_kind, hir::ClosureKind::Closure)
899 }
900 _ => false,
901 }
902 }
903
904 pub(super) fn for_coroutine(&self) -> bool {
906 match *self {
907 UseSpans::ClosureUse { closure_kind, .. } => {
909 matches!(closure_kind, hir::ClosureKind::Coroutine(..))
910 }
911 _ => false,
912 }
913 }
914
915 pub(super) fn or_else<F>(self, if_other: F) -> Self
916 where
917 F: FnOnce() -> Self,
918 {
919 match self {
920 closure @ UseSpans::ClosureUse { .. } => closure,
921 UseSpans::PatUse(_) | UseSpans::OtherUse(_) => if_other(),
922 fn_self @ UseSpans::FnSelfUse { .. } => fn_self,
923 }
924 }
925}
926
927pub(super) enum BorrowedContentSource<'tcx> {
928 DerefRawPointer,
929 DerefMutableRef,
930 DerefSharedRef,
931 OverloadedDeref(Ty<'tcx>),
932 OverloadedIndex(Ty<'tcx>),
933}
934
935impl<'tcx> BorrowedContentSource<'tcx> {
936 pub(super) fn describe_for_unnamed_place(&self, tcx: TyCtxt<'_>) -> String {
937 match *self {
938 BorrowedContentSource::DerefRawPointer => "a raw pointer".to_string(),
939 BorrowedContentSource::DerefSharedRef => "a shared reference".to_string(),
940 BorrowedContentSource::DerefMutableRef => "a mutable reference".to_string(),
941 BorrowedContentSource::OverloadedDeref(ty) => ty
942 .ty_adt_def()
943 .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? {
944 name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")),
945 _ => None,
946 })
947 .unwrap_or_else(|| format!("dereference of `{ty}`")),
948 BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{ty}`"),
949 }
950 }
951
952 pub(super) fn describe_for_named_place(&self) -> Option<&'static str> {
953 match *self {
954 BorrowedContentSource::DerefRawPointer => Some("raw pointer"),
955 BorrowedContentSource::DerefSharedRef => Some("shared reference"),
956 BorrowedContentSource::DerefMutableRef => Some("mutable reference"),
957 BorrowedContentSource::OverloadedDeref(_)
960 | BorrowedContentSource::OverloadedIndex(_) => None,
961 }
962 }
963
964 pub(super) fn describe_for_immutable_place(&self, tcx: TyCtxt<'_>) -> String {
965 match *self {
966 BorrowedContentSource::DerefRawPointer => "a `*const` pointer".to_string(),
967 BorrowedContentSource::DerefSharedRef => "a `&` reference".to_string(),
968 BorrowedContentSource::DerefMutableRef => {
969 bug!("describe_for_immutable_place: DerefMutableRef isn't immutable")
970 }
971 BorrowedContentSource::OverloadedDeref(ty) => ty
972 .ty_adt_def()
973 .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? {
974 name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")),
975 _ => None,
976 })
977 .unwrap_or_else(|| format!("dereference of `{ty}`")),
978 BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{ty}`"),
979 }
980 }
981
982 fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> {
983 match *func.kind() {
984 ty::FnDef(def_id, args) => {
985 let trait_id = tcx.trait_of_assoc(def_id)?;
986
987 if tcx.is_lang_item(trait_id, LangItem::Deref)
988 || tcx.is_lang_item(trait_id, LangItem::DerefMut)
989 {
990 Some(BorrowedContentSource::OverloadedDeref(args.type_at(0)))
991 } else if tcx.is_lang_item(trait_id, LangItem::Index)
992 || tcx.is_lang_item(trait_id, LangItem::IndexMut)
993 {
994 Some(BorrowedContentSource::OverloadedIndex(args.type_at(0)))
995 } else {
996 None
997 }
998 }
999 _ => None,
1000 }
1001 }
1002}
1003
1004struct CapturedMessageOpt {
1006 is_partial_move: bool,
1007 is_loop_message: bool,
1008 is_move_msg: bool,
1009 is_loop_move: bool,
1010 has_suggest_reborrow: bool,
1011 maybe_reinitialized_locations_is_empty: bool,
1012}
1013
1014impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
1015 pub(super) fn move_spans(
1017 &self,
1018 moved_place: PlaceRef<'tcx>, location: Location,
1020 ) -> UseSpans<'tcx> {
1021 use self::UseSpans::*;
1022
1023 let Some(stmt) = self.body[location.block].statements.get(location.statement_index) else {
1024 return OtherUse(self.body.source_info(location).span);
1025 };
1026
1027 debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
1028 if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind
1029 && let AggregateKind::Closure(def_id, _) | AggregateKind::Coroutine(def_id, _) = **kind
1030 {
1031 debug!("move_spans: def_id={:?} places={:?}", def_id, places);
1032 let def_id = def_id.expect_local();
1033 if let Some((args_span, closure_kind, capture_kind_span, path_span)) =
1034 self.closure_span(def_id, moved_place, places)
1035 {
1036 return ClosureUse { closure_kind, args_span, capture_kind_span, path_span };
1037 }
1038 }
1039
1040 if let StatementKind::FakeRead(box (cause, place)) = stmt.kind {
1043 match cause {
1044 FakeReadCause::ForMatchedPlace(Some(closure_def_id))
1045 | FakeReadCause::ForLet(Some(closure_def_id)) => {
1046 debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place);
1047 let places = &[Operand::Move(place)];
1048 if let Some((args_span, closure_kind, capture_kind_span, path_span)) =
1049 self.closure_span(closure_def_id, moved_place, IndexSlice::from_raw(places))
1050 {
1051 return ClosureUse {
1052 closure_kind,
1053 args_span,
1054 capture_kind_span,
1055 path_span,
1056 };
1057 }
1058 }
1059 _ => {}
1060 }
1061 }
1062
1063 let normal_ret =
1064 if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) {
1065 PatUse(stmt.source_info.span)
1066 } else {
1067 OtherUse(stmt.source_info.span)
1068 };
1069
1070 let target_temp = match stmt.kind {
1082 StatementKind::Assign(box (temp, _)) if temp.as_local().is_some() => {
1083 temp.as_local().unwrap()
1084 }
1085 _ => return normal_ret,
1086 };
1087
1088 debug!("move_spans: target_temp = {:?}", target_temp);
1089
1090 if let Some(Terminator {
1091 kind: TerminatorKind::Call { fn_span, call_source, .. }, ..
1092 }) = &self.body[location.block].terminator
1093 {
1094 let Some((method_did, method_args)) =
1095 find_self_call(self.infcx.tcx, self.body, target_temp, location.block)
1096 else {
1097 return normal_ret;
1098 };
1099
1100 let kind = call_kind(
1101 self.infcx.tcx,
1102 self.infcx.typing_env(self.infcx.param_env),
1103 method_did,
1104 method_args,
1105 *fn_span,
1106 call_source.from_hir_call(),
1107 self.infcx.tcx.fn_arg_idents(method_did)[0],
1108 );
1109
1110 return FnSelfUse {
1111 var_span: stmt.source_info.span,
1112 fn_call_span: *fn_span,
1113 fn_span: self.infcx.tcx.def_span(method_did),
1114 kind,
1115 };
1116 }
1117
1118 normal_ret
1119 }
1120
1121 pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx> {
1126 use self::UseSpans::*;
1127 debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
1128
1129 let target = match self.body[location.block].statements.get(location.statement_index) {
1130 Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) => {
1131 if let Some(local) = place.as_local() {
1132 local
1133 } else {
1134 return OtherUse(use_span);
1135 }
1136 }
1137 _ => return OtherUse(use_span),
1138 };
1139
1140 if self.body.local_kind(target) != LocalKind::Temp {
1141 return OtherUse(use_span);
1143 }
1144
1145 let maybe_additional_statement =
1147 if let TerminatorKind::Drop { target: drop_target, .. } =
1148 self.body[location.block].terminator().kind
1149 {
1150 self.body[drop_target].statements.first()
1151 } else {
1152 None
1153 };
1154
1155 let statements =
1156 self.body[location.block].statements[location.statement_index + 1..].iter();
1157
1158 for stmt in statements.chain(maybe_additional_statement) {
1159 if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind {
1160 let (&def_id, is_coroutine) = match kind {
1161 box AggregateKind::Closure(def_id, _) => (def_id, false),
1162 box AggregateKind::Coroutine(def_id, _) => (def_id, true),
1163 _ => continue,
1164 };
1165 let def_id = def_id.expect_local();
1166
1167 debug!(
1168 "borrow_spans: def_id={:?} is_coroutine={:?} places={:?}",
1169 def_id, is_coroutine, places
1170 );
1171 if let Some((args_span, closure_kind, capture_kind_span, path_span)) =
1172 self.closure_span(def_id, Place::from(target).as_ref(), places)
1173 {
1174 return ClosureUse { closure_kind, args_span, capture_kind_span, path_span };
1175 } else {
1176 return OtherUse(use_span);
1177 }
1178 }
1179
1180 if use_span != stmt.source_info.span {
1181 break;
1182 }
1183 }
1184
1185 OtherUse(use_span)
1186 }
1187
1188 fn closure_span(
1192 &self,
1193 def_id: LocalDefId,
1194 target_place: PlaceRef<'tcx>,
1195 places: &IndexSlice<FieldIdx, Operand<'tcx>>,
1196 ) -> Option<(Span, hir::ClosureKind, Span, Span)> {
1197 debug!(
1198 "closure_span: def_id={:?} target_place={:?} places={:?}",
1199 def_id, target_place, places
1200 );
1201 let hir_id = self.infcx.tcx.local_def_id_to_hir_id(def_id);
1202 let expr = &self.infcx.tcx.hir_expect_expr(hir_id).kind;
1203 debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
1204 if let &hir::ExprKind::Closure(&hir::Closure { kind, fn_decl_span, .. }) = expr {
1205 for (captured_place, place) in
1206 self.infcx.tcx.closure_captures(def_id).iter().zip(places)
1207 {
1208 match place {
1209 Operand::Copy(place) | Operand::Move(place)
1210 if target_place == place.as_ref() =>
1211 {
1212 debug!("closure_span: found captured local {:?}", place);
1213 return Some((
1214 fn_decl_span,
1215 kind,
1216 captured_place.get_capture_kind_span(self.infcx.tcx),
1217 captured_place.get_path_span(self.infcx.tcx),
1218 ));
1219 }
1220 _ => {}
1221 }
1222 }
1223 }
1224 None
1225 }
1226
1227 pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx> {
1230 let span = self.body.source_info(borrow.reserve_location).span;
1231 self.borrow_spans(span, borrow.reserve_location)
1232 }
1233
1234 #[allow(rustc::diagnostic_outside_of_impl)]
1235 #[allow(rustc::untranslatable_diagnostic)] fn explain_captures(
1237 &mut self,
1238 err: &mut Diag<'infcx>,
1239 span: Span,
1240 move_span: Span,
1241 move_spans: UseSpans<'tcx>,
1242 moved_place: Place<'tcx>,
1243 msg_opt: CapturedMessageOpt,
1244 ) {
1245 let CapturedMessageOpt {
1246 is_partial_move: is_partial,
1247 is_loop_message,
1248 is_move_msg,
1249 is_loop_move,
1250 has_suggest_reborrow,
1251 maybe_reinitialized_locations_is_empty,
1252 } = msg_opt;
1253 if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
1254 let place_name = self
1255 .describe_place(moved_place.as_ref())
1256 .map(|n| format!("`{n}`"))
1257 .unwrap_or_else(|| "value".to_owned());
1258 match kind {
1259 CallKind::FnCall { fn_trait_id, self_ty }
1260 if self.infcx.tcx.is_lang_item(fn_trait_id, LangItem::FnOnce) =>
1261 {
1262 err.subdiagnostic(CaptureReasonLabel::Call {
1263 fn_call_span,
1264 place_name: &place_name,
1265 is_partial,
1266 is_loop_message,
1267 });
1268 if let ty::Param(param_ty) = *self_ty.kind()
1290 && let generics = self.infcx.tcx.generics_of(self.mir_def_id())
1291 && let param = generics.type_param(param_ty, self.infcx.tcx)
1292 && let Some(hir_generics) = self
1293 .infcx
1294 .tcx
1295 .typeck_root_def_id(self.mir_def_id().to_def_id())
1296 .as_local()
1297 .and_then(|def_id| self.infcx.tcx.hir_get_generics(def_id))
1298 && let spans = hir_generics
1299 .predicates
1300 .iter()
1301 .filter_map(|pred| match pred.kind {
1302 hir::WherePredicateKind::BoundPredicate(pred) => Some(pred),
1303 _ => None,
1304 })
1305 .filter(|pred| {
1306 if let Some((id, _)) = pred.bounded_ty.as_generic_param() {
1307 id == param.def_id
1308 } else {
1309 false
1310 }
1311 })
1312 .flat_map(|pred| pred.bounds)
1313 .filter_map(|bound| {
1314 if let Some(trait_ref) = bound.trait_ref()
1315 && let Some(trait_def_id) = trait_ref.trait_def_id()
1316 && trait_def_id == fn_trait_id
1317 {
1318 Some(bound.span())
1319 } else {
1320 None
1321 }
1322 })
1323 .collect::<Vec<Span>>()
1324 && !spans.is_empty()
1325 {
1326 let mut span: MultiSpan = spans.clone().into();
1327 err.arg("ty", param_ty.to_string());
1328 let msg = err.dcx.eagerly_translate_to_string(
1329 fluent::borrowck_moved_a_fn_once_in_call_def,
1330 err.args.iter(),
1331 );
1332 err.remove_arg("ty");
1333 for sp in spans {
1334 span.push_span_label(sp, msg.clone());
1335 }
1336 span.push_span_label(
1337 fn_call_span,
1338 fluent::borrowck_moved_a_fn_once_in_call,
1339 );
1340 err.span_note(span, fluent::borrowck_moved_a_fn_once_in_call_call);
1341 } else {
1342 err.subdiagnostic(CaptureReasonNote::FnOnceMoveInCall { var_span });
1343 }
1344 }
1345 CallKind::Operator { self_arg, trait_id, .. } => {
1346 let self_arg = self_arg.unwrap();
1347 err.subdiagnostic(CaptureReasonLabel::OperatorUse {
1348 fn_call_span,
1349 place_name: &place_name,
1350 is_partial,
1351 is_loop_message,
1352 });
1353 if self.fn_self_span_reported.insert(fn_span) {
1354 let lang = self.infcx.tcx.lang_items();
1355 err.subdiagnostic(
1356 if [lang.not_trait(), lang.deref_trait(), lang.neg_trait()]
1357 .contains(&Some(trait_id))
1358 {
1359 CaptureReasonNote::UnOpMoveByOperator { span: self_arg.span }
1360 } else {
1361 CaptureReasonNote::LhsMoveByOperator { span: self_arg.span }
1362 },
1363 );
1364 }
1365 }
1366 CallKind::Normal { self_arg, desugaring, method_did, method_args } => {
1367 let self_arg = self_arg.unwrap();
1368 let mut has_sugg = false;
1369 let tcx = self.infcx.tcx;
1370 if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
1373 self.explain_iterator_advancement_in_for_loop_if_applicable(
1374 err,
1375 span,
1376 &move_spans,
1377 );
1378
1379 let func = tcx.def_path_str(method_did);
1380 err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
1381 func,
1382 place_name: place_name.clone(),
1383 span: self_arg.span,
1384 });
1385 }
1386 let parent_did = tcx.parent(method_did);
1387 let parent_self_ty =
1388 matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. })
1389 .then_some(parent_did)
1390 .and_then(|did| match tcx.type_of(did).instantiate_identity().kind() {
1391 ty::Adt(def, ..) => Some(def.did()),
1392 _ => None,
1393 });
1394 let is_option_or_result = parent_self_ty.is_some_and(|def_id| {
1395 matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
1396 });
1397 if is_option_or_result && maybe_reinitialized_locations_is_empty {
1398 err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
1399 }
1400 if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
1401 let ty = moved_place.ty(self.body, tcx).ty;
1402 let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) {
1403 Some(def_id) => type_known_to_meet_bound_modulo_regions(
1404 self.infcx,
1405 self.infcx.param_env,
1406 Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty),
1407 def_id,
1408 ),
1409 _ => false,
1410 };
1411 if suggest {
1412 err.subdiagnostic(CaptureReasonSuggest::IterateSlice {
1413 ty,
1414 span: move_span.shrink_to_lo(),
1415 });
1416 }
1417
1418 err.subdiagnostic(CaptureReasonLabel::ImplicitCall {
1419 fn_call_span,
1420 place_name: &place_name,
1421 is_partial,
1422 is_loop_message,
1423 });
1424 if let ty::Ref(_, _, hir::Mutability::Mut) =
1428 moved_place.ty(self.body, self.infcx.tcx).ty.kind()
1429 {
1430 if !is_loop_move && !has_suggest_reborrow {
1435 self.suggest_reborrow(
1436 err,
1437 move_span.shrink_to_lo(),
1438 moved_place.as_ref(),
1439 );
1440 }
1441 }
1442 } else {
1443 if let Some((CallDesugaringKind::Await, _)) = desugaring {
1444 err.subdiagnostic(CaptureReasonLabel::Await {
1445 fn_call_span,
1446 place_name: &place_name,
1447 is_partial,
1448 is_loop_message,
1449 });
1450 } else {
1451 err.subdiagnostic(CaptureReasonLabel::MethodCall {
1452 fn_call_span,
1453 place_name: &place_name,
1454 is_partial,
1455 is_loop_message,
1456 });
1457 }
1458 let ty = moved_place.ty(self.body, tcx).ty;
1460
1461 if let ty::Adt(def, args) = ty.peel_refs().kind()
1462 && tcx.is_lang_item(def.did(), LangItem::Pin)
1463 && let ty::Ref(_, _, hir::Mutability::Mut) = args.type_at(0).kind()
1464 && let self_ty = self.infcx.instantiate_binder_with_fresh_vars(
1465 fn_call_span,
1466 BoundRegionConversionTime::FnCall,
1467 tcx.fn_sig(method_did).instantiate(tcx, method_args).input(0),
1468 )
1469 && self.infcx.can_eq(self.infcx.param_env, ty, self_ty)
1470 {
1471 err.subdiagnostic(CaptureReasonSuggest::FreshReborrow {
1472 span: move_span.shrink_to_hi(),
1473 });
1474 has_sugg = true;
1475 }
1476 if let Some(clone_trait) = tcx.lang_items().clone_trait() {
1477 let sugg = if moved_place
1478 .iter_projections()
1479 .any(|(_, elem)| matches!(elem, ProjectionElem::Deref))
1480 {
1481 let (start, end) = if let Some(expr) = self.find_expr(move_span)
1482 && let Some(_) = self.clone_on_reference(expr)
1483 && let hir::ExprKind::MethodCall(_, rcvr, _, _) = expr.kind
1484 {
1485 (move_span.shrink_to_lo(), move_span.with_lo(rcvr.span.hi()))
1486 } else {
1487 (move_span.shrink_to_lo(), move_span.shrink_to_hi())
1488 };
1489 vec![
1490 (start, format!("<{ty} as Clone>::clone(&")),
1495 (end, ")".to_string()),
1496 ]
1497 } else {
1498 vec![(move_span.shrink_to_hi(), ".clone()".to_string())]
1499 };
1500 if let Some(errors) = self.infcx.type_implements_trait_shallow(
1501 clone_trait,
1502 ty,
1503 self.infcx.param_env,
1504 ) && !has_sugg
1505 {
1506 let msg = match &errors[..] {
1507 [] => "you can `clone` the value and consume it, but this \
1508 might not be your desired behavior"
1509 .to_string(),
1510 [error] => {
1511 format!(
1512 "you could `clone` the value and consume it, if the \
1513 `{}` trait bound could be satisfied",
1514 error.obligation.predicate,
1515 )
1516 }
1517 _ => {
1518 format!(
1519 "you could `clone` the value and consume it, if the \
1520 following trait bounds could be satisfied: {}",
1521 listify(&errors, |e: &FulfillmentError<'tcx>| format!(
1522 "`{}`",
1523 e.obligation.predicate
1524 ))
1525 .unwrap(),
1526 )
1527 }
1528 };
1529 err.multipart_suggestion_verbose(
1530 msg,
1531 sugg,
1532 Applicability::MaybeIncorrect,
1533 );
1534 for error in errors {
1535 if let FulfillmentErrorCode::Select(
1536 SelectionError::Unimplemented,
1537 ) = error.code
1538 && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
1539 pred,
1540 )) = error.obligation.predicate.kind().skip_binder()
1541 {
1542 self.infcx.err_ctxt().suggest_derive(
1543 &error.obligation,
1544 err,
1545 error.obligation.predicate.kind().rebind(pred),
1546 );
1547 }
1548 }
1549 }
1550 }
1551 }
1552 }
1553 _ => {}
1555 }
1556 } else {
1557 if move_span != span || is_loop_message {
1558 err.subdiagnostic(CaptureReasonLabel::MovedHere {
1559 move_span,
1560 is_partial,
1561 is_move_msg,
1562 is_loop_message,
1563 });
1564 }
1565 if !is_loop_message {
1568 move_spans.var_subdiag(err, None, |kind, var_span| match kind {
1569 hir::ClosureKind::Coroutine(_) => {
1570 CaptureVarCause::PartialMoveUseInCoroutine { var_span, is_partial }
1571 }
1572 hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
1573 CaptureVarCause::PartialMoveUseInClosure { var_span, is_partial }
1574 }
1575 })
1576 }
1577 }
1578 }
1579
1580 pub(crate) fn local_excluded_from_unused_mut_lint(&self, index: Local) -> bool {
1582 self.local_name(index).is_none_or(|name| name.as_str().starts_with('_'))
1583 }
1584
1585 pub(crate) fn lint_macro_extended_temporary_scope(
1587 &self,
1588 future_drop: Location,
1589 borrow: &BorrowData<'tcx>,
1590 ) {
1591 let tcx = self.infcx.tcx;
1592 let temp_decl = &self.body.local_decls[borrow.borrowed_place.local];
1593 let temp_span = temp_decl.source_info.span;
1594 let lint_root = self.body.source_scopes[temp_decl.source_info.scope]
1595 .local_data
1596 .as_ref()
1597 .unwrap_crate_local()
1598 .lint_root;
1599
1600 let mut labels = MultiSpan::from_span(temp_span);
1601 labels.push_span_label(temp_span, "this expression creates a temporary value...");
1602 labels.push_span_label(
1603 self.body.source_info(future_drop).span,
1604 "...which will be dropped at the end of this block in Rust 1.92",
1605 );
1606
1607 tcx.node_span_lint(MACRO_EXTENDED_TEMPORARY_SCOPES, lint_root, labels, |diag| {
1608 diag.primary_message("temporary lifetime will be shortened in Rust 1.92");
1609 diag.note("consider using a `let` binding to create a longer lived value");
1610 diag.note("some temporaries were previously incorrectly lifetime-extended since Rust 1.89 in formatting macros, and since Rust 1.88 in `pin!()`");
1611 });
1612 }
1613}