1 //! Print diagnostics to explain why values are borrowed. 2 3 use rustc_errors::{Applicability, Diagnostic}; 4 use rustc_hir as hir; 5 use rustc_hir::intravisit::Visitor; 6 use rustc_index::IndexSlice; 7 use rustc_infer::infer::NllRegionVariableOrigin; 8 use rustc_middle::mir::{ 9 Body, CallSource, CastKind, ConstraintCategory, FakeReadCause, Local, LocalInfo, Location, 10 Operand, Place, Rvalue, Statement, StatementKind, TerminatorKind, 11 }; 12 use rustc_middle::ty::adjustment::PointerCoercion; 13 use rustc_middle::ty::{self, RegionVid, TyCtxt}; 14 use rustc_span::symbol::{kw, Symbol}; 15 use rustc_span::{sym, DesugaringKind, Span}; 16 use rustc_trait_selection::traits::error_reporting::FindExprBySpan; 17 18 use crate::region_infer::{BlameConstraint, ExtraConstraintInfo}; 19 use crate::{ 20 borrow_set::BorrowData, nll::ConstraintDescription, region_infer::Cause, MirBorrowckCtxt, 21 WriteKind, 22 }; 23 24 use super::{find_use, RegionName, UseSpans}; 25 26 #[derive(Debug)] 27 pub(crate) enum BorrowExplanation<'tcx> { 28 UsedLater(LaterUseKind, Span, Option<Span>), 29 UsedLaterInLoop(LaterUseKind, Span, Option<Span>), 30 UsedLaterWhenDropped { 31 drop_loc: Location, 32 dropped_local: Local, 33 should_note_order: bool, 34 }, 35 MustBeValidFor { 36 category: ConstraintCategory<'tcx>, 37 from_closure: bool, 38 span: Span, 39 region_name: RegionName, 40 opt_place_desc: Option<String>, 41 extra_info: Vec<ExtraConstraintInfo>, 42 }, 43 Unexplained, 44 } 45 46 #[derive(Clone, Copy, Debug)] 47 pub(crate) enum LaterUseKind { 48 TraitCapture, 49 ClosureCapture, 50 Call, 51 FakeLetRead, 52 Other, 53 } 54 55 impl<'tcx> BorrowExplanation<'tcx> { is_explained(&self) -> bool56 pub(crate) fn is_explained(&self) -> bool { 57 !matches!(self, BorrowExplanation::Unexplained) 58 } add_explanation_to_diagnostic( &self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>, local_names: &IndexSlice<Local, Option<Symbol>>, err: &mut Diagnostic, borrow_desc: &str, borrow_span: Option<Span>, multiple_borrow_span: Option<(Span, Span)>, )59 pub(crate) fn add_explanation_to_diagnostic( 60 &self, 61 tcx: TyCtxt<'tcx>, 62 body: &Body<'tcx>, 63 local_names: &IndexSlice<Local, Option<Symbol>>, 64 err: &mut Diagnostic, 65 borrow_desc: &str, 66 borrow_span: Option<Span>, 67 multiple_borrow_span: Option<(Span, Span)>, 68 ) { 69 if let Some(span) = borrow_span { 70 let def_id = body.source.def_id(); 71 if let Some(node) = tcx.hir().get_if_local(def_id) 72 && let Some(body_id) = node.body_id() 73 { 74 let body = tcx.hir().body(body_id); 75 let mut expr_finder = FindExprBySpan::new(span); 76 expr_finder.visit_expr(body.value); 77 if let Some(mut expr) = expr_finder.result { 78 while let hir::ExprKind::AddrOf(_, _, inner) 79 | hir::ExprKind::Unary(hir::UnOp::Deref, inner) 80 | hir::ExprKind::Field(inner, _) 81 | hir::ExprKind::MethodCall(_, inner, _, _) 82 | hir::ExprKind::Index(inner, _) = &expr.kind 83 { 84 expr = inner; 85 } 86 if let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind 87 && let [hir::PathSegment { ident, args: None, .. }] = p.segments 88 && let hir::def::Res::Local(hir_id) = p.res 89 && let Some(hir::Node::Pat(pat)) = tcx.hir().find(hir_id) 90 { 91 err.span_label( 92 pat.span, 93 format!("binding `{ident}` declared here"), 94 ); 95 } 96 } 97 } 98 } 99 match *self { 100 BorrowExplanation::UsedLater(later_use_kind, var_or_use_span, path_span) => { 101 let message = match later_use_kind { 102 LaterUseKind::TraitCapture => "captured here by trait object", 103 LaterUseKind::ClosureCapture => "captured here by closure", 104 LaterUseKind::Call => "used by call", 105 LaterUseKind::FakeLetRead => "stored here", 106 LaterUseKind::Other => "used here", 107 }; 108 // We can use `var_or_use_span` if either `path_span` is not present, or both spans are the same 109 if path_span.map(|path_span| path_span == var_or_use_span).unwrap_or(true) { 110 if borrow_span.map(|sp| !sp.overlaps(var_or_use_span)).unwrap_or(true) { 111 err.span_label( 112 var_or_use_span, 113 format!("{borrow_desc}borrow later {message}"), 114 ); 115 } 116 } else { 117 // path_span must be `Some` as otherwise the if condition is true 118 let path_span = path_span.unwrap(); 119 // path_span is only present in the case of closure capture 120 assert!(matches!(later_use_kind, LaterUseKind::ClosureCapture)); 121 if !borrow_span.is_some_and(|sp| sp.overlaps(var_or_use_span)) { 122 let path_label = "used here by closure"; 123 let capture_kind_label = message; 124 err.span_label( 125 var_or_use_span, 126 format!("{borrow_desc}borrow later {capture_kind_label}"), 127 ); 128 err.span_label(path_span, path_label); 129 } 130 } 131 } 132 BorrowExplanation::UsedLaterInLoop(later_use_kind, var_or_use_span, path_span) => { 133 let message = match later_use_kind { 134 LaterUseKind::TraitCapture => { 135 "borrow captured here by trait object, in later iteration of loop" 136 } 137 LaterUseKind::ClosureCapture => { 138 "borrow captured here by closure, in later iteration of loop" 139 } 140 LaterUseKind::Call => "borrow used by call, in later iteration of loop", 141 LaterUseKind::FakeLetRead => "borrow later stored here", 142 LaterUseKind::Other => "borrow used here, in later iteration of loop", 143 }; 144 // We can use `var_or_use_span` if either `path_span` is not present, or both spans are the same 145 if path_span.map(|path_span| path_span == var_or_use_span).unwrap_or(true) { 146 err.span_label(var_or_use_span, format!("{borrow_desc}{message}")); 147 } else { 148 // path_span must be `Some` as otherwise the if condition is true 149 let path_span = path_span.unwrap(); 150 // path_span is only present in the case of closure capture 151 assert!(matches!(later_use_kind, LaterUseKind::ClosureCapture)); 152 if borrow_span.map(|sp| !sp.overlaps(var_or_use_span)).unwrap_or(true) { 153 let path_label = "used here by closure"; 154 let capture_kind_label = message; 155 err.span_label( 156 var_or_use_span, 157 format!("{borrow_desc}borrow later {capture_kind_label}"), 158 ); 159 err.span_label(path_span, path_label); 160 } 161 } 162 } 163 BorrowExplanation::UsedLaterWhenDropped { 164 drop_loc, 165 dropped_local, 166 should_note_order, 167 } => { 168 let local_decl = &body.local_decls[dropped_local]; 169 let mut ty = local_decl.ty; 170 if local_decl.source_info.span.desugaring_kind() == Some(DesugaringKind::ForLoop) { 171 if let ty::Adt(adt, substs) = local_decl.ty.kind() { 172 if tcx.is_diagnostic_item(sym::Option, adt.did()) { 173 // in for loop desugaring, only look at the `Some(..)` inner type 174 ty = substs.type_at(0); 175 } 176 } 177 } 178 let (dtor_desc, type_desc) = match ty.kind() { 179 // If type is an ADT that implements Drop, then 180 // simplify output by reporting just the ADT name. 181 ty::Adt(adt, _substs) if adt.has_dtor(tcx) && !adt.is_box() => { 182 ("`Drop` code", format!("type `{}`", tcx.def_path_str(adt.did()))) 183 } 184 185 // Otherwise, just report the whole type (and use 186 // the intentionally fuzzy phrase "destructor") 187 ty::Closure(..) => ("destructor", "closure".to_owned()), 188 ty::Generator(..) => ("destructor", "generator".to_owned()), 189 190 _ => ("destructor", format!("type `{}`", local_decl.ty)), 191 }; 192 193 match local_names[dropped_local] { 194 Some(local_name) if !local_decl.from_compiler_desugaring() => { 195 let message = format!( 196 "{borrow_desc}borrow might be used here, when `{local_name}` is dropped \ 197 and runs the {dtor_desc} for {type_desc}", 198 ); 199 err.span_label(body.source_info(drop_loc).span, message); 200 201 if should_note_order { 202 err.note( 203 "values in a scope are dropped \ 204 in the opposite order they are defined", 205 ); 206 } 207 } 208 _ => { 209 err.span_label( 210 local_decl.source_info.span, 211 format!( 212 "a temporary with access to the {borrow_desc}borrow \ 213 is created here ...", 214 ), 215 ); 216 let message = format!( 217 "... and the {borrow_desc}borrow might be used here, \ 218 when that temporary is dropped \ 219 and runs the {dtor_desc} for {type_desc}", 220 ); 221 err.span_label(body.source_info(drop_loc).span, message); 222 223 if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() { 224 if info.tail_result_is_ignored { 225 // #85581: If the first mutable borrow's scope contains 226 // the second borrow, this suggestion isn't helpful. 227 if !multiple_borrow_span.is_some_and(|(old, new)| { 228 old.to(info.span.shrink_to_hi()).contains(new) 229 }) { 230 err.span_suggestion_verbose( 231 info.span.shrink_to_hi(), 232 "consider adding semicolon after the expression so its \ 233 temporaries are dropped sooner, before the local variables \ 234 declared by the block are dropped", 235 ";", 236 Applicability::MaybeIncorrect, 237 ); 238 } 239 } else { 240 err.note( 241 "the temporary is part of an expression at the end of a \ 242 block;\nconsider forcing this temporary to be dropped sooner, \ 243 before the block's local variables are dropped", 244 ); 245 err.multipart_suggestion( 246 "for example, you could save the expression's value in a new \ 247 local variable `x` and then make `x` be the expression at the \ 248 end of the block", 249 vec![ 250 (info.span.shrink_to_lo(), "let x = ".to_string()), 251 (info.span.shrink_to_hi(), "; x".to_string()), 252 ], 253 Applicability::MaybeIncorrect, 254 ); 255 }; 256 } 257 } 258 } 259 } 260 BorrowExplanation::MustBeValidFor { 261 category, 262 span, 263 ref region_name, 264 ref opt_place_desc, 265 from_closure: _, 266 ref extra_info, 267 } => { 268 region_name.highlight_region_name(err); 269 270 if let Some(desc) = opt_place_desc { 271 err.span_label( 272 span, 273 format!( 274 "{}requires that `{desc}` is borrowed for `{region_name}`", 275 category.description(), 276 ), 277 ); 278 } else { 279 err.span_label( 280 span, 281 format!( 282 "{}requires that {borrow_desc}borrow lasts for `{region_name}`", 283 category.description(), 284 ), 285 ); 286 }; 287 288 for extra in extra_info { 289 match extra { 290 ExtraConstraintInfo::PlaceholderFromPredicate(span) => { 291 err.span_note(*span, "due to current limitations in the borrow checker, this implies a `'static` lifetime"); 292 } 293 } 294 } 295 296 self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name); 297 } 298 _ => {} 299 } 300 } 301 add_lifetime_bound_suggestion_to_diagnostic( &self, err: &mut Diagnostic, category: &ConstraintCategory<'tcx>, span: Span, region_name: &RegionName, )302 fn add_lifetime_bound_suggestion_to_diagnostic( 303 &self, 304 err: &mut Diagnostic, 305 category: &ConstraintCategory<'tcx>, 306 span: Span, 307 region_name: &RegionName, 308 ) { 309 if !span.is_desugaring(DesugaringKind::OpaqueTy) { 310 return; 311 } 312 if let ConstraintCategory::OpaqueType = category { 313 let suggestable_name = 314 if region_name.was_named() { region_name.name } else { kw::UnderscoreLifetime }; 315 316 let msg = format!( 317 "you can add a bound to the {}to make it last less than `'static` and match `{region_name}`", 318 category.description(), 319 ); 320 321 err.span_suggestion_verbose( 322 span.shrink_to_hi(), 323 msg, 324 format!(" + {suggestable_name}"), 325 Applicability::Unspecified, 326 ); 327 } 328 } 329 } 330 331 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { free_region_constraint_info( &self, borrow_region: RegionVid, outlived_region: RegionVid, ) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>, Vec<ExtraConstraintInfo>)332 fn free_region_constraint_info( 333 &self, 334 borrow_region: RegionVid, 335 outlived_region: RegionVid, 336 ) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>, Vec<ExtraConstraintInfo>) { 337 let (blame_constraint, extra_info) = self.regioncx.best_blame_constraint( 338 borrow_region, 339 NllRegionVariableOrigin::FreeRegion, 340 |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region), 341 ); 342 let BlameConstraint { category, from_closure, cause, .. } = blame_constraint; 343 344 let outlived_fr_name = self.give_region_a_name(outlived_region); 345 346 (category, from_closure, cause.span, outlived_fr_name, extra_info) 347 } 348 349 /// Returns structured explanation for *why* the borrow contains the 350 /// point from `location`. This is key for the "3-point errors" 351 /// [described in the NLL RFC][d]. 352 /// 353 /// # Parameters 354 /// 355 /// - `borrow`: the borrow in question 356 /// - `location`: where the borrow occurs 357 /// - `kind_place`: if Some, this describes the statement that triggered the error. 358 /// - first half is the kind of write, if any, being performed 359 /// - second half is the place being accessed 360 /// 361 /// [d]: https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points 362 #[instrument(level = "debug", skip(self))] explain_why_borrow_contains_point( &self, location: Location, borrow: &BorrowData<'tcx>, kind_place: Option<(WriteKind, Place<'tcx>)>, ) -> BorrowExplanation<'tcx>363 pub(crate) fn explain_why_borrow_contains_point( 364 &self, 365 location: Location, 366 borrow: &BorrowData<'tcx>, 367 kind_place: Option<(WriteKind, Place<'tcx>)>, 368 ) -> BorrowExplanation<'tcx> { 369 let regioncx = &self.regioncx; 370 let body: &Body<'_> = &self.body; 371 let tcx = self.infcx.tcx; 372 373 let borrow_region_vid = borrow.region; 374 debug!(?borrow_region_vid); 375 376 let mut region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location); 377 debug!(?region_sub); 378 379 let mut use_location = location; 380 let mut use_in_later_iteration_of_loop = false; 381 382 if region_sub == borrow_region_vid { 383 // When `region_sub` is the same as `borrow_region_vid` (the location where the borrow is 384 // issued is the same location that invalidates the reference), this is likely a loop iteration 385 // - in this case, try using the loop terminator location in `find_sub_region_live_at`. 386 if let Some(loop_terminator_location) = 387 regioncx.find_loop_terminator_location(borrow.region, body) 388 { 389 region_sub = self 390 .regioncx 391 .find_sub_region_live_at(borrow_region_vid, loop_terminator_location); 392 debug!("explain_why_borrow_contains_point: region_sub in loop={:?}", region_sub); 393 use_location = loop_terminator_location; 394 use_in_later_iteration_of_loop = true; 395 } 396 } 397 398 match find_use::find(body, regioncx, tcx, region_sub, use_location) { 399 Some(Cause::LiveVar(local, location)) => { 400 let span = body.source_info(location).span; 401 let spans = self 402 .move_spans(Place::from(local).as_ref(), location) 403 .or_else(|| self.borrow_spans(span, location)); 404 405 if use_in_later_iteration_of_loop { 406 let later_use = self.later_use_kind(borrow, spans, use_location); 407 BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1, later_use.2) 408 } else { 409 // Check if the location represents a `FakeRead`, and adapt the error 410 // message to the `FakeReadCause` it is from: in particular, 411 // the ones inserted in optimized `let var = <expr>` patterns. 412 let later_use = self.later_use_kind(borrow, spans, location); 413 BorrowExplanation::UsedLater(later_use.0, later_use.1, later_use.2) 414 } 415 } 416 417 Some(Cause::DropVar(local, location)) => { 418 let mut should_note_order = false; 419 if self.local_names[local].is_some() 420 && let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place 421 && let Some(borrowed_local) = place.as_local() 422 && self.local_names[borrowed_local].is_some() && local != borrowed_local 423 { 424 should_note_order = true; 425 } 426 427 BorrowExplanation::UsedLaterWhenDropped { 428 drop_loc: location, 429 dropped_local: local, 430 should_note_order, 431 } 432 } 433 434 None => { 435 if let Some(region) = self.to_error_region_vid(borrow_region_vid) { 436 let (category, from_closure, span, region_name, extra_info) = 437 self.free_region_constraint_info(borrow_region_vid, region); 438 if let Some(region_name) = region_name { 439 let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref()); 440 BorrowExplanation::MustBeValidFor { 441 category, 442 from_closure, 443 span, 444 region_name, 445 opt_place_desc, 446 extra_info, 447 } 448 } else { 449 debug!("Could not generate a region name"); 450 BorrowExplanation::Unexplained 451 } 452 } else { 453 debug!("Could not generate an error region vid"); 454 BorrowExplanation::Unexplained 455 } 456 } 457 } 458 } 459 460 /// Determine how the borrow was later used. 461 /// First span returned points to the location of the conflicting use 462 /// Second span if `Some` is returned in the case of closures and points 463 /// to the use of the path 464 #[instrument(level = "debug", skip(self))] later_use_kind( &self, borrow: &BorrowData<'tcx>, use_spans: UseSpans<'tcx>, location: Location, ) -> (LaterUseKind, Span, Option<Span>)465 fn later_use_kind( 466 &self, 467 borrow: &BorrowData<'tcx>, 468 use_spans: UseSpans<'tcx>, 469 location: Location, 470 ) -> (LaterUseKind, Span, Option<Span>) { 471 match use_spans { 472 UseSpans::ClosureUse { capture_kind_span, path_span, .. } => { 473 // Used in a closure. 474 (LaterUseKind::ClosureCapture, capture_kind_span, Some(path_span)) 475 } 476 UseSpans::PatUse(span) 477 | UseSpans::OtherUse(span) 478 | UseSpans::FnSelfUse { var_span: span, .. } => { 479 let block = &self.body.basic_blocks[location.block]; 480 481 let kind = if let Some(&Statement { 482 kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), place)), 483 .. 484 }) = block.statements.get(location.statement_index) 485 { 486 if let Some(l) = place.as_local() 487 && let local_decl = &self.body.local_decls[l] 488 && local_decl.ty.is_closure() 489 { 490 LaterUseKind::ClosureCapture 491 } else { 492 LaterUseKind::FakeLetRead 493 } 494 } else if self.was_captured_by_trait_object(borrow) { 495 LaterUseKind::TraitCapture 496 } else if location.statement_index == block.statements.len() { 497 if let TerminatorKind::Call { func, call_source: CallSource::Normal, .. } = 498 &block.terminator().kind 499 { 500 // Just point to the function, to reduce the chance of overlapping spans. 501 let function_span = match func { 502 Operand::Constant(c) => c.span, 503 Operand::Copy(place) | Operand::Move(place) => { 504 if let Some(l) = place.as_local() { 505 let local_decl = &self.body.local_decls[l]; 506 if self.local_names[l].is_none() { 507 local_decl.source_info.span 508 } else { 509 span 510 } 511 } else { 512 span 513 } 514 } 515 }; 516 return (LaterUseKind::Call, function_span, None); 517 } else { 518 LaterUseKind::Other 519 } 520 } else { 521 LaterUseKind::Other 522 }; 523 524 (kind, span, None) 525 } 526 } 527 } 528 529 /// Checks if a borrowed value was captured by a trait object. We do this by 530 /// looking forward in the MIR from the reserve location and checking if we see 531 /// an unsized cast to a trait object on our data. was_captured_by_trait_object(&self, borrow: &BorrowData<'tcx>) -> bool532 fn was_captured_by_trait_object(&self, borrow: &BorrowData<'tcx>) -> bool { 533 // Start at the reserve location, find the place that we want to see cast to a trait object. 534 let location = borrow.reserve_location; 535 let block = &self.body[location.block]; 536 let stmt = block.statements.get(location.statement_index); 537 debug!("was_captured_by_trait_object: location={:?} stmt={:?}", location, stmt); 538 539 // We make a `queue` vector that has the locations we want to visit. As of writing, this 540 // will only ever have one item at any given time, but by using a vector, we can pop from 541 // it which simplifies the termination logic. 542 let mut queue = vec![location]; 543 let mut target = 544 if let Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) = stmt { 545 if let Some(local) = place.as_local() { 546 local 547 } else { 548 return false; 549 } 550 } else { 551 return false; 552 }; 553 554 debug!("was_captured_by_trait: target={:?} queue={:?}", target, queue); 555 while let Some(current_location) = queue.pop() { 556 debug!("was_captured_by_trait: target={:?}", target); 557 let block = &self.body[current_location.block]; 558 // We need to check the current location to find out if it is a terminator. 559 let is_terminator = current_location.statement_index == block.statements.len(); 560 if !is_terminator { 561 let stmt = &block.statements[current_location.statement_index]; 562 debug!("was_captured_by_trait_object: stmt={:?}", stmt); 563 564 // The only kind of statement that we care about is assignments... 565 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind { 566 let Some(into) = place.local_or_deref_local() else { 567 // Continue at the next location. 568 queue.push(current_location.successor_within_block()); 569 continue; 570 }; 571 572 match rvalue { 573 // If we see a use, we should check whether it is our data, and if so 574 // update the place that we're looking for to that new place. 575 Rvalue::Use(operand) => match operand { 576 Operand::Copy(place) | Operand::Move(place) => { 577 if let Some(from) = place.as_local() { 578 if from == target { 579 target = into; 580 } 581 } 582 } 583 _ => {} 584 }, 585 // If we see an unsized cast, then if it is our data we should check 586 // whether it is being cast to a trait object. 587 Rvalue::Cast( 588 CastKind::PointerCoercion(PointerCoercion::Unsize), 589 operand, 590 ty, 591 ) => { 592 match operand { 593 Operand::Copy(place) | Operand::Move(place) => { 594 if let Some(from) = place.as_local() { 595 if from == target { 596 debug!("was_captured_by_trait_object: ty={:?}", ty); 597 // Check the type for a trait object. 598 return match ty.kind() { 599 // `&dyn Trait` 600 ty::Ref(_, ty, _) if ty.is_trait() => true, 601 // `Box<dyn Trait>` 602 _ if ty.is_box() && ty.boxed_ty().is_trait() => { 603 true 604 } 605 // `dyn Trait` 606 _ if ty.is_trait() => true, 607 // Anything else. 608 _ => false, 609 }; 610 } 611 } 612 return false; 613 } 614 _ => return false, 615 } 616 } 617 _ => {} 618 } 619 } 620 621 // Continue at the next location. 622 queue.push(current_location.successor_within_block()); 623 } else { 624 // The only thing we need to do for terminators is progress to the next block. 625 let terminator = block.terminator(); 626 debug!("was_captured_by_trait_object: terminator={:?}", terminator); 627 628 if let TerminatorKind::Call { destination, target: Some(block), args, .. } = 629 &terminator.kind 630 { 631 if let Some(dest) = destination.as_local() { 632 debug!( 633 "was_captured_by_trait_object: target={:?} dest={:?} args={:?}", 634 target, dest, args 635 ); 636 // Check if one of the arguments to this function is the target place. 637 let found_target = args.iter().any(|arg| { 638 if let Operand::Move(place) = arg { 639 if let Some(potential) = place.as_local() { 640 potential == target 641 } else { 642 false 643 } 644 } else { 645 false 646 } 647 }); 648 649 // If it is, follow this to the next block and update the target. 650 if found_target { 651 target = dest; 652 queue.push(block.start_location()); 653 } 654 } 655 } 656 } 657 658 debug!("was_captured_by_trait: queue={:?}", queue); 659 } 660 661 // We didn't find anything and ran out of locations to check. 662 false 663 } 664 } 665