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