]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
change fn_sig query to use EarlyBinder; remove bound_fn_sig query; add EarlyBinder...
[rust.git] / compiler / rustc_borrowck / src / diagnostics / explain_borrow.rs
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::vec::IndexVec;
7 use rustc_infer::infer::NllRegionVariableOrigin;
8 use rustc_middle::mir::{
9     Body, CastKind, ConstraintCategory, FakeReadCause, Local, Location, Operand, Place, Rvalue,
10     Statement, StatementKind, TerminatorKind,
11 };
12 use rustc_middle::ty::adjustment::PointerCast;
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> {
56     pub(crate) fn is_explained(&self) -> bool {
57         !matches!(self, BorrowExplanation::Unexplained)
58     }
59     pub(crate) fn add_explanation_to_diagnostic(
60         &self,
61         tcx: TyCtxt<'tcx>,
62         body: &Body<'tcx>,
63         local_names: &IndexVec<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.map_or(false, |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 Some(info) = &local_decl.is_block_tail {
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
228                                     .map(|(old, new)| {
229                                         old.to(info.span.shrink_to_hi()).contains(new)
230                                     })
231                                     .unwrap_or(false)
232                                 {
233                                     err.span_suggestion_verbose(
234                                         info.span.shrink_to_hi(),
235                                         "consider adding semicolon after the expression so its \
236                                         temporaries are dropped sooner, before the local variables \
237                                         declared by the block are dropped",
238                                         ";",
239                                         Applicability::MaybeIncorrect,
240                                     );
241                                 }
242                             } else {
243                                 err.note(
244                                     "the temporary is part of an expression at the end of a \
245                                      block;\nconsider forcing this temporary to be dropped sooner, \
246                                      before the block's local variables are dropped",
247                                 );
248                                 err.multipart_suggestion(
249                                     "for example, you could save the expression's value in a new \
250                                      local variable `x` and then make `x` be the expression at the \
251                                      end of the block",
252                                     vec![
253                                         (info.span.shrink_to_lo(), "let x = ".to_string()),
254                                         (info.span.shrink_to_hi(), "; x".to_string()),
255                                     ],
256                                     Applicability::MaybeIncorrect,
257                                 );
258                             };
259                         }
260                     }
261                 }
262             }
263             BorrowExplanation::MustBeValidFor {
264                 category,
265                 span,
266                 ref region_name,
267                 ref opt_place_desc,
268                 from_closure: _,
269                 ref extra_info,
270             } => {
271                 region_name.highlight_region_name(err);
272
273                 if let Some(desc) = opt_place_desc {
274                     err.span_label(
275                         span,
276                         format!(
277                             "{}requires that `{desc}` is borrowed for `{region_name}`",
278                             category.description(),
279                         ),
280                     );
281                 } else {
282                     err.span_label(
283                         span,
284                         format!(
285                             "{}requires that {borrow_desc}borrow lasts for `{region_name}`",
286                             category.description(),
287                         ),
288                     );
289                 };
290
291                 for extra in extra_info {
292                     match extra {
293                         ExtraConstraintInfo::PlaceholderFromPredicate(span) => {
294                             err.span_note(*span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
295                         }
296                     }
297                 }
298
299                 self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name);
300             }
301             _ => {}
302         }
303     }
304
305     fn add_lifetime_bound_suggestion_to_diagnostic(
306         &self,
307         err: &mut Diagnostic,
308         category: &ConstraintCategory<'tcx>,
309         span: Span,
310         region_name: &RegionName,
311     ) {
312         if !span.is_desugaring(DesugaringKind::OpaqueTy) {
313             return;
314         }
315         if let ConstraintCategory::OpaqueType = category {
316             let suggestable_name =
317                 if region_name.was_named() { region_name.name } else { kw::UnderscoreLifetime };
318
319             let msg = format!(
320                 "you can add a bound to the {}to make it last less than `'static` and match `{region_name}`",
321                 category.description(),
322             );
323
324             err.span_suggestion_verbose(
325                 span.shrink_to_hi(),
326                 &msg,
327                 format!(" + {suggestable_name}"),
328                 Applicability::Unspecified,
329             );
330         }
331     }
332 }
333
334 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
335     fn free_region_constraint_info(
336         &self,
337         borrow_region: RegionVid,
338         outlived_region: RegionVid,
339     ) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>, Vec<ExtraConstraintInfo>) {
340         let (blame_constraint, extra_info) = self.regioncx.best_blame_constraint(
341             borrow_region,
342             NllRegionVariableOrigin::FreeRegion,
343             |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
344         );
345         let BlameConstraint { category, from_closure, cause, .. } = blame_constraint;
346
347         let outlived_fr_name = self.give_region_a_name(outlived_region);
348
349         (category, from_closure, cause.span, outlived_fr_name, extra_info)
350     }
351
352     /// Returns structured explanation for *why* the borrow contains the
353     /// point from `location`. This is key for the "3-point errors"
354     /// [described in the NLL RFC][d].
355     ///
356     /// # Parameters
357     ///
358     /// - `borrow`: the borrow in question
359     /// - `location`: where the borrow occurs
360     /// - `kind_place`: if Some, this describes the statement that triggered the error.
361     ///   - first half is the kind of write, if any, being performed
362     ///   - second half is the place being accessed
363     ///
364     /// [d]: https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points
365     #[instrument(level = "debug", skip(self))]
366     pub(crate) fn explain_why_borrow_contains_point(
367         &self,
368         location: Location,
369         borrow: &BorrowData<'tcx>,
370         kind_place: Option<(WriteKind, Place<'tcx>)>,
371     ) -> BorrowExplanation<'tcx> {
372         let regioncx = &self.regioncx;
373         let body: &Body<'_> = &self.body;
374         let tcx = self.infcx.tcx;
375
376         let borrow_region_vid = borrow.region;
377         debug!(?borrow_region_vid);
378
379         let mut region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location);
380         debug!(?region_sub);
381
382         let mut use_location = location;
383         let mut use_in_later_iteration_of_loop = false;
384
385         if region_sub == borrow_region_vid {
386             // When `region_sub` is the same as `borrow_region_vid` (the location where the borrow is
387             // issued is the same location that invalidates the reference), this is likely a loop iteration
388             // - in this case, try using the loop terminator location in `find_sub_region_live_at`.
389             if let Some(loop_terminator_location) =
390                 regioncx.find_loop_terminator_location(borrow.region, body)
391             {
392                 region_sub = self
393                     .regioncx
394                     .find_sub_region_live_at(borrow_region_vid, loop_terminator_location);
395                 debug!("explain_why_borrow_contains_point: region_sub in loop={:?}", region_sub);
396                 use_location = loop_terminator_location;
397                 use_in_later_iteration_of_loop = true;
398             }
399         }
400
401         match find_use::find(body, regioncx, tcx, region_sub, use_location) {
402             Some(Cause::LiveVar(local, location)) => {
403                 let span = body.source_info(location).span;
404                 let spans = self
405                     .move_spans(Place::from(local).as_ref(), location)
406                     .or_else(|| self.borrow_spans(span, location));
407
408                 if use_in_later_iteration_of_loop {
409                     let later_use = self.later_use_kind(borrow, spans, use_location);
410                     BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1, later_use.2)
411                 } else {
412                     // Check if the location represents a `FakeRead`, and adapt the error
413                     // message to the `FakeReadCause` it is from: in particular,
414                     // the ones inserted in optimized `let var = <expr>` patterns.
415                     let later_use = self.later_use_kind(borrow, spans, location);
416                     BorrowExplanation::UsedLater(later_use.0, later_use.1, later_use.2)
417                 }
418             }
419
420             Some(Cause::DropVar(local, location)) => {
421                 let mut should_note_order = false;
422                 if self.local_names[local].is_some()
423                     && let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place
424                     && let Some(borrowed_local) = place.as_local()
425                     && self.local_names[borrowed_local].is_some() && local != borrowed_local
426                 {
427                     should_note_order = true;
428                 }
429
430                 BorrowExplanation::UsedLaterWhenDropped {
431                     drop_loc: location,
432                     dropped_local: local,
433                     should_note_order,
434                 }
435             }
436
437             None => {
438                 if let Some(region) = self.to_error_region_vid(borrow_region_vid) {
439                     let (category, from_closure, span, region_name, extra_info) =
440                         self.free_region_constraint_info(borrow_region_vid, region);
441                     if let Some(region_name) = region_name {
442                         let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref());
443                         BorrowExplanation::MustBeValidFor {
444                             category,
445                             from_closure,
446                             span,
447                             region_name,
448                             opt_place_desc,
449                             extra_info,
450                         }
451                     } else {
452                         debug!("Could not generate a region name");
453                         BorrowExplanation::Unexplained
454                     }
455                 } else {
456                     debug!("Could not generate an error region vid");
457                     BorrowExplanation::Unexplained
458                 }
459             }
460         }
461     }
462
463     /// Determine how the borrow was later used.
464     /// First span returned points to the location of the conflicting use
465     /// Second span if `Some` is returned in the case of closures and points
466     /// to the use of the path
467     #[instrument(level = "debug", skip(self))]
468     fn later_use_kind(
469         &self,
470         borrow: &BorrowData<'tcx>,
471         use_spans: UseSpans<'tcx>,
472         location: Location,
473     ) -> (LaterUseKind, Span, Option<Span>) {
474         match use_spans {
475             UseSpans::ClosureUse { capture_kind_span, path_span, .. } => {
476                 // Used in a closure.
477                 (LaterUseKind::ClosureCapture, capture_kind_span, Some(path_span))
478             }
479             UseSpans::PatUse(span)
480             | UseSpans::OtherUse(span)
481             | UseSpans::FnSelfUse { var_span: span, .. } => {
482                 let block = &self.body.basic_blocks[location.block];
483
484                 let kind = if let Some(&Statement {
485                     kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), place)),
486                     ..
487                 }) = block.statements.get(location.statement_index)
488                 {
489                     if let Some(l) = place.as_local()
490                         && let local_decl = &self.body.local_decls[l]
491                         && local_decl.ty.is_closure()
492                     {
493                         LaterUseKind::ClosureCapture
494                     } else {
495                         LaterUseKind::FakeLetRead
496                     }
497                 } else if self.was_captured_by_trait_object(borrow) {
498                     LaterUseKind::TraitCapture
499                 } else if location.statement_index == block.statements.len() {
500                     if let TerminatorKind::Call { func, from_hir_call: true, .. } =
501                         &block.terminator().kind
502                     {
503                         // Just point to the function, to reduce the chance of overlapping spans.
504                         let function_span = match func {
505                             Operand::Constant(c) => c.span,
506                             Operand::Copy(place) | Operand::Move(place) => {
507                                 if let Some(l) = place.as_local() {
508                                     let local_decl = &self.body.local_decls[l];
509                                     if self.local_names[l].is_none() {
510                                         local_decl.source_info.span
511                                     } else {
512                                         span
513                                     }
514                                 } else {
515                                     span
516                                 }
517                             }
518                         };
519                         return (LaterUseKind::Call, function_span, None);
520                     } else {
521                         LaterUseKind::Other
522                     }
523                 } else {
524                     LaterUseKind::Other
525                 };
526
527                 (kind, span, None)
528             }
529         }
530     }
531
532     /// Checks if a borrowed value was captured by a trait object. We do this by
533     /// looking forward in the MIR from the reserve location and checking if we see
534     /// an unsized cast to a trait object on our data.
535     fn was_captured_by_trait_object(&self, borrow: &BorrowData<'tcx>) -> bool {
536         // Start at the reserve location, find the place that we want to see cast to a trait object.
537         let location = borrow.reserve_location;
538         let block = &self.body[location.block];
539         let stmt = block.statements.get(location.statement_index);
540         debug!("was_captured_by_trait_object: location={:?} stmt={:?}", location, stmt);
541
542         // We make a `queue` vector that has the locations we want to visit. As of writing, this
543         // will only ever have one item at any given time, but by using a vector, we can pop from
544         // it which simplifies the termination logic.
545         let mut queue = vec![location];
546         let mut target =
547             if let Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) = stmt {
548                 if let Some(local) = place.as_local() {
549                     local
550                 } else {
551                     return false;
552                 }
553             } else {
554                 return false;
555             };
556
557         debug!("was_captured_by_trait: target={:?} queue={:?}", target, queue);
558         while let Some(current_location) = queue.pop() {
559             debug!("was_captured_by_trait: target={:?}", target);
560             let block = &self.body[current_location.block];
561             // We need to check the current location to find out if it is a terminator.
562             let is_terminator = current_location.statement_index == block.statements.len();
563             if !is_terminator {
564                 let stmt = &block.statements[current_location.statement_index];
565                 debug!("was_captured_by_trait_object: stmt={:?}", stmt);
566
567                 // The only kind of statement that we care about is assignments...
568                 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
569                     let Some(into) = place.local_or_deref_local() else {
570                         // Continue at the next location.
571                         queue.push(current_location.successor_within_block());
572                         continue;
573                     };
574
575                     match rvalue {
576                         // If we see a use, we should check whether it is our data, and if so
577                         // update the place that we're looking for to that new place.
578                         Rvalue::Use(operand) => match operand {
579                             Operand::Copy(place) | Operand::Move(place) => {
580                                 if let Some(from) = place.as_local() {
581                                     if from == target {
582                                         target = into;
583                                     }
584                                 }
585                             }
586                             _ => {}
587                         },
588                         // If we see an unsized cast, then if it is our data we should check
589                         // whether it is being cast to a trait object.
590                         Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), operand, ty) => {
591                             match operand {
592                                 Operand::Copy(place) | Operand::Move(place) => {
593                                     if let Some(from) = place.as_local() {
594                                         if from == target {
595                                             debug!("was_captured_by_trait_object: ty={:?}", ty);
596                                             // Check the type for a trait object.
597                                             return match ty.kind() {
598                                                 // `&dyn Trait`
599                                                 ty::Ref(_, ty, _) if ty.is_trait() => true,
600                                                 // `Box<dyn Trait>`
601                                                 _ if ty.is_box() && ty.boxed_ty().is_trait() => {
602                                                     true
603                                                 }
604                                                 // `dyn Trait`
605                                                 _ if ty.is_trait() => true,
606                                                 // Anything else.
607                                                 _ => false,
608                                             };
609                                         }
610                                     }
611                                     return false;
612                                 }
613                                 _ => return false,
614                             }
615                         }
616                         _ => {}
617                     }
618                 }
619
620                 // Continue at the next location.
621                 queue.push(current_location.successor_within_block());
622             } else {
623                 // The only thing we need to do for terminators is progress to the next block.
624                 let terminator = block.terminator();
625                 debug!("was_captured_by_trait_object: terminator={:?}", terminator);
626
627                 if let TerminatorKind::Call { destination, target: Some(block), args, .. } =
628                     &terminator.kind
629                 {
630                     if let Some(dest) = destination.as_local() {
631                         debug!(
632                             "was_captured_by_trait_object: target={:?} dest={:?} args={:?}",
633                             target, dest, args
634                         );
635                         // Check if one of the arguments to this function is the target place.
636                         let found_target = args.iter().any(|arg| {
637                             if let Operand::Move(place) = arg {
638                                 if let Some(potential) = place.as_local() {
639                                     potential == target
640                                 } else {
641                                     false
642                                 }
643                             } else {
644                                 false
645                             }
646                         });
647
648                         // If it is, follow this to the next block and update the target.
649                         if found_target {
650                             target = dest;
651                             queue.push(block.start_location());
652                         }
653                     }
654                 }
655             }
656
657             debug!("was_captured_by_trait: queue={:?}", queue);
658         }
659
660         // We didn't find anything and ran out of locations to check.
661         false
662     }
663 }