]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Auto merge of #101784 - reitermarkus:const-memchr, r=thomcc
[rust.git] / compiler / rustc_borrowck / src / diagnostics / conflict_errors.rs
1 use either::Either;
2 use rustc_const_eval::util::CallKind;
3 use rustc_data_structures::captures::Captures;
4 use rustc_data_structures::fx::FxHashSet;
5 use rustc_errors::{
6     struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
7 };
8 use rustc_hir as hir;
9 use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
10 use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
11 use rustc_infer::infer::TyCtxtInferExt;
12 use rustc_infer::traits::ObligationCause;
13 use rustc_middle::mir::tcx::PlaceTy;
14 use rustc_middle::mir::{
15     self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
16     FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
17     ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
18 };
19 use rustc_middle::ty::{self, subst::Subst, suggest_constraining_type_params, PredicateKind, Ty};
20 use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
21 use rustc_span::def_id::LocalDefId;
22 use rustc_span::hygiene::DesugaringKind;
23 use rustc_span::symbol::sym;
24 use rustc_span::{BytePos, Span, Symbol};
25 use rustc_trait_selection::infer::InferCtxtExt;
26 use rustc_trait_selection::traits::TraitEngineExt as _;
27
28 use crate::borrow_set::TwoPhaseActivation;
29 use crate::borrowck_errors;
30
31 use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;
32 use crate::diagnostics::find_all_local_uses;
33 use crate::{
34     borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
35     InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
36 };
37
38 use super::{
39     explain_borrow::{BorrowExplanation, LaterUseKind},
40     DescribePlaceOpt, RegionName, RegionNameSource, UseSpans,
41 };
42
43 #[derive(Debug)]
44 struct MoveSite {
45     /// Index of the "move out" that we found. The `MoveData` can
46     /// then tell us where the move occurred.
47     moi: MoveOutIndex,
48
49     /// `true` if we traversed a back edge while walking from the point
50     /// of error to the move site.
51     traversed_back_edge: bool,
52 }
53
54 /// Which case a StorageDeadOrDrop is for.
55 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
56 enum StorageDeadOrDrop<'tcx> {
57     LocalStorageDead,
58     BoxedStorageDead,
59     Destructor(Ty<'tcx>),
60 }
61
62 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
63     pub(crate) fn report_use_of_moved_or_uninitialized(
64         &mut self,
65         location: Location,
66         desired_action: InitializationRequiringAction,
67         (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),
68         mpi: MovePathIndex,
69     ) {
70         debug!(
71             "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
72              moved_place={:?} used_place={:?} span={:?} mpi={:?}",
73             location, desired_action, moved_place, used_place, span, mpi
74         );
75
76         let use_spans =
77             self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
78         let span = use_spans.args_or_use();
79
80         let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
81         debug!(
82             "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
83             move_site_vec, use_spans
84         );
85         let move_out_indices: Vec<_> =
86             move_site_vec.iter().map(|move_site| move_site.moi).collect();
87
88         if move_out_indices.is_empty() {
89             let root_place = PlaceRef { projection: &[], ..used_place };
90
91             if !self.uninitialized_error_reported.insert(root_place) {
92                 debug!(
93                     "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
94                     root_place
95                 );
96                 return;
97             }
98
99             let err = self.report_use_of_uninitialized(
100                 mpi,
101                 used_place,
102                 moved_place,
103                 desired_action,
104                 span,
105                 use_spans,
106             );
107             self.buffer_error(err);
108         } else {
109             if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) {
110                 if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) {
111                     debug!(
112                         "report_use_of_moved_or_uninitialized place: error suppressed mois={:?}",
113                         move_out_indices
114                     );
115                     return;
116                 }
117             }
118
119             let is_partial_move = move_site_vec.iter().any(|move_site| {
120                 let move_out = self.move_data.moves[(*move_site).moi];
121                 let moved_place = &self.move_data.move_paths[move_out.path].place;
122                 // `*(_1)` where `_1` is a `Box` is actually a move out.
123                 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]
124                     && self.body.local_decls[moved_place.local].ty.is_box();
125
126                 !is_box_move
127                     && used_place != moved_place.as_ref()
128                     && used_place.is_prefix_of(moved_place.as_ref())
129             });
130
131             let partial_str = if is_partial_move { "partial " } else { "" };
132             let partially_str = if is_partial_move { "partially " } else { "" };
133
134             let mut err = self.cannot_act_on_moved_value(
135                 span,
136                 desired_action.as_noun(),
137                 partially_str,
138                 self.describe_place_with_options(
139                     moved_place,
140                     DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
141                 ),
142             );
143
144             let reinit_spans = maybe_reinitialized_locations
145                 .iter()
146                 .take(3)
147                 .map(|loc| {
148                     self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
149                         .args_or_use()
150                 })
151                 .collect::<Vec<Span>>();
152
153             let reinits = maybe_reinitialized_locations.len();
154             if reinits == 1 {
155                 err.span_label(reinit_spans[0], "this reinitialization might get skipped");
156             } else if reinits > 1 {
157                 err.span_note(
158                     MultiSpan::from_spans(reinit_spans),
159                     &if reinits <= 3 {
160                         format!("these {} reinitializations might get skipped", reinits)
161                     } else {
162                         format!(
163                             "these 3 reinitializations and {} other{} might get skipped",
164                             reinits - 3,
165                             if reinits == 4 { "" } else { "s" }
166                         )
167                     },
168                 );
169             }
170
171             self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
172
173             let mut is_loop_move = false;
174             let mut in_pattern = false;
175
176             for move_site in &move_site_vec {
177                 let move_out = self.move_data.moves[(*move_site).moi];
178                 let moved_place = &self.move_data.move_paths[move_out.path].place;
179
180                 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
181                 let move_span = move_spans.args_or_use();
182
183                 let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
184
185                 let loop_message = if location == move_out.source || move_site.traversed_back_edge {
186                     ", in previous iteration of loop"
187                 } else {
188                     ""
189                 };
190
191                 if location == move_out.source {
192                     is_loop_move = true;
193                 }
194
195                 self.explain_captures(
196                     &mut err,
197                     span,
198                     move_span,
199                     move_spans,
200                     *moved_place,
201                     Some(used_place),
202                     partially_str,
203                     loop_message,
204                     move_msg,
205                     is_loop_move,
206                     maybe_reinitialized_locations.is_empty(),
207                 );
208
209                 if let (UseSpans::PatUse(span), []) =
210                     (move_spans, &maybe_reinitialized_locations[..])
211                 {
212                     if maybe_reinitialized_locations.is_empty() {
213                         err.span_suggestion_verbose(
214                             span.shrink_to_lo(),
215                             &format!(
216                                 "borrow this field in the pattern to avoid moving {}",
217                                 self.describe_place(moved_place.as_ref())
218                                     .map(|n| format!("`{}`", n))
219                                     .unwrap_or_else(|| "the value".to_string())
220                             ),
221                             "ref ",
222                             Applicability::MachineApplicable,
223                         );
224                         in_pattern = true;
225                     }
226                 }
227             }
228
229             use_spans.var_span_label_path_only(
230                 &mut err,
231                 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
232             );
233
234             if !is_loop_move {
235                 err.span_label(
236                     span,
237                     format!(
238                         "value {} here after {}move",
239                         desired_action.as_verb_in_past_tense(),
240                         partial_str
241                     ),
242                 );
243             }
244
245             let ty = used_place.ty(self.body, self.infcx.tcx).ty;
246             let needs_note = match ty.kind() {
247                 ty::Closure(id, _) => {
248                     let tables = self.infcx.tcx.typeck(id.expect_local());
249                     let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
250
251                     tables.closure_kind_origins().get(hir_id).is_none()
252                 }
253                 _ => true,
254             };
255
256             let mpi = self.move_data.moves[move_out_indices[0]].path;
257             let place = &self.move_data.move_paths[mpi].place;
258             let ty = place.ty(self.body, self.infcx.tcx).ty;
259
260             // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
261             // Same for if we're in a loop, see #101119.
262             if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
263                 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
264                     // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
265                     err.span_suggestion_verbose(
266                         span.shrink_to_lo(),
267                         &format!(
268                             "consider creating a fresh reborrow of {} here",
269                             self.describe_place(moved_place)
270                                 .map(|n| format!("`{}`", n))
271                                 .unwrap_or_else(|| "the mutable reference".to_string()),
272                         ),
273                         "&mut *",
274                         Applicability::MachineApplicable,
275                     );
276                 }
277             }
278
279             let opt_name = self.describe_place_with_options(
280                 place.as_ref(),
281                 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
282             );
283             let note_msg = match opt_name {
284                 Some(ref name) => format!("`{}`", name),
285                 None => "value".to_owned(),
286             };
287             if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, &note_msg) {
288                 // Suppress the next suggestion since we don't want to put more bounds onto
289                 // something that already has `Fn`-like bounds (or is a closure), so we can't
290                 // restrict anyways.
291             } else {
292                 self.suggest_adding_copy_bounds(&mut err, ty, span);
293             }
294
295             if needs_note {
296                 let span = if let Some(local) = place.as_local() {
297                     Some(self.body.local_decls[local].source_info.span)
298                 } else {
299                     None
300                 };
301                 self.note_type_does_not_implement_copy(&mut err, &note_msg, ty, span, partial_str);
302             }
303
304             if let UseSpans::FnSelfUse {
305                 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
306                 ..
307             } = use_spans
308             {
309                 err.note(&format!(
310                     "{} occurs due to deref coercion to `{}`",
311                     desired_action.as_noun(),
312                     deref_target_ty
313                 ));
314
315                 // Check first whether the source is accessible (issue #87060)
316                 if self.infcx.tcx.sess.source_map().is_span_accessible(deref_target) {
317                     err.span_note(deref_target, "deref defined here");
318                 }
319             }
320
321             self.buffer_move_error(move_out_indices, (used_place, err));
322         }
323     }
324
325     fn report_use_of_uninitialized(
326         &self,
327         mpi: MovePathIndex,
328         used_place: PlaceRef<'tcx>,
329         moved_place: PlaceRef<'tcx>,
330         desired_action: InitializationRequiringAction,
331         span: Span,
332         use_spans: UseSpans<'tcx>,
333     ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
334         // We need all statements in the body where the binding was assigned to to later find all
335         // the branching code paths where the binding *wasn't* assigned to.
336         let inits = &self.move_data.init_path_map[mpi];
337         let move_path = &self.move_data.move_paths[mpi];
338         let decl_span = self.body.local_decls[move_path.place.local].source_info.span;
339         let mut spans = vec![];
340         for init_idx in inits {
341             let init = &self.move_data.inits[*init_idx];
342             let span = init.span(&self.body);
343             if !span.is_dummy() {
344                 spans.push(span);
345             }
346         }
347
348         let (name, desc) = match self.describe_place_with_options(
349             moved_place,
350             DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
351         ) {
352             Some(name) => (format!("`{name}`"), format!("`{name}` ")),
353             None => ("the variable".to_string(), String::new()),
354         };
355         let path = match self.describe_place_with_options(
356             used_place,
357             DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
358         ) {
359             Some(name) => format!("`{name}`"),
360             None => "value".to_string(),
361         };
362
363         // We use the statements were the binding was initialized, and inspect the HIR to look
364         // for the branching codepaths that aren't covered, to point at them.
365         let map = self.infcx.tcx.hir();
366         let body_id = map.body_owned_by(self.mir_def_id());
367         let body = map.body(body_id);
368
369         let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] };
370         visitor.visit_body(&body);
371
372         let isnt_initialized = if let InitializationRequiringAction::PartialAssignment
373         | InitializationRequiringAction::Assignment = desired_action
374         {
375             // The same error is emitted for bindings that are *sometimes* initialized and the ones
376             // that are *partially* initialized by assigning to a field of an uninitialized
377             // binding. We differentiate between them for more accurate wording here.
378             "isn't fully initialized"
379         } else if spans
380             .iter()
381             .filter(|i| {
382                 // We filter these to avoid misleading wording in cases like the following,
383                 // where `x` has an `init`, but it is in the same place we're looking at:
384                 // ```
385                 // let x;
386                 // x += 1;
387                 // ```
388                 !i.contains(span)
389                     // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs`
390                         && !visitor
391                             .errors
392                             .iter()
393                             .map(|(sp, _)| *sp)
394                             .any(|sp| span < sp && !sp.contains(span))
395             })
396             .count()
397             == 0
398         {
399             "isn't initialized"
400         } else {
401             "is possibly-uninitialized"
402         };
403
404         let used = desired_action.as_general_verb_in_past_tense();
405         let mut err =
406             struct_span_err!(self, span, E0381, "{used} binding {desc}{isnt_initialized}");
407         use_spans.var_span_label_path_only(
408             &mut err,
409             format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
410         );
411
412         if let InitializationRequiringAction::PartialAssignment
413         | InitializationRequiringAction::Assignment = desired_action
414         {
415             err.help(
416                 "partial initialization isn't supported, fully initialize the binding with a \
417                  default value and mutate it, or use `std::mem::MaybeUninit`",
418             );
419         }
420         err.span_label(span, format!("{path} {used} here but it {isnt_initialized}"));
421
422         let mut shown = false;
423         for (sp, label) in visitor.errors {
424             if sp < span && !sp.overlaps(span) {
425                 // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention
426                 // match arms coming after the primary span because they aren't relevant:
427                 // ```
428                 // let x;
429                 // match y {
430                 //     _ if { x = 2; true } => {}
431                 //     _ if {
432                 //         x; //~ ERROR
433                 //         false
434                 //     } => {}
435                 //     _ => {} // We don't want to point to this.
436                 // };
437                 // ```
438                 err.span_label(sp, &label);
439                 shown = true;
440             }
441         }
442         if !shown {
443             for sp in &spans {
444                 if *sp < span && !sp.overlaps(span) {
445                     err.span_label(*sp, "binding initialized here in some conditions");
446                 }
447             }
448         }
449         err.span_label(decl_span, "binding declared here but left uninitialized");
450         err
451     }
452
453     fn suggest_borrow_fn_like(
454         &self,
455         err: &mut Diagnostic,
456         ty: Ty<'tcx>,
457         move_sites: &[MoveSite],
458         value_name: &str,
459     ) -> bool {
460         let tcx = self.infcx.tcx;
461
462         // Find out if the predicates show that the type is a Fn or FnMut
463         let find_fn_kind_from_did =
464             |predicates: ty::EarlyBinder<&[(ty::Predicate<'tcx>, Span)]>, substs| {
465                 predicates.0.iter().find_map(|(pred, _)| {
466                     let pred = if let Some(substs) = substs {
467                         predicates.rebind(*pred).subst(tcx, substs).kind().skip_binder()
468                     } else {
469                         pred.kind().skip_binder()
470                     };
471                     if let ty::PredicateKind::Trait(pred) = pred && pred.self_ty() == ty {
472                     if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
473                         return Some(hir::Mutability::Not);
474                     } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
475                         return Some(hir::Mutability::Mut);
476                     }
477                 }
478                     None
479                 })
480             };
481
482         // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
483         // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
484         // These types seem reasonably opaque enough that they could be substituted with their
485         // borrowed variants in a function body when we see a move error.
486         let borrow_level = match ty.kind() {
487             ty::Param(_) => find_fn_kind_from_did(
488                 tcx.bound_explicit_predicates_of(self.mir_def_id().to_def_id())
489                     .map_bound(|p| p.predicates),
490                 None,
491             ),
492             ty::Opaque(did, substs) => {
493                 find_fn_kind_from_did(tcx.bound_explicit_item_bounds(*did), Some(*substs))
494             }
495             ty::Closure(_, substs) => match substs.as_closure().kind() {
496                 ty::ClosureKind::Fn => Some(hir::Mutability::Not),
497                 ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
498                 _ => None,
499             },
500             _ => None,
501         };
502
503         let Some(borrow_level) = borrow_level else { return false; };
504         let sugg = move_sites
505             .iter()
506             .map(|move_site| {
507                 let move_out = self.move_data.moves[(*move_site).moi];
508                 let moved_place = &self.move_data.move_paths[move_out.path].place;
509                 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
510                 let move_span = move_spans.args_or_use();
511                 let suggestion = if borrow_level == hir::Mutability::Mut {
512                     "&mut ".to_string()
513                 } else {
514                     "&".to_string()
515                 };
516                 (move_span.shrink_to_lo(), suggestion)
517             })
518             .collect();
519         err.multipart_suggestion_verbose(
520             &format!(
521                 "consider {}borrowing {value_name}",
522                 if borrow_level == hir::Mutability::Mut { "mutably " } else { "" }
523             ),
524             sugg,
525             Applicability::MaybeIncorrect,
526         );
527         true
528     }
529
530     fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
531         let tcx = self.infcx.tcx;
532         let generics = tcx.generics_of(self.mir_def_id());
533
534         let Some(hir_generics) = tcx
535             .typeck_root_def_id(self.mir_def_id().to_def_id())
536             .as_local()
537             .and_then(|def_id| tcx.hir().get_generics(def_id))
538         else { return; };
539         // Try to find predicates on *generic params* that would allow copying `ty`
540         let predicates: Result<Vec<_>, _> = tcx.infer_ctxt().enter(|infcx| {
541             let mut fulfill_cx = <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx);
542
543             let copy_did = infcx.tcx.lang_items().copy_trait().unwrap();
544             let cause = ObligationCause::new(
545                 span,
546                 self.mir_hir_id(),
547                 rustc_infer::traits::ObligationCauseCode::MiscObligation,
548             );
549             fulfill_cx.register_bound(
550                 &infcx,
551                 self.param_env,
552                 // Erase any region vids from the type, which may not be resolved
553                 infcx.tcx.erase_regions(ty),
554                 copy_did,
555                 cause,
556             );
557             // Select all, including ambiguous predicates
558             let errors = fulfill_cx.select_all_or_error(&infcx);
559
560             // Only emit suggestion if all required predicates are on generic
561             errors
562                 .into_iter()
563                 .map(|err| match err.obligation.predicate.kind().skip_binder() {
564                     PredicateKind::Trait(predicate) => match predicate.self_ty().kind() {
565                         ty::Param(param_ty) => Ok((
566                             generics.type_param(param_ty, tcx),
567                             predicate.trait_ref.print_only_trait_path().to_string(),
568                         )),
569                         _ => Err(()),
570                     },
571                     _ => Err(()),
572                 })
573                 .collect()
574         });
575
576         if let Ok(predicates) = predicates {
577             suggest_constraining_type_params(
578                 tcx,
579                 hir_generics,
580                 err,
581                 predicates
582                     .iter()
583                     .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
584             );
585         }
586     }
587
588     pub(crate) fn report_move_out_while_borrowed(
589         &mut self,
590         location: Location,
591         (place, span): (Place<'tcx>, Span),
592         borrow: &BorrowData<'tcx>,
593     ) {
594         debug!(
595             "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
596             location, place, span, borrow
597         );
598         let value_msg = self.describe_any_place(place.as_ref());
599         let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
600
601         let borrow_spans = self.retrieve_borrow_spans(borrow);
602         let borrow_span = borrow_spans.args_or_use();
603
604         let move_spans = self.move_spans(place.as_ref(), location);
605         let span = move_spans.args_or_use();
606
607         let mut err =
608             self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
609         err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
610         err.span_label(span, format!("move out of {} occurs here", value_msg));
611
612         borrow_spans.var_span_label_path_only(
613             &mut err,
614             format!("borrow occurs due to use{}", borrow_spans.describe()),
615         );
616
617         move_spans.var_span_label(
618             &mut err,
619             format!("move occurs due to use{}", move_spans.describe()),
620             "moved",
621         );
622
623         self.explain_why_borrow_contains_point(location, borrow, None)
624             .add_explanation_to_diagnostic(
625                 self.infcx.tcx,
626                 &self.body,
627                 &self.local_names,
628                 &mut err,
629                 "",
630                 Some(borrow_span),
631                 None,
632             );
633         self.buffer_error(err);
634     }
635
636     pub(crate) fn report_use_while_mutably_borrowed(
637         &mut self,
638         location: Location,
639         (place, _span): (Place<'tcx>, Span),
640         borrow: &BorrowData<'tcx>,
641     ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
642         let borrow_spans = self.retrieve_borrow_spans(borrow);
643         let borrow_span = borrow_spans.args_or_use();
644
645         // Conflicting borrows are reported separately, so only check for move
646         // captures.
647         let use_spans = self.move_spans(place.as_ref(), location);
648         let span = use_spans.var_or_use();
649
650         // If the attempted use is in a closure then we do not care about the path span of the place we are currently trying to use
651         // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
652         let mut err = self.cannot_use_when_mutably_borrowed(
653             span,
654             &self.describe_any_place(place.as_ref()),
655             borrow_span,
656             &self.describe_any_place(borrow.borrowed_place.as_ref()),
657         );
658
659         borrow_spans.var_span_label(
660             &mut err,
661             {
662                 let place = &borrow.borrowed_place;
663                 let desc_place = self.describe_any_place(place.as_ref());
664                 format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
665             },
666             "mutable",
667         );
668
669         self.explain_why_borrow_contains_point(location, borrow, None)
670             .add_explanation_to_diagnostic(
671                 self.infcx.tcx,
672                 &self.body,
673                 &self.local_names,
674                 &mut err,
675                 "",
676                 None,
677                 None,
678             );
679         err
680     }
681
682     pub(crate) fn report_conflicting_borrow(
683         &mut self,
684         location: Location,
685         (place, span): (Place<'tcx>, Span),
686         gen_borrow_kind: BorrowKind,
687         issued_borrow: &BorrowData<'tcx>,
688     ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
689         let issued_spans = self.retrieve_borrow_spans(issued_borrow);
690         let issued_span = issued_spans.args_or_use();
691
692         let borrow_spans = self.borrow_spans(span, location);
693         let span = borrow_spans.args_or_use();
694
695         let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
696             "generator"
697         } else {
698             "closure"
699         };
700
701         let (desc_place, msg_place, msg_borrow, union_type_name) =
702             self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
703
704         let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
705         let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
706
707         // FIXME: supply non-"" `opt_via` when appropriate
708         let first_borrow_desc;
709         let mut err = match (gen_borrow_kind, issued_borrow.kind) {
710             (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
711                 first_borrow_desc = "mutable ";
712                 self.cannot_reborrow_already_borrowed(
713                     span,
714                     &desc_place,
715                     &msg_place,
716                     "immutable",
717                     issued_span,
718                     "it",
719                     "mutable",
720                     &msg_borrow,
721                     None,
722                 )
723             }
724             (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
725                 first_borrow_desc = "immutable ";
726                 self.cannot_reborrow_already_borrowed(
727                     span,
728                     &desc_place,
729                     &msg_place,
730                     "mutable",
731                     issued_span,
732                     "it",
733                     "immutable",
734                     &msg_borrow,
735                     None,
736                 )
737             }
738
739             (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
740                 first_borrow_desc = "first ";
741                 let mut err = self.cannot_mutably_borrow_multiply(
742                     span,
743                     &desc_place,
744                     &msg_place,
745                     issued_span,
746                     &msg_borrow,
747                     None,
748                 );
749                 self.suggest_split_at_mut_if_applicable(
750                     &mut err,
751                     place,
752                     issued_borrow.borrowed_place,
753                 );
754                 err
755             }
756
757             (BorrowKind::Unique, BorrowKind::Unique) => {
758                 first_borrow_desc = "first ";
759                 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
760             }
761
762             (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
763                 if let Some(immutable_section_description) =
764                     self.classify_immutable_section(issued_borrow.assigned_place)
765                 {
766                     let mut err = self.cannot_mutate_in_immutable_section(
767                         span,
768                         issued_span,
769                         &desc_place,
770                         immutable_section_description,
771                         "mutably borrow",
772                     );
773                     borrow_spans.var_span_label(
774                         &mut err,
775                         format!(
776                             "borrow occurs due to use of {}{}",
777                             desc_place,
778                             borrow_spans.describe(),
779                         ),
780                         "immutable",
781                     );
782
783                     return err;
784                 } else {
785                     first_borrow_desc = "immutable ";
786                     self.cannot_reborrow_already_borrowed(
787                         span,
788                         &desc_place,
789                         &msg_place,
790                         "mutable",
791                         issued_span,
792                         "it",
793                         "immutable",
794                         &msg_borrow,
795                         None,
796                     )
797                 }
798             }
799
800             (BorrowKind::Unique, _) => {
801                 first_borrow_desc = "first ";
802                 self.cannot_uniquely_borrow_by_one_closure(
803                     span,
804                     container_name,
805                     &desc_place,
806                     "",
807                     issued_span,
808                     "it",
809                     "",
810                     None,
811                 )
812             }
813
814             (BorrowKind::Shared, BorrowKind::Unique) => {
815                 first_borrow_desc = "first ";
816                 self.cannot_reborrow_already_uniquely_borrowed(
817                     span,
818                     container_name,
819                     &desc_place,
820                     "",
821                     "immutable",
822                     issued_span,
823                     "",
824                     None,
825                     second_borrow_desc,
826                 )
827             }
828
829             (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
830                 first_borrow_desc = "first ";
831                 self.cannot_reborrow_already_uniquely_borrowed(
832                     span,
833                     container_name,
834                     &desc_place,
835                     "",
836                     "mutable",
837                     issued_span,
838                     "",
839                     None,
840                     second_borrow_desc,
841                 )
842             }
843
844             (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
845             | (
846                 BorrowKind::Shallow,
847                 BorrowKind::Mut { .. }
848                 | BorrowKind::Unique
849                 | BorrowKind::Shared
850                 | BorrowKind::Shallow,
851             ) => unreachable!(),
852         };
853
854         if issued_spans == borrow_spans {
855             borrow_spans.var_span_label(
856                 &mut err,
857                 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
858                 gen_borrow_kind.describe_mutability(),
859             );
860         } else {
861             let borrow_place = &issued_borrow.borrowed_place;
862             let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
863             issued_spans.var_span_label(
864                 &mut err,
865                 format!(
866                     "first borrow occurs due to use of {}{}",
867                     borrow_place_desc,
868                     issued_spans.describe(),
869                 ),
870                 issued_borrow.kind.describe_mutability(),
871             );
872
873             borrow_spans.var_span_label(
874                 &mut err,
875                 format!(
876                     "second borrow occurs due to use of {}{}",
877                     desc_place,
878                     borrow_spans.describe(),
879                 ),
880                 gen_borrow_kind.describe_mutability(),
881             );
882         }
883
884         if union_type_name != "" {
885             err.note(&format!(
886                 "{} is a field of the union `{}`, so it overlaps the field {}",
887                 msg_place, union_type_name, msg_borrow,
888             ));
889         }
890
891         explanation.add_explanation_to_diagnostic(
892             self.infcx.tcx,
893             &self.body,
894             &self.local_names,
895             &mut err,
896             first_borrow_desc,
897             None,
898             Some((issued_span, span)),
899         );
900
901         self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation);
902
903         err
904     }
905
906     #[instrument(level = "debug", skip(self, err))]
907     fn suggest_using_local_if_applicable(
908         &self,
909         err: &mut Diagnostic,
910         location: Location,
911         issued_borrow: &BorrowData<'tcx>,
912         explanation: BorrowExplanation<'tcx>,
913     ) {
914         let used_in_call = matches!(
915             explanation,
916             BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _)
917         );
918         if !used_in_call {
919             debug!("not later used in call");
920             return;
921         }
922
923         let use_span =
924             if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation {
925                 Some(use_span)
926             } else {
927                 None
928             };
929
930         let outer_call_loc =
931             if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
932                 loc
933             } else {
934                 issued_borrow.reserve_location
935             };
936         let outer_call_stmt = self.body.stmt_at(outer_call_loc);
937
938         let inner_param_location = location;
939         let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
940             debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
941             return;
942         };
943         let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
944             debug!(
945                 "`inner_param_location` {:?} is not for an assignment: {:?}",
946                 inner_param_location, inner_param_stmt
947             );
948             return;
949         };
950         let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
951         let Some((inner_call_loc, inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
952             let Either::Right(term) = self.body.stmt_at(loc) else {
953                 debug!("{:?} is a statement, so it can't be a call", loc);
954                 return None;
955             };
956             let TerminatorKind::Call { args, .. } = &term.kind else {
957                 debug!("not a call: {:?}", term);
958                 return None;
959             };
960             debug!("checking call args for uses of inner_param: {:?}", args);
961             if args.contains(&Operand::Move(inner_param)) {
962                 Some((loc, term))
963             } else {
964                 None
965             }
966         }) else {
967             debug!("no uses of inner_param found as a by-move call arg");
968             return;
969         };
970         debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
971
972         let inner_call_span = inner_call_term.source_info.span;
973         let outer_call_span = match use_span {
974             Some(span) => span,
975             None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span,
976         };
977         if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
978             // FIXME: This stops the suggestion in some cases where it should be emitted.
979             //        Fix the spans for those cases so it's emitted correctly.
980             debug!(
981                 "outer span {:?} does not strictly contain inner span {:?}",
982                 outer_call_span, inner_call_span
983             );
984             return;
985         }
986         err.span_help(
987             inner_call_span,
988             &format!(
989                 "try adding a local storing this{}...",
990                 if use_span.is_some() { "" } else { " argument" }
991             ),
992         );
993         err.span_help(
994             outer_call_span,
995             &format!(
996                 "...and then using that local {}",
997                 if use_span.is_some() { "here" } else { "as the argument to this call" }
998             ),
999         );
1000     }
1001
1002     fn suggest_split_at_mut_if_applicable(
1003         &self,
1004         err: &mut Diagnostic,
1005         place: Place<'tcx>,
1006         borrowed_place: Place<'tcx>,
1007     ) {
1008         if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
1009             (&place.projection[..], &borrowed_place.projection[..])
1010         {
1011             err.help(
1012                 "consider using `.split_at_mut(position)` or similar method to obtain \
1013                      two mutable non-overlapping sub-slices",
1014             );
1015         }
1016     }
1017
1018     /// Returns the description of the root place for a conflicting borrow and the full
1019     /// descriptions of the places that caused the conflict.
1020     ///
1021     /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
1022     /// attempted while a shared borrow is live, then this function will return:
1023     /// ```
1024     /// ("x", "", "")
1025     /// # ;
1026     /// ```
1027     /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
1028     /// a shared borrow of another field `x.y`, then this function will return:
1029     /// ```
1030     /// ("x", "x.z", "x.y")
1031     /// # ;
1032     /// ```
1033     /// In the more complex union case, where the union is a field of a struct, then if a mutable
1034     /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
1035     /// another field `x.u.y`, then this function will return:
1036     /// ```
1037     /// ("x.u", "x.u.z", "x.u.y")
1038     /// # ;
1039     /// ```
1040     /// This is used when creating error messages like below:
1041     ///
1042     /// ```text
1043     /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
1044     /// mutable (via `a.u.s.b`) [E0502]
1045     /// ```
1046     pub(crate) fn describe_place_for_conflicting_borrow(
1047         &self,
1048         first_borrowed_place: Place<'tcx>,
1049         second_borrowed_place: Place<'tcx>,
1050     ) -> (String, String, String, String) {
1051         // Define a small closure that we can use to check if the type of a place
1052         // is a union.
1053         let union_ty = |place_base| {
1054             // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
1055             // using a type annotation in the closure argument instead leads to a lifetime error.
1056             let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
1057             ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
1058         };
1059
1060         // Start with an empty tuple, so we can use the functions on `Option` to reduce some
1061         // code duplication (particularly around returning an empty description in the failure
1062         // case).
1063         Some(())
1064             .filter(|_| {
1065                 // If we have a conflicting borrow of the same place, then we don't want to add
1066                 // an extraneous "via x.y" to our diagnostics, so filter out this case.
1067                 first_borrowed_place != second_borrowed_place
1068             })
1069             .and_then(|_| {
1070                 // We're going to want to traverse the first borrowed place to see if we can find
1071                 // field access to a union. If we find that, then we will keep the place of the
1072                 // union being accessed and the field that was being accessed so we can check the
1073                 // second borrowed place for the same union and an access to a different field.
1074                 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
1075                     match elem {
1076                         ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
1077                             return Some((place_base, field));
1078                         }
1079                         _ => {}
1080                     }
1081                 }
1082                 None
1083             })
1084             .and_then(|(target_base, target_field)| {
1085                 // With the place of a union and a field access into it, we traverse the second
1086                 // borrowed place and look for an access to a different field of the same union.
1087                 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
1088                     if let ProjectionElem::Field(field, _) = elem {
1089                         if let Some(union_ty) = union_ty(place_base) {
1090                             if field != target_field && place_base == target_base {
1091                                 return Some((
1092                                     self.describe_any_place(place_base),
1093                                     self.describe_any_place(first_borrowed_place.as_ref()),
1094                                     self.describe_any_place(second_borrowed_place.as_ref()),
1095                                     union_ty.to_string(),
1096                                 ));
1097                             }
1098                         }
1099                     }
1100                 }
1101                 None
1102             })
1103             .unwrap_or_else(|| {
1104                 // If we didn't find a field access into a union, or both places match, then
1105                 // only return the description of the first place.
1106                 (
1107                     self.describe_any_place(first_borrowed_place.as_ref()),
1108                     "".to_string(),
1109                     "".to_string(),
1110                     "".to_string(),
1111                 )
1112             })
1113     }
1114
1115     /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
1116     ///
1117     /// This means that some data referenced by `borrow` needs to live
1118     /// past the point where the StorageDeadOrDrop of `place` occurs.
1119     /// This is usually interpreted as meaning that `place` has too
1120     /// short a lifetime. (But sometimes it is more useful to report
1121     /// it as a more direct conflict between the execution of a
1122     /// `Drop::drop` with an aliasing borrow.)
1123     #[instrument(level = "debug", skip(self))]
1124     pub(crate) fn report_borrowed_value_does_not_live_long_enough(
1125         &mut self,
1126         location: Location,
1127         borrow: &BorrowData<'tcx>,
1128         place_span: (Place<'tcx>, Span),
1129         kind: Option<WriteKind>,
1130     ) {
1131         let drop_span = place_span.1;
1132         let root_place =
1133             self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1134
1135         let borrow_spans = self.retrieve_borrow_spans(borrow);
1136         let borrow_span = borrow_spans.var_or_use_path_span();
1137
1138         assert!(root_place.projection.is_empty());
1139         let proper_span = self.body.local_decls[root_place.local].source_info.span;
1140
1141         let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1142
1143         if self.access_place_error_reported.contains(&(
1144             Place { local: root_place.local, projection: root_place_projection },
1145             borrow_span,
1146         )) {
1147             debug!(
1148                 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1149                 borrow_span
1150             );
1151             return;
1152         }
1153
1154         self.access_place_error_reported.insert((
1155             Place { local: root_place.local, projection: root_place_projection },
1156             borrow_span,
1157         ));
1158
1159         let borrowed_local = borrow.borrowed_place.local;
1160         if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1161             let err =
1162                 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1163             self.buffer_error(err);
1164             return;
1165         }
1166
1167         if let StorageDeadOrDrop::Destructor(dropped_ty) =
1168             self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1169         {
1170             // If a borrow of path `B` conflicts with drop of `D` (and
1171             // we're not in the uninteresting case where `B` is a
1172             // prefix of `D`), then report this as a more interesting
1173             // destructor conflict.
1174             if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1175                 self.report_borrow_conflicts_with_destructor(
1176                     location, borrow, place_span, kind, dropped_ty,
1177                 );
1178                 return;
1179             }
1180         }
1181
1182         let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1183
1184         let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1185         let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1186
1187         debug!(?place_desc, ?explanation);
1188
1189         let err = match (place_desc, explanation) {
1190             // If the outlives constraint comes from inside the closure,
1191             // for example:
1192             //
1193             // let x = 0;
1194             // let y = &x;
1195             // Box::new(|| y) as Box<Fn() -> &'static i32>
1196             //
1197             // then just use the normal error. The closure isn't escaping
1198             // and `move` will not help here.
1199             (
1200                 Some(ref name),
1201                 BorrowExplanation::MustBeValidFor {
1202                     category:
1203                         category @ (ConstraintCategory::Return(_)
1204                         | ConstraintCategory::CallArgument(_)
1205                         | ConstraintCategory::OpaqueType),
1206                     from_closure: false,
1207                     ref region_name,
1208                     span,
1209                     ..
1210                 },
1211             ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1212                 .report_escaping_closure_capture(
1213                     borrow_spans,
1214                     borrow_span,
1215                     region_name,
1216                     category,
1217                     span,
1218                     &format!("`{}`", name),
1219                 ),
1220             (
1221                 ref name,
1222                 BorrowExplanation::MustBeValidFor {
1223                     category: ConstraintCategory::Assignment,
1224                     from_closure: false,
1225                     region_name:
1226                         RegionName {
1227                             source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
1228                             ..
1229                         },
1230                     span,
1231                     ..
1232                 },
1233             ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1234             (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1235                 location,
1236                 &name,
1237                 &borrow,
1238                 drop_span,
1239                 borrow_spans,
1240                 explanation,
1241             ),
1242             (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1243                 location,
1244                 &borrow,
1245                 drop_span,
1246                 borrow_spans,
1247                 proper_span,
1248                 explanation,
1249             ),
1250         };
1251
1252         self.buffer_error(err);
1253     }
1254
1255     fn report_local_value_does_not_live_long_enough(
1256         &mut self,
1257         location: Location,
1258         name: &str,
1259         borrow: &BorrowData<'tcx>,
1260         drop_span: Span,
1261         borrow_spans: UseSpans<'tcx>,
1262         explanation: BorrowExplanation<'tcx>,
1263     ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1264         debug!(
1265             "report_local_value_does_not_live_long_enough(\
1266              {:?}, {:?}, {:?}, {:?}, {:?}\
1267              )",
1268             location, name, borrow, drop_span, borrow_spans
1269         );
1270
1271         let borrow_span = borrow_spans.var_or_use_path_span();
1272         if let BorrowExplanation::MustBeValidFor {
1273             category,
1274             span,
1275             ref opt_place_desc,
1276             from_closure: false,
1277             ..
1278         } = explanation
1279         {
1280             if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1281                 borrow,
1282                 borrow_span,
1283                 span,
1284                 category,
1285                 opt_place_desc.as_ref(),
1286             ) {
1287                 return diag;
1288             }
1289         }
1290
1291         let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1292
1293         if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1294             let region_name = annotation.emit(self, &mut err);
1295
1296             err.span_label(
1297                 borrow_span,
1298                 format!("`{}` would have to be valid for `{}`...", name, region_name),
1299             );
1300
1301             let fn_hir_id = self.mir_hir_id();
1302             err.span_label(
1303                 drop_span,
1304                 format!(
1305                     "...but `{}` will be dropped here, when the {} returns",
1306                     name,
1307                     self.infcx
1308                         .tcx
1309                         .hir()
1310                         .opt_name(fn_hir_id)
1311                         .map(|name| format!("function `{}`", name))
1312                         .unwrap_or_else(|| {
1313                             match &self
1314                                 .infcx
1315                                 .tcx
1316                                 .typeck(self.mir_def_id())
1317                                 .node_type(fn_hir_id)
1318                                 .kind()
1319                             {
1320                                 ty::Closure(..) => "enclosing closure",
1321                                 ty::Generator(..) => "enclosing generator",
1322                                 kind => bug!("expected closure or generator, found {:?}", kind),
1323                             }
1324                             .to_string()
1325                         })
1326                 ),
1327             );
1328
1329             err.note(
1330                 "functions cannot return a borrow to data owned within the function's scope, \
1331                     functions can only return borrows to data passed as arguments",
1332             );
1333             err.note(
1334                 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1335                     references-and-borrowing.html#dangling-references>",
1336             );
1337
1338             if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1339             } else {
1340                 explanation.add_explanation_to_diagnostic(
1341                     self.infcx.tcx,
1342                     &self.body,
1343                     &self.local_names,
1344                     &mut err,
1345                     "",
1346                     None,
1347                     None,
1348                 );
1349             }
1350         } else {
1351             err.span_label(borrow_span, "borrowed value does not live long enough");
1352             err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1353
1354             let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1355
1356             borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1357
1358             explanation.add_explanation_to_diagnostic(
1359                 self.infcx.tcx,
1360                 &self.body,
1361                 &self.local_names,
1362                 &mut err,
1363                 "",
1364                 None,
1365                 None,
1366             );
1367         }
1368
1369         err
1370     }
1371
1372     fn report_borrow_conflicts_with_destructor(
1373         &mut self,
1374         location: Location,
1375         borrow: &BorrowData<'tcx>,
1376         (place, drop_span): (Place<'tcx>, Span),
1377         kind: Option<WriteKind>,
1378         dropped_ty: Ty<'tcx>,
1379     ) {
1380         debug!(
1381             "report_borrow_conflicts_with_destructor(\
1382              {:?}, {:?}, ({:?}, {:?}), {:?}\
1383              )",
1384             location, borrow, place, drop_span, kind,
1385         );
1386
1387         let borrow_spans = self.retrieve_borrow_spans(borrow);
1388         let borrow_span = borrow_spans.var_or_use();
1389
1390         let mut err = self.cannot_borrow_across_destructor(borrow_span);
1391
1392         let what_was_dropped = match self.describe_place(place.as_ref()) {
1393             Some(name) => format!("`{}`", name),
1394             None => String::from("temporary value"),
1395         };
1396
1397         let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1398             Some(borrowed) => format!(
1399                 "here, drop of {D} needs exclusive access to `{B}`, \
1400                  because the type `{T}` implements the `Drop` trait",
1401                 D = what_was_dropped,
1402                 T = dropped_ty,
1403                 B = borrowed
1404             ),
1405             None => format!(
1406                 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1407                 D = what_was_dropped,
1408                 T = dropped_ty
1409             ),
1410         };
1411         err.span_label(drop_span, label);
1412
1413         // Only give this note and suggestion if they could be relevant.
1414         let explanation =
1415             self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1416         match explanation {
1417             BorrowExplanation::UsedLater { .. }
1418             | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1419                 err.note("consider using a `let` binding to create a longer lived value");
1420             }
1421             _ => {}
1422         }
1423
1424         explanation.add_explanation_to_diagnostic(
1425             self.infcx.tcx,
1426             &self.body,
1427             &self.local_names,
1428             &mut err,
1429             "",
1430             None,
1431             None,
1432         );
1433
1434         self.buffer_error(err);
1435     }
1436
1437     fn report_thread_local_value_does_not_live_long_enough(
1438         &mut self,
1439         drop_span: Span,
1440         borrow_span: Span,
1441     ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1442         debug!(
1443             "report_thread_local_value_does_not_live_long_enough(\
1444              {:?}, {:?}\
1445              )",
1446             drop_span, borrow_span
1447         );
1448
1449         let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1450
1451         err.span_label(
1452             borrow_span,
1453             "thread-local variables cannot be borrowed beyond the end of the function",
1454         );
1455         err.span_label(drop_span, "end of enclosing function is here");
1456
1457         err
1458     }
1459
1460     #[instrument(level = "debug", skip(self))]
1461     fn report_temporary_value_does_not_live_long_enough(
1462         &mut self,
1463         location: Location,
1464         borrow: &BorrowData<'tcx>,
1465         drop_span: Span,
1466         borrow_spans: UseSpans<'tcx>,
1467         proper_span: Span,
1468         explanation: BorrowExplanation<'tcx>,
1469     ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1470         if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1471             explanation
1472         {
1473             if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1474                 borrow,
1475                 proper_span,
1476                 span,
1477                 category,
1478                 None,
1479             ) {
1480                 return diag;
1481             }
1482         }
1483
1484         let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1485         err.span_label(proper_span, "creates a temporary which is freed while still in use");
1486         err.span_label(drop_span, "temporary value is freed at the end of this statement");
1487
1488         match explanation {
1489             BorrowExplanation::UsedLater(..)
1490             | BorrowExplanation::UsedLaterInLoop(..)
1491             | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1492                 // Only give this note and suggestion if it could be relevant.
1493                 let sm = self.infcx.tcx.sess.source_map();
1494                 let mut suggested = false;
1495                 let msg = "consider using a `let` binding to create a longer lived value";
1496
1497                 /// We check that there's a single level of block nesting to ensure always correct
1498                 /// suggestions. If we don't, then we only provide a free-form message to avoid
1499                 /// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`.
1500                 /// We could expand the analysis to suggest hoising all of the relevant parts of
1501                 /// the users' code to make the code compile, but that could be too much.
1502                 struct NestedStatementVisitor {
1503                     span: Span,
1504                     current: usize,
1505                     found: usize,
1506                 }
1507
1508                 impl<'tcx> Visitor<'tcx> for NestedStatementVisitor {
1509                     fn visit_block(&mut self, block: &hir::Block<'tcx>) {
1510                         self.current += 1;
1511                         walk_block(self, block);
1512                         self.current -= 1;
1513                     }
1514                     fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) {
1515                         if self.span == expr.span {
1516                             self.found = self.current;
1517                         }
1518                         walk_expr(self, expr);
1519                     }
1520                 }
1521                 let source_info = self.body.source_info(location);
1522                 if let Some(scope) = self.body.source_scopes.get(source_info.scope)
1523                     && let ClearCrossCrate::Set(scope_data) = &scope.local_data
1524                     && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root)
1525                     && let Some(id) = node.body_id()
1526                     && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind
1527                 {
1528                     for stmt in block.stmts {
1529                         let mut visitor = NestedStatementVisitor {
1530                             span: proper_span,
1531                             current: 0,
1532                             found: 0,
1533                         };
1534                         visitor.visit_stmt(stmt);
1535                         if visitor.found == 0
1536                             && stmt.span.contains(proper_span)
1537                             && let Some(p) = sm.span_to_margin(stmt.span)
1538                             && let Ok(s) = sm.span_to_snippet(proper_span)
1539                         {
1540                             let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
1541                             err.multipart_suggestion_verbose(
1542                                 msg,
1543                                 vec![
1544                                     (stmt.span.shrink_to_lo(), addition),
1545                                     (proper_span, "binding".to_string()),
1546                                 ],
1547                                 Applicability::MaybeIncorrect,
1548                             );
1549                             suggested = true;
1550                             break;
1551                         }
1552                     }
1553                 }
1554                 if !suggested {
1555                     err.note(msg);
1556                 }
1557             }
1558             _ => {}
1559         }
1560         explanation.add_explanation_to_diagnostic(
1561             self.infcx.tcx,
1562             &self.body,
1563             &self.local_names,
1564             &mut err,
1565             "",
1566             None,
1567             None,
1568         );
1569
1570         let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1571
1572         borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1573
1574         err
1575     }
1576
1577     fn try_report_cannot_return_reference_to_local(
1578         &self,
1579         borrow: &BorrowData<'tcx>,
1580         borrow_span: Span,
1581         return_span: Span,
1582         category: ConstraintCategory<'tcx>,
1583         opt_place_desc: Option<&String>,
1584     ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1585         let return_kind = match category {
1586             ConstraintCategory::Return(_) => "return",
1587             ConstraintCategory::Yield => "yield",
1588             _ => return None,
1589         };
1590
1591         // FIXME use a better heuristic than Spans
1592         let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1593             "reference to"
1594         } else {
1595             "value referencing"
1596         };
1597
1598         let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1599             let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1600                 match self.body.local_kind(local) {
1601                     LocalKind::ReturnPointer | LocalKind::Temp => {
1602                         bug!("temporary or return pointer with a name")
1603                     }
1604                     LocalKind::Var => "local variable ",
1605                     LocalKind::Arg
1606                         if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1607                     {
1608                         "variable captured by `move` "
1609                     }
1610                     LocalKind::Arg => "function parameter ",
1611                 }
1612             } else {
1613                 "local data "
1614             };
1615             (
1616                 format!("{}`{}`", local_kind, place_desc),
1617                 format!("`{}` is borrowed here", place_desc),
1618             )
1619         } else {
1620             let root_place =
1621                 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1622             let local = root_place.local;
1623             match self.body.local_kind(local) {
1624                 LocalKind::ReturnPointer | LocalKind::Temp => {
1625                     ("temporary value".to_string(), "temporary value created here".to_string())
1626                 }
1627                 LocalKind::Arg => (
1628                     "function parameter".to_string(),
1629                     "function parameter borrowed here".to_string(),
1630                 ),
1631                 LocalKind::Var => {
1632                     ("local binding".to_string(), "local binding introduced here".to_string())
1633                 }
1634             }
1635         };
1636
1637         let mut err = self.cannot_return_reference_to_local(
1638             return_span,
1639             return_kind,
1640             reference_desc,
1641             &place_desc,
1642         );
1643
1644         if return_span != borrow_span {
1645             err.span_label(borrow_span, note);
1646
1647             let tcx = self.infcx.tcx;
1648             let ty_params = ty::List::empty();
1649
1650             let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1651             let return_ty = tcx.erase_regions(return_ty);
1652
1653             // to avoid panics
1654             if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator)
1655                 && self
1656                     .infcx
1657                     .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1658                     .must_apply_modulo_regions()
1659             {
1660                 err.span_suggestion_hidden(
1661                     return_span.shrink_to_hi(),
1662                     "use `.collect()` to allocate the iterator",
1663                     ".collect::<Vec<_>>()",
1664                     Applicability::MaybeIncorrect,
1665                 );
1666             }
1667         }
1668
1669         Some(err)
1670     }
1671
1672     fn report_escaping_closure_capture(
1673         &mut self,
1674         use_span: UseSpans<'tcx>,
1675         var_span: Span,
1676         fr_name: &RegionName,
1677         category: ConstraintCategory<'tcx>,
1678         constraint_span: Span,
1679         captured_var: &str,
1680     ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1681         let tcx = self.infcx.tcx;
1682         let args_span = use_span.args_or_use();
1683
1684         let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1685             Ok(string) => {
1686                 if string.starts_with("async ") {
1687                     let pos = args_span.lo() + BytePos(6);
1688                     (args_span.with_lo(pos).with_hi(pos), "move ")
1689                 } else if string.starts_with("async|") {
1690                     let pos = args_span.lo() + BytePos(5);
1691                     (args_span.with_lo(pos).with_hi(pos), " move")
1692                 } else {
1693                     (args_span.shrink_to_lo(), "move ")
1694                 }
1695             }
1696             Err(_) => (args_span, "move |<args>| <body>"),
1697         };
1698         let kind = match use_span.generator_kind() {
1699             Some(generator_kind) => match generator_kind {
1700                 GeneratorKind::Async(async_kind) => match async_kind {
1701                     AsyncGeneratorKind::Block => "async block",
1702                     AsyncGeneratorKind::Closure => "async closure",
1703                     _ => bug!("async block/closure expected, but async function found."),
1704                 },
1705                 GeneratorKind::Gen => "generator",
1706             },
1707             None => "closure",
1708         };
1709
1710         let mut err =
1711             self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1712         err.span_suggestion_verbose(
1713             sugg_span,
1714             &format!(
1715                 "to force the {} to take ownership of {} (and any \
1716                  other referenced variables), use the `move` keyword",
1717                 kind, captured_var
1718             ),
1719             suggestion,
1720             Applicability::MachineApplicable,
1721         );
1722
1723         match category {
1724             ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1725                 let msg = format!("{} is returned here", kind);
1726                 err.span_note(constraint_span, &msg);
1727             }
1728             ConstraintCategory::CallArgument(_) => {
1729                 fr_name.highlight_region_name(&mut err);
1730                 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1731                     err.note(
1732                         "async blocks are not executed immediately and must either take a \
1733                     reference or ownership of outside variables they use",
1734                     );
1735                 } else {
1736                     let msg = format!("function requires argument type to outlive `{}`", fr_name);
1737                     err.span_note(constraint_span, &msg);
1738                 }
1739             }
1740             _ => bug!(
1741                 "report_escaping_closure_capture called with unexpected constraint \
1742                  category: `{:?}`",
1743                 category
1744             ),
1745         }
1746
1747         err
1748     }
1749
1750     fn report_escaping_data(
1751         &mut self,
1752         borrow_span: Span,
1753         name: &Option<String>,
1754         upvar_span: Span,
1755         upvar_name: Symbol,
1756         escape_span: Span,
1757     ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1758         let tcx = self.infcx.tcx;
1759
1760         let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1761
1762         let mut err =
1763             borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1764
1765         err.span_label(
1766             upvar_span,
1767             format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1768         );
1769
1770         err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1771
1772         if let Some(name) = name {
1773             err.span_label(
1774                 escape_span,
1775                 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1776             );
1777         } else {
1778             err.span_label(
1779                 escape_span,
1780                 format!("reference escapes the {} body here", escapes_from),
1781             );
1782         }
1783
1784         err
1785     }
1786
1787     fn get_moved_indexes(
1788         &mut self,
1789         location: Location,
1790         mpi: MovePathIndex,
1791     ) -> (Vec<MoveSite>, Vec<Location>) {
1792         fn predecessor_locations<'tcx, 'a>(
1793             body: &'a mir::Body<'tcx>,
1794             location: Location,
1795         ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
1796             if location.statement_index == 0 {
1797                 let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
1798                 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1799             } else {
1800                 Either::Right(std::iter::once(Location {
1801                     statement_index: location.statement_index - 1,
1802                     ..location
1803                 }))
1804             }
1805         }
1806
1807         let mut mpis = vec![mpi];
1808         let move_paths = &self.move_data.move_paths;
1809         mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1810
1811         let mut stack = Vec::new();
1812         let mut back_edge_stack = Vec::new();
1813
1814         predecessor_locations(self.body, location).for_each(|predecessor| {
1815             if location.dominates(predecessor, &self.dominators) {
1816                 back_edge_stack.push(predecessor)
1817             } else {
1818                 stack.push(predecessor);
1819             }
1820         });
1821
1822         let mut reached_start = false;
1823
1824         /* Check if the mpi is initialized as an argument */
1825         let mut is_argument = false;
1826         for arg in self.body.args_iter() {
1827             let path = self.move_data.rev_lookup.find_local(arg);
1828             if mpis.contains(&path) {
1829                 is_argument = true;
1830             }
1831         }
1832
1833         let mut visited = FxHashSet::default();
1834         let mut move_locations = FxHashSet::default();
1835         let mut reinits = vec![];
1836         let mut result = vec![];
1837
1838         let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1839             debug!(
1840                 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1841                 location, is_back_edge
1842             );
1843
1844             if !visited.insert(location) {
1845                 return true;
1846             }
1847
1848             // check for moves
1849             let stmt_kind =
1850                 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1851             if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1852                 // this analysis only tries to find moves explicitly
1853                 // written by the user, so we ignore the move-outs
1854                 // created by `StorageDead` and at the beginning
1855                 // of a function.
1856             } else {
1857                 // If we are found a use of a.b.c which was in error, then we want to look for
1858                 // moves not only of a.b.c but also a.b and a.
1859                 //
1860                 // Note that the moves data already includes "parent" paths, so we don't have to
1861                 // worry about the other case: that is, if there is a move of a.b.c, it is already
1862                 // marked as a move of a.b and a as well, so we will generate the correct errors
1863                 // there.
1864                 for moi in &self.move_data.loc_map[location] {
1865                     debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1866                     let path = self.move_data.moves[*moi].path;
1867                     if mpis.contains(&path) {
1868                         debug!(
1869                             "report_use_of_moved_or_uninitialized: found {:?}",
1870                             move_paths[path].place
1871                         );
1872                         result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1873                         move_locations.insert(location);
1874
1875                         // Strictly speaking, we could continue our DFS here. There may be
1876                         // other moves that can reach the point of error. But it is kind of
1877                         // confusing to highlight them.
1878                         //
1879                         // Example:
1880                         //
1881                         // ```
1882                         // let a = vec![];
1883                         // let b = a;
1884                         // let c = a;
1885                         // drop(a); // <-- current point of error
1886                         // ```
1887                         //
1888                         // Because we stop the DFS here, we only highlight `let c = a`,
1889                         // and not `let b = a`. We will of course also report an error at
1890                         // `let c = a` which highlights `let b = a` as the move.
1891                         return true;
1892                     }
1893                 }
1894             }
1895
1896             // check for inits
1897             let mut any_match = false;
1898             for ii in &self.move_data.init_loc_map[location] {
1899                 let init = self.move_data.inits[*ii];
1900                 match init.kind {
1901                     InitKind::Deep | InitKind::NonPanicPathOnly => {
1902                         if mpis.contains(&init.path) {
1903                             any_match = true;
1904                         }
1905                     }
1906                     InitKind::Shallow => {
1907                         if mpi == init.path {
1908                             any_match = true;
1909                         }
1910                     }
1911                 }
1912             }
1913             if any_match {
1914                 reinits.push(location);
1915                 return true;
1916             }
1917             return false;
1918         };
1919
1920         while let Some(location) = stack.pop() {
1921             if dfs_iter(&mut result, location, false) {
1922                 continue;
1923             }
1924
1925             let mut has_predecessor = false;
1926             predecessor_locations(self.body, location).for_each(|predecessor| {
1927                 if location.dominates(predecessor, &self.dominators) {
1928                     back_edge_stack.push(predecessor)
1929                 } else {
1930                     stack.push(predecessor);
1931                 }
1932                 has_predecessor = true;
1933             });
1934
1935             if !has_predecessor {
1936                 reached_start = true;
1937             }
1938         }
1939         if (is_argument || !reached_start) && result.is_empty() {
1940             /* Process back edges (moves in future loop iterations) only if
1941                the move path is definitely initialized upon loop entry,
1942                to avoid spurious "in previous iteration" errors.
1943                During DFS, if there's a path from the error back to the start
1944                of the function with no intervening init or move, then the
1945                move path may be uninitialized at loop entry.
1946             */
1947             while let Some(location) = back_edge_stack.pop() {
1948                 if dfs_iter(&mut result, location, true) {
1949                     continue;
1950                 }
1951
1952                 predecessor_locations(self.body, location)
1953                     .for_each(|predecessor| back_edge_stack.push(predecessor));
1954             }
1955         }
1956
1957         // Check if we can reach these reinits from a move location.
1958         let reinits_reachable = reinits
1959             .into_iter()
1960             .filter(|reinit| {
1961                 let mut visited = FxHashSet::default();
1962                 let mut stack = vec![*reinit];
1963                 while let Some(location) = stack.pop() {
1964                     if !visited.insert(location) {
1965                         continue;
1966                     }
1967                     if move_locations.contains(&location) {
1968                         return true;
1969                     }
1970                     stack.extend(predecessor_locations(self.body, location));
1971                 }
1972                 false
1973             })
1974             .collect::<Vec<Location>>();
1975         (result, reinits_reachable)
1976     }
1977
1978     pub(crate) fn report_illegal_mutation_of_borrowed(
1979         &mut self,
1980         location: Location,
1981         (place, span): (Place<'tcx>, Span),
1982         loan: &BorrowData<'tcx>,
1983     ) {
1984         let loan_spans = self.retrieve_borrow_spans(loan);
1985         let loan_span = loan_spans.args_or_use();
1986
1987         let descr_place = self.describe_any_place(place.as_ref());
1988         if loan.kind == BorrowKind::Shallow {
1989             if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
1990                 let mut err = self.cannot_mutate_in_immutable_section(
1991                     span,
1992                     loan_span,
1993                     &descr_place,
1994                     section,
1995                     "assign",
1996                 );
1997                 loan_spans.var_span_label(
1998                     &mut err,
1999                     format!("borrow occurs due to use{}", loan_spans.describe()),
2000                     loan.kind.describe_mutability(),
2001                 );
2002
2003                 self.buffer_error(err);
2004
2005                 return;
2006             }
2007         }
2008
2009         let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
2010
2011         loan_spans.var_span_label(
2012             &mut err,
2013             format!("borrow occurs due to use{}", loan_spans.describe()),
2014             loan.kind.describe_mutability(),
2015         );
2016
2017         self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
2018             self.infcx.tcx,
2019             &self.body,
2020             &self.local_names,
2021             &mut err,
2022             "",
2023             None,
2024             None,
2025         );
2026
2027         self.explain_deref_coercion(loan, &mut err);
2028
2029         self.buffer_error(err);
2030     }
2031
2032     fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
2033         let tcx = self.infcx.tcx;
2034         if let (
2035             Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
2036             Some((method_did, method_substs)),
2037         ) = (
2038             &self.body[loan.reserve_location.block].terminator,
2039             rustc_const_eval::util::find_self_call(
2040                 tcx,
2041                 self.body,
2042                 loan.assigned_place.local,
2043                 loan.reserve_location.block,
2044             ),
2045         ) {
2046             if tcx.is_diagnostic_item(sym::deref_method, method_did) {
2047                 let deref_target =
2048                     tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
2049                         Instance::resolve(tcx, self.param_env, deref_target, method_substs)
2050                             .transpose()
2051                     });
2052                 if let Some(Ok(instance)) = deref_target {
2053                     let deref_target_ty = instance.ty(tcx, self.param_env);
2054                     err.note(&format!(
2055                         "borrow occurs due to deref coercion to `{}`",
2056                         deref_target_ty
2057                     ));
2058                     err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
2059                 }
2060             }
2061         }
2062     }
2063
2064     /// Reports an illegal reassignment; for example, an assignment to
2065     /// (part of) a non-`mut` local that occurs potentially after that
2066     /// local has already been initialized. `place` is the path being
2067     /// assigned; `err_place` is a place providing a reason why
2068     /// `place` is not mutable (e.g., the non-`mut` local `x` in an
2069     /// assignment to `x.f`).
2070     pub(crate) fn report_illegal_reassignment(
2071         &mut self,
2072         _location: Location,
2073         (place, span): (Place<'tcx>, Span),
2074         assigned_span: Span,
2075         err_place: Place<'tcx>,
2076     ) {
2077         let (from_arg, local_decl, local_name) = match err_place.as_local() {
2078             Some(local) => (
2079                 self.body.local_kind(local) == LocalKind::Arg,
2080                 Some(&self.body.local_decls[local]),
2081                 self.local_names[local],
2082             ),
2083             None => (false, None, None),
2084         };
2085
2086         // If root local is initialized immediately (everything apart from let
2087         // PATTERN;) then make the error refer to that local, rather than the
2088         // place being assigned later.
2089         let (place_description, assigned_span) = match local_decl {
2090             Some(LocalDecl {
2091                 local_info:
2092                     Some(box LocalInfo::User(
2093                         ClearCrossCrate::Clear
2094                         | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
2095                             opt_match_place: None,
2096                             ..
2097                         })),
2098                     ))
2099                     | Some(box LocalInfo::StaticRef { .. })
2100                     | None,
2101                 ..
2102             })
2103             | None => (self.describe_any_place(place.as_ref()), assigned_span),
2104             Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
2105         };
2106
2107         let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
2108         let msg = if from_arg {
2109             "cannot assign to immutable argument"
2110         } else {
2111             "cannot assign twice to immutable variable"
2112         };
2113         if span != assigned_span && !from_arg {
2114             err.span_label(assigned_span, format!("first assignment to {}", place_description));
2115         }
2116         if let Some(decl) = local_decl
2117             && let Some(name) = local_name
2118             && decl.can_be_made_mutable()
2119         {
2120             err.span_suggestion(
2121                 decl.source_info.span,
2122                 "consider making this binding mutable",
2123                 format!("mut {}", name),
2124                 Applicability::MachineApplicable,
2125             );
2126         }
2127         err.span_label(span, msg);
2128         self.buffer_error(err);
2129     }
2130
2131     fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
2132         let tcx = self.infcx.tcx;
2133         let (kind, _place_ty) = place.projection.iter().fold(
2134             (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)),
2135             |(kind, place_ty), &elem| {
2136                 (
2137                     match elem {
2138                         ProjectionElem::Deref => match kind {
2139                             StorageDeadOrDrop::LocalStorageDead
2140                             | StorageDeadOrDrop::BoxedStorageDead => {
2141                                 assert!(
2142                                     place_ty.ty.is_box(),
2143                                     "Drop of value behind a reference or raw pointer"
2144                                 );
2145                                 StorageDeadOrDrop::BoxedStorageDead
2146                             }
2147                             StorageDeadOrDrop::Destructor(_) => kind,
2148                         },
2149                         ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
2150                             match place_ty.ty.kind() {
2151                                 ty::Adt(def, _) if def.has_dtor(tcx) => {
2152                                     // Report the outermost adt with a destructor
2153                                     match kind {
2154                                         StorageDeadOrDrop::Destructor(_) => kind,
2155                                         StorageDeadOrDrop::LocalStorageDead
2156                                         | StorageDeadOrDrop::BoxedStorageDead => {
2157                                             StorageDeadOrDrop::Destructor(place_ty.ty)
2158                                         }
2159                                     }
2160                                 }
2161                                 _ => kind,
2162                             }
2163                         }
2164                         ProjectionElem::ConstantIndex { .. }
2165                         | ProjectionElem::Subslice { .. }
2166                         | ProjectionElem::Index(_) => kind,
2167                     },
2168                     place_ty.projection_ty(tcx, elem),
2169                 )
2170             },
2171         );
2172         kind
2173     }
2174
2175     /// Describe the reason for the fake borrow that was assigned to `place`.
2176     fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
2177         use rustc_middle::mir::visit::Visitor;
2178         struct FakeReadCauseFinder<'tcx> {
2179             place: Place<'tcx>,
2180             cause: Option<FakeReadCause>,
2181         }
2182         impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
2183             fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
2184                 match statement {
2185                     Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
2186                         if *place == self.place =>
2187                     {
2188                         self.cause = Some(*cause);
2189                     }
2190                     _ => (),
2191                 }
2192             }
2193         }
2194         let mut visitor = FakeReadCauseFinder { place, cause: None };
2195         visitor.visit_body(&self.body);
2196         match visitor.cause {
2197             Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2198             Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2199             _ => None,
2200         }
2201     }
2202
2203     /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2204     /// borrow of local value that does not live long enough.
2205     fn annotate_argument_and_return_for_borrow(
2206         &self,
2207         borrow: &BorrowData<'tcx>,
2208     ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2209         // Define a fallback for when we can't match a closure.
2210         let fallback = || {
2211             let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2212             if is_closure {
2213                 None
2214             } else {
2215                 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2216                 match ty.kind() {
2217                     ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2218                         self.mir_def_id(),
2219                         self.infcx.tcx.fn_sig(self.mir_def_id()),
2220                     ),
2221                     _ => None,
2222                 }
2223             }
2224         };
2225
2226         // In order to determine whether we need to annotate, we need to check whether the reserve
2227         // place was an assignment into a temporary.
2228         //
2229         // If it was, we check whether or not that temporary is eventually assigned into the return
2230         // place. If it was, we can add annotations about the function's return type and arguments
2231         // and it'll make sense.
2232         let location = borrow.reserve_location;
2233         debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2234         if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
2235             &self.body[location.block].statements.get(location.statement_index)
2236         {
2237             debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2238             // Check that the initial assignment of the reserve location is into a temporary.
2239             let mut target = match reservation.as_local() {
2240                 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2241                 _ => return None,
2242             };
2243
2244             // Next, look through the rest of the block, checking if we are assigning the
2245             // `target` (that is, the place that contains our borrow) to anything.
2246             let mut annotated_closure = None;
2247             for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2248                 debug!(
2249                     "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2250                     target, stmt
2251                 );
2252                 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2253                     if let Some(assigned_to) = place.as_local() {
2254                         debug!(
2255                             "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2256                              rvalue={:?}",
2257                             assigned_to, rvalue
2258                         );
2259                         // Check if our `target` was captured by a closure.
2260                         if let Rvalue::Aggregate(
2261                             box AggregateKind::Closure(def_id, substs),
2262                             ref operands,
2263                         ) = *rvalue
2264                         {
2265                             for operand in operands {
2266                                 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2267                                     continue;
2268                                 };
2269                                 debug!(
2270                                     "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2271                                     assigned_from
2272                                 );
2273
2274                                 // Find the local from the operand.
2275                                 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2276                                     continue;
2277                                 };
2278
2279                                 if assigned_from_local != target {
2280                                     continue;
2281                                 }
2282
2283                                 // If a closure captured our `target` and then assigned
2284                                 // into a place then we should annotate the closure in
2285                                 // case it ends up being assigned into the return place.
2286                                 annotated_closure =
2287                                     self.annotate_fn_sig(def_id, substs.as_closure().sig());
2288                                 debug!(
2289                                     "annotate_argument_and_return_for_borrow: \
2290                                      annotated_closure={:?} assigned_from_local={:?} \
2291                                      assigned_to={:?}",
2292                                     annotated_closure, assigned_from_local, assigned_to
2293                                 );
2294
2295                                 if assigned_to == mir::RETURN_PLACE {
2296                                     // If it was assigned directly into the return place, then
2297                                     // return now.
2298                                     return annotated_closure;
2299                                 } else {
2300                                     // Otherwise, update the target.
2301                                     target = assigned_to;
2302                                 }
2303                             }
2304
2305                             // If none of our closure's operands matched, then skip to the next
2306                             // statement.
2307                             continue;
2308                         }
2309
2310                         // Otherwise, look at other types of assignment.
2311                         let assigned_from = match rvalue {
2312                             Rvalue::Ref(_, _, assigned_from) => assigned_from,
2313                             Rvalue::Use(operand) => match operand {
2314                                 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2315                                     assigned_from
2316                                 }
2317                                 _ => continue,
2318                             },
2319                             _ => continue,
2320                         };
2321                         debug!(
2322                             "annotate_argument_and_return_for_borrow: \
2323                              assigned_from={:?}",
2324                             assigned_from,
2325                         );
2326
2327                         // Find the local from the rvalue.
2328                         let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2329                         debug!(
2330                             "annotate_argument_and_return_for_borrow: \
2331                              assigned_from_local={:?}",
2332                             assigned_from_local,
2333                         );
2334
2335                         // Check if our local matches the target - if so, we've assigned our
2336                         // borrow to a new place.
2337                         if assigned_from_local != target {
2338                             continue;
2339                         }
2340
2341                         // If we assigned our `target` into a new place, then we should
2342                         // check if it was the return place.
2343                         debug!(
2344                             "annotate_argument_and_return_for_borrow: \
2345                              assigned_from_local={:?} assigned_to={:?}",
2346                             assigned_from_local, assigned_to
2347                         );
2348                         if assigned_to == mir::RETURN_PLACE {
2349                             // If it was then return the annotated closure if there was one,
2350                             // else, annotate this function.
2351                             return annotated_closure.or_else(fallback);
2352                         }
2353
2354                         // If we didn't assign into the return place, then we just update
2355                         // the target.
2356                         target = assigned_to;
2357                     }
2358                 }
2359             }
2360
2361             // Check the terminator if we didn't find anything in the statements.
2362             let terminator = &self.body[location.block].terminator();
2363             debug!(
2364                 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2365                 target, terminator
2366             );
2367             if let TerminatorKind::Call { destination, target: Some(_), args, .. } =
2368                 &terminator.kind
2369             {
2370                 if let Some(assigned_to) = destination.as_local() {
2371                     debug!(
2372                         "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2373                         assigned_to, args
2374                     );
2375                     for operand in args {
2376                         let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2377                             continue;
2378                         };
2379                         debug!(
2380                             "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2381                             assigned_from,
2382                         );
2383
2384                         if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2385                             debug!(
2386                                 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2387                                 assigned_from_local,
2388                             );
2389
2390                             if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2391                                 return annotated_closure.or_else(fallback);
2392                             }
2393                         }
2394                     }
2395                 }
2396             }
2397         }
2398
2399         // If we haven't found an assignment into the return place, then we need not add
2400         // any annotations.
2401         debug!("annotate_argument_and_return_for_borrow: none found");
2402         None
2403     }
2404
2405     /// Annotate the first argument and return type of a function signature if they are
2406     /// references.
2407     fn annotate_fn_sig(
2408         &self,
2409         did: LocalDefId,
2410         sig: ty::PolyFnSig<'tcx>,
2411     ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2412         debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2413         let is_closure = self.infcx.tcx.is_closure(did.to_def_id());
2414         let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
2415         let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2416
2417         // We need to work out which arguments to highlight. We do this by looking
2418         // at the return type, where there are three cases:
2419         //
2420         // 1. If there are named arguments, then we should highlight the return type and
2421         //    highlight any of the arguments that are also references with that lifetime.
2422         //    If there are no arguments that have the same lifetime as the return type,
2423         //    then don't highlight anything.
2424         // 2. The return type is a reference with an anonymous lifetime. If this is
2425         //    the case, then we can take advantage of (and teach) the lifetime elision
2426         //    rules.
2427         //
2428         //    We know that an error is being reported. So the arguments and return type
2429         //    must satisfy the elision rules. Therefore, if there is a single argument
2430         //    then that means the return type and first (and only) argument have the same
2431         //    lifetime and the borrow isn't meeting that, we can highlight the argument
2432         //    and return type.
2433         //
2434         //    If there are multiple arguments then the first argument must be self (else
2435         //    it would not satisfy the elision rules), so we can highlight self and the
2436         //    return type.
2437         // 3. The return type is not a reference. In this case, we don't highlight
2438         //    anything.
2439         let return_ty = sig.output();
2440         match return_ty.skip_binder().kind() {
2441             ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2442                 // This is case 1 from above, return type is a named reference so we need to
2443                 // search for relevant arguments.
2444                 let mut arguments = Vec::new();
2445                 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2446                     if let ty::Ref(argument_region, _, _) = argument.kind() {
2447                         if argument_region == return_region {
2448                             // Need to use the `rustc_middle::ty` types to compare against the
2449                             // `return_region`. Then use the `rustc_hir` type to get only
2450                             // the lifetime span.
2451                             if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2452                                 // With access to the lifetime, we can get
2453                                 // the span of it.
2454                                 arguments.push((*argument, lifetime.span));
2455                             } else {
2456                                 bug!("ty type is a ref but hir type is not");
2457                             }
2458                         }
2459                     }
2460                 }
2461
2462                 // We need to have arguments. This shouldn't happen, but it's worth checking.
2463                 if arguments.is_empty() {
2464                     return None;
2465                 }
2466
2467                 // We use a mix of the HIR and the Ty types to get information
2468                 // as the HIR doesn't have full types for closure arguments.
2469                 let return_ty = sig.output().skip_binder();
2470                 let mut return_span = fn_decl.output.span();
2471                 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2472                     if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2473                         return_span = lifetime.span;
2474                     }
2475                 }
2476
2477                 Some(AnnotatedBorrowFnSignature::NamedFunction {
2478                     arguments,
2479                     return_ty,
2480                     return_span,
2481                 })
2482             }
2483             ty::Ref(_, _, _) if is_closure => {
2484                 // This is case 2 from above but only for closures, return type is anonymous
2485                 // reference so we select
2486                 // the first argument.
2487                 let argument_span = fn_decl.inputs.first()?.span;
2488                 let argument_ty = sig.inputs().skip_binder().first()?;
2489
2490                 // Closure arguments are wrapped in a tuple, so we need to get the first
2491                 // from that.
2492                 if let ty::Tuple(elems) = argument_ty.kind() {
2493                     let &argument_ty = elems.first()?;
2494                     if let ty::Ref(_, _, _) = argument_ty.kind() {
2495                         return Some(AnnotatedBorrowFnSignature::Closure {
2496                             argument_ty,
2497                             argument_span,
2498                         });
2499                     }
2500                 }
2501
2502                 None
2503             }
2504             ty::Ref(_, _, _) => {
2505                 // This is also case 2 from above but for functions, return type is still an
2506                 // anonymous reference so we select the first argument.
2507                 let argument_span = fn_decl.inputs.first()?.span;
2508                 let argument_ty = *sig.inputs().skip_binder().first()?;
2509
2510                 let return_span = fn_decl.output.span();
2511                 let return_ty = sig.output().skip_binder();
2512
2513                 // We expect the first argument to be a reference.
2514                 match argument_ty.kind() {
2515                     ty::Ref(_, _, _) => {}
2516                     _ => return None,
2517                 }
2518
2519                 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2520                     argument_ty,
2521                     argument_span,
2522                     return_ty,
2523                     return_span,
2524                 })
2525             }
2526             _ => {
2527                 // This is case 3 from above, return type is not a reference so don't highlight
2528                 // anything.
2529                 None
2530             }
2531         }
2532     }
2533 }
2534
2535 #[derive(Debug)]
2536 enum AnnotatedBorrowFnSignature<'tcx> {
2537     NamedFunction {
2538         arguments: Vec<(Ty<'tcx>, Span)>,
2539         return_ty: Ty<'tcx>,
2540         return_span: Span,
2541     },
2542     AnonymousFunction {
2543         argument_ty: Ty<'tcx>,
2544         argument_span: Span,
2545         return_ty: Ty<'tcx>,
2546         return_span: Span,
2547     },
2548     Closure {
2549         argument_ty: Ty<'tcx>,
2550         argument_span: Span,
2551     },
2552 }
2553
2554 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2555     /// Annotate the provided diagnostic with information about borrow from the fn signature that
2556     /// helps explain.
2557     pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2558         match self {
2559             &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2560                 diag.span_label(
2561                     argument_span,
2562                     format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2563                 );
2564
2565                 cx.get_region_name_for_ty(argument_ty, 0)
2566             }
2567             &AnnotatedBorrowFnSignature::AnonymousFunction {
2568                 argument_ty,
2569                 argument_span,
2570                 return_ty,
2571                 return_span,
2572             } => {
2573                 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2574                 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2575
2576                 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2577                 let types_equal = return_ty_name == argument_ty_name;
2578                 diag.span_label(
2579                     return_span,
2580                     format!(
2581                         "{}has type `{}`",
2582                         if types_equal { "also " } else { "" },
2583                         return_ty_name,
2584                     ),
2585                 );
2586
2587                 diag.note(
2588                     "argument and return type have the same lifetime due to lifetime elision rules",
2589                 );
2590                 diag.note(
2591                     "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2592                      lifetime-syntax.html#lifetime-elision>",
2593                 );
2594
2595                 cx.get_region_name_for_ty(return_ty, 0)
2596             }
2597             AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2598                 // Region of return type and arguments checked to be the same earlier.
2599                 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2600                 for (_, argument_span) in arguments {
2601                     diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2602                 }
2603
2604                 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2605
2606                 diag.help(&format!(
2607                     "use data from the highlighted arguments which match the `{}` lifetime of \
2608                      the return type",
2609                     region_name,
2610                 ));
2611
2612                 region_name
2613             }
2614         }
2615     }
2616 }
2617
2618 /// Detect whether one of the provided spans is a statement nested within the top-most visited expr
2619 struct ReferencedStatementsVisitor<'a>(&'a [Span], bool);
2620
2621 impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> {
2622     fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
2623         match s.kind {
2624             hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => {
2625                 self.1 = true;
2626             }
2627             _ => {}
2628         }
2629     }
2630 }
2631
2632 /// Given a set of spans representing statements initializing the relevant binding, visit all the
2633 /// function expressions looking for branching code paths that *do not* initialize the binding.
2634 struct ConditionVisitor<'b> {
2635     spans: &'b [Span],
2636     name: &'b str,
2637     errors: Vec<(Span, String)>,
2638 }
2639
2640 impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
2641     fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
2642         match ex.kind {
2643             hir::ExprKind::If(cond, body, None) => {
2644                 // `if` expressions with no `else` that initialize the binding might be missing an
2645                 // `else` arm.
2646                 let mut v = ReferencedStatementsVisitor(self.spans, false);
2647                 v.visit_expr(body);
2648                 if v.1 {
2649                     self.errors.push((
2650                         cond.span,
2651                         format!(
2652                             "if this `if` condition is `false`, {} is not initialized",
2653                             self.name,
2654                         ),
2655                     ));
2656                     self.errors.push((
2657                         ex.span.shrink_to_hi(),
2658                         format!("an `else` arm might be missing here, initializing {}", self.name),
2659                     ));
2660                 }
2661             }
2662             hir::ExprKind::If(cond, body, Some(other)) => {
2663                 // `if` expressions where the binding is only initialized in one of the two arms
2664                 // might be missing a binding initialization.
2665                 let mut a = ReferencedStatementsVisitor(self.spans, false);
2666                 a.visit_expr(body);
2667                 let mut b = ReferencedStatementsVisitor(self.spans, false);
2668                 b.visit_expr(other);
2669                 match (a.1, b.1) {
2670                     (true, true) | (false, false) => {}
2671                     (true, false) => {
2672                         if other.span.is_desugaring(DesugaringKind::WhileLoop) {
2673                             self.errors.push((
2674                                 cond.span,
2675                                 format!(
2676                                     "if this condition isn't met and the `while` loop runs 0 \
2677                                      times, {} is not initialized",
2678                                     self.name
2679                                 ),
2680                             ));
2681                         } else {
2682                             self.errors.push((
2683                                 body.span.shrink_to_hi().until(other.span),
2684                                 format!(
2685                                     "if the `if` condition is `false` and this `else` arm is \
2686                                      executed, {} is not initialized",
2687                                     self.name
2688                                 ),
2689                             ));
2690                         }
2691                     }
2692                     (false, true) => {
2693                         self.errors.push((
2694                             cond.span,
2695                             format!(
2696                                 "if this condition is `true`, {} is not initialized",
2697                                 self.name
2698                             ),
2699                         ));
2700                     }
2701                 }
2702             }
2703             hir::ExprKind::Match(e, arms, loop_desugar) => {
2704                 // If the binding is initialized in one of the match arms, then the other match
2705                 // arms might be missing an initialization.
2706                 let results: Vec<bool> = arms
2707                     .iter()
2708                     .map(|arm| {
2709                         let mut v = ReferencedStatementsVisitor(self.spans, false);
2710                         v.visit_arm(arm);
2711                         v.1
2712                     })
2713                     .collect();
2714                 if results.iter().any(|x| *x) && !results.iter().all(|x| *x) {
2715                     for (arm, seen) in arms.iter().zip(results) {
2716                         if !seen {
2717                             if loop_desugar == hir::MatchSource::ForLoopDesugar {
2718                                 self.errors.push((
2719                                     e.span,
2720                                     format!(
2721                                         "if the `for` loop runs 0 times, {} is not initialized",
2722                                         self.name
2723                                     ),
2724                                 ));
2725                             } else if let Some(guard) = &arm.guard {
2726                                 self.errors.push((
2727                                     arm.pat.span.to(guard.body().span),
2728                                     format!(
2729                                         "if this pattern and condition are matched, {} is not \
2730                                          initialized",
2731                                         self.name
2732                                     ),
2733                                 ));
2734                             } else {
2735                                 self.errors.push((
2736                                     arm.pat.span,
2737                                     format!(
2738                                         "if this pattern is matched, {} is not initialized",
2739                                         self.name
2740                                     ),
2741                                 ));
2742                             }
2743                         }
2744                     }
2745                 }
2746             }
2747             // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should
2748             // also be accounted for. For now it is fine, as if we don't find *any* relevant
2749             // branching code paths, we point at the places where the binding *is* initialized for
2750             // *some* context.
2751             _ => {}
2752         }
2753         walk_expr(self, ex);
2754     }
2755 }