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