2 use rustc_const_eval::util::CallKind;
3 use rustc_data_structures::captures::Captures;
4 use rustc_data_structures::fx::FxHashSet;
6 struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
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,
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;
27 use crate::borrow_set::TwoPhaseActivation;
28 use crate::borrowck_errors;
30 use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;
31 use crate::diagnostics::find_all_local_uses;
33 borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
34 InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
38 explain_borrow::{BorrowExplanation, LaterUseKind},
39 DescribePlaceOpt, RegionName, RegionNameSource, UseSpans,
44 /// Index of the "move out" that we found. The `MoveData` can
45 /// then tell us where the move occurred.
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,
53 /// Which case a StorageDeadOrDrop is for.
54 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
55 enum StorageDeadOrDrop<'tcx> {
61 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
62 pub(crate) fn report_use_of_moved_or_uninitialized(
65 desired_action: InitializationRequiringAction,
66 (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),
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
76 self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
77 let span = use_spans.args_or_use();
79 let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
81 "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
82 move_site_vec, use_spans
84 let move_out_indices: Vec<_> =
85 move_site_vec.iter().map(|move_site| move_site.moi).collect();
87 if move_out_indices.is_empty() {
88 let root_place = PlaceRef { projection: &[], ..used_place };
90 if !self.uninitialized_error_reported.insert(root_place) {
92 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
98 let err = self.report_use_of_uninitialized(
106 self.buffer_error(err);
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) {
111 "report_use_of_moved_or_uninitialized place: error suppressed mois={:?}",
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();
126 && used_place != moved_place.as_ref()
127 && used_place.is_prefix_of(moved_place.as_ref())
130 let partial_str = if is_partial_move { "partial " } else { "" };
131 let partially_str = if is_partial_move { "partially " } else { "" };
133 let mut err = self.cannot_act_on_moved_value(
135 desired_action.as_noun(),
137 self.describe_place_with_options(
139 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
143 let reinit_spans = maybe_reinitialized_locations
147 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
150 .collect::<Vec<Span>>();
152 let reinits = maybe_reinitialized_locations.len();
154 err.span_label(reinit_spans[0], "this reinitialization might get skipped");
155 } else if reinits > 1 {
157 MultiSpan::from_spans(reinit_spans),
159 format!("these {} reinitializations might get skipped", reinits)
162 "these 3 reinitializations and {} other{} might get skipped",
164 if reinits == 4 { "" } else { "s" }
170 let closure = self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
172 let mut is_loop_move = false;
173 let mut in_pattern = false;
174 let mut seen_spans = FxHashSet::default();
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;
180 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
181 let move_span = move_spans.args_or_use();
183 let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
185 let loop_message = if location == move_out.source || move_site.traversed_back_edge {
186 ", in previous iteration of loop"
191 if location == move_out.source {
195 if !seen_spans.contains(&move_span) {
197 self.suggest_ref_or_clone(
206 self.explain_captures(
216 maybe_reinitialized_locations.is_empty(),
219 seen_spans.insert(move_span);
222 use_spans.var_path_only_subdiag(&mut err, desired_action);
228 "value {} here after {}move",
229 desired_action.as_verb_in_past_tense(),
235 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
236 let needs_note = match ty.kind() {
237 ty::Closure(id, _) => {
238 let tables = self.infcx.tcx.typeck(id.expect_local());
239 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
241 tables.closure_kind_origins().get(hir_id).is_none()
246 let mpi = self.move_data.moves[move_out_indices[0]].path;
247 let place = &self.move_data.move_paths[mpi].place;
248 let ty = place.ty(self.body, self.infcx.tcx).ty;
250 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
251 // Same for if we're in a loop, see #101119.
252 if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
253 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
254 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
255 err.span_suggestion_verbose(
258 "consider creating a fresh reborrow of {} here",
259 self.describe_place(moved_place)
260 .map(|n| format!("`{}`", n))
261 .unwrap_or_else(|| "the mutable reference".to_string()),
264 Applicability::MachineApplicable,
269 let opt_name = self.describe_place_with_options(
271 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
273 let note_msg = match opt_name {
274 Some(name) => format!("`{}`", name),
275 None => "value".to_owned(),
277 if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) {
278 // Suppress the next suggestion since we don't want to put more bounds onto
279 // something that already has `Fn`-like bounds (or is a closure), so we can't
282 self.suggest_adding_copy_bounds(&mut err, ty, span);
286 let span = if let Some(local) = place.as_local() {
287 Some(self.body.local_decls[local].source_info.span)
291 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
294 if let UseSpans::FnSelfUse {
295 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
300 "{} occurs due to deref coercion to `{}`",
301 desired_action.as_noun(),
305 // Check first whether the source is accessible (issue #87060)
306 if self.infcx.tcx.sess.source_map().is_span_accessible(deref_target) {
307 err.span_note(deref_target, "deref defined here");
311 self.buffer_move_error(move_out_indices, (used_place, err));
315 fn suggest_ref_or_clone(
319 err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
320 in_pattern: &mut bool,
321 move_spans: UseSpans<'_>,
323 struct ExpressionFinder<'hir> {
325 expr: Option<&'hir hir::Expr<'hir>>,
326 pat: Option<&'hir hir::Pat<'hir>>,
327 parent_pat: Option<&'hir hir::Pat<'hir>>,
329 impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
330 fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
331 if e.span == self.expr_span {
334 hir::intravisit::walk_expr(self, e);
336 fn visit_pat(&mut self, p: &'hir hir::Pat<'hir>) {
337 if p.span == self.expr_span {
340 if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, i, sub) = p.kind {
341 if i.span == self.expr_span || p.span == self.expr_span {
344 // Check if we are in a situation of `ident @ ident` where we want to suggest
345 // `ref ident @ ref ident` or `ref ident @ Struct { ref ident }`.
346 if let Some(subpat) = sub && self.pat.is_none() {
347 self.visit_pat(subpat);
348 if self.pat.is_some() {
349 self.parent_pat = Some(p);
354 hir::intravisit::walk_pat(self, p);
357 let hir = self.infcx.tcx.hir();
358 if let Some(hir::Node::Item(hir::Item {
359 kind: hir::ItemKind::Fn(_, _, body_id),
361 })) = hir.find(hir.local_def_id_to_hir_id(self.mir_def_id()))
362 && let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id)
364 let place = &self.move_data.move_paths[mpi].place;
365 let span = place.as_local()
366 .map(|local| self.body.local_decls[local].source_info.span);
367 let mut finder = ExpressionFinder {
368 expr_span: move_span,
373 finder.visit_expr(expr);
374 if let Some(span) = span && let Some(expr) = finder.expr {
375 for (_, expr) in hir.parent_iter(expr.hir_id) {
376 if let hir::Node::Expr(expr) = expr {
377 if expr.span.contains(span) {
378 // If the let binding occurs within the same loop, then that
379 // loop isn't relevant, like in the following, the outermost `loop`
380 // doesn't play into `x` being moved.
383 // let x = String::new();
391 if let hir::ExprKind::Loop(.., loop_span) = expr.kind {
392 err.span_label(loop_span, "inside of this loop");
396 let typeck = self.infcx.tcx.typeck(self.mir_def_id());
397 let hir_id = hir.get_parent_node(expr.hir_id);
398 if let Some(parent) = hir.find(hir_id) {
399 let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
400 && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
401 && let Some(def_id) = typeck.type_dependent_def_id(parent_expr.hir_id)
403 (def_id.as_local(), args, 1)
404 } else if let hir::Node::Expr(parent_expr) = parent
405 && let hir::ExprKind::Call(call, args) = parent_expr.kind
406 && let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind()
408 (def_id.as_local(), args, 0)
412 if let Some(def_id) = def_id
413 && let Some(node) = hir.find(hir.local_def_id_to_hir_id(def_id))
414 && let Some(fn_sig) = node.fn_sig()
415 && let Some(ident) = node.ident()
416 && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
417 && let Some(arg) = fn_sig.decl.inputs.get(pos + offset)
419 let mut span: MultiSpan = arg.span.into();
420 span.push_span_label(
422 "this parameter takes ownership of the value".to_string(),
424 let descr = match node.fn_kind() {
425 Some(hir::intravisit::FnKind::ItemFn(..)) | None => "function",
426 Some(hir::intravisit::FnKind::Method(..)) => "method",
427 Some(hir::intravisit::FnKind::Closure) => "closure",
429 span.push_span_label(
431 format!("in this {descr}"),
436 "consider changing this parameter type in {descr} `{ident}` to \
437 borrow instead if owning the value isn't necessary",
441 let place = &self.move_data.move_paths[mpi].place;
442 let ty = place.ty(self.body, self.infcx.tcx).ty;
443 if let hir::Node::Expr(parent_expr) = parent
444 && let hir::ExprKind::Call(call_expr, _) = parent_expr.kind
445 && let hir::ExprKind::Path(
446 hir::QPath::LangItem(LangItem::IntoIterIntoIter, _, _)
449 // Do not suggest `.clone()` in a `for` loop, we already suggest borrowing.
450 } else if let UseSpans::FnSelfUse {
451 kind: CallKind::Normal { .. },
454 // We already suggest cloning for these cases in `explain_captures`.
456 self.suggest_cloning(err, ty, move_span);
460 if let Some(pat) = finder.pat {
462 let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())];
463 if let Some(pat) = finder.parent_pat {
464 sugg.insert(0, (pat.span.shrink_to_lo(), "ref ".to_string()));
466 err.multipart_suggestion_verbose(
467 "borrow this binding in the pattern to avoid moving the value",
469 Applicability::MachineApplicable,
475 fn report_use_of_uninitialized(
478 used_place: PlaceRef<'tcx>,
479 moved_place: PlaceRef<'tcx>,
480 desired_action: InitializationRequiringAction,
482 use_spans: UseSpans<'tcx>,
483 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
484 // We need all statements in the body where the binding was assigned to to later find all
485 // the branching code paths where the binding *wasn't* assigned to.
486 let inits = &self.move_data.init_path_map[mpi];
487 let move_path = &self.move_data.move_paths[mpi];
488 let decl_span = self.body.local_decls[move_path.place.local].source_info.span;
489 let mut spans = vec![];
490 for init_idx in inits {
491 let init = &self.move_data.inits[*init_idx];
492 let span = init.span(&self.body);
493 if !span.is_dummy() {
498 let (name, desc) = match self.describe_place_with_options(
500 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
502 Some(name) => (format!("`{name}`"), format!("`{name}` ")),
503 None => ("the variable".to_string(), String::new()),
505 let path = match self.describe_place_with_options(
507 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
509 Some(name) => format!("`{name}`"),
510 None => "value".to_string(),
513 // We use the statements were the binding was initialized, and inspect the HIR to look
514 // for the branching codepaths that aren't covered, to point at them.
515 let map = self.infcx.tcx.hir();
516 let body_id = map.body_owned_by(self.mir_def_id());
517 let body = map.body(body_id);
519 let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] };
520 visitor.visit_body(&body);
522 let mut show_assign_sugg = false;
523 let isnt_initialized = if let InitializationRequiringAction::PartialAssignment
524 | InitializationRequiringAction::Assignment = desired_action
526 // The same error is emitted for bindings that are *sometimes* initialized and the ones
527 // that are *partially* initialized by assigning to a field of an uninitialized
528 // binding. We differentiate between them for more accurate wording here.
529 "isn't fully initialized"
533 // We filter these to avoid misleading wording in cases like the following,
534 // where `x` has an `init`, but it is in the same place we're looking at:
540 // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs`
545 .any(|sp| span < sp && !sp.contains(span))
550 show_assign_sugg = true;
553 "is possibly-uninitialized"
556 let used = desired_action.as_general_verb_in_past_tense();
558 struct_span_err!(self, span, E0381, "{used} binding {desc}{isnt_initialized}");
559 use_spans.var_path_only_subdiag(&mut err, desired_action);
561 if let InitializationRequiringAction::PartialAssignment
562 | InitializationRequiringAction::Assignment = desired_action
565 "partial initialization isn't supported, fully initialize the binding with a \
566 default value and mutate it, or use `std::mem::MaybeUninit`",
569 err.span_label(span, format!("{path} {used} here but it {isnt_initialized}"));
571 let mut shown = false;
572 for (sp, label) in visitor.errors {
573 if sp < span && !sp.overlaps(span) {
574 // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention
575 // match arms coming after the primary span because they aren't relevant:
579 // _ if { x = 2; true } => {}
584 // _ => {} // We don't want to point to this.
587 err.span_label(sp, &label);
593 if *sp < span && !sp.overlaps(span) {
594 err.span_label(*sp, "binding initialized here in some conditions");
599 err.span_label(decl_span, "binding declared here but left uninitialized");
600 if show_assign_sugg {
603 sugg_span: Option<Span>,
606 impl<'v> Visitor<'v> for LetVisitor {
607 fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
608 if self.sugg_span.is_some() {
611 if let hir::StmtKind::Local(hir::Local {
612 span, ty, init: None, ..
613 }) = &ex.kind && span.contains(self.decl_span) {
614 self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span));
616 hir::intravisit::walk_stmt(self, ex);
620 let mut visitor = LetVisitor { decl_span, sugg_span: None };
621 visitor.visit_body(&body);
622 if let Some(span) = visitor.sugg_span {
623 self.suggest_assign_value(&mut err, moved_place, span);
629 fn suggest_assign_value(
631 err: &mut Diagnostic,
632 moved_place: PlaceRef<'tcx>,
635 let ty = moved_place.ty(self.body, self.infcx.tcx).ty;
636 debug!("ty: {:?}, kind: {:?}", ty, ty.kind());
638 let tcx = self.infcx.tcx;
639 let implements_default = |ty, param_env| {
640 let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else {
643 // Regions are already solved, so we must use a fresh InferCtxt,
644 // but the type has region variables, so erase those.
647 .type_implements_trait(default_trait, [tcx.erase_regions(ty)], param_env)
648 .must_apply_modulo_regions()
651 let assign_value = match ty.kind() {
653 ty::Float(_) => "0.0",
654 ty::Int(_) | ty::Uint(_) => "0",
655 ty::Never | ty::Error(_) => "",
656 ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => "vec![]",
657 ty::Adt(_, _) if implements_default(ty, self.param_env) => "Default::default()",
661 if !assign_value.is_empty() {
662 err.span_suggestion_verbose(
663 sugg_span.shrink_to_hi(),
664 "consider assigning a value",
665 format!(" = {}", assign_value),
666 Applicability::MaybeIncorrect,
671 fn suggest_borrow_fn_like(
673 err: &mut Diagnostic,
675 move_sites: &[MoveSite],
678 let tcx = self.infcx.tcx;
680 // Find out if the predicates show that the type is a Fn or FnMut
681 let find_fn_kind_from_did = |predicates: ty::EarlyBinder<
682 &[(ty::Predicate<'tcx>, Span)],
685 predicates.0.iter().find_map(|(pred, _)| {
686 let pred = if let Some(substs) = substs {
687 predicates.rebind(*pred).subst(tcx, substs).kind().skip_binder()
689 pred.kind().skip_binder()
691 if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred && pred.self_ty() == ty {
692 if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
693 return Some(hir::Mutability::Not);
694 } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
695 return Some(hir::Mutability::Mut);
702 // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
703 // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
704 // These types seem reasonably opaque enough that they could be substituted with their
705 // borrowed variants in a function body when we see a move error.
706 let borrow_level = match ty.kind() {
707 ty::Param(_) => find_fn_kind_from_did(
708 tcx.bound_explicit_predicates_of(self.mir_def_id().to_def_id())
709 .map_bound(|p| p.predicates),
712 ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
713 find_fn_kind_from_did(tcx.bound_explicit_item_bounds(*def_id), Some(*substs))
715 ty::Closure(_, substs) => match substs.as_closure().kind() {
716 ty::ClosureKind::Fn => Some(hir::Mutability::Not),
717 ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
723 let Some(borrow_level) = borrow_level else { return false; };
724 let sugg = move_sites
727 let move_out = self.move_data.moves[(*move_site).moi];
728 let moved_place = &self.move_data.move_paths[move_out.path].place;
729 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
730 let move_span = move_spans.args_or_use();
731 let suggestion = borrow_level.ref_prefix_str().to_owned();
732 (move_span.shrink_to_lo(), suggestion)
735 err.multipart_suggestion_verbose(
736 format!("consider {}borrowing {value_name}", borrow_level.mutably_str()),
738 Applicability::MaybeIncorrect,
743 fn suggest_cloning(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
744 let tcx = self.infcx.tcx;
745 // Try to find predicates on *generic params* that would allow copying `ty`
746 let infcx = tcx.infer_ctxt().build();
748 if let Some(clone_trait_def) = tcx.lang_items().clone_trait()
750 .type_implements_trait(
752 [tcx.erase_regions(ty)],
755 .must_apply_modulo_regions()
757 err.span_suggestion_verbose(
759 "consider cloning the value if the performance cost is acceptable",
761 Applicability::MachineApplicable,
766 fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
767 let tcx = self.infcx.tcx;
768 let generics = tcx.generics_of(self.mir_def_id());
770 let Some(hir_generics) = tcx
771 .typeck_root_def_id(self.mir_def_id().to_def_id())
773 .and_then(|def_id| tcx.hir().get_generics(def_id))
775 // Try to find predicates on *generic params* that would allow copying `ty`
776 let infcx = tcx.infer_ctxt().build();
777 let copy_did = infcx.tcx.require_lang_item(LangItem::Copy, Some(span));
778 let cause = ObligationCause::new(
781 rustc_infer::traits::ObligationCauseCode::MiscObligation,
783 let errors = rustc_trait_selection::traits::fully_solve_bound(
787 // Erase any region vids from the type, which may not be resolved
788 infcx.tcx.erase_regions(ty),
792 // Only emit suggestion if all required predicates are on generic
793 let predicates: Result<Vec<_>, _> = errors
795 .map(|err| match err.obligation.predicate.kind().skip_binder() {
796 PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
797 match predicate.self_ty().kind() {
798 ty::Param(param_ty) => Ok((
799 generics.type_param(param_ty, tcx),
800 predicate.trait_ref.print_only_trait_path().to_string(),
809 if let Ok(predicates) = predicates {
810 suggest_constraining_type_params(
816 .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
821 pub(crate) fn report_move_out_while_borrowed(
824 (place, span): (Place<'tcx>, Span),
825 borrow: &BorrowData<'tcx>,
828 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
829 location, place, span, borrow
831 let value_msg = self.describe_any_place(place.as_ref());
832 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
834 let borrow_spans = self.retrieve_borrow_spans(borrow);
835 let borrow_span = borrow_spans.args_or_use();
837 let move_spans = self.move_spans(place.as_ref(), location);
838 let span = move_spans.args_or_use();
840 let mut err = self.cannot_move_when_borrowed(
843 &self.describe_any_place(place.as_ref()),
848 borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow);
850 move_spans.var_span_label(
852 format!("move occurs due to use{}", move_spans.describe()),
856 self.explain_why_borrow_contains_point(location, borrow, None)
857 .add_explanation_to_diagnostic(
866 self.buffer_error(err);
869 pub(crate) fn report_use_while_mutably_borrowed(
872 (place, _span): (Place<'tcx>, Span),
873 borrow: &BorrowData<'tcx>,
874 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
875 let borrow_spans = self.retrieve_borrow_spans(borrow);
876 let borrow_span = borrow_spans.args_or_use();
878 // Conflicting borrows are reported separately, so only check for move
880 let use_spans = self.move_spans(place.as_ref(), location);
881 let span = use_spans.var_or_use();
883 // 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
884 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
885 let mut err = self.cannot_use_when_mutably_borrowed(
887 &self.describe_any_place(place.as_ref()),
889 &self.describe_any_place(borrow.borrowed_place.as_ref()),
891 borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| {
892 use crate::session_diagnostics::CaptureVarCause::*;
893 let place = &borrow.borrowed_place;
894 let desc_place = self.describe_any_place(place.as_ref());
896 Some(_) => BorrowUsePlaceGenerator { place: desc_place, var_span },
897 None => BorrowUsePlaceClosure { place: desc_place, var_span },
901 self.explain_why_borrow_contains_point(location, borrow, None)
902 .add_explanation_to_diagnostic(
914 pub(crate) fn report_conflicting_borrow(
917 (place, span): (Place<'tcx>, Span),
918 gen_borrow_kind: BorrowKind,
919 issued_borrow: &BorrowData<'tcx>,
920 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
921 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
922 let issued_span = issued_spans.args_or_use();
924 let borrow_spans = self.borrow_spans(span, location);
925 let span = borrow_spans.args_or_use();
927 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
933 let (desc_place, msg_place, msg_borrow, union_type_name) =
934 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
936 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
937 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
939 // FIXME: supply non-"" `opt_via` when appropriate
940 let first_borrow_desc;
941 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
942 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
943 first_borrow_desc = "mutable ";
944 self.cannot_reborrow_already_borrowed(
956 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
957 first_borrow_desc = "immutable ";
958 self.cannot_reborrow_already_borrowed(
971 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
972 first_borrow_desc = "first ";
973 let mut err = self.cannot_mutably_borrow_multiply(
981 self.suggest_split_at_mut_if_applicable(
984 issued_borrow.borrowed_place,
989 (BorrowKind::Unique, BorrowKind::Unique) => {
990 first_borrow_desc = "first ";
991 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
994 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
995 if let Some(immutable_section_description) =
996 self.classify_immutable_section(issued_borrow.assigned_place)
998 let mut err = self.cannot_mutate_in_immutable_section(
1002 immutable_section_description,
1005 borrow_spans.var_span_label(
1008 "borrow occurs due to use of {}{}",
1010 borrow_spans.describe(),
1017 first_borrow_desc = "immutable ";
1018 self.cannot_reborrow_already_borrowed(
1032 (BorrowKind::Unique, _) => {
1033 first_borrow_desc = "first ";
1034 self.cannot_uniquely_borrow_by_one_closure(
1046 (BorrowKind::Shared, BorrowKind::Unique) => {
1047 first_borrow_desc = "first ";
1048 self.cannot_reborrow_already_uniquely_borrowed(
1061 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
1062 first_borrow_desc = "first ";
1063 self.cannot_reborrow_already_uniquely_borrowed(
1076 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
1078 BorrowKind::Shallow,
1079 BorrowKind::Mut { .. }
1080 | BorrowKind::Unique
1081 | BorrowKind::Shared
1082 | BorrowKind::Shallow,
1083 ) => unreachable!(),
1086 if issued_spans == borrow_spans {
1087 borrow_spans.var_span_label(
1089 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
1090 gen_borrow_kind.describe_mutability(),
1093 let borrow_place = &issued_borrow.borrowed_place;
1094 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
1095 issued_spans.var_span_label(
1098 "first borrow occurs due to use of {}{}",
1100 issued_spans.describe(),
1102 issued_borrow.kind.describe_mutability(),
1105 borrow_spans.var_span_label(
1108 "second borrow occurs due to use of {}{}",
1110 borrow_spans.describe(),
1112 gen_borrow_kind.describe_mutability(),
1116 if union_type_name != "" {
1118 "{} is a field of the union `{}`, so it overlaps the field {}",
1119 msg_place, union_type_name, msg_borrow,
1123 explanation.add_explanation_to_diagnostic(
1130 Some((issued_span, span)),
1133 self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation);
1138 #[instrument(level = "debug", skip(self, err))]
1139 fn suggest_using_local_if_applicable(
1141 err: &mut Diagnostic,
1143 issued_borrow: &BorrowData<'tcx>,
1144 explanation: BorrowExplanation<'tcx>,
1146 let used_in_call = matches!(
1148 BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _)
1151 debug!("not later used in call");
1156 if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation {
1162 let outer_call_loc =
1163 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
1166 issued_borrow.reserve_location
1168 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
1170 let inner_param_location = location;
1171 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
1172 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
1175 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
1177 "`inner_param_location` {:?} is not for an assignment: {:?}",
1178 inner_param_location, inner_param_stmt
1182 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
1183 let Some((inner_call_loc, inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
1184 let Either::Right(term) = self.body.stmt_at(loc) else {
1185 debug!("{:?} is a statement, so it can't be a call", loc);
1188 let TerminatorKind::Call { args, .. } = &term.kind else {
1189 debug!("not a call: {:?}", term);
1192 debug!("checking call args for uses of inner_param: {:?}", args);
1193 if args.contains(&Operand::Move(inner_param)) {
1199 debug!("no uses of inner_param found as a by-move call arg");
1202 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
1204 let inner_call_span = inner_call_term.source_info.span;
1205 let outer_call_span = match use_span {
1207 None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span,
1209 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
1210 // FIXME: This stops the suggestion in some cases where it should be emitted.
1211 // Fix the spans for those cases so it's emitted correctly.
1213 "outer span {:?} does not strictly contain inner span {:?}",
1214 outer_call_span, inner_call_span
1221 "try adding a local storing this{}...",
1222 if use_span.is_some() { "" } else { " argument" }
1228 "...and then using that local {}",
1229 if use_span.is_some() { "here" } else { "as the argument to this call" }
1234 fn suggest_split_at_mut_if_applicable(
1236 err: &mut Diagnostic,
1238 borrowed_place: Place<'tcx>,
1240 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
1241 (&place.projection[..], &borrowed_place.projection[..])
1244 "consider using `.split_at_mut(position)` or similar method to obtain \
1245 two mutable non-overlapping sub-slices",
1250 /// Returns the description of the root place for a conflicting borrow and the full
1251 /// descriptions of the places that caused the conflict.
1253 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
1254 /// attempted while a shared borrow is live, then this function will return:
1259 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
1260 /// a shared borrow of another field `x.y`, then this function will return:
1262 /// ("x", "x.z", "x.y")
1265 /// In the more complex union case, where the union is a field of a struct, then if a mutable
1266 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
1267 /// another field `x.u.y`, then this function will return:
1269 /// ("x.u", "x.u.z", "x.u.y")
1272 /// This is used when creating error messages like below:
1275 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
1276 /// mutable (via `a.u.s.b`) [E0502]
1278 pub(crate) fn describe_place_for_conflicting_borrow(
1280 first_borrowed_place: Place<'tcx>,
1281 second_borrowed_place: Place<'tcx>,
1282 ) -> (String, String, String, String) {
1283 // Define a small closure that we can use to check if the type of a place
1285 let union_ty = |place_base| {
1286 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
1287 // using a type annotation in the closure argument instead leads to a lifetime error.
1288 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
1289 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
1292 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
1293 // code duplication (particularly around returning an empty description in the failure
1297 // If we have a conflicting borrow of the same place, then we don't want to add
1298 // an extraneous "via x.y" to our diagnostics, so filter out this case.
1299 first_borrowed_place != second_borrowed_place
1302 // We're going to want to traverse the first borrowed place to see if we can find
1303 // field access to a union. If we find that, then we will keep the place of the
1304 // union being accessed and the field that was being accessed so we can check the
1305 // second borrowed place for the same union and an access to a different field.
1306 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
1308 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
1309 return Some((place_base, field));
1316 .and_then(|(target_base, target_field)| {
1317 // With the place of a union and a field access into it, we traverse the second
1318 // borrowed place and look for an access to a different field of the same union.
1319 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
1320 if let ProjectionElem::Field(field, _) = elem {
1321 if let Some(union_ty) = union_ty(place_base) {
1322 if field != target_field && place_base == target_base {
1324 self.describe_any_place(place_base),
1325 self.describe_any_place(first_borrowed_place.as_ref()),
1326 self.describe_any_place(second_borrowed_place.as_ref()),
1327 union_ty.to_string(),
1335 .unwrap_or_else(|| {
1336 // If we didn't find a field access into a union, or both places match, then
1337 // only return the description of the first place.
1339 self.describe_any_place(first_borrowed_place.as_ref()),
1347 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
1349 /// This means that some data referenced by `borrow` needs to live
1350 /// past the point where the StorageDeadOrDrop of `place` occurs.
1351 /// This is usually interpreted as meaning that `place` has too
1352 /// short a lifetime. (But sometimes it is more useful to report
1353 /// it as a more direct conflict between the execution of a
1354 /// `Drop::drop` with an aliasing borrow.)
1355 #[instrument(level = "debug", skip(self))]
1356 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
1359 borrow: &BorrowData<'tcx>,
1360 place_span: (Place<'tcx>, Span),
1361 kind: Option<WriteKind>,
1363 let drop_span = place_span.1;
1365 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1367 let borrow_spans = self.retrieve_borrow_spans(borrow);
1368 let borrow_span = borrow_spans.var_or_use_path_span();
1370 assert!(root_place.projection.is_empty());
1371 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1373 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1375 if self.access_place_error_reported.contains(&(
1376 Place { local: root_place.local, projection: root_place_projection },
1380 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1386 self.access_place_error_reported.insert((
1387 Place { local: root_place.local, projection: root_place_projection },
1391 let borrowed_local = borrow.borrowed_place.local;
1392 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1394 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1395 self.buffer_error(err);
1399 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1400 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1402 // If a borrow of path `B` conflicts with drop of `D` (and
1403 // we're not in the uninteresting case where `B` is a
1404 // prefix of `D`), then report this as a more interesting
1405 // destructor conflict.
1406 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1407 self.report_borrow_conflicts_with_destructor(
1408 location, borrow, place_span, kind, dropped_ty,
1414 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1416 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1417 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1419 debug!(?place_desc, ?explanation);
1421 let err = match (place_desc, explanation) {
1422 // If the outlives constraint comes from inside the closure,
1427 // Box::new(|| y) as Box<Fn() -> &'static i32>
1429 // then just use the normal error. The closure isn't escaping
1430 // and `move` will not help here.
1433 BorrowExplanation::MustBeValidFor {
1435 category @ (ConstraintCategory::Return(_)
1436 | ConstraintCategory::CallArgument(_)
1437 | ConstraintCategory::OpaqueType),
1438 from_closure: false,
1443 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1444 .report_escaping_closure_capture(
1450 &format!("`{}`", name),
1454 BorrowExplanation::MustBeValidFor {
1455 category: ConstraintCategory::Assignment,
1456 from_closure: false,
1459 source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
1465 ) => self.report_escaping_data(borrow_span, &name, upvar_span, upvar_name, span),
1466 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1474 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1484 self.buffer_error(err);
1487 fn report_local_value_does_not_live_long_enough(
1491 borrow: &BorrowData<'tcx>,
1493 borrow_spans: UseSpans<'tcx>,
1494 explanation: BorrowExplanation<'tcx>,
1495 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1497 "report_local_value_does_not_live_long_enough(\
1498 {:?}, {:?}, {:?}, {:?}, {:?}\
1500 location, name, borrow, drop_span, borrow_spans
1503 let borrow_span = borrow_spans.var_or_use_path_span();
1504 if let BorrowExplanation::MustBeValidFor {
1508 from_closure: false,
1512 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1517 opt_place_desc.as_ref(),
1523 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1525 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1526 let region_name = annotation.emit(self, &mut err);
1530 format!("`{}` would have to be valid for `{}`...", name, region_name),
1533 let fn_hir_id = self.mir_hir_id();
1537 "...but `{}` will be dropped here, when the {} returns",
1542 .opt_name(fn_hir_id)
1543 .map(|name| format!("function `{}`", name))
1544 .unwrap_or_else(|| {
1548 .typeck(self.mir_def_id())
1549 .node_type(fn_hir_id)
1552 ty::Closure(..) => "enclosing closure",
1553 ty::Generator(..) => "enclosing generator",
1554 kind => bug!("expected closure or generator, found {:?}", kind),
1562 "functions cannot return a borrow to data owned within the function's scope, \
1563 functions can only return borrows to data passed as arguments",
1566 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1567 references-and-borrowing.html#dangling-references>",
1570 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1572 explanation.add_explanation_to_diagnostic(
1583 err.span_label(borrow_span, "borrowed value does not live long enough");
1584 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1586 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1588 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1590 explanation.add_explanation_to_diagnostic(
1604 fn report_borrow_conflicts_with_destructor(
1607 borrow: &BorrowData<'tcx>,
1608 (place, drop_span): (Place<'tcx>, Span),
1609 kind: Option<WriteKind>,
1610 dropped_ty: Ty<'tcx>,
1613 "report_borrow_conflicts_with_destructor(\
1614 {:?}, {:?}, ({:?}, {:?}), {:?}\
1616 location, borrow, place, drop_span, kind,
1619 let borrow_spans = self.retrieve_borrow_spans(borrow);
1620 let borrow_span = borrow_spans.var_or_use();
1622 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1624 let what_was_dropped = match self.describe_place(place.as_ref()) {
1625 Some(name) => format!("`{}`", name),
1626 None => String::from("temporary value"),
1629 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1630 Some(borrowed) => format!(
1631 "here, drop of {D} needs exclusive access to `{B}`, \
1632 because the type `{T}` implements the `Drop` trait",
1633 D = what_was_dropped,
1638 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1639 D = what_was_dropped,
1643 err.span_label(drop_span, label);
1645 // Only give this note and suggestion if they could be relevant.
1647 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1649 BorrowExplanation::UsedLater { .. }
1650 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1651 err.note("consider using a `let` binding to create a longer lived value");
1656 explanation.add_explanation_to_diagnostic(
1666 self.buffer_error(err);
1669 fn report_thread_local_value_does_not_live_long_enough(
1673 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1675 "report_thread_local_value_does_not_live_long_enough(\
1678 drop_span, borrow_span
1681 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1685 "thread-local variables cannot be borrowed beyond the end of the function",
1687 err.span_label(drop_span, "end of enclosing function is here");
1692 #[instrument(level = "debug", skip(self))]
1693 fn report_temporary_value_does_not_live_long_enough(
1696 borrow: &BorrowData<'tcx>,
1698 borrow_spans: UseSpans<'tcx>,
1700 explanation: BorrowExplanation<'tcx>,
1701 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1702 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1705 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1716 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1717 err.span_label(proper_span, "creates a temporary value which is freed while still in use");
1718 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1721 BorrowExplanation::UsedLater(..)
1722 | BorrowExplanation::UsedLaterInLoop(..)
1723 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1724 // Only give this note and suggestion if it could be relevant.
1725 let sm = self.infcx.tcx.sess.source_map();
1726 let mut suggested = false;
1727 let msg = "consider using a `let` binding to create a longer lived value";
1729 /// We check that there's a single level of block nesting to ensure always correct
1730 /// suggestions. If we don't, then we only provide a free-form message to avoid
1731 /// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`.
1732 /// We could expand the analysis to suggest hoising all of the relevant parts of
1733 /// the users' code to make the code compile, but that could be too much.
1734 struct NestedStatementVisitor {
1740 impl<'tcx> Visitor<'tcx> for NestedStatementVisitor {
1741 fn visit_block(&mut self, block: &hir::Block<'tcx>) {
1743 walk_block(self, block);
1746 fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) {
1747 if self.span == expr.span {
1748 self.found = self.current;
1750 walk_expr(self, expr);
1753 let source_info = self.body.source_info(location);
1754 if let Some(scope) = self.body.source_scopes.get(source_info.scope)
1755 && let ClearCrossCrate::Set(scope_data) = &scope.local_data
1756 && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root)
1757 && let Some(id) = node.body_id()
1758 && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind
1760 for stmt in block.stmts {
1761 let mut visitor = NestedStatementVisitor {
1766 visitor.visit_stmt(stmt);
1767 if visitor.found == 0
1768 && stmt.span.contains(proper_span)
1769 && let Some(p) = sm.span_to_margin(stmt.span)
1770 && let Ok(s) = sm.span_to_snippet(proper_span)
1772 let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
1773 err.multipart_suggestion_verbose(
1776 (stmt.span.shrink_to_lo(), addition),
1777 (proper_span, "binding".to_string()),
1779 Applicability::MaybeIncorrect,
1792 explanation.add_explanation_to_diagnostic(
1802 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1804 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1809 fn try_report_cannot_return_reference_to_local(
1811 borrow: &BorrowData<'tcx>,
1814 category: ConstraintCategory<'tcx>,
1815 opt_place_desc: Option<&String>,
1816 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1817 let return_kind = match category {
1818 ConstraintCategory::Return(_) => "return",
1819 ConstraintCategory::Yield => "yield",
1823 // FIXME use a better heuristic than Spans
1824 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1830 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1831 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1832 match self.body.local_kind(local) {
1833 LocalKind::ReturnPointer | LocalKind::Temp => {
1834 bug!("temporary or return pointer with a name")
1836 LocalKind::Var => "local variable ",
1838 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1840 "variable captured by `move` "
1842 LocalKind::Arg => "function parameter ",
1848 format!("{}`{}`", local_kind, place_desc),
1849 format!("`{}` is borrowed here", place_desc),
1853 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1854 let local = root_place.local;
1855 match self.body.local_kind(local) {
1856 LocalKind::ReturnPointer | LocalKind::Temp => {
1857 ("temporary value".to_string(), "temporary value created here".to_string())
1860 "function parameter".to_string(),
1861 "function parameter borrowed here".to_string(),
1864 ("local binding".to_string(), "local binding introduced here".to_string())
1869 let mut err = self.cannot_return_reference_to_local(
1876 if return_span != borrow_span {
1877 err.span_label(borrow_span, note);
1879 let tcx = self.infcx.tcx;
1881 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1882 let return_ty = tcx.erase_regions(return_ty);
1885 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator)
1888 .type_implements_trait(iter_trait, [return_ty], self.param_env)
1889 .must_apply_modulo_regions()
1891 err.span_suggestion_hidden(
1892 return_span.shrink_to_hi(),
1893 "use `.collect()` to allocate the iterator",
1894 ".collect::<Vec<_>>()",
1895 Applicability::MaybeIncorrect,
1903 fn report_escaping_closure_capture(
1905 use_span: UseSpans<'tcx>,
1907 fr_name: &RegionName,
1908 category: ConstraintCategory<'tcx>,
1909 constraint_span: Span,
1911 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1912 let tcx = self.infcx.tcx;
1913 let args_span = use_span.args_or_use();
1915 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1917 if string.starts_with("async ") {
1918 let pos = args_span.lo() + BytePos(6);
1919 (args_span.with_lo(pos).with_hi(pos), "move ")
1920 } else if string.starts_with("async|") {
1921 let pos = args_span.lo() + BytePos(5);
1922 (args_span.with_lo(pos).with_hi(pos), " move")
1924 (args_span.shrink_to_lo(), "move ")
1927 Err(_) => (args_span, "move |<args>| <body>"),
1929 let kind = match use_span.generator_kind() {
1930 Some(generator_kind) => match generator_kind {
1931 GeneratorKind::Async(async_kind) => match async_kind {
1932 AsyncGeneratorKind::Block => "async block",
1933 AsyncGeneratorKind::Closure => "async closure",
1934 _ => bug!("async block/closure expected, but async function found."),
1936 GeneratorKind::Gen => "generator",
1942 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1943 err.span_suggestion_verbose(
1946 "to force the {} to take ownership of {} (and any \
1947 other referenced variables), use the `move` keyword",
1951 Applicability::MachineApplicable,
1955 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1956 let msg = format!("{} is returned here", kind);
1957 err.span_note(constraint_span, &msg);
1959 ConstraintCategory::CallArgument(_) => {
1960 fr_name.highlight_region_name(&mut err);
1961 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1963 "async blocks are not executed immediately and must either take a \
1964 reference or ownership of outside variables they use",
1967 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1968 err.span_note(constraint_span, &msg);
1972 "report_escaping_closure_capture called with unexpected constraint \
1981 fn report_escaping_data(
1984 name: &Option<String>,
1988 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1989 let tcx = self.infcx.tcx;
1991 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1994 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1998 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
2001 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
2003 if let Some(name) = name {
2006 format!("reference to `{}` escapes the {} body here", name, escapes_from),
2011 format!("reference escapes the {} body here", escapes_from),
2018 fn get_moved_indexes(
2022 ) -> (Vec<MoveSite>, Vec<Location>) {
2023 fn predecessor_locations<'tcx, 'a>(
2024 body: &'a mir::Body<'tcx>,
2026 ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
2027 if location.statement_index == 0 {
2028 let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
2029 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
2031 Either::Right(std::iter::once(Location {
2032 statement_index: location.statement_index - 1,
2038 let mut mpis = vec![mpi];
2039 let move_paths = &self.move_data.move_paths;
2040 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
2042 let mut stack = Vec::new();
2043 let mut back_edge_stack = Vec::new();
2045 predecessor_locations(self.body, location).for_each(|predecessor| {
2046 if location.dominates(predecessor, &self.dominators) {
2047 back_edge_stack.push(predecessor)
2049 stack.push(predecessor);
2053 let mut reached_start = false;
2055 /* Check if the mpi is initialized as an argument */
2056 let mut is_argument = false;
2057 for arg in self.body.args_iter() {
2058 let path = self.move_data.rev_lookup.find_local(arg);
2059 if mpis.contains(&path) {
2064 let mut visited = FxHashSet::default();
2065 let mut move_locations = FxHashSet::default();
2066 let mut reinits = vec![];
2067 let mut result = vec![];
2069 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
2071 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
2072 location, is_back_edge
2075 if !visited.insert(location) {
2081 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
2082 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
2083 // this analysis only tries to find moves explicitly
2084 // written by the user, so we ignore the move-outs
2085 // created by `StorageDead` and at the beginning
2088 // If we are found a use of a.b.c which was in error, then we want to look for
2089 // moves not only of a.b.c but also a.b and a.
2091 // Note that the moves data already includes "parent" paths, so we don't have to
2092 // worry about the other case: that is, if there is a move of a.b.c, it is already
2093 // marked as a move of a.b and a as well, so we will generate the correct errors
2095 for moi in &self.move_data.loc_map[location] {
2096 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
2097 let path = self.move_data.moves[*moi].path;
2098 if mpis.contains(&path) {
2100 "report_use_of_moved_or_uninitialized: found {:?}",
2101 move_paths[path].place
2103 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
2104 move_locations.insert(location);
2106 // Strictly speaking, we could continue our DFS here. There may be
2107 // other moves that can reach the point of error. But it is kind of
2108 // confusing to highlight them.
2116 // drop(a); // <-- current point of error
2119 // Because we stop the DFS here, we only highlight `let c = a`,
2120 // and not `let b = a`. We will of course also report an error at
2121 // `let c = a` which highlights `let b = a` as the move.
2128 let mut any_match = false;
2129 for ii in &self.move_data.init_loc_map[location] {
2130 let init = self.move_data.inits[*ii];
2132 InitKind::Deep | InitKind::NonPanicPathOnly => {
2133 if mpis.contains(&init.path) {
2137 InitKind::Shallow => {
2138 if mpi == init.path {
2145 reinits.push(location);
2151 while let Some(location) = stack.pop() {
2152 if dfs_iter(&mut result, location, false) {
2156 let mut has_predecessor = false;
2157 predecessor_locations(self.body, location).for_each(|predecessor| {
2158 if location.dominates(predecessor, &self.dominators) {
2159 back_edge_stack.push(predecessor)
2161 stack.push(predecessor);
2163 has_predecessor = true;
2166 if !has_predecessor {
2167 reached_start = true;
2170 if (is_argument || !reached_start) && result.is_empty() {
2171 /* Process back edges (moves in future loop iterations) only if
2172 the move path is definitely initialized upon loop entry,
2173 to avoid spurious "in previous iteration" errors.
2174 During DFS, if there's a path from the error back to the start
2175 of the function with no intervening init or move, then the
2176 move path may be uninitialized at loop entry.
2178 while let Some(location) = back_edge_stack.pop() {
2179 if dfs_iter(&mut result, location, true) {
2183 predecessor_locations(self.body, location)
2184 .for_each(|predecessor| back_edge_stack.push(predecessor));
2188 // Check if we can reach these reinits from a move location.
2189 let reinits_reachable = reinits
2192 let mut visited = FxHashSet::default();
2193 let mut stack = vec![*reinit];
2194 while let Some(location) = stack.pop() {
2195 if !visited.insert(location) {
2198 if move_locations.contains(&location) {
2201 stack.extend(predecessor_locations(self.body, location));
2205 .collect::<Vec<Location>>();
2206 (result, reinits_reachable)
2209 pub(crate) fn report_illegal_mutation_of_borrowed(
2212 (place, span): (Place<'tcx>, Span),
2213 loan: &BorrowData<'tcx>,
2215 let loan_spans = self.retrieve_borrow_spans(loan);
2216 let loan_span = loan_spans.args_or_use();
2218 let descr_place = self.describe_any_place(place.as_ref());
2219 if loan.kind == BorrowKind::Shallow {
2220 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
2221 let mut err = self.cannot_mutate_in_immutable_section(
2228 loan_spans.var_span_label(
2230 format!("borrow occurs due to use{}", loan_spans.describe()),
2231 loan.kind.describe_mutability(),
2234 self.buffer_error(err);
2240 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
2242 loan_spans.var_span_label(
2244 format!("borrow occurs due to use{}", loan_spans.describe()),
2245 loan.kind.describe_mutability(),
2248 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
2258 self.explain_deref_coercion(loan, &mut err);
2260 self.buffer_error(err);
2263 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
2264 let tcx = self.infcx.tcx;
2266 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
2267 Some((method_did, method_substs)),
2269 &self.body[loan.reserve_location.block].terminator,
2270 rustc_const_eval::util::find_self_call(
2273 loan.assigned_place.local,
2274 loan.reserve_location.block,
2277 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
2279 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
2280 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
2283 if let Some(Ok(instance)) = deref_target {
2284 let deref_target_ty = instance.ty(tcx, self.param_env);
2286 "borrow occurs due to deref coercion to `{}`",
2289 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
2295 /// Reports an illegal reassignment; for example, an assignment to
2296 /// (part of) a non-`mut` local that occurs potentially after that
2297 /// local has already been initialized. `place` is the path being
2298 /// assigned; `err_place` is a place providing a reason why
2299 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
2300 /// assignment to `x.f`).
2301 pub(crate) fn report_illegal_reassignment(
2303 _location: Location,
2304 (place, span): (Place<'tcx>, Span),
2305 assigned_span: Span,
2306 err_place: Place<'tcx>,
2308 let (from_arg, local_decl, local_name) = match err_place.as_local() {
2310 self.body.local_kind(local) == LocalKind::Arg,
2311 Some(&self.body.local_decls[local]),
2312 self.local_names[local],
2314 None => (false, None, None),
2317 // If root local is initialized immediately (everything apart from let
2318 // PATTERN;) then make the error refer to that local, rather than the
2319 // place being assigned later.
2320 let (place_description, assigned_span) = match local_decl {
2323 Some(box LocalInfo::User(
2324 ClearCrossCrate::Clear
2325 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
2326 opt_match_place: None,
2330 | Some(box LocalInfo::StaticRef { .. })
2334 | None => (self.describe_any_place(place.as_ref()), assigned_span),
2335 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
2338 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
2339 let msg = if from_arg {
2340 "cannot assign to immutable argument"
2342 "cannot assign twice to immutable variable"
2344 if span != assigned_span && !from_arg {
2345 err.span_label(assigned_span, format!("first assignment to {}", place_description));
2347 if let Some(decl) = local_decl
2348 && let Some(name) = local_name
2349 && decl.can_be_made_mutable()
2351 err.span_suggestion(
2352 decl.source_info.span,
2353 "consider making this binding mutable",
2354 format!("mut {}", name),
2355 Applicability::MachineApplicable,
2358 err.span_label(span, msg);
2359 self.buffer_error(err);
2362 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
2363 let tcx = self.infcx.tcx;
2364 let (kind, _place_ty) = place.projection.iter().fold(
2365 (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)),
2366 |(kind, place_ty), &elem| {
2369 ProjectionElem::Deref => match kind {
2370 StorageDeadOrDrop::LocalStorageDead
2371 | StorageDeadOrDrop::BoxedStorageDead => {
2373 place_ty.ty.is_box(),
2374 "Drop of value behind a reference or raw pointer"
2376 StorageDeadOrDrop::BoxedStorageDead
2378 StorageDeadOrDrop::Destructor(_) => kind,
2380 ProjectionElem::OpaqueCast { .. }
2381 | ProjectionElem::Field(..)
2382 | ProjectionElem::Downcast(..) => {
2383 match place_ty.ty.kind() {
2384 ty::Adt(def, _) if def.has_dtor(tcx) => {
2385 // Report the outermost adt with a destructor
2387 StorageDeadOrDrop::Destructor(_) => kind,
2388 StorageDeadOrDrop::LocalStorageDead
2389 | StorageDeadOrDrop::BoxedStorageDead => {
2390 StorageDeadOrDrop::Destructor(place_ty.ty)
2397 ProjectionElem::ConstantIndex { .. }
2398 | ProjectionElem::Subslice { .. }
2399 | ProjectionElem::Index(_) => kind,
2401 place_ty.projection_ty(tcx, elem),
2408 /// Describe the reason for the fake borrow that was assigned to `place`.
2409 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
2410 use rustc_middle::mir::visit::Visitor;
2411 struct FakeReadCauseFinder<'tcx> {
2413 cause: Option<FakeReadCause>,
2415 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
2416 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
2418 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
2419 if *place == self.place =>
2421 self.cause = Some(*cause);
2427 let mut visitor = FakeReadCauseFinder { place, cause: None };
2428 visitor.visit_body(&self.body);
2429 match visitor.cause {
2430 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2431 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2436 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2437 /// borrow of local value that does not live long enough.
2438 fn annotate_argument_and_return_for_borrow(
2440 borrow: &BorrowData<'tcx>,
2441 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2442 // Define a fallback for when we can't match a closure.
2444 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2448 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2450 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2452 self.infcx.tcx.fn_sig(self.mir_def_id()),
2459 // In order to determine whether we need to annotate, we need to check whether the reserve
2460 // place was an assignment into a temporary.
2462 // If it was, we check whether or not that temporary is eventually assigned into the return
2463 // place. If it was, we can add annotations about the function's return type and arguments
2464 // and it'll make sense.
2465 let location = borrow.reserve_location;
2466 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2467 if let Some(Statement { kind: StatementKind::Assign(box (reservation, _)), .. }) =
2468 &self.body[location.block].statements.get(location.statement_index)
2470 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2471 // Check that the initial assignment of the reserve location is into a temporary.
2472 let mut target = match reservation.as_local() {
2473 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2477 // Next, look through the rest of the block, checking if we are assigning the
2478 // `target` (that is, the place that contains our borrow) to anything.
2479 let mut annotated_closure = None;
2480 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2482 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2485 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2486 if let Some(assigned_to) = place.as_local() {
2488 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2492 // Check if our `target` was captured by a closure.
2493 if let Rvalue::Aggregate(
2494 box AggregateKind::Closure(def_id, substs),
2498 for operand in operands {
2499 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2503 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2507 // Find the local from the operand.
2508 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2512 if assigned_from_local != target {
2516 // If a closure captured our `target` and then assigned
2517 // into a place then we should annotate the closure in
2518 // case it ends up being assigned into the return place.
2520 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2522 "annotate_argument_and_return_for_borrow: \
2523 annotated_closure={:?} assigned_from_local={:?} \
2525 annotated_closure, assigned_from_local, assigned_to
2528 if assigned_to == mir::RETURN_PLACE {
2529 // If it was assigned directly into the return place, then
2531 return annotated_closure;
2533 // Otherwise, update the target.
2534 target = assigned_to;
2538 // If none of our closure's operands matched, then skip to the next
2543 // Otherwise, look at other types of assignment.
2544 let assigned_from = match rvalue {
2545 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2546 Rvalue::Use(operand) => match operand {
2547 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2555 "annotate_argument_and_return_for_borrow: \
2556 assigned_from={:?}",
2560 // Find the local from the rvalue.
2561 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2563 "annotate_argument_and_return_for_borrow: \
2564 assigned_from_local={:?}",
2565 assigned_from_local,
2568 // Check if our local matches the target - if so, we've assigned our
2569 // borrow to a new place.
2570 if assigned_from_local != target {
2574 // If we assigned our `target` into a new place, then we should
2575 // check if it was the return place.
2577 "annotate_argument_and_return_for_borrow: \
2578 assigned_from_local={:?} assigned_to={:?}",
2579 assigned_from_local, assigned_to
2581 if assigned_to == mir::RETURN_PLACE {
2582 // If it was then return the annotated closure if there was one,
2583 // else, annotate this function.
2584 return annotated_closure.or_else(fallback);
2587 // If we didn't assign into the return place, then we just update
2589 target = assigned_to;
2594 // Check the terminator if we didn't find anything in the statements.
2595 let terminator = &self.body[location.block].terminator();
2597 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2600 if let TerminatorKind::Call { destination, target: Some(_), args, .. } =
2603 if let Some(assigned_to) = destination.as_local() {
2605 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2608 for operand in args {
2609 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2613 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2617 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2619 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2620 assigned_from_local,
2623 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2624 return annotated_closure.or_else(fallback);
2632 // If we haven't found an assignment into the return place, then we need not add
2634 debug!("annotate_argument_and_return_for_borrow: none found");
2638 /// Annotate the first argument and return type of a function signature if they are
2643 sig: ty::PolyFnSig<'tcx>,
2644 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2645 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2646 let is_closure = self.infcx.tcx.is_closure(did.to_def_id());
2647 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
2648 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2650 // We need to work out which arguments to highlight. We do this by looking
2651 // at the return type, where there are three cases:
2653 // 1. If there are named arguments, then we should highlight the return type and
2654 // highlight any of the arguments that are also references with that lifetime.
2655 // If there are no arguments that have the same lifetime as the return type,
2656 // then don't highlight anything.
2657 // 2. The return type is a reference with an anonymous lifetime. If this is
2658 // the case, then we can take advantage of (and teach) the lifetime elision
2661 // We know that an error is being reported. So the arguments and return type
2662 // must satisfy the elision rules. Therefore, if there is a single argument
2663 // then that means the return type and first (and only) argument have the same
2664 // lifetime and the borrow isn't meeting that, we can highlight the argument
2667 // If there are multiple arguments then the first argument must be self (else
2668 // it would not satisfy the elision rules), so we can highlight self and the
2670 // 3. The return type is not a reference. In this case, we don't highlight
2672 let return_ty = sig.output();
2673 match return_ty.skip_binder().kind() {
2674 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2675 // This is case 1 from above, return type is a named reference so we need to
2676 // search for relevant arguments.
2677 let mut arguments = Vec::new();
2678 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2679 if let ty::Ref(argument_region, _, _) = argument.kind() {
2680 if argument_region == return_region {
2681 // Need to use the `rustc_middle::ty` types to compare against the
2682 // `return_region`. Then use the `rustc_hir` type to get only
2683 // the lifetime span.
2684 if let hir::TyKind::Ref(lifetime, _) = &fn_decl.inputs[index].kind {
2685 // With access to the lifetime, we can get
2687 arguments.push((*argument, lifetime.ident.span));
2689 bug!("ty type is a ref but hir type is not");
2695 // We need to have arguments. This shouldn't happen, but it's worth checking.
2696 if arguments.is_empty() {
2700 // We use a mix of the HIR and the Ty types to get information
2701 // as the HIR doesn't have full types for closure arguments.
2702 let return_ty = sig.output().skip_binder();
2703 let mut return_span = fn_decl.output.span();
2704 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2705 if let hir::TyKind::Ref(lifetime, _) = ty.kind {
2706 return_span = lifetime.ident.span;
2710 Some(AnnotatedBorrowFnSignature::NamedFunction {
2716 ty::Ref(_, _, _) if is_closure => {
2717 // This is case 2 from above but only for closures, return type is anonymous
2718 // reference so we select
2719 // the first argument.
2720 let argument_span = fn_decl.inputs.first()?.span;
2721 let argument_ty = sig.inputs().skip_binder().first()?;
2723 // Closure arguments are wrapped in a tuple, so we need to get the first
2725 if let ty::Tuple(elems) = argument_ty.kind() {
2726 let &argument_ty = elems.first()?;
2727 if let ty::Ref(_, _, _) = argument_ty.kind() {
2728 return Some(AnnotatedBorrowFnSignature::Closure {
2737 ty::Ref(_, _, _) => {
2738 // This is also case 2 from above but for functions, return type is still an
2739 // anonymous reference so we select the first argument.
2740 let argument_span = fn_decl.inputs.first()?.span;
2741 let argument_ty = *sig.inputs().skip_binder().first()?;
2743 let return_span = fn_decl.output.span();
2744 let return_ty = sig.output().skip_binder();
2746 // We expect the first argument to be a reference.
2747 match argument_ty.kind() {
2748 ty::Ref(_, _, _) => {}
2752 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2760 // This is case 3 from above, return type is not a reference so don't highlight
2769 enum AnnotatedBorrowFnSignature<'tcx> {
2771 arguments: Vec<(Ty<'tcx>, Span)>,
2772 return_ty: Ty<'tcx>,
2776 argument_ty: Ty<'tcx>,
2777 argument_span: Span,
2778 return_ty: Ty<'tcx>,
2782 argument_ty: Ty<'tcx>,
2783 argument_span: Span,
2787 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2788 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2790 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2792 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2795 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2798 cx.get_region_name_for_ty(argument_ty, 0)
2800 &AnnotatedBorrowFnSignature::AnonymousFunction {
2806 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2807 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2809 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2810 let types_equal = return_ty_name == argument_ty_name;
2815 if types_equal { "also " } else { "" },
2821 "argument and return type have the same lifetime due to lifetime elision rules",
2824 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2825 lifetime-syntax.html#lifetime-elision>",
2828 cx.get_region_name_for_ty(return_ty, 0)
2830 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2831 // Region of return type and arguments checked to be the same earlier.
2832 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2833 for (_, argument_span) in arguments {
2834 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2837 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2840 "use data from the highlighted arguments which match the `{}` lifetime of \
2851 /// Detect whether one of the provided spans is a statement nested within the top-most visited expr
2852 struct ReferencedStatementsVisitor<'a>(&'a [Span], bool);
2854 impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> {
2855 fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
2857 hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => {
2865 /// Given a set of spans representing statements initializing the relevant binding, visit all the
2866 /// function expressions looking for branching code paths that *do not* initialize the binding.
2867 struct ConditionVisitor<'b> {
2870 errors: Vec<(Span, String)>,
2873 impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
2874 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
2876 hir::ExprKind::If(cond, body, None) => {
2877 // `if` expressions with no `else` that initialize the binding might be missing an
2879 let mut v = ReferencedStatementsVisitor(self.spans, false);
2885 "if this `if` condition is `false`, {} is not initialized",
2890 ex.span.shrink_to_hi(),
2891 format!("an `else` arm might be missing here, initializing {}", self.name),
2895 hir::ExprKind::If(cond, body, Some(other)) => {
2896 // `if` expressions where the binding is only initialized in one of the two arms
2897 // might be missing a binding initialization.
2898 let mut a = ReferencedStatementsVisitor(self.spans, false);
2900 let mut b = ReferencedStatementsVisitor(self.spans, false);
2901 b.visit_expr(other);
2903 (true, true) | (false, false) => {}
2905 if other.span.is_desugaring(DesugaringKind::WhileLoop) {
2909 "if this condition isn't met and the `while` loop runs 0 \
2910 times, {} is not initialized",
2916 body.span.shrink_to_hi().until(other.span),
2918 "if the `if` condition is `false` and this `else` arm is \
2919 executed, {} is not initialized",
2929 "if this condition is `true`, {} is not initialized",
2936 hir::ExprKind::Match(e, arms, loop_desugar) => {
2937 // If the binding is initialized in one of the match arms, then the other match
2938 // arms might be missing an initialization.
2939 let results: Vec<bool> = arms
2942 let mut v = ReferencedStatementsVisitor(self.spans, false);
2947 if results.iter().any(|x| *x) && !results.iter().all(|x| *x) {
2948 for (arm, seen) in arms.iter().zip(results) {
2950 if loop_desugar == hir::MatchSource::ForLoopDesugar {
2954 "if the `for` loop runs 0 times, {} is not initialized",
2958 } else if let Some(guard) = &arm.guard {
2960 arm.pat.span.to(guard.body().span),
2962 "if this pattern and condition are matched, {} is not \
2971 "if this pattern is matched, {} is not initialized",
2980 // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should
2981 // also be accounted for. For now it is fine, as if we don't find *any* relevant
2982 // branching code paths, we point at the places where the binding *is* initialized for
2986 walk_expr(self, ex);