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};
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;
26 use rustc_trait_selection::traits::TraitEngineExt as _;
28 use crate::borrow_set::TwoPhaseActivation;
29 use crate::borrowck_errors;
31 use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;
32 use crate::diagnostics::find_all_local_uses;
34 borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
35 InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
39 explain_borrow::{BorrowExplanation, LaterUseKind},
40 DescribePlaceOpt, RegionName, RegionNameSource, UseSpans,
45 /// Index of the "move out" that we found. The `MoveData` can
46 /// then tell us where the move occurred.
49 /// `true` if we traversed a back edge while walking from the point
50 /// of error to the move site.
51 traversed_back_edge: bool,
54 /// Which case a StorageDeadOrDrop is for.
55 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
56 enum StorageDeadOrDrop<'tcx> {
62 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
63 pub(crate) fn report_use_of_moved_or_uninitialized(
66 desired_action: InitializationRequiringAction,
67 (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),
71 "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
72 moved_place={:?} used_place={:?} span={:?} mpi={:?}",
73 location, desired_action, moved_place, used_place, span, mpi
77 self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
78 let span = use_spans.args_or_use();
80 let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
82 "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
83 move_site_vec, use_spans
85 let move_out_indices: Vec<_> =
86 move_site_vec.iter().map(|move_site| move_site.moi).collect();
88 if move_out_indices.is_empty() {
89 let root_place = PlaceRef { projection: &[], ..used_place };
91 if !self.uninitialized_error_reported.insert(root_place) {
93 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
99 let err = self.report_use_of_uninitialized(
107 self.buffer_error(err);
109 if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) {
110 if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) {
112 "report_use_of_moved_or_uninitialized place: error suppressed mois={:?}",
119 let is_partial_move = move_site_vec.iter().any(|move_site| {
120 let move_out = self.move_data.moves[(*move_site).moi];
121 let moved_place = &self.move_data.move_paths[move_out.path].place;
122 // `*(_1)` where `_1` is a `Box` is actually a move out.
123 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]
124 && self.body.local_decls[moved_place.local].ty.is_box();
127 && used_place != moved_place.as_ref()
128 && used_place.is_prefix_of(moved_place.as_ref())
131 let partial_str = if is_partial_move { "partial " } else { "" };
132 let partially_str = if is_partial_move { "partially " } else { "" };
134 let mut err = self.cannot_act_on_moved_value(
136 desired_action.as_noun(),
138 self.describe_place_with_options(
140 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
144 let reinit_spans = maybe_reinitialized_locations
148 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
151 .collect::<Vec<Span>>();
153 let reinits = maybe_reinitialized_locations.len();
155 err.span_label(reinit_spans[0], "this reinitialization might get skipped");
156 } else if reinits > 1 {
158 MultiSpan::from_spans(reinit_spans),
160 format!("these {} reinitializations might get skipped", reinits)
163 "these 3 reinitializations and {} other{} might get skipped",
165 if reinits == 4 { "" } else { "s" }
171 self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
173 let mut is_loop_move = false;
174 let mut in_pattern = false;
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 self.explain_captures(
205 maybe_reinitialized_locations.is_empty(),
208 if let (UseSpans::PatUse(span), []) =
209 (move_spans, &maybe_reinitialized_locations[..])
211 if maybe_reinitialized_locations.is_empty() {
212 err.span_suggestion_verbose(
215 "borrow this field in the pattern to avoid moving {}",
216 self.describe_place(moved_place.as_ref())
217 .map(|n| format!("`{}`", n))
218 .unwrap_or_else(|| "the value".to_string())
221 Applicability::MachineApplicable,
228 use_spans.var_span_label_path_only(
230 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
237 "value {} here after {}move",
238 desired_action.as_verb_in_past_tense(),
244 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
245 let needs_note = match ty.kind() {
246 ty::Closure(id, _) => {
247 let tables = self.infcx.tcx.typeck(id.expect_local());
248 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
250 tables.closure_kind_origins().get(hir_id).is_none()
255 let mpi = self.move_data.moves[move_out_indices[0]].path;
256 let place = &self.move_data.move_paths[mpi].place;
257 let ty = place.ty(self.body, self.infcx.tcx).ty;
259 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
260 // Same for if we're in a loop, see #101119.
261 if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
262 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
263 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
264 err.span_suggestion_verbose(
267 "consider creating a fresh reborrow of {} here",
268 self.describe_place(moved_place)
269 .map(|n| format!("`{}`", n))
270 .unwrap_or_else(|| "the mutable reference".to_string()),
273 Applicability::MachineApplicable,
278 let opt_name = self.describe_place_with_options(
280 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
282 let note_msg = match opt_name {
283 Some(ref name) => format!("`{}`", name),
284 None => "value".to_owned(),
286 if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) {
287 // Suppress the next suggestion since we don't want to put more bounds onto
288 // something that already has `Fn`-like bounds (or is a closure), so we can't
291 self.suggest_adding_copy_bounds(&mut err, ty, span);
295 let span = if let Some(local) = place.as_local() {
296 Some(self.body.local_decls[local].source_info.span)
300 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
303 if let UseSpans::FnSelfUse {
304 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
309 "{} occurs due to deref coercion to `{}`",
310 desired_action.as_noun(),
314 // Check first whether the source is accessible (issue #87060)
315 if self.infcx.tcx.sess.source_map().is_span_accessible(deref_target) {
316 err.span_note(deref_target, "deref defined here");
320 self.buffer_move_error(move_out_indices, (used_place, err));
324 fn report_use_of_uninitialized(
327 used_place: PlaceRef<'tcx>,
328 moved_place: PlaceRef<'tcx>,
329 desired_action: InitializationRequiringAction,
331 use_spans: UseSpans<'tcx>,
332 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
333 // We need all statements in the body where the binding was assigned to to later find all
334 // the branching code paths where the binding *wasn't* assigned to.
335 let inits = &self.move_data.init_path_map[mpi];
336 let move_path = &self.move_data.move_paths[mpi];
337 let decl_span = self.body.local_decls[move_path.place.local].source_info.span;
338 let mut spans = vec![];
339 for init_idx in inits {
340 let init = &self.move_data.inits[*init_idx];
341 let span = init.span(&self.body);
342 if !span.is_dummy() {
347 let (name, desc) = match self.describe_place_with_options(
349 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
351 Some(name) => (format!("`{name}`"), format!("`{name}` ")),
352 None => ("the variable".to_string(), String::new()),
354 let path = match self.describe_place_with_options(
356 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
358 Some(name) => format!("`{name}`"),
359 None => "value".to_string(),
362 // We use the statements were the binding was initialized, and inspect the HIR to look
363 // for the branching codepaths that aren't covered, to point at them.
364 let map = self.infcx.tcx.hir();
365 let body_id = map.body_owned_by(self.mir_def_id());
366 let body = map.body(body_id);
368 let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] };
369 visitor.visit_body(&body);
371 let mut show_assign_sugg = false;
372 let isnt_initialized = if let InitializationRequiringAction::PartialAssignment
373 | InitializationRequiringAction::Assignment = desired_action
375 // The same error is emitted for bindings that are *sometimes* initialized and the ones
376 // that are *partially* initialized by assigning to a field of an uninitialized
377 // binding. We differentiate between them for more accurate wording here.
378 "isn't fully initialized"
382 // We filter these to avoid misleading wording in cases like the following,
383 // where `x` has an `init`, but it is in the same place we're looking at:
389 // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs`
394 .any(|sp| span < sp && !sp.contains(span))
399 show_assign_sugg = true;
402 "is possibly-uninitialized"
405 let used = desired_action.as_general_verb_in_past_tense();
407 struct_span_err!(self, span, E0381, "{used} binding {desc}{isnt_initialized}");
408 use_spans.var_span_label_path_only(
410 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
413 if let InitializationRequiringAction::PartialAssignment
414 | InitializationRequiringAction::Assignment = desired_action
417 "partial initialization isn't supported, fully initialize the binding with a \
418 default value and mutate it, or use `std::mem::MaybeUninit`",
421 err.span_label(span, format!("{path} {used} here but it {isnt_initialized}"));
423 let mut shown = false;
424 for (sp, label) in visitor.errors {
425 if sp < span && !sp.overlaps(span) {
426 // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention
427 // match arms coming after the primary span because they aren't relevant:
431 // _ if { x = 2; true } => {}
436 // _ => {} // We don't want to point to this.
439 err.span_label(sp, &label);
445 if *sp < span && !sp.overlaps(span) {
446 err.span_label(*sp, "binding initialized here in some conditions");
451 err.span_label(decl_span, "binding declared here but left uninitialized");
452 if show_assign_sugg {
455 sugg_span: Option<Span>,
458 impl<'v> Visitor<'v> for LetVisitor {
459 fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
460 if self.sugg_span.is_some() {
463 if let hir::StmtKind::Local(hir::Local {
464 span, ty, init: None, ..
465 }) = &ex.kind && span.contains(self.decl_span) {
466 self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span));
468 hir::intravisit::walk_stmt(self, ex);
472 let mut visitor = LetVisitor { decl_span, sugg_span: None };
473 visitor.visit_body(&body);
474 if let Some(span) = visitor.sugg_span {
475 self.suggest_assign_value(&mut err, moved_place, span);
481 fn suggest_assign_value(
483 err: &mut Diagnostic,
484 moved_place: PlaceRef<'tcx>,
487 let ty = moved_place.ty(self.body, self.infcx.tcx).ty;
488 debug!("ty: {:?}, kind: {:?}", ty, ty.kind());
490 let tcx = self.infcx.tcx;
491 let implements_default = |ty, param_env| {
492 let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else {
495 // Regions are already solved, so we must use a fresh InferCtxt,
496 // but the type has region variables, so erase those.
499 .type_implements_trait(
501 tcx.erase_regions(ty),
505 .must_apply_modulo_regions()
508 let assign_value = match ty.kind() {
510 ty::Float(_) => "0.0",
511 ty::Int(_) | ty::Uint(_) => "0",
512 ty::Never | ty::Error(_) => "",
513 ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => "vec![]",
514 ty::Adt(_, _) if implements_default(ty, self.param_env) => "Default::default()",
518 if !assign_value.is_empty() {
519 err.span_suggestion_verbose(
520 sugg_span.shrink_to_hi(),
521 format!("consider assigning a value"),
522 format!(" = {}", assign_value),
523 Applicability::MaybeIncorrect,
528 fn suggest_borrow_fn_like(
530 err: &mut Diagnostic,
532 move_sites: &[MoveSite],
535 let tcx = self.infcx.tcx;
537 // Find out if the predicates show that the type is a Fn or FnMut
538 let find_fn_kind_from_did =
539 |predicates: ty::EarlyBinder<&[(ty::Predicate<'tcx>, Span)]>, substs| {
540 predicates.0.iter().find_map(|(pred, _)| {
541 let pred = if let Some(substs) = substs {
542 predicates.rebind(*pred).subst(tcx, substs).kind().skip_binder()
544 pred.kind().skip_binder()
546 if let ty::PredicateKind::Trait(pred) = pred && pred.self_ty() == ty {
547 if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
548 return Some(hir::Mutability::Not);
549 } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
550 return Some(hir::Mutability::Mut);
557 // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
558 // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
559 // These types seem reasonably opaque enough that they could be substituted with their
560 // borrowed variants in a function body when we see a move error.
561 let borrow_level = match ty.kind() {
562 ty::Param(_) => find_fn_kind_from_did(
563 tcx.bound_explicit_predicates_of(self.mir_def_id().to_def_id())
564 .map_bound(|p| p.predicates),
567 ty::Opaque(did, substs) => {
568 find_fn_kind_from_did(tcx.bound_explicit_item_bounds(*did), Some(*substs))
570 ty::Closure(_, substs) => match substs.as_closure().kind() {
571 ty::ClosureKind::Fn => Some(hir::Mutability::Not),
572 ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
578 let Some(borrow_level) = borrow_level else { return false; };
579 let sugg = move_sites
582 let move_out = self.move_data.moves[(*move_site).moi];
583 let moved_place = &self.move_data.move_paths[move_out.path].place;
584 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
585 let move_span = move_spans.args_or_use();
586 let suggestion = if borrow_level == hir::Mutability::Mut {
591 (move_span.shrink_to_lo(), suggestion)
594 err.multipart_suggestion_verbose(
596 "consider {}borrowing {value_name}",
597 if borrow_level == hir::Mutability::Mut { "mutably " } else { "" }
600 Applicability::MaybeIncorrect,
605 fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
606 let tcx = self.infcx.tcx;
607 let generics = tcx.generics_of(self.mir_def_id());
609 let Some(hir_generics) = tcx
610 .typeck_root_def_id(self.mir_def_id().to_def_id())
612 .and_then(|def_id| tcx.hir().get_generics(def_id))
614 // Try to find predicates on *generic params* that would allow copying `ty`
615 let infcx = tcx.infer_ctxt().build();
616 let mut fulfill_cx = <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx);
618 let copy_did = infcx.tcx.lang_items().copy_trait().unwrap();
619 let cause = ObligationCause::new(
622 rustc_infer::traits::ObligationCauseCode::MiscObligation,
624 fulfill_cx.register_bound(
627 // Erase any region vids from the type, which may not be resolved
628 infcx.tcx.erase_regions(ty),
632 // Select all, including ambiguous predicates
633 let errors = fulfill_cx.select_all_or_error(&infcx);
635 // Only emit suggestion if all required predicates are on generic
636 let predicates: Result<Vec<_>, _> = errors
638 .map(|err| match err.obligation.predicate.kind().skip_binder() {
639 PredicateKind::Trait(predicate) => match predicate.self_ty().kind() {
640 ty::Param(param_ty) => Ok((
641 generics.type_param(param_ty, tcx),
642 predicate.trait_ref.print_only_trait_path().to_string(),
650 if let Ok(predicates) = predicates {
651 suggest_constraining_type_params(
657 .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
662 pub(crate) fn report_move_out_while_borrowed(
665 (place, span): (Place<'tcx>, Span),
666 borrow: &BorrowData<'tcx>,
669 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
670 location, place, span, borrow
672 let value_msg = self.describe_any_place(place.as_ref());
673 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
675 let borrow_spans = self.retrieve_borrow_spans(borrow);
676 let borrow_span = borrow_spans.args_or_use();
678 let move_spans = self.move_spans(place.as_ref(), location);
679 let span = move_spans.args_or_use();
682 self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
683 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
684 err.span_label(span, format!("move out of {} occurs here", value_msg));
686 borrow_spans.var_span_label_path_only(
688 format!("borrow occurs due to use{}", borrow_spans.describe()),
691 move_spans.var_span_label(
693 format!("move occurs due to use{}", move_spans.describe()),
697 self.explain_why_borrow_contains_point(location, borrow, None)
698 .add_explanation_to_diagnostic(
707 self.buffer_error(err);
710 pub(crate) fn report_use_while_mutably_borrowed(
713 (place, _span): (Place<'tcx>, Span),
714 borrow: &BorrowData<'tcx>,
715 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
716 let borrow_spans = self.retrieve_borrow_spans(borrow);
717 let borrow_span = borrow_spans.args_or_use();
719 // Conflicting borrows are reported separately, so only check for move
721 let use_spans = self.move_spans(place.as_ref(), location);
722 let span = use_spans.var_or_use();
724 // If the attempted use is in a closure then we do not care about the path span of the place we are currently trying to use
725 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
726 let mut err = self.cannot_use_when_mutably_borrowed(
728 &self.describe_any_place(place.as_ref()),
730 &self.describe_any_place(borrow.borrowed_place.as_ref()),
733 borrow_spans.var_span_label(
736 let place = &borrow.borrowed_place;
737 let desc_place = self.describe_any_place(place.as_ref());
738 format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
743 self.explain_why_borrow_contains_point(location, borrow, None)
744 .add_explanation_to_diagnostic(
756 pub(crate) fn report_conflicting_borrow(
759 (place, span): (Place<'tcx>, Span),
760 gen_borrow_kind: BorrowKind,
761 issued_borrow: &BorrowData<'tcx>,
762 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
763 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
764 let issued_span = issued_spans.args_or_use();
766 let borrow_spans = self.borrow_spans(span, location);
767 let span = borrow_spans.args_or_use();
769 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
775 let (desc_place, msg_place, msg_borrow, union_type_name) =
776 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
778 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
779 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
781 // FIXME: supply non-"" `opt_via` when appropriate
782 let first_borrow_desc;
783 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
784 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
785 first_borrow_desc = "mutable ";
786 self.cannot_reborrow_already_borrowed(
798 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
799 first_borrow_desc = "immutable ";
800 self.cannot_reborrow_already_borrowed(
813 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
814 first_borrow_desc = "first ";
815 let mut err = self.cannot_mutably_borrow_multiply(
823 self.suggest_split_at_mut_if_applicable(
826 issued_borrow.borrowed_place,
831 (BorrowKind::Unique, BorrowKind::Unique) => {
832 first_borrow_desc = "first ";
833 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
836 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
837 if let Some(immutable_section_description) =
838 self.classify_immutable_section(issued_borrow.assigned_place)
840 let mut err = self.cannot_mutate_in_immutable_section(
844 immutable_section_description,
847 borrow_spans.var_span_label(
850 "borrow occurs due to use of {}{}",
852 borrow_spans.describe(),
859 first_borrow_desc = "immutable ";
860 self.cannot_reborrow_already_borrowed(
874 (BorrowKind::Unique, _) => {
875 first_borrow_desc = "first ";
876 self.cannot_uniquely_borrow_by_one_closure(
888 (BorrowKind::Shared, BorrowKind::Unique) => {
889 first_borrow_desc = "first ";
890 self.cannot_reborrow_already_uniquely_borrowed(
903 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
904 first_borrow_desc = "first ";
905 self.cannot_reborrow_already_uniquely_borrowed(
918 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
921 BorrowKind::Mut { .. }
924 | BorrowKind::Shallow,
928 if issued_spans == borrow_spans {
929 borrow_spans.var_span_label(
931 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
932 gen_borrow_kind.describe_mutability(),
935 let borrow_place = &issued_borrow.borrowed_place;
936 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
937 issued_spans.var_span_label(
940 "first borrow occurs due to use of {}{}",
942 issued_spans.describe(),
944 issued_borrow.kind.describe_mutability(),
947 borrow_spans.var_span_label(
950 "second borrow occurs due to use of {}{}",
952 borrow_spans.describe(),
954 gen_borrow_kind.describe_mutability(),
958 if union_type_name != "" {
960 "{} is a field of the union `{}`, so it overlaps the field {}",
961 msg_place, union_type_name, msg_borrow,
965 explanation.add_explanation_to_diagnostic(
972 Some((issued_span, span)),
975 self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation);
980 #[instrument(level = "debug", skip(self, err))]
981 fn suggest_using_local_if_applicable(
983 err: &mut Diagnostic,
985 issued_borrow: &BorrowData<'tcx>,
986 explanation: BorrowExplanation<'tcx>,
988 let used_in_call = matches!(
990 BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _)
993 debug!("not later used in call");
998 if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation {
1004 let outer_call_loc =
1005 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
1008 issued_borrow.reserve_location
1010 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
1012 let inner_param_location = location;
1013 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
1014 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
1017 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
1019 "`inner_param_location` {:?} is not for an assignment: {:?}",
1020 inner_param_location, inner_param_stmt
1024 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
1025 let Some((inner_call_loc, inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
1026 let Either::Right(term) = self.body.stmt_at(loc) else {
1027 debug!("{:?} is a statement, so it can't be a call", loc);
1030 let TerminatorKind::Call { args, .. } = &term.kind else {
1031 debug!("not a call: {:?}", term);
1034 debug!("checking call args for uses of inner_param: {:?}", args);
1035 if args.contains(&Operand::Move(inner_param)) {
1041 debug!("no uses of inner_param found as a by-move call arg");
1044 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
1046 let inner_call_span = inner_call_term.source_info.span;
1047 let outer_call_span = match use_span {
1049 None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span,
1051 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
1052 // FIXME: This stops the suggestion in some cases where it should be emitted.
1053 // Fix the spans for those cases so it's emitted correctly.
1055 "outer span {:?} does not strictly contain inner span {:?}",
1056 outer_call_span, inner_call_span
1063 "try adding a local storing this{}...",
1064 if use_span.is_some() { "" } else { " argument" }
1070 "...and then using that local {}",
1071 if use_span.is_some() { "here" } else { "as the argument to this call" }
1076 fn suggest_split_at_mut_if_applicable(
1078 err: &mut Diagnostic,
1080 borrowed_place: Place<'tcx>,
1082 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
1083 (&place.projection[..], &borrowed_place.projection[..])
1086 "consider using `.split_at_mut(position)` or similar method to obtain \
1087 two mutable non-overlapping sub-slices",
1092 /// Returns the description of the root place for a conflicting borrow and the full
1093 /// descriptions of the places that caused the conflict.
1095 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
1096 /// attempted while a shared borrow is live, then this function will return:
1101 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
1102 /// a shared borrow of another field `x.y`, then this function will return:
1104 /// ("x", "x.z", "x.y")
1107 /// In the more complex union case, where the union is a field of a struct, then if a mutable
1108 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
1109 /// another field `x.u.y`, then this function will return:
1111 /// ("x.u", "x.u.z", "x.u.y")
1114 /// This is used when creating error messages like below:
1117 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
1118 /// mutable (via `a.u.s.b`) [E0502]
1120 pub(crate) fn describe_place_for_conflicting_borrow(
1122 first_borrowed_place: Place<'tcx>,
1123 second_borrowed_place: Place<'tcx>,
1124 ) -> (String, String, String, String) {
1125 // Define a small closure that we can use to check if the type of a place
1127 let union_ty = |place_base| {
1128 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
1129 // using a type annotation in the closure argument instead leads to a lifetime error.
1130 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
1131 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
1134 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
1135 // code duplication (particularly around returning an empty description in the failure
1139 // If we have a conflicting borrow of the same place, then we don't want to add
1140 // an extraneous "via x.y" to our diagnostics, so filter out this case.
1141 first_borrowed_place != second_borrowed_place
1144 // We're going to want to traverse the first borrowed place to see if we can find
1145 // field access to a union. If we find that, then we will keep the place of the
1146 // union being accessed and the field that was being accessed so we can check the
1147 // second borrowed place for the same union and an access to a different field.
1148 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
1150 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
1151 return Some((place_base, field));
1158 .and_then(|(target_base, target_field)| {
1159 // With the place of a union and a field access into it, we traverse the second
1160 // borrowed place and look for an access to a different field of the same union.
1161 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
1162 if let ProjectionElem::Field(field, _) = elem {
1163 if let Some(union_ty) = union_ty(place_base) {
1164 if field != target_field && place_base == target_base {
1166 self.describe_any_place(place_base),
1167 self.describe_any_place(first_borrowed_place.as_ref()),
1168 self.describe_any_place(second_borrowed_place.as_ref()),
1169 union_ty.to_string(),
1177 .unwrap_or_else(|| {
1178 // If we didn't find a field access into a union, or both places match, then
1179 // only return the description of the first place.
1181 self.describe_any_place(first_borrowed_place.as_ref()),
1189 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
1191 /// This means that some data referenced by `borrow` needs to live
1192 /// past the point where the StorageDeadOrDrop of `place` occurs.
1193 /// This is usually interpreted as meaning that `place` has too
1194 /// short a lifetime. (But sometimes it is more useful to report
1195 /// it as a more direct conflict between the execution of a
1196 /// `Drop::drop` with an aliasing borrow.)
1197 #[instrument(level = "debug", skip(self))]
1198 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
1201 borrow: &BorrowData<'tcx>,
1202 place_span: (Place<'tcx>, Span),
1203 kind: Option<WriteKind>,
1205 let drop_span = place_span.1;
1207 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1209 let borrow_spans = self.retrieve_borrow_spans(borrow);
1210 let borrow_span = borrow_spans.var_or_use_path_span();
1212 assert!(root_place.projection.is_empty());
1213 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1215 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1217 if self.access_place_error_reported.contains(&(
1218 Place { local: root_place.local, projection: root_place_projection },
1222 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1228 self.access_place_error_reported.insert((
1229 Place { local: root_place.local, projection: root_place_projection },
1233 let borrowed_local = borrow.borrowed_place.local;
1234 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1236 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1237 self.buffer_error(err);
1241 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1242 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1244 // If a borrow of path `B` conflicts with drop of `D` (and
1245 // we're not in the uninteresting case where `B` is a
1246 // prefix of `D`), then report this as a more interesting
1247 // destructor conflict.
1248 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1249 self.report_borrow_conflicts_with_destructor(
1250 location, borrow, place_span, kind, dropped_ty,
1256 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1258 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1259 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1261 debug!(?place_desc, ?explanation);
1263 let err = match (place_desc, explanation) {
1264 // If the outlives constraint comes from inside the closure,
1269 // Box::new(|| y) as Box<Fn() -> &'static i32>
1271 // then just use the normal error. The closure isn't escaping
1272 // and `move` will not help here.
1275 BorrowExplanation::MustBeValidFor {
1277 category @ (ConstraintCategory::Return(_)
1278 | ConstraintCategory::CallArgument(_)
1279 | ConstraintCategory::OpaqueType),
1280 from_closure: false,
1285 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1286 .report_escaping_closure_capture(
1292 &format!("`{}`", name),
1296 BorrowExplanation::MustBeValidFor {
1297 category: ConstraintCategory::Assignment,
1298 from_closure: false,
1301 source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
1307 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1308 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1316 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1326 self.buffer_error(err);
1329 fn report_local_value_does_not_live_long_enough(
1333 borrow: &BorrowData<'tcx>,
1335 borrow_spans: UseSpans<'tcx>,
1336 explanation: BorrowExplanation<'tcx>,
1337 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1339 "report_local_value_does_not_live_long_enough(\
1340 {:?}, {:?}, {:?}, {:?}, {:?}\
1342 location, name, borrow, drop_span, borrow_spans
1345 let borrow_span = borrow_spans.var_or_use_path_span();
1346 if let BorrowExplanation::MustBeValidFor {
1350 from_closure: false,
1354 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1359 opt_place_desc.as_ref(),
1365 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1367 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1368 let region_name = annotation.emit(self, &mut err);
1372 format!("`{}` would have to be valid for `{}`...", name, region_name),
1375 let fn_hir_id = self.mir_hir_id();
1379 "...but `{}` will be dropped here, when the {} returns",
1384 .opt_name(fn_hir_id)
1385 .map(|name| format!("function `{}`", name))
1386 .unwrap_or_else(|| {
1390 .typeck(self.mir_def_id())
1391 .node_type(fn_hir_id)
1394 ty::Closure(..) => "enclosing closure",
1395 ty::Generator(..) => "enclosing generator",
1396 kind => bug!("expected closure or generator, found {:?}", kind),
1404 "functions cannot return a borrow to data owned within the function's scope, \
1405 functions can only return borrows to data passed as arguments",
1408 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1409 references-and-borrowing.html#dangling-references>",
1412 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1414 explanation.add_explanation_to_diagnostic(
1425 err.span_label(borrow_span, "borrowed value does not live long enough");
1426 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1428 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1430 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1432 explanation.add_explanation_to_diagnostic(
1446 fn report_borrow_conflicts_with_destructor(
1449 borrow: &BorrowData<'tcx>,
1450 (place, drop_span): (Place<'tcx>, Span),
1451 kind: Option<WriteKind>,
1452 dropped_ty: Ty<'tcx>,
1455 "report_borrow_conflicts_with_destructor(\
1456 {:?}, {:?}, ({:?}, {:?}), {:?}\
1458 location, borrow, place, drop_span, kind,
1461 let borrow_spans = self.retrieve_borrow_spans(borrow);
1462 let borrow_span = borrow_spans.var_or_use();
1464 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1466 let what_was_dropped = match self.describe_place(place.as_ref()) {
1467 Some(name) => format!("`{}`", name),
1468 None => String::from("temporary value"),
1471 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1472 Some(borrowed) => format!(
1473 "here, drop of {D} needs exclusive access to `{B}`, \
1474 because the type `{T}` implements the `Drop` trait",
1475 D = what_was_dropped,
1480 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1481 D = what_was_dropped,
1485 err.span_label(drop_span, label);
1487 // Only give this note and suggestion if they could be relevant.
1489 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1491 BorrowExplanation::UsedLater { .. }
1492 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1493 err.note("consider using a `let` binding to create a longer lived value");
1498 explanation.add_explanation_to_diagnostic(
1508 self.buffer_error(err);
1511 fn report_thread_local_value_does_not_live_long_enough(
1515 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1517 "report_thread_local_value_does_not_live_long_enough(\
1520 drop_span, borrow_span
1523 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1527 "thread-local variables cannot be borrowed beyond the end of the function",
1529 err.span_label(drop_span, "end of enclosing function is here");
1534 #[instrument(level = "debug", skip(self))]
1535 fn report_temporary_value_does_not_live_long_enough(
1538 borrow: &BorrowData<'tcx>,
1540 borrow_spans: UseSpans<'tcx>,
1542 explanation: BorrowExplanation<'tcx>,
1543 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1544 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1547 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1558 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1559 err.span_label(proper_span, "creates a temporary which is freed while still in use");
1560 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1563 BorrowExplanation::UsedLater(..)
1564 | BorrowExplanation::UsedLaterInLoop(..)
1565 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1566 // Only give this note and suggestion if it could be relevant.
1567 let sm = self.infcx.tcx.sess.source_map();
1568 let mut suggested = false;
1569 let msg = "consider using a `let` binding to create a longer lived value";
1571 /// We check that there's a single level of block nesting to ensure always correct
1572 /// suggestions. If we don't, then we only provide a free-form message to avoid
1573 /// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`.
1574 /// We could expand the analysis to suggest hoising all of the relevant parts of
1575 /// the users' code to make the code compile, but that could be too much.
1576 struct NestedStatementVisitor {
1582 impl<'tcx> Visitor<'tcx> for NestedStatementVisitor {
1583 fn visit_block(&mut self, block: &hir::Block<'tcx>) {
1585 walk_block(self, block);
1588 fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) {
1589 if self.span == expr.span {
1590 self.found = self.current;
1592 walk_expr(self, expr);
1595 let source_info = self.body.source_info(location);
1596 if let Some(scope) = self.body.source_scopes.get(source_info.scope)
1597 && let ClearCrossCrate::Set(scope_data) = &scope.local_data
1598 && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root)
1599 && let Some(id) = node.body_id()
1600 && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind
1602 for stmt in block.stmts {
1603 let mut visitor = NestedStatementVisitor {
1608 visitor.visit_stmt(stmt);
1609 if visitor.found == 0
1610 && stmt.span.contains(proper_span)
1611 && let Some(p) = sm.span_to_margin(stmt.span)
1612 && let Ok(s) = sm.span_to_snippet(proper_span)
1614 let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
1615 err.multipart_suggestion_verbose(
1618 (stmt.span.shrink_to_lo(), addition),
1619 (proper_span, "binding".to_string()),
1621 Applicability::MaybeIncorrect,
1634 explanation.add_explanation_to_diagnostic(
1644 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1646 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1651 fn try_report_cannot_return_reference_to_local(
1653 borrow: &BorrowData<'tcx>,
1656 category: ConstraintCategory<'tcx>,
1657 opt_place_desc: Option<&String>,
1658 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1659 let return_kind = match category {
1660 ConstraintCategory::Return(_) => "return",
1661 ConstraintCategory::Yield => "yield",
1665 // FIXME use a better heuristic than Spans
1666 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1672 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1673 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1674 match self.body.local_kind(local) {
1675 LocalKind::ReturnPointer | LocalKind::Temp => {
1676 bug!("temporary or return pointer with a name")
1678 LocalKind::Var => "local variable ",
1680 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1682 "variable captured by `move` "
1684 LocalKind::Arg => "function parameter ",
1690 format!("{}`{}`", local_kind, place_desc),
1691 format!("`{}` is borrowed here", place_desc),
1695 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1696 let local = root_place.local;
1697 match self.body.local_kind(local) {
1698 LocalKind::ReturnPointer | LocalKind::Temp => {
1699 ("temporary value".to_string(), "temporary value created here".to_string())
1702 "function parameter".to_string(),
1703 "function parameter borrowed here".to_string(),
1706 ("local binding".to_string(), "local binding introduced here".to_string())
1711 let mut err = self.cannot_return_reference_to_local(
1718 if return_span != borrow_span {
1719 err.span_label(borrow_span, note);
1721 let tcx = self.infcx.tcx;
1722 let ty_params = ty::List::empty();
1724 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1725 let return_ty = tcx.erase_regions(return_ty);
1728 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator)
1731 .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1732 .must_apply_modulo_regions()
1734 err.span_suggestion_hidden(
1735 return_span.shrink_to_hi(),
1736 "use `.collect()` to allocate the iterator",
1737 ".collect::<Vec<_>>()",
1738 Applicability::MaybeIncorrect,
1746 fn report_escaping_closure_capture(
1748 use_span: UseSpans<'tcx>,
1750 fr_name: &RegionName,
1751 category: ConstraintCategory<'tcx>,
1752 constraint_span: Span,
1754 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1755 let tcx = self.infcx.tcx;
1756 let args_span = use_span.args_or_use();
1758 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1760 if string.starts_with("async ") {
1761 let pos = args_span.lo() + BytePos(6);
1762 (args_span.with_lo(pos).with_hi(pos), "move ")
1763 } else if string.starts_with("async|") {
1764 let pos = args_span.lo() + BytePos(5);
1765 (args_span.with_lo(pos).with_hi(pos), " move")
1767 (args_span.shrink_to_lo(), "move ")
1770 Err(_) => (args_span, "move |<args>| <body>"),
1772 let kind = match use_span.generator_kind() {
1773 Some(generator_kind) => match generator_kind {
1774 GeneratorKind::Async(async_kind) => match async_kind {
1775 AsyncGeneratorKind::Block => "async block",
1776 AsyncGeneratorKind::Closure => "async closure",
1777 _ => bug!("async block/closure expected, but async function found."),
1779 GeneratorKind::Gen => "generator",
1785 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1786 err.span_suggestion_verbose(
1789 "to force the {} to take ownership of {} (and any \
1790 other referenced variables), use the `move` keyword",
1794 Applicability::MachineApplicable,
1798 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1799 let msg = format!("{} is returned here", kind);
1800 err.span_note(constraint_span, &msg);
1802 ConstraintCategory::CallArgument(_) => {
1803 fr_name.highlight_region_name(&mut err);
1804 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1806 "async blocks are not executed immediately and must either take a \
1807 reference or ownership of outside variables they use",
1810 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1811 err.span_note(constraint_span, &msg);
1815 "report_escaping_closure_capture called with unexpected constraint \
1824 fn report_escaping_data(
1827 name: &Option<String>,
1831 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1832 let tcx = self.infcx.tcx;
1834 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1837 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1841 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1844 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1846 if let Some(name) = name {
1849 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1854 format!("reference escapes the {} body here", escapes_from),
1861 fn get_moved_indexes(
1865 ) -> (Vec<MoveSite>, Vec<Location>) {
1866 fn predecessor_locations<'tcx, 'a>(
1867 body: &'a mir::Body<'tcx>,
1869 ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
1870 if location.statement_index == 0 {
1871 let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
1872 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1874 Either::Right(std::iter::once(Location {
1875 statement_index: location.statement_index - 1,
1881 let mut mpis = vec![mpi];
1882 let move_paths = &self.move_data.move_paths;
1883 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1885 let mut stack = Vec::new();
1886 let mut back_edge_stack = Vec::new();
1888 predecessor_locations(self.body, location).for_each(|predecessor| {
1889 if location.dominates(predecessor, &self.dominators) {
1890 back_edge_stack.push(predecessor)
1892 stack.push(predecessor);
1896 let mut reached_start = false;
1898 /* Check if the mpi is initialized as an argument */
1899 let mut is_argument = false;
1900 for arg in self.body.args_iter() {
1901 let path = self.move_data.rev_lookup.find_local(arg);
1902 if mpis.contains(&path) {
1907 let mut visited = FxHashSet::default();
1908 let mut move_locations = FxHashSet::default();
1909 let mut reinits = vec![];
1910 let mut result = vec![];
1912 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1914 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1915 location, is_back_edge
1918 if !visited.insert(location) {
1924 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1925 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1926 // this analysis only tries to find moves explicitly
1927 // written by the user, so we ignore the move-outs
1928 // created by `StorageDead` and at the beginning
1931 // If we are found a use of a.b.c which was in error, then we want to look for
1932 // moves not only of a.b.c but also a.b and a.
1934 // Note that the moves data already includes "parent" paths, so we don't have to
1935 // worry about the other case: that is, if there is a move of a.b.c, it is already
1936 // marked as a move of a.b and a as well, so we will generate the correct errors
1938 for moi in &self.move_data.loc_map[location] {
1939 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1940 let path = self.move_data.moves[*moi].path;
1941 if mpis.contains(&path) {
1943 "report_use_of_moved_or_uninitialized: found {:?}",
1944 move_paths[path].place
1946 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1947 move_locations.insert(location);
1949 // Strictly speaking, we could continue our DFS here. There may be
1950 // other moves that can reach the point of error. But it is kind of
1951 // confusing to highlight them.
1959 // drop(a); // <-- current point of error
1962 // Because we stop the DFS here, we only highlight `let c = a`,
1963 // and not `let b = a`. We will of course also report an error at
1964 // `let c = a` which highlights `let b = a` as the move.
1971 let mut any_match = false;
1972 for ii in &self.move_data.init_loc_map[location] {
1973 let init = self.move_data.inits[*ii];
1975 InitKind::Deep | InitKind::NonPanicPathOnly => {
1976 if mpis.contains(&init.path) {
1980 InitKind::Shallow => {
1981 if mpi == init.path {
1988 reinits.push(location);
1994 while let Some(location) = stack.pop() {
1995 if dfs_iter(&mut result, location, false) {
1999 let mut has_predecessor = false;
2000 predecessor_locations(self.body, location).for_each(|predecessor| {
2001 if location.dominates(predecessor, &self.dominators) {
2002 back_edge_stack.push(predecessor)
2004 stack.push(predecessor);
2006 has_predecessor = true;
2009 if !has_predecessor {
2010 reached_start = true;
2013 if (is_argument || !reached_start) && result.is_empty() {
2014 /* Process back edges (moves in future loop iterations) only if
2015 the move path is definitely initialized upon loop entry,
2016 to avoid spurious "in previous iteration" errors.
2017 During DFS, if there's a path from the error back to the start
2018 of the function with no intervening init or move, then the
2019 move path may be uninitialized at loop entry.
2021 while let Some(location) = back_edge_stack.pop() {
2022 if dfs_iter(&mut result, location, true) {
2026 predecessor_locations(self.body, location)
2027 .for_each(|predecessor| back_edge_stack.push(predecessor));
2031 // Check if we can reach these reinits from a move location.
2032 let reinits_reachable = reinits
2035 let mut visited = FxHashSet::default();
2036 let mut stack = vec![*reinit];
2037 while let Some(location) = stack.pop() {
2038 if !visited.insert(location) {
2041 if move_locations.contains(&location) {
2044 stack.extend(predecessor_locations(self.body, location));
2048 .collect::<Vec<Location>>();
2049 (result, reinits_reachable)
2052 pub(crate) fn report_illegal_mutation_of_borrowed(
2055 (place, span): (Place<'tcx>, Span),
2056 loan: &BorrowData<'tcx>,
2058 let loan_spans = self.retrieve_borrow_spans(loan);
2059 let loan_span = loan_spans.args_or_use();
2061 let descr_place = self.describe_any_place(place.as_ref());
2062 if loan.kind == BorrowKind::Shallow {
2063 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
2064 let mut err = self.cannot_mutate_in_immutable_section(
2071 loan_spans.var_span_label(
2073 format!("borrow occurs due to use{}", loan_spans.describe()),
2074 loan.kind.describe_mutability(),
2077 self.buffer_error(err);
2083 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
2085 loan_spans.var_span_label(
2087 format!("borrow occurs due to use{}", loan_spans.describe()),
2088 loan.kind.describe_mutability(),
2091 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
2101 self.explain_deref_coercion(loan, &mut err);
2103 self.buffer_error(err);
2106 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
2107 let tcx = self.infcx.tcx;
2109 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
2110 Some((method_did, method_substs)),
2112 &self.body[loan.reserve_location.block].terminator,
2113 rustc_const_eval::util::find_self_call(
2116 loan.assigned_place.local,
2117 loan.reserve_location.block,
2120 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
2122 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
2123 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
2126 if let Some(Ok(instance)) = deref_target {
2127 let deref_target_ty = instance.ty(tcx, self.param_env);
2129 "borrow occurs due to deref coercion to `{}`",
2132 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
2138 /// Reports an illegal reassignment; for example, an assignment to
2139 /// (part of) a non-`mut` local that occurs potentially after that
2140 /// local has already been initialized. `place` is the path being
2141 /// assigned; `err_place` is a place providing a reason why
2142 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
2143 /// assignment to `x.f`).
2144 pub(crate) fn report_illegal_reassignment(
2146 _location: Location,
2147 (place, span): (Place<'tcx>, Span),
2148 assigned_span: Span,
2149 err_place: Place<'tcx>,
2151 let (from_arg, local_decl, local_name) = match err_place.as_local() {
2153 self.body.local_kind(local) == LocalKind::Arg,
2154 Some(&self.body.local_decls[local]),
2155 self.local_names[local],
2157 None => (false, None, None),
2160 // If root local is initialized immediately (everything apart from let
2161 // PATTERN;) then make the error refer to that local, rather than the
2162 // place being assigned later.
2163 let (place_description, assigned_span) = match local_decl {
2166 Some(box LocalInfo::User(
2167 ClearCrossCrate::Clear
2168 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
2169 opt_match_place: None,
2173 | Some(box LocalInfo::StaticRef { .. })
2177 | None => (self.describe_any_place(place.as_ref()), assigned_span),
2178 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
2181 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
2182 let msg = if from_arg {
2183 "cannot assign to immutable argument"
2185 "cannot assign twice to immutable variable"
2187 if span != assigned_span && !from_arg {
2188 err.span_label(assigned_span, format!("first assignment to {}", place_description));
2190 if let Some(decl) = local_decl
2191 && let Some(name) = local_name
2192 && decl.can_be_made_mutable()
2194 err.span_suggestion(
2195 decl.source_info.span,
2196 "consider making this binding mutable",
2197 format!("mut {}", name),
2198 Applicability::MachineApplicable,
2201 err.span_label(span, msg);
2202 self.buffer_error(err);
2205 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
2206 let tcx = self.infcx.tcx;
2207 let (kind, _place_ty) = place.projection.iter().fold(
2208 (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)),
2209 |(kind, place_ty), &elem| {
2212 ProjectionElem::Deref => match kind {
2213 StorageDeadOrDrop::LocalStorageDead
2214 | StorageDeadOrDrop::BoxedStorageDead => {
2216 place_ty.ty.is_box(),
2217 "Drop of value behind a reference or raw pointer"
2219 StorageDeadOrDrop::BoxedStorageDead
2221 StorageDeadOrDrop::Destructor(_) => kind,
2223 ProjectionElem::OpaqueCast { .. }
2224 | ProjectionElem::Field(..)
2225 | ProjectionElem::Downcast(..) => {
2226 match place_ty.ty.kind() {
2227 ty::Adt(def, _) if def.has_dtor(tcx) => {
2228 // Report the outermost adt with a destructor
2230 StorageDeadOrDrop::Destructor(_) => kind,
2231 StorageDeadOrDrop::LocalStorageDead
2232 | StorageDeadOrDrop::BoxedStorageDead => {
2233 StorageDeadOrDrop::Destructor(place_ty.ty)
2240 ProjectionElem::ConstantIndex { .. }
2241 | ProjectionElem::Subslice { .. }
2242 | ProjectionElem::Index(_) => kind,
2244 place_ty.projection_ty(tcx, elem),
2251 /// Describe the reason for the fake borrow that was assigned to `place`.
2252 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
2253 use rustc_middle::mir::visit::Visitor;
2254 struct FakeReadCauseFinder<'tcx> {
2256 cause: Option<FakeReadCause>,
2258 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
2259 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
2261 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
2262 if *place == self.place =>
2264 self.cause = Some(*cause);
2270 let mut visitor = FakeReadCauseFinder { place, cause: None };
2271 visitor.visit_body(&self.body);
2272 match visitor.cause {
2273 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2274 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2279 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2280 /// borrow of local value that does not live long enough.
2281 fn annotate_argument_and_return_for_borrow(
2283 borrow: &BorrowData<'tcx>,
2284 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2285 // Define a fallback for when we can't match a closure.
2287 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2291 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2293 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2295 self.infcx.tcx.fn_sig(self.mir_def_id()),
2302 // In order to determine whether we need to annotate, we need to check whether the reserve
2303 // place was an assignment into a temporary.
2305 // If it was, we check whether or not that temporary is eventually assigned into the return
2306 // place. If it was, we can add annotations about the function's return type and arguments
2307 // and it'll make sense.
2308 let location = borrow.reserve_location;
2309 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2310 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
2311 &self.body[location.block].statements.get(location.statement_index)
2313 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2314 // Check that the initial assignment of the reserve location is into a temporary.
2315 let mut target = match reservation.as_local() {
2316 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2320 // Next, look through the rest of the block, checking if we are assigning the
2321 // `target` (that is, the place that contains our borrow) to anything.
2322 let mut annotated_closure = None;
2323 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2325 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2328 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2329 if let Some(assigned_to) = place.as_local() {
2331 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2335 // Check if our `target` was captured by a closure.
2336 if let Rvalue::Aggregate(
2337 box AggregateKind::Closure(def_id, substs),
2341 for operand in operands {
2342 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2346 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2350 // Find the local from the operand.
2351 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2355 if assigned_from_local != target {
2359 // If a closure captured our `target` and then assigned
2360 // into a place then we should annotate the closure in
2361 // case it ends up being assigned into the return place.
2363 self.annotate_fn_sig(def_id, substs.as_closure().sig());
2365 "annotate_argument_and_return_for_borrow: \
2366 annotated_closure={:?} assigned_from_local={:?} \
2368 annotated_closure, assigned_from_local, assigned_to
2371 if assigned_to == mir::RETURN_PLACE {
2372 // If it was assigned directly into the return place, then
2374 return annotated_closure;
2376 // Otherwise, update the target.
2377 target = assigned_to;
2381 // If none of our closure's operands matched, then skip to the next
2386 // Otherwise, look at other types of assignment.
2387 let assigned_from = match rvalue {
2388 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2389 Rvalue::Use(operand) => match operand {
2390 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2398 "annotate_argument_and_return_for_borrow: \
2399 assigned_from={:?}",
2403 // Find the local from the rvalue.
2404 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2406 "annotate_argument_and_return_for_borrow: \
2407 assigned_from_local={:?}",
2408 assigned_from_local,
2411 // Check if our local matches the target - if so, we've assigned our
2412 // borrow to a new place.
2413 if assigned_from_local != target {
2417 // If we assigned our `target` into a new place, then we should
2418 // check if it was the return place.
2420 "annotate_argument_and_return_for_borrow: \
2421 assigned_from_local={:?} assigned_to={:?}",
2422 assigned_from_local, assigned_to
2424 if assigned_to == mir::RETURN_PLACE {
2425 // If it was then return the annotated closure if there was one,
2426 // else, annotate this function.
2427 return annotated_closure.or_else(fallback);
2430 // If we didn't assign into the return place, then we just update
2432 target = assigned_to;
2437 // Check the terminator if we didn't find anything in the statements.
2438 let terminator = &self.body[location.block].terminator();
2440 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2443 if let TerminatorKind::Call { destination, target: Some(_), args, .. } =
2446 if let Some(assigned_to) = destination.as_local() {
2448 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2451 for operand in args {
2452 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2456 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2460 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2462 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2463 assigned_from_local,
2466 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2467 return annotated_closure.or_else(fallback);
2475 // If we haven't found an assignment into the return place, then we need not add
2477 debug!("annotate_argument_and_return_for_borrow: none found");
2481 /// Annotate the first argument and return type of a function signature if they are
2486 sig: ty::PolyFnSig<'tcx>,
2487 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2488 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2489 let is_closure = self.infcx.tcx.is_closure(did.to_def_id());
2490 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
2491 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2493 // We need to work out which arguments to highlight. We do this by looking
2494 // at the return type, where there are three cases:
2496 // 1. If there are named arguments, then we should highlight the return type and
2497 // highlight any of the arguments that are also references with that lifetime.
2498 // If there are no arguments that have the same lifetime as the return type,
2499 // then don't highlight anything.
2500 // 2. The return type is a reference with an anonymous lifetime. If this is
2501 // the case, then we can take advantage of (and teach) the lifetime elision
2504 // We know that an error is being reported. So the arguments and return type
2505 // must satisfy the elision rules. Therefore, if there is a single argument
2506 // then that means the return type and first (and only) argument have the same
2507 // lifetime and the borrow isn't meeting that, we can highlight the argument
2510 // If there are multiple arguments then the first argument must be self (else
2511 // it would not satisfy the elision rules), so we can highlight self and the
2513 // 3. The return type is not a reference. In this case, we don't highlight
2515 let return_ty = sig.output();
2516 match return_ty.skip_binder().kind() {
2517 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2518 // This is case 1 from above, return type is a named reference so we need to
2519 // search for relevant arguments.
2520 let mut arguments = Vec::new();
2521 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2522 if let ty::Ref(argument_region, _, _) = argument.kind() {
2523 if argument_region == return_region {
2524 // Need to use the `rustc_middle::ty` types to compare against the
2525 // `return_region`. Then use the `rustc_hir` type to get only
2526 // the lifetime span.
2527 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2528 // With access to the lifetime, we can get
2530 arguments.push((*argument, lifetime.span));
2532 bug!("ty type is a ref but hir type is not");
2538 // We need to have arguments. This shouldn't happen, but it's worth checking.
2539 if arguments.is_empty() {
2543 // We use a mix of the HIR and the Ty types to get information
2544 // as the HIR doesn't have full types for closure arguments.
2545 let return_ty = sig.output().skip_binder();
2546 let mut return_span = fn_decl.output.span();
2547 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2548 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2549 return_span = lifetime.span;
2553 Some(AnnotatedBorrowFnSignature::NamedFunction {
2559 ty::Ref(_, _, _) if is_closure => {
2560 // This is case 2 from above but only for closures, return type is anonymous
2561 // reference so we select
2562 // the first argument.
2563 let argument_span = fn_decl.inputs.first()?.span;
2564 let argument_ty = sig.inputs().skip_binder().first()?;
2566 // Closure arguments are wrapped in a tuple, so we need to get the first
2568 if let ty::Tuple(elems) = argument_ty.kind() {
2569 let &argument_ty = elems.first()?;
2570 if let ty::Ref(_, _, _) = argument_ty.kind() {
2571 return Some(AnnotatedBorrowFnSignature::Closure {
2580 ty::Ref(_, _, _) => {
2581 // This is also case 2 from above but for functions, return type is still an
2582 // anonymous reference so we select the first argument.
2583 let argument_span = fn_decl.inputs.first()?.span;
2584 let argument_ty = *sig.inputs().skip_binder().first()?;
2586 let return_span = fn_decl.output.span();
2587 let return_ty = sig.output().skip_binder();
2589 // We expect the first argument to be a reference.
2590 match argument_ty.kind() {
2591 ty::Ref(_, _, _) => {}
2595 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2603 // This is case 3 from above, return type is not a reference so don't highlight
2612 enum AnnotatedBorrowFnSignature<'tcx> {
2614 arguments: Vec<(Ty<'tcx>, Span)>,
2615 return_ty: Ty<'tcx>,
2619 argument_ty: Ty<'tcx>,
2620 argument_span: Span,
2621 return_ty: Ty<'tcx>,
2625 argument_ty: Ty<'tcx>,
2626 argument_span: Span,
2630 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2631 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2633 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2635 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2638 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2641 cx.get_region_name_for_ty(argument_ty, 0)
2643 &AnnotatedBorrowFnSignature::AnonymousFunction {
2649 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2650 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2652 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2653 let types_equal = return_ty_name == argument_ty_name;
2658 if types_equal { "also " } else { "" },
2664 "argument and return type have the same lifetime due to lifetime elision rules",
2667 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2668 lifetime-syntax.html#lifetime-elision>",
2671 cx.get_region_name_for_ty(return_ty, 0)
2673 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2674 // Region of return type and arguments checked to be the same earlier.
2675 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2676 for (_, argument_span) in arguments {
2677 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2680 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2683 "use data from the highlighted arguments which match the `{}` lifetime of \
2694 /// Detect whether one of the provided spans is a statement nested within the top-most visited expr
2695 struct ReferencedStatementsVisitor<'a>(&'a [Span], bool);
2697 impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> {
2698 fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
2700 hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => {
2708 /// Given a set of spans representing statements initializing the relevant binding, visit all the
2709 /// function expressions looking for branching code paths that *do not* initialize the binding.
2710 struct ConditionVisitor<'b> {
2713 errors: Vec<(Span, String)>,
2716 impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
2717 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
2719 hir::ExprKind::If(cond, body, None) => {
2720 // `if` expressions with no `else` that initialize the binding might be missing an
2722 let mut v = ReferencedStatementsVisitor(self.spans, false);
2728 "if this `if` condition is `false`, {} is not initialized",
2733 ex.span.shrink_to_hi(),
2734 format!("an `else` arm might be missing here, initializing {}", self.name),
2738 hir::ExprKind::If(cond, body, Some(other)) => {
2739 // `if` expressions where the binding is only initialized in one of the two arms
2740 // might be missing a binding initialization.
2741 let mut a = ReferencedStatementsVisitor(self.spans, false);
2743 let mut b = ReferencedStatementsVisitor(self.spans, false);
2744 b.visit_expr(other);
2746 (true, true) | (false, false) => {}
2748 if other.span.is_desugaring(DesugaringKind::WhileLoop) {
2752 "if this condition isn't met and the `while` loop runs 0 \
2753 times, {} is not initialized",
2759 body.span.shrink_to_hi().until(other.span),
2761 "if the `if` condition is `false` and this `else` arm is \
2762 executed, {} is not initialized",
2772 "if this condition is `true`, {} is not initialized",
2779 hir::ExprKind::Match(e, arms, loop_desugar) => {
2780 // If the binding is initialized in one of the match arms, then the other match
2781 // arms might be missing an initialization.
2782 let results: Vec<bool> = arms
2785 let mut v = ReferencedStatementsVisitor(self.spans, false);
2790 if results.iter().any(|x| *x) && !results.iter().all(|x| *x) {
2791 for (arm, seen) in arms.iter().zip(results) {
2793 if loop_desugar == hir::MatchSource::ForLoopDesugar {
2797 "if the `for` loop runs 0 times, {} is not initialized",
2801 } else if let Some(guard) = &arm.guard {
2803 arm.pat.span.to(guard.body().span),
2805 "if this pattern and condition are matched, {} is not \
2814 "if this pattern is matched, {} is not initialized",
2823 // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should
2824 // also be accounted for. For now it is fine, as if we don't find *any* relevant
2825 // branching code paths, we point at the places where the binding *is* initialized for
2829 walk_expr(self, ex);