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