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