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