1#![allow(rustc::diagnostic_outside_of_impl)]
4#![allow(rustc::untranslatable_diagnostic)]
5
6use std::assert_matches::assert_matches;
7
8use rustc_errors::{Applicability, Diag, EmissionGuarantee};
9use rustc_hir as hir;
10use rustc_hir::intravisit::Visitor;
11use rustc_infer::infer::NllRegionVariableOrigin;
12use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
13use rustc_middle::mir::{
14 Body, CallSource, CastKind, ConstraintCategory, FakeReadCause, Local, LocalInfo, Location,
15 Operand, Place, Rvalue, Statement, StatementKind, TerminatorKind,
16};
17use rustc_middle::ty::adjustment::PointerCoercion;
18use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt};
19use rustc_span::{DesugaringKind, Span, kw, sym};
20use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
21use rustc_trait_selection::error_reporting::traits::call_kind::CallKind;
22use tracing::{debug, instrument};
23
24use super::{RegionName, UseSpans, find_use};
25use crate::borrow_set::BorrowData;
26use crate::constraints::OutlivesConstraint;
27use crate::nll::ConstraintDescription;
28use crate::region_infer::{BlameConstraint, Cause};
29use crate::{MirBorrowckCtxt, WriteKind};
30
31#[derive(Debug)]
32pub(crate) enum BorrowExplanation<'tcx> {
33 UsedLater(Local, LaterUseKind, Span, Option<Span>),
34 UsedLaterInLoop(LaterUseKind, Span, Option<Span>),
35 UsedLaterWhenDropped {
36 drop_loc: Location,
37 dropped_local: Local,
38 should_note_order: bool,
39 },
40 MustBeValidFor {
41 category: ConstraintCategory<'tcx>,
42 from_closure: bool,
43 span: Span,
44 region_name: RegionName,
45 opt_place_desc: Option<String>,
46 path: Vec<OutlivesConstraint<'tcx>>,
47 },
48 Unexplained,
49}
50
51#[derive(Clone, Copy, Debug)]
52pub(crate) enum LaterUseKind {
53 TraitCapture,
54 ClosureCapture,
55 Call,
56 FakeLetRead,
57 Other,
58}
59
60impl<'tcx> BorrowExplanation<'tcx> {
61 pub(crate) fn is_explained(&self) -> bool {
62 !matches!(self, BorrowExplanation::Unexplained)
63 }
64 pub(crate) fn add_explanation_to_diagnostic<G: EmissionGuarantee>(
65 &self,
66 cx: &MirBorrowckCtxt<'_, '_, 'tcx>,
67 err: &mut Diag<'_, G>,
68 borrow_desc: &str,
69 borrow_span: Option<Span>,
70 multiple_borrow_span: Option<(Span, Span)>,
71 ) {
72 let tcx = cx.infcx.tcx;
73 let body = cx.body;
74 let local_names = &cx.local_names;
75
76 if let Some(span) = borrow_span {
77 let def_id = body.source.def_id();
78 if let Some(node) = tcx.hir_get_if_local(def_id)
79 && let Some(body_id) = node.body_id()
80 {
81 let body = tcx.hir_body(body_id);
82 let mut expr_finder = FindExprBySpan::new(span, tcx);
83 expr_finder.visit_expr(body.value);
84 if let Some(mut expr) = expr_finder.result {
85 while let hir::ExprKind::AddrOf(_, _, inner)
86 | hir::ExprKind::Unary(hir::UnOp::Deref, inner)
87 | hir::ExprKind::Field(inner, _)
88 | hir::ExprKind::MethodCall(_, inner, _, _)
89 | hir::ExprKind::Index(inner, _, _) = &expr.kind
90 {
91 expr = inner;
92 }
93 if let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind
94 && let [hir::PathSegment { ident, args: None, .. }] = p.segments
95 && let hir::def::Res::Local(hir_id) = p.res
96 && let hir::Node::Pat(pat) = tcx.hir_node(hir_id)
97 {
98 if !ident.span.in_external_macro(tcx.sess.source_map()) {
99 err.span_label(pat.span, format!("binding `{ident}` declared here"));
100 }
101 }
102 }
103 }
104 }
105 match *self {
106 BorrowExplanation::UsedLater(
107 dropped_local,
108 later_use_kind,
109 var_or_use_span,
110 path_span,
111 ) => {
112 let message = match later_use_kind {
113 LaterUseKind::TraitCapture => "captured here by trait object",
114 LaterUseKind::ClosureCapture => "captured here by closure",
115 LaterUseKind::Call => "used by call",
116 LaterUseKind::FakeLetRead => "stored here",
117 LaterUseKind::Other => "used here",
118 };
119 let local_decl = &body.local_decls[dropped_local];
120
121 if let &LocalInfo::IfThenRescopeTemp { if_then } = local_decl.local_info()
122 && let Some((_, hir::Node::Expr(expr))) = tcx.hir_parent_iter(if_then).next()
123 && let hir::ExprKind::If(cond, conseq, alt) = expr.kind
124 && let hir::ExprKind::Let(&hir::LetExpr {
125 span: _,
126 pat,
127 init,
128 ty: None,
130 recovered: _,
131 }) = cond.kind
132 && pat.span.can_be_used_for_suggestions()
133 && let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span)
134 {
135 suggest_rewrite_if_let(tcx, expr, &pat, init, conseq, alt, err);
136 } else if path_span.is_none_or(|path_span| path_span == var_or_use_span) {
137 if borrow_span.is_none_or(|sp| !sp.overlaps(var_or_use_span)) {
140 err.span_label(
141 var_or_use_span,
142 format!("{borrow_desc}borrow later {message}"),
143 );
144 }
145 } else {
146 let path_span = path_span.unwrap();
148 assert_matches!(later_use_kind, LaterUseKind::ClosureCapture);
150 if !borrow_span.is_some_and(|sp| sp.overlaps(var_or_use_span)) {
151 let path_label = "used here by closure";
152 let capture_kind_label = message;
153 err.span_label(
154 var_or_use_span,
155 format!("{borrow_desc}borrow later {capture_kind_label}"),
156 );
157 err.span_label(path_span, path_label);
158 }
159 }
160 }
161 BorrowExplanation::UsedLaterInLoop(later_use_kind, var_or_use_span, path_span) => {
162 let message = match later_use_kind {
163 LaterUseKind::TraitCapture => {
164 "borrow captured here by trait object, in later iteration of loop"
165 }
166 LaterUseKind::ClosureCapture => {
167 "borrow captured here by closure, in later iteration of loop"
168 }
169 LaterUseKind::Call => "borrow used by call, in later iteration of loop",
170 LaterUseKind::FakeLetRead => "borrow later stored here",
171 LaterUseKind::Other => "borrow used here, in later iteration of loop",
172 };
173 if path_span.map(|path_span| path_span == var_or_use_span).unwrap_or(true) {
176 err.span_label(var_or_use_span, format!("{borrow_desc}{message}"));
177 } else {
178 let path_span = path_span.unwrap();
180 assert_matches!(later_use_kind, LaterUseKind::ClosureCapture);
182 if borrow_span.map(|sp| !sp.overlaps(var_or_use_span)).unwrap_or(true) {
183 let path_label = "used here by closure";
184 let capture_kind_label = message;
185 err.span_label(
186 var_or_use_span,
187 format!("{borrow_desc}borrow later {capture_kind_label}"),
188 );
189 err.span_label(path_span, path_label);
190 }
191 }
192 }
193 BorrowExplanation::UsedLaterWhenDropped {
194 drop_loc,
195 dropped_local,
196 should_note_order,
197 } => {
198 let local_decl = &body.local_decls[dropped_local];
199 let mut ty = local_decl.ty;
200 if local_decl.source_info.span.desugaring_kind() == Some(DesugaringKind::ForLoop) {
201 if let ty::Adt(adt, args) = local_decl.ty.kind() {
202 if tcx.is_diagnostic_item(sym::Option, adt.did()) {
203 ty = args.type_at(0);
205 }
206 }
207 }
208 let (dtor_desc, type_desc) = match ty.kind() {
209 ty::Adt(adt, _args) if adt.has_dtor(tcx) && !adt.is_box() => {
212 ("`Drop` code", format!("type `{}`", tcx.def_path_str(adt.did())))
213 }
214
215 ty::Closure(..) => ("destructor", "closure".to_owned()),
218 ty::Coroutine(..) => ("destructor", "coroutine".to_owned()),
219
220 _ => ("destructor", format!("type `{}`", local_decl.ty)),
221 };
222
223 match local_names[dropped_local] {
224 Some(local_name) if !local_decl.from_compiler_desugaring() => {
225 let message = format!(
226 "{borrow_desc}borrow might be used here, when `{local_name}` is dropped \
227 and runs the {dtor_desc} for {type_desc}",
228 );
229 err.span_label(body.source_info(drop_loc).span, message);
230
231 if should_note_order {
232 err.note(
233 "values in a scope are dropped \
234 in the opposite order they are defined",
235 );
236 }
237 }
238 _ => {
239 err.span_label(
240 local_decl.source_info.span,
241 format!(
242 "a temporary with access to the {borrow_desc}borrow \
243 is created here ...",
244 ),
245 );
246 let message = format!(
247 "... and the {borrow_desc}borrow might be used here, \
248 when that temporary is dropped \
249 and runs the {dtor_desc} for {type_desc}",
250 );
251 err.span_label(body.source_info(drop_loc).span, message);
252
253 struct FindLetExpr<'hir> {
254 span: Span,
255 result: Option<(Span, &'hir hir::Pat<'hir>, &'hir hir::Expr<'hir>)>,
256 tcx: TyCtxt<'hir>,
257 }
258
259 impl<'hir> rustc_hir::intravisit::Visitor<'hir> for FindLetExpr<'hir> {
260 type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies;
261 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
262 self.tcx
263 }
264 fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
265 if let hir::ExprKind::If(cond, _conseq, _alt)
266 | hir::ExprKind::Loop(
267 &hir::Block {
268 expr:
269 Some(&hir::Expr {
270 kind: hir::ExprKind::If(cond, _conseq, _alt),
271 ..
272 }),
273 ..
274 },
275 _,
276 hir::LoopSource::While,
277 _,
278 ) = expr.kind
279 && let hir::ExprKind::Let(hir::LetExpr {
280 init: let_expr_init,
281 span: let_expr_span,
282 pat: let_expr_pat,
283 ..
284 }) = cond.kind
285 && let_expr_init.span.contains(self.span)
286 {
287 self.result =
288 Some((*let_expr_span, let_expr_pat, let_expr_init))
289 } else {
290 hir::intravisit::walk_expr(self, expr);
291 }
292 }
293 }
294
295 if let &LocalInfo::IfThenRescopeTemp { if_then } = local_decl.local_info()
296 && let hir::Node::Expr(expr) = tcx.hir_node(if_then)
297 && let hir::ExprKind::If(cond, conseq, alt) = expr.kind
298 && let hir::ExprKind::Let(&hir::LetExpr {
299 span: _,
300 pat,
301 init,
302 ty: None,
305 recovered: _,
306 }) = cond.kind
307 && pat.span.can_be_used_for_suggestions()
308 && let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span)
309 {
310 suggest_rewrite_if_let(tcx, expr, &pat, init, conseq, alt, err);
311 } else if let Some((old, new)) = multiple_borrow_span
312 && let def_id = body.source.def_id()
313 && let Some(node) = tcx.hir_get_if_local(def_id)
314 && let Some(body_id) = node.body_id()
315 && let hir_body = tcx.hir_body(body_id)
316 && let mut expr_finder = (FindLetExpr { span: old, result: None, tcx })
317 && let Some((let_expr_span, let_expr_pat, let_expr_init)) = {
318 expr_finder.visit_expr(hir_body.value);
319 expr_finder.result
320 }
321 && !let_expr_span.contains(new)
322 {
323 if let_expr_pat
327 .walk_short(|pat| !matches!(pat.kind, hir::PatKind::Binding(..)))
328 {
329 if let Ok(pat_snippet) =
330 tcx.sess.source_map().span_to_snippet(let_expr_pat.span)
331 && let Ok(init_snippet) =
332 tcx.sess.source_map().span_to_snippet(let_expr_init.span)
333 {
334 err.span_suggestion_verbose(
335 let_expr_span,
336 "consider using the `matches!` macro",
337 format!("matches!({init_snippet}, {pat_snippet})"),
338 Applicability::MaybeIncorrect,
339 );
340 } else {
341 err.note("consider using the `matches!` macro");
342 }
343 }
344 } else if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() {
345 let sp = info.span.find_oldest_ancestor_in_same_ctxt();
346 if info.tail_result_is_ignored {
347 if !multiple_borrow_span.is_some_and(|(old, new)| {
350 old.to(info.span.shrink_to_hi()).contains(new)
351 }) {
352 err.span_suggestion_verbose(
353 sp.shrink_to_hi(),
354 "consider adding semicolon after the expression so its \
355 temporaries are dropped sooner, before the local variables \
356 declared by the block are dropped",
357 ";",
358 Applicability::MaybeIncorrect,
359 );
360 }
361 } else {
362 err.note(
363 "the temporary is part of an expression at the end of a \
364 block;\nconsider forcing this temporary to be dropped sooner, \
365 before the block's local variables are dropped",
366 );
367 err.multipart_suggestion(
368 "for example, you could save the expression's value in a new \
369 local variable `x` and then make `x` be the expression at the \
370 end of the block",
371 vec![
372 (sp.shrink_to_lo(), "let x = ".to_string()),
373 (sp.shrink_to_hi(), "; x".to_string()),
374 ],
375 Applicability::MaybeIncorrect,
376 );
377 };
378 }
379 }
380 }
381 }
382 BorrowExplanation::MustBeValidFor {
383 category,
384 span,
385 ref region_name,
386 ref opt_place_desc,
387 from_closure: _,
388 ref path,
389 } => {
390 region_name.highlight_region_name(err);
391
392 if let Some(desc) = opt_place_desc {
393 err.span_label(
394 span,
395 format!(
396 "{}requires that `{desc}` is borrowed for `{region_name}`",
397 category.description(),
398 ),
399 );
400 } else {
401 err.span_label(
402 span,
403 format!(
404 "{}requires that {borrow_desc}borrow lasts for `{region_name}`",
405 category.description(),
406 ),
407 );
408 };
409
410 cx.add_placeholder_from_predicate_note(err, &path);
411 cx.add_sized_or_copy_bound_info(err, category, &path);
412
413 if let ConstraintCategory::Cast {
414 is_implicit_coercion: true,
415 unsize_to: Some(unsize_ty),
416 } = category
417 {
418 self.add_object_lifetime_default_note(tcx, err, unsize_ty);
419 }
420 self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name);
421 }
422 _ => {}
423 }
424 }
425
426 fn add_object_lifetime_default_note<G: EmissionGuarantee>(
427 &self,
428 tcx: TyCtxt<'tcx>,
429 err: &mut Diag<'_, G>,
430 unsize_ty: Ty<'tcx>,
431 ) {
432 if let ty::Adt(def, args) = unsize_ty.kind() {
433 let generics = tcx.generics_of(def.did());
436
437 let mut has_dyn = false;
438 let mut failed = false;
439
440 let elaborated_args =
441 std::iter::zip(*args, &generics.own_params).map(|(arg, param)| {
442 if let Some(ty::Dynamic(obj, _, ty::Dyn)) = arg.as_type().map(Ty::kind) {
443 let default = tcx.object_lifetime_default(param.def_id);
444
445 let re_static = tcx.lifetimes.re_static;
446
447 let implied_region = match default {
448 ObjectLifetimeDefault::Empty => re_static,
450 ObjectLifetimeDefault::Ambiguous => {
451 failed = true;
452 re_static
453 }
454 ObjectLifetimeDefault::Param(param_def_id) => {
455 let index = generics.param_def_id_to_index[¶m_def_id] as usize;
456 args.get(index).and_then(|arg| arg.as_region()).unwrap_or_else(
457 || {
458 failed = true;
459 re_static
460 },
461 )
462 }
463 ObjectLifetimeDefault::Static => re_static,
464 };
465
466 has_dyn = true;
467
468 Ty::new_dynamic(tcx, obj, implied_region, ty::Dyn).into()
469 } else {
470 arg
471 }
472 });
473 let elaborated_ty = Ty::new_adt(tcx, *def, tcx.mk_args_from_iter(elaborated_args));
474
475 if has_dyn && !failed {
476 err.note(format!(
477 "due to object lifetime defaults, `{unsize_ty}` actually means `{elaborated_ty}`"
478 ));
479 }
480 }
481 }
482
483 fn add_lifetime_bound_suggestion_to_diagnostic<G: EmissionGuarantee>(
484 &self,
485 err: &mut Diag<'_, G>,
486 category: &ConstraintCategory<'tcx>,
487 span: Span,
488 region_name: &RegionName,
489 ) {
490 if !span.is_desugaring(DesugaringKind::OpaqueTy) {
491 return;
492 }
493 if let ConstraintCategory::OpaqueType = category {
494 let suggestable_name =
495 if region_name.was_named() { region_name.name } else { kw::UnderscoreLifetime };
496
497 let msg = format!(
498 "you can add a bound to the {}to make it last less than `'static` and match `{region_name}`",
499 category.description(),
500 );
501
502 err.span_suggestion_verbose(
503 span.shrink_to_hi(),
504 msg,
505 format!(" + {suggestable_name}"),
506 Applicability::Unspecified,
507 );
508 }
509 }
510}
511
512fn suggest_rewrite_if_let<G: EmissionGuarantee>(
513 tcx: TyCtxt<'_>,
514 expr: &hir::Expr<'_>,
515 pat: &str,
516 init: &hir::Expr<'_>,
517 conseq: &hir::Expr<'_>,
518 alt: Option<&hir::Expr<'_>>,
519 err: &mut Diag<'_, G>,
520) {
521 let source_map = tcx.sess.source_map();
522 err.span_note(
523 source_map.end_point(conseq.span),
524 "lifetimes for temporaries generated in `if let`s have been shortened in Edition 2024 so that they are dropped here instead",
525 );
526 if expr.span.can_be_used_for_suggestions() && conseq.span.can_be_used_for_suggestions() {
527 let needs_block = if let Some(hir::Node::Expr(expr)) =
528 alt.and_then(|alt| tcx.hir_parent_iter(alt.hir_id).next()).map(|(_, node)| node)
529 {
530 matches!(expr.kind, hir::ExprKind::If(..))
531 } else {
532 false
533 };
534 let mut sugg = vec![
535 (
536 expr.span.shrink_to_lo().between(init.span),
537 if needs_block { "{ match ".into() } else { "match ".into() },
538 ),
539 (conseq.span.shrink_to_lo(), format!(" {{ {pat} => ")),
540 ];
541 let expr_end = expr.span.shrink_to_hi();
542 let mut expr_end_code;
543 if let Some(alt) = alt {
544 sugg.push((conseq.span.between(alt.span), " _ => ".into()));
545 expr_end_code = "}".to_string();
546 } else {
547 expr_end_code = " _ => {} }".into();
548 }
549 expr_end_code.push('}');
550 sugg.push((expr_end, expr_end_code));
551 err.multipart_suggestion(
552 "consider rewriting the `if` into `match` which preserves the extended lifetime",
553 sugg,
554 Applicability::MaybeIncorrect,
555 );
556 }
557}
558
559impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
560 fn free_region_constraint_info(
561 &self,
562 borrow_region: RegionVid,
563 outlived_region: RegionVid,
564 ) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>, Vec<OutlivesConstraint<'tcx>>)
565 {
566 let (blame_constraint, path) = self.regioncx.best_blame_constraint(
567 borrow_region,
568 NllRegionVariableOrigin::FreeRegion,
569 |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
570 );
571 let BlameConstraint { category, from_closure, cause, .. } = blame_constraint;
572
573 let outlived_fr_name = self.give_region_a_name(outlived_region);
574
575 (category, from_closure, cause.span, outlived_fr_name, path)
576 }
577
578 #[instrument(level = "debug", skip(self))]
592 pub(crate) fn explain_why_borrow_contains_point(
593 &self,
594 location: Location,
595 borrow: &BorrowData<'tcx>,
596 kind_place: Option<(WriteKind, Place<'tcx>)>,
597 ) -> BorrowExplanation<'tcx> {
598 let regioncx = &self.regioncx;
599 let body: &Body<'_> = self.body;
600 let tcx = self.infcx.tcx;
601
602 let borrow_region_vid = borrow.region;
603 debug!(?borrow_region_vid);
604
605 let mut region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location);
606 debug!(?region_sub);
607
608 let mut use_location = location;
609 let mut use_in_later_iteration_of_loop = false;
610
611 if region_sub == borrow_region_vid {
612 if let Some(loop_terminator_location) =
617 regioncx.find_loop_terminator_location(borrow.region, body)
618 {
619 region_sub = self
620 .regioncx
621 .find_sub_region_live_at(borrow_region_vid, loop_terminator_location);
622 debug!("explain_why_borrow_contains_point: region_sub in loop={:?}", region_sub);
623 use_location = loop_terminator_location;
624 use_in_later_iteration_of_loop = true;
625 }
626 }
627
628 let is_local_boring = |local| {
636 if let Some(polonius_diagnostics) = self.polonius_diagnostics {
637 polonius_diagnostics.boring_nll_locals.contains(&local)
638 } else {
639 assert!(!tcx.sess.opts.unstable_opts.polonius.is_next_enabled());
640
641 false
643 }
644 };
645 match find_use::find(body, regioncx, tcx, region_sub, use_location) {
646 Some(Cause::LiveVar(local, location)) if !is_local_boring(local) => {
647 let span = body.source_info(location).span;
648 let spans = self
649 .move_spans(Place::from(local).as_ref(), location)
650 .or_else(|| self.borrow_spans(span, location));
651
652 if use_in_later_iteration_of_loop {
653 let (later_use_kind, var_or_use_span, path_span) =
654 self.later_use_kind(borrow, spans, use_location);
655 BorrowExplanation::UsedLaterInLoop(later_use_kind, var_or_use_span, path_span)
656 } else {
657 let (later_use_kind, var_or_use_span, path_span) =
661 self.later_use_kind(borrow, spans, location);
662 BorrowExplanation::UsedLater(
663 borrow.borrowed_place.local,
664 later_use_kind,
665 var_or_use_span,
666 path_span,
667 )
668 }
669 }
670
671 Some(Cause::DropVar(local, location)) => {
672 let mut should_note_order = false;
673 if self.local_names[local].is_some()
674 && let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place
675 && let Some(borrowed_local) = place.as_local()
676 && self.local_names[borrowed_local].is_some()
677 && local != borrowed_local
678 {
679 should_note_order = true;
680 }
681
682 BorrowExplanation::UsedLaterWhenDropped {
683 drop_loc: location,
684 dropped_local: local,
685 should_note_order,
686 }
687 }
688
689 Some(Cause::LiveVar(..)) | None => {
690 if let Some(region) = self.to_error_region_vid(borrow_region_vid) {
693 let (category, from_closure, span, region_name, path) =
694 self.free_region_constraint_info(borrow_region_vid, region);
695 if let Some(region_name) = region_name {
696 let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref());
697 BorrowExplanation::MustBeValidFor {
698 category,
699 from_closure,
700 span,
701 region_name,
702 opt_place_desc,
703 path,
704 }
705 } else {
706 debug!("Could not generate a region name");
707 BorrowExplanation::Unexplained
708 }
709 } else {
710 debug!("Could not generate an error region vid");
711 BorrowExplanation::Unexplained
712 }
713 }
714 }
715 }
716
717 #[instrument(level = "debug", skip(self))]
722 fn later_use_kind(
723 &self,
724 borrow: &BorrowData<'tcx>,
725 use_spans: UseSpans<'tcx>,
726 location: Location,
727 ) -> (LaterUseKind, Span, Option<Span>) {
728 match use_spans {
729 UseSpans::ClosureUse { capture_kind_span, path_span, .. } => {
730 (LaterUseKind::ClosureCapture, capture_kind_span, Some(path_span))
732 }
733 UseSpans::FnSelfUse {
736 var_span: span,
737 kind: CallKind::Normal { desugaring: None, .. },
738 ..
739 } if span
740 .overlaps(self.body.local_decls[borrow.assigned_place.local].source_info.span) =>
741 {
742 if let TerminatorKind::Call { func, call_source: CallSource::Normal, .. } =
743 &self.body.basic_blocks[location.block].terminator().kind
744 {
745 let function_span = match func {
747 Operand::Constant(c) => c.span,
748 Operand::Copy(place) | Operand::Move(place) => {
749 if let Some(l) = place.as_local() {
750 let local_decl = &self.body.local_decls[l];
751 if self.local_names[l].is_none() {
752 local_decl.source_info.span
753 } else {
754 span
755 }
756 } else {
757 span
758 }
759 }
760 };
761 (LaterUseKind::Call, function_span, None)
762 } else {
763 (LaterUseKind::Other, span, None)
764 }
765 }
766 UseSpans::PatUse(span)
767 | UseSpans::OtherUse(span)
768 | UseSpans::FnSelfUse { var_span: span, .. } => {
769 let block = &self.body.basic_blocks[location.block];
770
771 let kind = if let Some(&Statement {
772 kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), place)),
773 ..
774 }) = block.statements.get(location.statement_index)
775 {
776 if let Some(l) = place.as_local()
777 && let local_decl = &self.body.local_decls[l]
778 && local_decl.ty.is_closure()
779 {
780 LaterUseKind::ClosureCapture
781 } else {
782 LaterUseKind::FakeLetRead
783 }
784 } else if self.was_captured_by_trait_object(borrow) {
785 LaterUseKind::TraitCapture
786 } else if location.statement_index == block.statements.len() {
787 if let TerminatorKind::Call { func, call_source: CallSource::Normal, .. } =
788 &block.terminator().kind
789 {
790 let function_span = match func {
792 Operand::Constant(c) => c.span,
793 Operand::Copy(place) | Operand::Move(place) => {
794 if let Some(l) = place.as_local() {
795 let local_decl = &self.body.local_decls[l];
796 if self.local_names[l].is_none() {
797 local_decl.source_info.span
798 } else {
799 span
800 }
801 } else {
802 span
803 }
804 }
805 };
806 return (LaterUseKind::Call, function_span, None);
807 } else {
808 LaterUseKind::Other
809 }
810 } else {
811 LaterUseKind::Other
812 };
813
814 (kind, span, None)
815 }
816 }
817 }
818
819 fn was_captured_by_trait_object(&self, borrow: &BorrowData<'tcx>) -> bool {
823 let location = borrow.reserve_location;
825 let block = &self.body[location.block];
826 let stmt = block.statements.get(location.statement_index);
827 debug!("was_captured_by_trait_object: location={:?} stmt={:?}", location, stmt);
828
829 let mut queue = vec![location];
833 let mut target =
834 if let Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) = stmt {
835 if let Some(local) = place.as_local() {
836 local
837 } else {
838 return false;
839 }
840 } else {
841 return false;
842 };
843
844 debug!("was_captured_by_trait: target={:?} queue={:?}", target, queue);
845 while let Some(current_location) = queue.pop() {
846 debug!("was_captured_by_trait: target={:?}", target);
847 let block = &self.body[current_location.block];
848 let is_terminator = current_location.statement_index == block.statements.len();
850 if !is_terminator {
851 let stmt = &block.statements[current_location.statement_index];
852 debug!("was_captured_by_trait_object: stmt={:?}", stmt);
853
854 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
856 let Some(into) = place.local_or_deref_local() else {
857 queue.push(current_location.successor_within_block());
859 continue;
860 };
861
862 match rvalue {
863 Rvalue::Use(operand) => match operand {
866 Operand::Copy(place) | Operand::Move(place) => {
867 if let Some(from) = place.as_local() {
868 if from == target {
869 target = into;
870 }
871 }
872 }
873 _ => {}
874 },
875 Rvalue::Cast(
878 CastKind::PointerCoercion(PointerCoercion::Unsize, _),
879 operand,
880 ty,
881 ) => {
882 match operand {
883 Operand::Copy(place) | Operand::Move(place) => {
884 if let Some(from) = place.as_local() {
885 if from == target {
886 debug!("was_captured_by_trait_object: ty={:?}", ty);
887 return match ty.kind() {
889 ty::Ref(_, ty, _) if ty.is_trait() => true,
891 _ if ty.boxed_ty().is_some_and(Ty::is_trait) => {
893 true
894 }
895
896 _ if ty.is_trait() => true,
898 _ => false,
900 };
901 }
902 }
903 return false;
904 }
905 _ => return false,
906 }
907 }
908 _ => {}
909 }
910 }
911
912 queue.push(current_location.successor_within_block());
914 } else {
915 let terminator = block.terminator();
917 debug!("was_captured_by_trait_object: terminator={:?}", terminator);
918
919 if let TerminatorKind::Call { destination, target: Some(block), args, .. } =
920 &terminator.kind
921 {
922 if let Some(dest) = destination.as_local() {
923 debug!(
924 "was_captured_by_trait_object: target={:?} dest={:?} args={:?}",
925 target, dest, args
926 );
927 let found_target = args.iter().any(|arg| {
929 if let Operand::Move(place) = arg.node {
930 if let Some(potential) = place.as_local() {
931 potential == target
932 } else {
933 false
934 }
935 } else {
936 false
937 }
938 });
939
940 if found_target {
942 target = dest;
943 queue.push(block.start_location());
944 }
945 }
946 }
947 }
948
949 debug!("was_captured_by_trait: queue={:?}", queue);
950 }
951
952 false
954 }
955}