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::def::Res;
10 use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
11 use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem};
12 use rustc_infer::infer::TyCtxtInferExt;
13 use rustc_infer::traits::ObligationCause;
14 use rustc_middle::mir::tcx::PlaceTy;
15 use rustc_middle::mir::{
16 self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
17 FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
18 ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
20 use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty};
21 use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
22 use rustc_span::def_id::LocalDefId;
23 use rustc_span::hygiene::DesugaringKind;
24 use rustc_span::symbol::{kw, sym};
25 use rustc_span::{BytePos, Span, Symbol};
26 use rustc_trait_selection::infer::InferCtxtExt;
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;
33 use crate::diagnostics::mutability_errors::mut_borrow_of_mutable_ref;
35 borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
36 InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
40 explain_borrow::{BorrowExplanation, LaterUseKind},
41 DescribePlaceOpt, RegionName, RegionNameSource, UseSpans,
46 /// Index of the "move out" that we found. The `MoveData` can
47 /// then tell us where the move occurred.
50 /// `true` if we traversed a back edge while walking from the point
51 /// of error to the move site.
52 traversed_back_edge: bool,
55 /// Which case a StorageDeadOrDrop is for.
56 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
57 enum StorageDeadOrDrop<'tcx> {
63 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
64 pub(crate) fn report_use_of_moved_or_uninitialized(
67 desired_action: InitializationRequiringAction,
68 (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),
72 "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
73 moved_place={:?} used_place={:?} span={:?} mpi={:?}",
74 location, desired_action, moved_place, used_place, span, mpi
78 self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
79 let span = use_spans.args_or_use();
81 let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
83 "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
84 move_site_vec, use_spans
86 let move_out_indices: Vec<_> =
87 move_site_vec.iter().map(|move_site| move_site.moi).collect();
89 if move_out_indices.is_empty() {
90 let root_place = PlaceRef { projection: &[], ..used_place };
92 if !self.uninitialized_error_reported.insert(root_place) {
94 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
100 let err = self.report_use_of_uninitialized(
108 self.buffer_error(err);
110 if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) {
111 if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) {
113 "report_use_of_moved_or_uninitialized place: error suppressed mois={:?}",
120 let is_partial_move = move_site_vec.iter().any(|move_site| {
121 let move_out = self.move_data.moves[(*move_site).moi];
122 let moved_place = &self.move_data.move_paths[move_out.path].place;
123 // `*(_1)` where `_1` is a `Box` is actually a move out.
124 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]
125 && self.body.local_decls[moved_place.local].ty.is_box();
128 && used_place != moved_place.as_ref()
129 && used_place.is_prefix_of(moved_place.as_ref())
132 let partial_str = if is_partial_move { "partial " } else { "" };
133 let partially_str = if is_partial_move { "partially " } else { "" };
135 let mut err = self.cannot_act_on_moved_value(
137 desired_action.as_noun(),
139 self.describe_place_with_options(
141 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
145 let reinit_spans = maybe_reinitialized_locations
149 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
152 .collect::<Vec<Span>>();
154 let reinits = maybe_reinitialized_locations.len();
156 err.span_label(reinit_spans[0], "this reinitialization might get skipped");
157 } else if reinits > 1 {
159 MultiSpan::from_spans(reinit_spans),
161 format!("these {reinits} reinitializations might get skipped")
164 "these 3 reinitializations and {} other{} might get skipped",
166 if reinits == 4 { "" } else { "s" }
172 let closure = self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
174 let mut is_loop_move = false;
175 let mut in_pattern = false;
176 let mut seen_spans = FxHashSet::default();
178 for move_site in &move_site_vec {
179 let move_out = self.move_data.moves[(*move_site).moi];
180 let moved_place = &self.move_data.move_paths[move_out.path].place;
182 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
183 let move_span = move_spans.args_or_use();
185 let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
187 let loop_message = if location == move_out.source || move_site.traversed_back_edge {
188 ", in previous iteration of loop"
193 if location == move_out.source {
197 if !seen_spans.contains(&move_span) {
199 self.suggest_ref_or_clone(
208 self.explain_captures(
218 maybe_reinitialized_locations.is_empty(),
221 seen_spans.insert(move_span);
224 use_spans.var_path_only_subdiag(&mut err, desired_action);
230 "value {} here after {partial_str}move",
231 desired_action.as_verb_in_past_tense(),
236 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
237 let needs_note = match ty.kind() {
238 ty::Closure(id, _) => {
239 let tables = self.infcx.tcx.typeck(id.expect_local());
240 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
242 tables.closure_kind_origins().get(hir_id).is_none()
247 let mpi = self.move_data.moves[move_out_indices[0]].path;
248 let place = &self.move_data.move_paths[mpi].place;
249 let ty = place.ty(self.body, self.infcx.tcx).ty;
251 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
252 // Same for if we're in a loop, see #101119.
253 if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
254 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
255 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
256 err.span_suggestion_verbose(
259 "consider creating a fresh reborrow of {} here",
260 self.describe_place(moved_place)
261 .map(|n| format!("`{n}`"))
262 .unwrap_or_else(|| "the mutable reference".to_string()),
265 Applicability::MachineApplicable,
270 let opt_name = self.describe_place_with_options(
272 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
274 let note_msg = match opt_name {
275 Some(name) => format!("`{name}`"),
276 None => "value".to_owned(),
278 if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) {
279 // Suppress the next suggestion since we don't want to put more bounds onto
280 // something that already has `Fn`-like bounds (or is a closure), so we can't
283 self.suggest_adding_copy_bounds(&mut err, ty, span);
287 let span = if let Some(local) = place.as_local() {
288 Some(self.body.local_decls[local].source_info.span)
292 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
295 if let UseSpans::FnSelfUse {
296 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
301 "{} occurs due to deref coercion to `{deref_target_ty}`",
302 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(self.mir_hir_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.parent_id(expr.hir_id);
398 if let Some(parent) = hir.find(hir_id) {
399 let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
400 && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
401 && let Some(def_id) = typeck.type_dependent_def_id(parent_expr.hir_id)
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"
530 } else if !spans.iter().any(|i| {
531 // We filter these to avoid misleading wording in cases like the following,
532 // where `x` has an `init`, but it is in the same place we're looking at:
538 // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs`
543 .any(|sp| span < sp && !sp.contains(span))
545 show_assign_sugg = true;
548 "is possibly-uninitialized"
551 let used = desired_action.as_general_verb_in_past_tense();
553 struct_span_err!(self, span, E0381, "{used} binding {desc}{isnt_initialized}");
554 use_spans.var_path_only_subdiag(&mut err, desired_action);
556 if let InitializationRequiringAction::PartialAssignment
557 | InitializationRequiringAction::Assignment = desired_action
560 "partial initialization isn't supported, fully initialize the binding with a \
561 default value and mutate it, or use `std::mem::MaybeUninit`",
564 err.span_label(span, format!("{path} {used} here but it {isnt_initialized}"));
566 let mut shown = false;
567 for (sp, label) in visitor.errors {
568 if sp < span && !sp.overlaps(span) {
569 // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention
570 // match arms coming after the primary span because they aren't relevant:
574 // _ if { x = 2; true } => {}
579 // _ => {} // We don't want to point to this.
582 err.span_label(sp, &label);
588 if *sp < span && !sp.overlaps(span) {
589 err.span_label(*sp, "binding initialized here in some conditions");
594 err.span_label(decl_span, "binding declared here but left uninitialized");
595 if show_assign_sugg {
598 sugg_span: Option<Span>,
601 impl<'v> Visitor<'v> for LetVisitor {
602 fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
603 if self.sugg_span.is_some() {
606 if let hir::StmtKind::Local(hir::Local {
607 span, ty, init: None, ..
608 }) = &ex.kind && span.contains(self.decl_span) {
609 self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span));
611 hir::intravisit::walk_stmt(self, ex);
615 let mut visitor = LetVisitor { decl_span, sugg_span: None };
616 visitor.visit_body(&body);
617 if let Some(span) = visitor.sugg_span {
618 self.suggest_assign_value(&mut err, moved_place, span);
624 fn suggest_assign_value(
626 err: &mut Diagnostic,
627 moved_place: PlaceRef<'tcx>,
630 let ty = moved_place.ty(self.body, self.infcx.tcx).ty;
631 debug!("ty: {:?}, kind: {:?}", ty, ty.kind());
633 let tcx = self.infcx.tcx;
634 let implements_default = |ty, param_env| {
635 let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else {
638 // Regions are already solved, so we must use a fresh InferCtxt,
639 // but the type has region variables, so erase those.
642 .type_implements_trait(default_trait, [tcx.erase_regions(ty)], param_env)
643 .must_apply_modulo_regions()
646 let assign_value = match ty.kind() {
648 ty::Float(_) => "0.0",
649 ty::Int(_) | ty::Uint(_) => "0",
650 ty::Never | ty::Error(_) => "",
651 ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => "vec![]",
652 ty::Adt(_, _) if implements_default(ty, self.param_env) => "Default::default()",
656 if !assign_value.is_empty() {
657 err.span_suggestion_verbose(
658 sugg_span.shrink_to_hi(),
659 "consider assigning a value",
660 format!(" = {}", assign_value),
661 Applicability::MaybeIncorrect,
666 fn suggest_borrow_fn_like(
668 err: &mut Diagnostic,
670 move_sites: &[MoveSite],
673 let tcx = self.infcx.tcx;
675 // Find out if the predicates show that the type is a Fn or FnMut
676 let find_fn_kind_from_did = |(pred, _): (ty::Predicate<'tcx>, _)| {
677 if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred.kind().skip_binder()
678 && pred.self_ty() == ty
680 if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
681 return Some(hir::Mutability::Not);
682 } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
683 return Some(hir::Mutability::Mut);
689 // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
690 // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
691 // These types seem reasonably opaque enough that they could be substituted with their
692 // borrowed variants in a function body when we see a move error.
693 let borrow_level = match *ty.kind() {
695 .explicit_predicates_of(self.mir_def_id().to_def_id())
699 .find_map(find_fn_kind_from_did),
700 ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => tcx
701 .bound_explicit_item_bounds(def_id)
702 .subst_iter_copied(tcx, substs)
703 .find_map(find_fn_kind_from_did),
704 ty::Closure(_, substs) => match substs.as_closure().kind() {
705 ty::ClosureKind::Fn => Some(hir::Mutability::Not),
706 ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
712 let Some(borrow_level) = borrow_level else { return false; };
713 let sugg = move_sites
716 let move_out = self.move_data.moves[(*move_site).moi];
717 let moved_place = &self.move_data.move_paths[move_out.path].place;
718 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
719 let move_span = move_spans.args_or_use();
720 let suggestion = borrow_level.ref_prefix_str().to_owned();
721 (move_span.shrink_to_lo(), suggestion)
724 err.multipart_suggestion_verbose(
725 format!("consider {}borrowing {value_name}", borrow_level.mutably_str()),
727 Applicability::MaybeIncorrect,
732 fn suggest_cloning(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
733 let tcx = self.infcx.tcx;
734 // Try to find predicates on *generic params* that would allow copying `ty`
735 let infcx = tcx.infer_ctxt().build();
737 if let Some(clone_trait_def) = tcx.lang_items().clone_trait()
739 .type_implements_trait(
741 [tcx.erase_regions(ty)],
744 .must_apply_modulo_regions()
746 err.span_suggestion_verbose(
748 "consider cloning the value if the performance cost is acceptable",
750 Applicability::MachineApplicable,
755 fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
756 let tcx = self.infcx.tcx;
757 let generics = tcx.generics_of(self.mir_def_id());
759 let Some(hir_generics) = tcx
760 .typeck_root_def_id(self.mir_def_id().to_def_id())
762 .and_then(|def_id| tcx.hir().get_generics(def_id))
764 // Try to find predicates on *generic params* that would allow copying `ty`
765 let infcx = tcx.infer_ctxt().build();
766 let copy_did = infcx.tcx.require_lang_item(LangItem::Copy, Some(span));
767 let cause = ObligationCause::new(
770 rustc_infer::traits::ObligationCauseCode::MiscObligation,
772 let errors = rustc_trait_selection::traits::fully_solve_bound(
776 // Erase any region vids from the type, which may not be resolved
777 infcx.tcx.erase_regions(ty),
781 // Only emit suggestion if all required predicates are on generic
782 let predicates: Result<Vec<_>, _> = errors
784 .map(|err| match err.obligation.predicate.kind().skip_binder() {
785 PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
786 match predicate.self_ty().kind() {
787 ty::Param(param_ty) => Ok((
788 generics.type_param(param_ty, tcx),
789 predicate.trait_ref.print_only_trait_path().to_string(),
798 if let Ok(predicates) = predicates {
799 suggest_constraining_type_params(
805 .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
810 pub(crate) fn report_move_out_while_borrowed(
813 (place, span): (Place<'tcx>, Span),
814 borrow: &BorrowData<'tcx>,
817 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
818 location, place, span, borrow
820 let value_msg = self.describe_any_place(place.as_ref());
821 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
823 let borrow_spans = self.retrieve_borrow_spans(borrow);
824 let borrow_span = borrow_spans.args_or_use();
826 let move_spans = self.move_spans(place.as_ref(), location);
827 let span = move_spans.args_or_use();
829 let mut err = self.cannot_move_when_borrowed(
832 &self.describe_any_place(place.as_ref()),
837 borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow);
839 move_spans.var_span_label(
841 format!("move occurs due to use{}", move_spans.describe()),
845 self.explain_why_borrow_contains_point(location, borrow, None)
846 .add_explanation_to_diagnostic(
855 self.buffer_error(err);
858 pub(crate) fn report_use_while_mutably_borrowed(
861 (place, _span): (Place<'tcx>, Span),
862 borrow: &BorrowData<'tcx>,
863 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
864 let borrow_spans = self.retrieve_borrow_spans(borrow);
865 let borrow_span = borrow_spans.args_or_use();
867 // Conflicting borrows are reported separately, so only check for move
869 let use_spans = self.move_spans(place.as_ref(), location);
870 let span = use_spans.var_or_use();
872 // 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
873 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
874 let mut err = self.cannot_use_when_mutably_borrowed(
876 &self.describe_any_place(place.as_ref()),
878 &self.describe_any_place(borrow.borrowed_place.as_ref()),
880 borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| {
881 use crate::session_diagnostics::CaptureVarCause::*;
882 let place = &borrow.borrowed_place;
883 let desc_place = self.describe_any_place(place.as_ref());
885 Some(_) => BorrowUsePlaceGenerator { place: desc_place, var_span },
886 None => BorrowUsePlaceClosure { place: desc_place, var_span },
890 self.explain_why_borrow_contains_point(location, borrow, None)
891 .add_explanation_to_diagnostic(
903 pub(crate) fn report_conflicting_borrow(
906 (place, span): (Place<'tcx>, Span),
907 gen_borrow_kind: BorrowKind,
908 issued_borrow: &BorrowData<'tcx>,
909 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
910 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
911 let issued_span = issued_spans.args_or_use();
913 let borrow_spans = self.borrow_spans(span, location);
914 let span = borrow_spans.args_or_use();
916 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
922 let (desc_place, msg_place, msg_borrow, union_type_name) =
923 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
925 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
926 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
928 // FIXME: supply non-"" `opt_via` when appropriate
929 let first_borrow_desc;
930 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
931 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
932 first_borrow_desc = "mutable ";
933 self.cannot_reborrow_already_borrowed(
945 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
946 first_borrow_desc = "immutable ";
947 let mut err = self.cannot_reborrow_already_borrowed(
958 self.suggest_binding_for_closure_capture_self(
960 issued_borrow.borrowed_place,
966 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
967 first_borrow_desc = "first ";
968 let mut err = self.cannot_mutably_borrow_multiply(
976 self.suggest_split_at_mut_if_applicable(
979 issued_borrow.borrowed_place,
984 (BorrowKind::Unique, BorrowKind::Unique) => {
985 first_borrow_desc = "first ";
986 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
989 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
990 if let Some(immutable_section_description) =
991 self.classify_immutable_section(issued_borrow.assigned_place)
993 let mut err = self.cannot_mutate_in_immutable_section(
997 immutable_section_description,
1000 borrow_spans.var_span_label(
1003 "borrow occurs due to use of {}{}",
1005 borrow_spans.describe(),
1012 first_borrow_desc = "immutable ";
1013 self.cannot_reborrow_already_borrowed(
1027 (BorrowKind::Unique, _) => {
1028 first_borrow_desc = "first ";
1029 self.cannot_uniquely_borrow_by_one_closure(
1041 (BorrowKind::Shared, BorrowKind::Unique) => {
1042 first_borrow_desc = "first ";
1043 self.cannot_reborrow_already_uniquely_borrowed(
1056 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
1057 first_borrow_desc = "first ";
1058 self.cannot_reborrow_already_uniquely_borrowed(
1071 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
1073 BorrowKind::Shallow,
1074 BorrowKind::Mut { .. }
1075 | BorrowKind::Unique
1076 | BorrowKind::Shared
1077 | BorrowKind::Shallow,
1078 ) => unreachable!(),
1081 if issued_spans == borrow_spans {
1082 borrow_spans.var_span_label(
1084 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
1085 gen_borrow_kind.describe_mutability(),
1088 let borrow_place = &issued_borrow.borrowed_place;
1089 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
1090 issued_spans.var_span_label(
1093 "first borrow occurs due to use of {}{}",
1095 issued_spans.describe(),
1097 issued_borrow.kind.describe_mutability(),
1100 borrow_spans.var_span_label(
1103 "second borrow occurs due to use of {}{}",
1105 borrow_spans.describe(),
1107 gen_borrow_kind.describe_mutability(),
1111 if union_type_name != "" {
1113 "{} is a field of the union `{}`, so it overlaps the field {}",
1114 msg_place, union_type_name, msg_borrow,
1118 explanation.add_explanation_to_diagnostic(
1125 Some((issued_span, span)),
1128 self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation);
1133 #[instrument(level = "debug", skip(self, err))]
1134 fn suggest_using_local_if_applicable(
1136 err: &mut Diagnostic,
1138 issued_borrow: &BorrowData<'tcx>,
1139 explanation: BorrowExplanation<'tcx>,
1141 let used_in_call = matches!(
1143 BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _)
1146 debug!("not later used in call");
1151 if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation {
1157 let outer_call_loc =
1158 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
1161 issued_borrow.reserve_location
1163 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
1165 let inner_param_location = location;
1166 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
1167 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
1170 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
1172 "`inner_param_location` {:?} is not for an assignment: {:?}",
1173 inner_param_location, inner_param_stmt
1177 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
1178 let Some((inner_call_loc, inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
1179 let Either::Right(term) = self.body.stmt_at(loc) else {
1180 debug!("{:?} is a statement, so it can't be a call", loc);
1183 let TerminatorKind::Call { args, .. } = &term.kind else {
1184 debug!("not a call: {:?}", term);
1187 debug!("checking call args for uses of inner_param: {:?}", args);
1188 if args.contains(&Operand::Move(inner_param)) {
1194 debug!("no uses of inner_param found as a by-move call arg");
1197 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
1199 let inner_call_span = inner_call_term.source_info.span;
1200 let outer_call_span = match use_span {
1202 None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span,
1204 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
1205 // FIXME: This stops the suggestion in some cases where it should be emitted.
1206 // Fix the spans for those cases so it's emitted correctly.
1208 "outer span {:?} does not strictly contain inner span {:?}",
1209 outer_call_span, inner_call_span
1216 "try adding a local storing this{}...",
1217 if use_span.is_some() { "" } else { " argument" }
1223 "...and then using that local {}",
1224 if use_span.is_some() { "here" } else { "as the argument to this call" }
1229 fn suggest_split_at_mut_if_applicable(
1231 err: &mut Diagnostic,
1233 borrowed_place: Place<'tcx>,
1235 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
1236 (&place.projection[..], &borrowed_place.projection[..])
1239 "consider using `.split_at_mut(position)` or similar method to obtain \
1240 two mutable non-overlapping sub-slices",
1245 fn suggest_binding_for_closure_capture_self(
1247 err: &mut Diagnostic,
1248 borrowed_place: Place<'tcx>,
1249 issued_spans: &UseSpans<'tcx>,
1251 let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return };
1252 let hir = self.infcx.tcx.hir();
1254 // check whether the borrowed place is capturing `self` by mut reference
1255 let local = borrowed_place.local;
1260 .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) else { return };
1262 struct ExpressionFinder<'hir> {
1264 closure_change_spans: Vec<Span>,
1265 closure_arg_span: Option<Span>,
1267 suggest_arg: String,
1268 hir: rustc_middle::hir::map::Map<'hir>,
1269 closure_local_id: Option<hir::HirId>,
1270 closure_call_changes: Vec<(Span, String)>,
1272 impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
1273 fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
1274 if e.span.contains(self.capture_span) {
1275 if let hir::ExprKind::Closure(&hir::Closure {
1279 fn_decl: hir::FnDecl{ inputs, .. },
1282 let Some(hir::Node::Expr(body )) = self.hir.find(body.hir_id) {
1283 self.suggest_arg = "this: &Self".to_string();
1284 if inputs.len() > 0 {
1285 self.suggest_arg.push_str(", ");
1287 self.in_closure = true;
1288 self.closure_arg_span = fn_arg_span;
1289 self.visit_expr(body);
1290 self.in_closure = false;
1293 if let hir::Expr { kind: hir::ExprKind::Path(path), .. } = e {
1294 if let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path &&
1295 seg.ident.name == kw::SelfLower && self.in_closure {
1296 self.closure_change_spans.push(e.span);
1299 hir::intravisit::walk_expr(self, e);
1302 fn visit_local(&mut self, local: &'hir hir::Local<'hir>) {
1303 if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = local.pat &&
1304 let Some(init) = local.init
1306 if let hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure {
1310 init.span.contains(self.capture_span) {
1311 self.closure_local_id = Some(*hir_id);
1314 hir::intravisit::walk_local(self, local);
1317 fn visit_stmt(&mut self, s: &'hir hir::Stmt<'hir>) {
1318 if let hir::StmtKind::Semi(e) = s.kind &&
1319 let hir::ExprKind::Call(hir::Expr { kind: hir::ExprKind::Path(path), ..}, args) = e.kind &&
1320 let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path &&
1321 let Res::Local(hir_id) = seg.res &&
1322 Some(hir_id) == self.closure_local_id {
1323 let (span, arg_str) = if args.len() > 0 {
1324 (args[0].span.shrink_to_lo(), "self, ".to_string())
1326 let span = e.span.trim_start(seg.ident.span).unwrap_or(e.span);
1327 (span, "(self)".to_string())
1329 self.closure_call_changes.push((span, arg_str));
1331 hir::intravisit::walk_stmt(self, s);
1335 if let Some(hir::Node::ImplItem(
1336 hir::ImplItem { kind: hir::ImplItemKind::Fn(_fn_sig, body_id), .. }
1337 )) = hir.find(self.mir_hir_id()) &&
1338 let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) {
1339 let mut finder = ExpressionFinder {
1340 capture_span: *capture_kind_span,
1341 closure_change_spans: vec![],
1342 closure_arg_span: None,
1344 suggest_arg: String::new(),
1345 closure_local_id: None,
1346 closure_call_changes: vec![],
1349 finder.visit_expr(expr);
1351 if finder.closure_change_spans.is_empty() || finder.closure_call_changes.is_empty() {
1355 let mut sugg = vec![];
1356 let sm = self.infcx.tcx.sess.source_map();
1358 if let Some(span) = finder.closure_arg_span {
1359 sugg.push((sm.next_point(span.shrink_to_lo()).shrink_to_hi(), finder.suggest_arg));
1361 for span in finder.closure_change_spans {
1362 sugg.push((span, "this".to_string()));
1365 for (span, suggest) in finder.closure_call_changes {
1366 sugg.push((span, suggest));
1369 err.multipart_suggestion_verbose(
1370 "try explicitly pass `&Self` into the Closure as an argument",
1372 Applicability::MachineApplicable,
1377 /// Returns the description of the root place for a conflicting borrow and the full
1378 /// descriptions of the places that caused the conflict.
1380 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
1381 /// attempted while a shared borrow is live, then this function will return:
1386 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
1387 /// a shared borrow of another field `x.y`, then this function will return:
1389 /// ("x", "x.z", "x.y")
1392 /// In the more complex union case, where the union is a field of a struct, then if a mutable
1393 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
1394 /// another field `x.u.y`, then this function will return:
1396 /// ("x.u", "x.u.z", "x.u.y")
1399 /// This is used when creating error messages like below:
1402 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
1403 /// mutable (via `a.u.s.b`) [E0502]
1405 pub(crate) fn describe_place_for_conflicting_borrow(
1407 first_borrowed_place: Place<'tcx>,
1408 second_borrowed_place: Place<'tcx>,
1409 ) -> (String, String, String, String) {
1410 // Define a small closure that we can use to check if the type of a place
1412 let union_ty = |place_base| {
1413 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
1414 // using a type annotation in the closure argument instead leads to a lifetime error.
1415 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
1416 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
1419 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
1420 // code duplication (particularly around returning an empty description in the failure
1424 // If we have a conflicting borrow of the same place, then we don't want to add
1425 // an extraneous "via x.y" to our diagnostics, so filter out this case.
1426 first_borrowed_place != second_borrowed_place
1429 // We're going to want to traverse the first borrowed place to see if we can find
1430 // field access to a union. If we find that, then we will keep the place of the
1431 // union being accessed and the field that was being accessed so we can check the
1432 // second borrowed place for the same union and an access to a different field.
1433 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
1435 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
1436 return Some((place_base, field));
1443 .and_then(|(target_base, target_field)| {
1444 // With the place of a union and a field access into it, we traverse the second
1445 // borrowed place and look for an access to a different field of the same union.
1446 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
1447 if let ProjectionElem::Field(field, _) = elem {
1448 if let Some(union_ty) = union_ty(place_base) {
1449 if field != target_field && place_base == target_base {
1451 self.describe_any_place(place_base),
1452 self.describe_any_place(first_borrowed_place.as_ref()),
1453 self.describe_any_place(second_borrowed_place.as_ref()),
1454 union_ty.to_string(),
1462 .unwrap_or_else(|| {
1463 // If we didn't find a field access into a union, or both places match, then
1464 // only return the description of the first place.
1466 self.describe_any_place(first_borrowed_place.as_ref()),
1474 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
1476 /// This means that some data referenced by `borrow` needs to live
1477 /// past the point where the StorageDeadOrDrop of `place` occurs.
1478 /// This is usually interpreted as meaning that `place` has too
1479 /// short a lifetime. (But sometimes it is more useful to report
1480 /// it as a more direct conflict between the execution of a
1481 /// `Drop::drop` with an aliasing borrow.)
1482 #[instrument(level = "debug", skip(self))]
1483 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
1486 borrow: &BorrowData<'tcx>,
1487 place_span: (Place<'tcx>, Span),
1488 kind: Option<WriteKind>,
1490 let drop_span = place_span.1;
1492 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1494 let borrow_spans = self.retrieve_borrow_spans(borrow);
1495 let borrow_span = borrow_spans.var_or_use_path_span();
1497 assert!(root_place.projection.is_empty());
1498 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1500 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1502 if self.access_place_error_reported.contains(&(
1503 Place { local: root_place.local, projection: root_place_projection },
1507 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1513 self.access_place_error_reported.insert((
1514 Place { local: root_place.local, projection: root_place_projection },
1518 let borrowed_local = borrow.borrowed_place.local;
1519 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1521 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1522 self.buffer_error(err);
1526 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1527 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1529 // If a borrow of path `B` conflicts with drop of `D` (and
1530 // we're not in the uninteresting case where `B` is a
1531 // prefix of `D`), then report this as a more interesting
1532 // destructor conflict.
1533 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1534 self.report_borrow_conflicts_with_destructor(
1535 location, borrow, place_span, kind, dropped_ty,
1541 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1543 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1544 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1546 debug!(?place_desc, ?explanation);
1548 let err = match (place_desc, explanation) {
1549 // If the outlives constraint comes from inside the closure,
1554 // Box::new(|| y) as Box<Fn() -> &'static i32>
1556 // then just use the normal error. The closure isn't escaping
1557 // and `move` will not help here.
1560 BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _),
1561 ) => self.report_escaping_closure_capture(
1565 name: self.synthesize_region_name(),
1566 source: RegionNameSource::Static,
1568 ConstraintCategory::CallArgument(None),
1570 &format!("`{}`", name),
1575 BorrowExplanation::MustBeValidFor {
1577 category @ (ConstraintCategory::Return(_)
1578 | ConstraintCategory::CallArgument(_)
1579 | ConstraintCategory::OpaqueType),
1580 from_closure: false,
1585 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1586 .report_escaping_closure_capture(
1592 &format!("`{}`", name),
1597 BorrowExplanation::MustBeValidFor {
1598 category: ConstraintCategory::Assignment,
1599 from_closure: false,
1602 source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
1608 ) => self.report_escaping_data(borrow_span, &name, upvar_span, upvar_name, span),
1609 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1617 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1627 self.buffer_error(err);
1630 fn report_local_value_does_not_live_long_enough(
1634 borrow: &BorrowData<'tcx>,
1636 borrow_spans: UseSpans<'tcx>,
1637 explanation: BorrowExplanation<'tcx>,
1638 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1640 "report_local_value_does_not_live_long_enough(\
1641 {:?}, {:?}, {:?}, {:?}, {:?}\
1643 location, name, borrow, drop_span, borrow_spans
1646 let borrow_span = borrow_spans.var_or_use_path_span();
1647 if let BorrowExplanation::MustBeValidFor {
1651 from_closure: false,
1655 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1660 opt_place_desc.as_ref(),
1666 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1668 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1669 let region_name = annotation.emit(self, &mut err);
1673 format!("`{}` would have to be valid for `{}`...", name, region_name),
1676 let fn_hir_id = self.mir_hir_id();
1680 "...but `{}` will be dropped here, when the {} returns",
1685 .opt_name(fn_hir_id)
1686 .map(|name| format!("function `{}`", name))
1687 .unwrap_or_else(|| {
1691 .typeck(self.mir_def_id())
1692 .node_type(fn_hir_id)
1695 ty::Closure(..) => "enclosing closure",
1696 ty::Generator(..) => "enclosing generator",
1697 kind => bug!("expected closure or generator, found {:?}", kind),
1705 "functions cannot return a borrow to data owned within the function's scope, \
1706 functions can only return borrows to data passed as arguments",
1709 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1710 references-and-borrowing.html#dangling-references>",
1713 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1715 explanation.add_explanation_to_diagnostic(
1726 err.span_label(borrow_span, "borrowed value does not live long enough");
1727 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1729 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1731 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1733 explanation.add_explanation_to_diagnostic(
1747 fn report_borrow_conflicts_with_destructor(
1750 borrow: &BorrowData<'tcx>,
1751 (place, drop_span): (Place<'tcx>, Span),
1752 kind: Option<WriteKind>,
1753 dropped_ty: Ty<'tcx>,
1756 "report_borrow_conflicts_with_destructor(\
1757 {:?}, {:?}, ({:?}, {:?}), {:?}\
1759 location, borrow, place, drop_span, kind,
1762 let borrow_spans = self.retrieve_borrow_spans(borrow);
1763 let borrow_span = borrow_spans.var_or_use();
1765 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1767 let what_was_dropped = match self.describe_place(place.as_ref()) {
1768 Some(name) => format!("`{}`", name),
1769 None => String::from("temporary value"),
1772 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1773 Some(borrowed) => format!(
1774 "here, drop of {D} needs exclusive access to `{B}`, \
1775 because the type `{T}` implements the `Drop` trait",
1776 D = what_was_dropped,
1781 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1782 D = what_was_dropped,
1786 err.span_label(drop_span, label);
1788 // Only give this note and suggestion if they could be relevant.
1790 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1792 BorrowExplanation::UsedLater { .. }
1793 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1794 err.note("consider using a `let` binding to create a longer lived value");
1799 explanation.add_explanation_to_diagnostic(
1809 self.buffer_error(err);
1812 fn report_thread_local_value_does_not_live_long_enough(
1816 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1818 "report_thread_local_value_does_not_live_long_enough(\
1821 drop_span, borrow_span
1824 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1828 "thread-local variables cannot be borrowed beyond the end of the function",
1830 err.span_label(drop_span, "end of enclosing function is here");
1835 #[instrument(level = "debug", skip(self))]
1836 fn report_temporary_value_does_not_live_long_enough(
1839 borrow: &BorrowData<'tcx>,
1841 borrow_spans: UseSpans<'tcx>,
1843 explanation: BorrowExplanation<'tcx>,
1844 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1845 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1848 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1859 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1860 err.span_label(proper_span, "creates a temporary value which is freed while still in use");
1861 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1864 BorrowExplanation::UsedLater(..)
1865 | BorrowExplanation::UsedLaterInLoop(..)
1866 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1867 // Only give this note and suggestion if it could be relevant.
1868 let sm = self.infcx.tcx.sess.source_map();
1869 let mut suggested = false;
1870 let msg = "consider using a `let` binding to create a longer lived value";
1872 /// We check that there's a single level of block nesting to ensure always correct
1873 /// suggestions. If we don't, then we only provide a free-form message to avoid
1874 /// misleading users in cases like `tests/ui/nll/borrowed-temporary-error.rs`.
1875 /// We could expand the analysis to suggest hoising all of the relevant parts of
1876 /// the users' code to make the code compile, but that could be too much.
1877 struct NestedStatementVisitor {
1883 impl<'tcx> Visitor<'tcx> for NestedStatementVisitor {
1884 fn visit_block(&mut self, block: &hir::Block<'tcx>) {
1886 walk_block(self, block);
1889 fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) {
1890 if self.span == expr.span {
1891 self.found = self.current;
1893 walk_expr(self, expr);
1896 let source_info = self.body.source_info(location);
1897 if let Some(scope) = self.body.source_scopes.get(source_info.scope)
1898 && let ClearCrossCrate::Set(scope_data) = &scope.local_data
1899 && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root)
1900 && let Some(id) = node.body_id()
1901 && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind
1903 for stmt in block.stmts {
1904 let mut visitor = NestedStatementVisitor {
1909 visitor.visit_stmt(stmt);
1910 if visitor.found == 0
1911 && stmt.span.contains(proper_span)
1912 && let Some(p) = sm.span_to_margin(stmt.span)
1913 && let Ok(s) = sm.span_to_snippet(proper_span)
1915 let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
1916 err.multipart_suggestion_verbose(
1919 (stmt.span.shrink_to_lo(), addition),
1920 (proper_span, "binding".to_string()),
1922 Applicability::MaybeIncorrect,
1935 explanation.add_explanation_to_diagnostic(
1945 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1947 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1952 fn try_report_cannot_return_reference_to_local(
1954 borrow: &BorrowData<'tcx>,
1957 category: ConstraintCategory<'tcx>,
1958 opt_place_desc: Option<&String>,
1959 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1960 let return_kind = match category {
1961 ConstraintCategory::Return(_) => "return",
1962 ConstraintCategory::Yield => "yield",
1966 // FIXME use a better heuristic than Spans
1967 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1973 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1974 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1975 match self.body.local_kind(local) {
1976 LocalKind::ReturnPointer | LocalKind::Temp => {
1977 bug!("temporary or return pointer with a name")
1979 LocalKind::Var => "local variable ",
1981 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1983 "variable captured by `move` "
1985 LocalKind::Arg => "function parameter ",
1991 format!("{}`{}`", local_kind, place_desc),
1992 format!("`{}` is borrowed here", place_desc),
1996 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1997 let local = root_place.local;
1998 match self.body.local_kind(local) {
1999 LocalKind::ReturnPointer | LocalKind::Temp => {
2000 ("temporary value".to_string(), "temporary value created here".to_string())
2003 "function parameter".to_string(),
2004 "function parameter borrowed here".to_string(),
2007 ("local binding".to_string(), "local binding introduced here".to_string())
2012 let mut err = self.cannot_return_reference_to_local(
2019 if return_span != borrow_span {
2020 err.span_label(borrow_span, note);
2022 let tcx = self.infcx.tcx;
2024 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
2025 let return_ty = tcx.erase_regions(return_ty);
2028 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator)
2031 .type_implements_trait(iter_trait, [return_ty], self.param_env)
2032 .must_apply_modulo_regions()
2034 err.span_suggestion_hidden(
2035 return_span.shrink_to_hi(),
2036 "use `.collect()` to allocate the iterator",
2037 ".collect::<Vec<_>>()",
2038 Applicability::MaybeIncorrect,
2046 #[instrument(level = "debug", skip(self))]
2047 fn report_escaping_closure_capture(
2049 use_span: UseSpans<'tcx>,
2051 fr_name: &RegionName,
2052 category: ConstraintCategory<'tcx>,
2053 constraint_span: Span,
2056 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
2057 let tcx = self.infcx.tcx;
2058 let args_span = use_span.args_or_use();
2060 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
2062 if string.starts_with("async ") {
2063 let pos = args_span.lo() + BytePos(6);
2064 (args_span.with_lo(pos).with_hi(pos), "move ")
2065 } else if string.starts_with("async|") {
2066 let pos = args_span.lo() + BytePos(5);
2067 (args_span.with_lo(pos).with_hi(pos), " move")
2069 (args_span.shrink_to_lo(), "move ")
2072 Err(_) => (args_span, "move |<args>| <body>"),
2074 let kind = match use_span.generator_kind() {
2075 Some(generator_kind) => match generator_kind {
2076 GeneratorKind::Async(async_kind) => match async_kind {
2077 AsyncGeneratorKind::Block => "async block",
2078 AsyncGeneratorKind::Closure => "async closure",
2079 _ => bug!("async block/closure expected, but async function found."),
2081 GeneratorKind::Gen => "generator",
2086 let mut err = self.cannot_capture_in_long_lived_closure(
2093 err.span_suggestion_verbose(
2096 "to force the {} to take ownership of {} (and any \
2097 other referenced variables), use the `move` keyword",
2101 Applicability::MachineApplicable,
2105 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
2106 let msg = format!("{} is returned here", kind);
2107 err.span_note(constraint_span, &msg);
2109 ConstraintCategory::CallArgument(_) => {
2110 fr_name.highlight_region_name(&mut err);
2111 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
2113 "async blocks are not executed immediately and must either take a \
2114 reference or ownership of outside variables they use",
2117 let msg = format!("{scope} requires argument type to outlive `{fr_name}`");
2118 err.span_note(constraint_span, &msg);
2122 "report_escaping_closure_capture called with unexpected constraint \
2131 fn report_escaping_data(
2134 name: &Option<String>,
2138 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
2139 let tcx = self.infcx.tcx;
2141 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
2144 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
2148 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
2151 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
2153 if let Some(name) = name {
2156 format!("reference to `{}` escapes the {} body here", name, escapes_from),
2161 format!("reference escapes the {} body here", escapes_from),
2168 fn get_moved_indexes(
2172 ) -> (Vec<MoveSite>, Vec<Location>) {
2173 fn predecessor_locations<'tcx, 'a>(
2174 body: &'a mir::Body<'tcx>,
2176 ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
2177 if location.statement_index == 0 {
2178 let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
2179 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
2181 Either::Right(std::iter::once(Location {
2182 statement_index: location.statement_index - 1,
2188 let mut mpis = vec![mpi];
2189 let move_paths = &self.move_data.move_paths;
2190 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
2192 let mut stack = Vec::new();
2193 let mut back_edge_stack = Vec::new();
2195 predecessor_locations(self.body, location).for_each(|predecessor| {
2196 if location.dominates(predecessor, &self.dominators) {
2197 back_edge_stack.push(predecessor)
2199 stack.push(predecessor);
2203 let mut reached_start = false;
2205 /* Check if the mpi is initialized as an argument */
2206 let mut is_argument = false;
2207 for arg in self.body.args_iter() {
2208 let path = self.move_data.rev_lookup.find_local(arg);
2209 if mpis.contains(&path) {
2214 let mut visited = FxHashSet::default();
2215 let mut move_locations = FxHashSet::default();
2216 let mut reinits = vec![];
2217 let mut result = vec![];
2219 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
2221 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
2222 location, is_back_edge
2225 if !visited.insert(location) {
2231 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
2232 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
2233 // this analysis only tries to find moves explicitly
2234 // written by the user, so we ignore the move-outs
2235 // created by `StorageDead` and at the beginning
2238 // If we are found a use of a.b.c which was in error, then we want to look for
2239 // moves not only of a.b.c but also a.b and a.
2241 // Note that the moves data already includes "parent" paths, so we don't have to
2242 // worry about the other case: that is, if there is a move of a.b.c, it is already
2243 // marked as a move of a.b and a as well, so we will generate the correct errors
2245 for moi in &self.move_data.loc_map[location] {
2246 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
2247 let path = self.move_data.moves[*moi].path;
2248 if mpis.contains(&path) {
2250 "report_use_of_moved_or_uninitialized: found {:?}",
2251 move_paths[path].place
2253 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
2254 move_locations.insert(location);
2256 // Strictly speaking, we could continue our DFS here. There may be
2257 // other moves that can reach the point of error. But it is kind of
2258 // confusing to highlight them.
2266 // drop(a); // <-- current point of error
2269 // Because we stop the DFS here, we only highlight `let c = a`,
2270 // and not `let b = a`. We will of course also report an error at
2271 // `let c = a` which highlights `let b = a` as the move.
2278 let mut any_match = false;
2279 for ii in &self.move_data.init_loc_map[location] {
2280 let init = self.move_data.inits[*ii];
2282 InitKind::Deep | InitKind::NonPanicPathOnly => {
2283 if mpis.contains(&init.path) {
2287 InitKind::Shallow => {
2288 if mpi == init.path {
2295 reinits.push(location);
2301 while let Some(location) = stack.pop() {
2302 if dfs_iter(&mut result, location, false) {
2306 let mut has_predecessor = false;
2307 predecessor_locations(self.body, location).for_each(|predecessor| {
2308 if location.dominates(predecessor, &self.dominators) {
2309 back_edge_stack.push(predecessor)
2311 stack.push(predecessor);
2313 has_predecessor = true;
2316 if !has_predecessor {
2317 reached_start = true;
2320 if (is_argument || !reached_start) && result.is_empty() {
2321 /* Process back edges (moves in future loop iterations) only if
2322 the move path is definitely initialized upon loop entry,
2323 to avoid spurious "in previous iteration" errors.
2324 During DFS, if there's a path from the error back to the start
2325 of the function with no intervening init or move, then the
2326 move path may be uninitialized at loop entry.
2328 while let Some(location) = back_edge_stack.pop() {
2329 if dfs_iter(&mut result, location, true) {
2333 predecessor_locations(self.body, location)
2334 .for_each(|predecessor| back_edge_stack.push(predecessor));
2338 // Check if we can reach these reinits from a move location.
2339 let reinits_reachable = reinits
2342 let mut visited = FxHashSet::default();
2343 let mut stack = vec![*reinit];
2344 while let Some(location) = stack.pop() {
2345 if !visited.insert(location) {
2348 if move_locations.contains(&location) {
2351 stack.extend(predecessor_locations(self.body, location));
2355 .collect::<Vec<Location>>();
2356 (result, reinits_reachable)
2359 pub(crate) fn report_illegal_mutation_of_borrowed(
2362 (place, span): (Place<'tcx>, Span),
2363 loan: &BorrowData<'tcx>,
2365 let loan_spans = self.retrieve_borrow_spans(loan);
2366 let loan_span = loan_spans.args_or_use();
2368 let descr_place = self.describe_any_place(place.as_ref());
2369 if loan.kind == BorrowKind::Shallow {
2370 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
2371 let mut err = self.cannot_mutate_in_immutable_section(
2378 loan_spans.var_span_label(
2380 format!("borrow occurs due to use{}", loan_spans.describe()),
2381 loan.kind.describe_mutability(),
2384 self.buffer_error(err);
2390 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
2392 loan_spans.var_span_label(
2394 format!("borrow occurs due to use{}", loan_spans.describe()),
2395 loan.kind.describe_mutability(),
2398 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
2408 self.explain_deref_coercion(loan, &mut err);
2410 self.buffer_error(err);
2413 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
2414 let tcx = self.infcx.tcx;
2416 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
2417 Some((method_did, method_substs)),
2419 &self.body[loan.reserve_location.block].terminator,
2420 rustc_const_eval::util::find_self_call(
2423 loan.assigned_place.local,
2424 loan.reserve_location.block,
2427 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
2429 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
2430 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
2433 if let Some(Ok(instance)) = deref_target {
2434 let deref_target_ty = instance.ty(tcx, self.param_env);
2436 "borrow occurs due to deref coercion to `{}`",
2439 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
2445 /// Reports an illegal reassignment; for example, an assignment to
2446 /// (part of) a non-`mut` local that occurs potentially after that
2447 /// local has already been initialized. `place` is the path being
2448 /// assigned; `err_place` is a place providing a reason why
2449 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
2450 /// assignment to `x.f`).
2451 pub(crate) fn report_illegal_reassignment(
2453 _location: Location,
2454 (place, span): (Place<'tcx>, Span),
2455 assigned_span: Span,
2456 err_place: Place<'tcx>,
2458 let (from_arg, local_decl, local_name) = match err_place.as_local() {
2460 self.body.local_kind(local) == LocalKind::Arg,
2461 Some(&self.body.local_decls[local]),
2462 self.local_names[local],
2464 None => (false, None, None),
2467 // If root local is initialized immediately (everything apart from let
2468 // PATTERN;) then make the error refer to that local, rather than the
2469 // place being assigned later.
2470 let (place_description, assigned_span) = match local_decl {
2473 Some(box LocalInfo::User(
2474 ClearCrossCrate::Clear
2475 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
2476 opt_match_place: None,
2480 | Some(box LocalInfo::StaticRef { .. })
2484 | None => (self.describe_any_place(place.as_ref()), assigned_span),
2485 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
2488 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
2489 let msg = if from_arg {
2490 "cannot assign to immutable argument"
2492 "cannot assign twice to immutable variable"
2494 if span != assigned_span && !from_arg {
2495 err.span_label(assigned_span, format!("first assignment to {}", place_description));
2497 if let Some(decl) = local_decl
2498 && let Some(name) = local_name
2499 && decl.can_be_made_mutable()
2501 err.span_suggestion(
2502 decl.source_info.span,
2503 "consider making this binding mutable",
2504 format!("mut {}", name),
2505 Applicability::MachineApplicable,
2508 err.span_label(span, msg);
2509 self.buffer_error(err);
2512 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
2513 let tcx = self.infcx.tcx;
2514 let (kind, _place_ty) = place.projection.iter().fold(
2515 (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)),
2516 |(kind, place_ty), &elem| {
2519 ProjectionElem::Deref => match kind {
2520 StorageDeadOrDrop::LocalStorageDead
2521 | StorageDeadOrDrop::BoxedStorageDead => {
2523 place_ty.ty.is_box(),
2524 "Drop of value behind a reference or raw pointer"
2526 StorageDeadOrDrop::BoxedStorageDead
2528 StorageDeadOrDrop::Destructor(_) => kind,
2530 ProjectionElem::OpaqueCast { .. }
2531 | ProjectionElem::Field(..)
2532 | ProjectionElem::Downcast(..) => {
2533 match place_ty.ty.kind() {
2534 ty::Adt(def, _) if def.has_dtor(tcx) => {
2535 // Report the outermost adt with a destructor
2537 StorageDeadOrDrop::Destructor(_) => kind,
2538 StorageDeadOrDrop::LocalStorageDead
2539 | StorageDeadOrDrop::BoxedStorageDead => {
2540 StorageDeadOrDrop::Destructor(place_ty.ty)
2547 ProjectionElem::ConstantIndex { .. }
2548 | ProjectionElem::Subslice { .. }
2549 | ProjectionElem::Index(_) => kind,
2551 place_ty.projection_ty(tcx, elem),
2558 /// Describe the reason for the fake borrow that was assigned to `place`.
2559 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
2560 use rustc_middle::mir::visit::Visitor;
2561 struct FakeReadCauseFinder<'tcx> {
2563 cause: Option<FakeReadCause>,
2565 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
2566 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
2568 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
2569 if *place == self.place =>
2571 self.cause = Some(*cause);
2577 let mut visitor = FakeReadCauseFinder { place, cause: None };
2578 visitor.visit_body(&self.body);
2579 match visitor.cause {
2580 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2581 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2586 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2587 /// borrow of local value that does not live long enough.
2588 fn annotate_argument_and_return_for_borrow(
2590 borrow: &BorrowData<'tcx>,
2591 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2592 // Define a fallback for when we can't match a closure.
2594 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2598 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2600 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2602 self.infcx.tcx.fn_sig(self.mir_def_id()),
2609 // In order to determine whether we need to annotate, we need to check whether the reserve
2610 // place was an assignment into a temporary.
2612 // If it was, we check whether or not that temporary is eventually assigned into the return
2613 // place. If it was, we can add annotations about the function's return type and arguments
2614 // and it'll make sense.
2615 let location = borrow.reserve_location;
2616 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2617 if let Some(Statement { kind: StatementKind::Assign(box (reservation, _)), .. }) =
2618 &self.body[location.block].statements.get(location.statement_index)
2620 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2621 // Check that the initial assignment of the reserve location is into a temporary.
2622 let mut target = match reservation.as_local() {
2623 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2627 // Next, look through the rest of the block, checking if we are assigning the
2628 // `target` (that is, the place that contains our borrow) to anything.
2629 let mut annotated_closure = None;
2630 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2632 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2635 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2636 if let Some(assigned_to) = place.as_local() {
2638 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2642 // Check if our `target` was captured by a closure.
2643 if let Rvalue::Aggregate(
2644 box AggregateKind::Closure(def_id, substs),
2648 for operand in operands {
2649 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2653 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2657 // Find the local from the operand.
2658 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2662 if assigned_from_local != target {
2666 // If a closure captured our `target` and then assigned
2667 // into a place then we should annotate the closure in
2668 // case it ends up being assigned into the return place.
2670 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2672 "annotate_argument_and_return_for_borrow: \
2673 annotated_closure={:?} assigned_from_local={:?} \
2675 annotated_closure, assigned_from_local, assigned_to
2678 if assigned_to == mir::RETURN_PLACE {
2679 // If it was assigned directly into the return place, then
2681 return annotated_closure;
2683 // Otherwise, update the target.
2684 target = assigned_to;
2688 // If none of our closure's operands matched, then skip to the next
2693 // Otherwise, look at other types of assignment.
2694 let assigned_from = match rvalue {
2695 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2696 Rvalue::Use(operand) => match operand {
2697 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2705 "annotate_argument_and_return_for_borrow: \
2706 assigned_from={:?}",
2710 // Find the local from the rvalue.
2711 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2713 "annotate_argument_and_return_for_borrow: \
2714 assigned_from_local={:?}",
2715 assigned_from_local,
2718 // Check if our local matches the target - if so, we've assigned our
2719 // borrow to a new place.
2720 if assigned_from_local != target {
2724 // If we assigned our `target` into a new place, then we should
2725 // check if it was the return place.
2727 "annotate_argument_and_return_for_borrow: \
2728 assigned_from_local={:?} assigned_to={:?}",
2729 assigned_from_local, assigned_to
2731 if assigned_to == mir::RETURN_PLACE {
2732 // If it was then return the annotated closure if there was one,
2733 // else, annotate this function.
2734 return annotated_closure.or_else(fallback);
2737 // If we didn't assign into the return place, then we just update
2739 target = assigned_to;
2744 // Check the terminator if we didn't find anything in the statements.
2745 let terminator = &self.body[location.block].terminator();
2747 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2750 if let TerminatorKind::Call { destination, target: Some(_), args, .. } =
2753 if let Some(assigned_to) = destination.as_local() {
2755 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2758 for operand in args {
2759 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2763 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2767 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2769 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2770 assigned_from_local,
2773 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2774 return annotated_closure.or_else(fallback);
2782 // If we haven't found an assignment into the return place, then we need not add
2784 debug!("annotate_argument_and_return_for_borrow: none found");
2788 /// Annotate the first argument and return type of a function signature if they are
2793 sig: ty::PolyFnSig<'tcx>,
2794 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2795 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2796 let is_closure = self.infcx.tcx.is_closure(did.to_def_id());
2797 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
2798 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2800 // We need to work out which arguments to highlight. We do this by looking
2801 // at the return type, where there are three cases:
2803 // 1. If there are named arguments, then we should highlight the return type and
2804 // highlight any of the arguments that are also references with that lifetime.
2805 // If there are no arguments that have the same lifetime as the return type,
2806 // then don't highlight anything.
2807 // 2. The return type is a reference with an anonymous lifetime. If this is
2808 // the case, then we can take advantage of (and teach) the lifetime elision
2811 // We know that an error is being reported. So the arguments and return type
2812 // must satisfy the elision rules. Therefore, if there is a single argument
2813 // then that means the return type and first (and only) argument have the same
2814 // lifetime and the borrow isn't meeting that, we can highlight the argument
2817 // If there are multiple arguments then the first argument must be self (else
2818 // it would not satisfy the elision rules), so we can highlight self and the
2820 // 3. The return type is not a reference. In this case, we don't highlight
2822 let return_ty = sig.output();
2823 match return_ty.skip_binder().kind() {
2824 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2825 // This is case 1 from above, return type is a named reference so we need to
2826 // search for relevant arguments.
2827 let mut arguments = Vec::new();
2828 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2829 if let ty::Ref(argument_region, _, _) = argument.kind() {
2830 if argument_region == return_region {
2831 // Need to use the `rustc_middle::ty` types to compare against the
2832 // `return_region`. Then use the `rustc_hir` type to get only
2833 // the lifetime span.
2834 if let hir::TyKind::Ref(lifetime, _) = &fn_decl.inputs[index].kind {
2835 // With access to the lifetime, we can get
2837 arguments.push((*argument, lifetime.ident.span));
2839 bug!("ty type is a ref but hir type is not");
2845 // We need to have arguments. This shouldn't happen, but it's worth checking.
2846 if arguments.is_empty() {
2850 // We use a mix of the HIR and the Ty types to get information
2851 // as the HIR doesn't have full types for closure arguments.
2852 let return_ty = sig.output().skip_binder();
2853 let mut return_span = fn_decl.output.span();
2854 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2855 if let hir::TyKind::Ref(lifetime, _) = ty.kind {
2856 return_span = lifetime.ident.span;
2860 Some(AnnotatedBorrowFnSignature::NamedFunction {
2866 ty::Ref(_, _, _) if is_closure => {
2867 // This is case 2 from above but only for closures, return type is anonymous
2868 // reference so we select
2869 // the first argument.
2870 let argument_span = fn_decl.inputs.first()?.span;
2871 let argument_ty = sig.inputs().skip_binder().first()?;
2873 // Closure arguments are wrapped in a tuple, so we need to get the first
2875 if let ty::Tuple(elems) = argument_ty.kind() {
2876 let &argument_ty = elems.first()?;
2877 if let ty::Ref(_, _, _) = argument_ty.kind() {
2878 return Some(AnnotatedBorrowFnSignature::Closure {
2887 ty::Ref(_, _, _) => {
2888 // This is also case 2 from above but for functions, return type is still an
2889 // anonymous reference so we select the first argument.
2890 let argument_span = fn_decl.inputs.first()?.span;
2891 let argument_ty = *sig.inputs().skip_binder().first()?;
2893 let return_span = fn_decl.output.span();
2894 let return_ty = sig.output().skip_binder();
2896 // We expect the first argument to be a reference.
2897 match argument_ty.kind() {
2898 ty::Ref(_, _, _) => {}
2902 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2910 // This is case 3 from above, return type is not a reference so don't highlight
2919 enum AnnotatedBorrowFnSignature<'tcx> {
2921 arguments: Vec<(Ty<'tcx>, Span)>,
2922 return_ty: Ty<'tcx>,
2926 argument_ty: Ty<'tcx>,
2927 argument_span: Span,
2928 return_ty: Ty<'tcx>,
2932 argument_ty: Ty<'tcx>,
2933 argument_span: Span,
2937 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2938 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2940 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2942 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2945 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2948 cx.get_region_name_for_ty(argument_ty, 0)
2950 &AnnotatedBorrowFnSignature::AnonymousFunction {
2956 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2957 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2959 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2960 let types_equal = return_ty_name == argument_ty_name;
2965 if types_equal { "also " } else { "" },
2971 "argument and return type have the same lifetime due to lifetime elision rules",
2974 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2975 lifetime-syntax.html#lifetime-elision>",
2978 cx.get_region_name_for_ty(return_ty, 0)
2980 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2981 // Region of return type and arguments checked to be the same earlier.
2982 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2983 for (_, argument_span) in arguments {
2984 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2987 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2990 "use data from the highlighted arguments which match the `{}` lifetime of \
3001 /// Detect whether one of the provided spans is a statement nested within the top-most visited expr
3002 struct ReferencedStatementsVisitor<'a>(&'a [Span], bool);
3004 impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> {
3005 fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
3007 hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => {
3015 /// Given a set of spans representing statements initializing the relevant binding, visit all the
3016 /// function expressions looking for branching code paths that *do not* initialize the binding.
3017 struct ConditionVisitor<'b> {
3020 errors: Vec<(Span, String)>,
3023 impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
3024 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
3026 hir::ExprKind::If(cond, body, None) => {
3027 // `if` expressions with no `else` that initialize the binding might be missing an
3029 let mut v = ReferencedStatementsVisitor(self.spans, false);
3035 "if this `if` condition is `false`, {} is not initialized",
3040 ex.span.shrink_to_hi(),
3041 format!("an `else` arm might be missing here, initializing {}", self.name),
3045 hir::ExprKind::If(cond, body, Some(other)) => {
3046 // `if` expressions where the binding is only initialized in one of the two arms
3047 // might be missing a binding initialization.
3048 let mut a = ReferencedStatementsVisitor(self.spans, false);
3050 let mut b = ReferencedStatementsVisitor(self.spans, false);
3051 b.visit_expr(other);
3053 (true, true) | (false, false) => {}
3055 if other.span.is_desugaring(DesugaringKind::WhileLoop) {
3059 "if this condition isn't met and the `while` loop runs 0 \
3060 times, {} is not initialized",
3066 body.span.shrink_to_hi().until(other.span),
3068 "if the `if` condition is `false` and this `else` arm is \
3069 executed, {} is not initialized",
3079 "if this condition is `true`, {} is not initialized",
3086 hir::ExprKind::Match(e, arms, loop_desugar) => {
3087 // If the binding is initialized in one of the match arms, then the other match
3088 // arms might be missing an initialization.
3089 let results: Vec<bool> = arms
3092 let mut v = ReferencedStatementsVisitor(self.spans, false);
3097 if results.iter().any(|x| *x) && !results.iter().all(|x| *x) {
3098 for (arm, seen) in arms.iter().zip(results) {
3100 if loop_desugar == hir::MatchSource::ForLoopDesugar {
3104 "if the `for` loop runs 0 times, {} is not initialized",
3108 } else if let Some(guard) = &arm.guard {
3110 arm.pat.span.to(guard.body().span),
3112 "if this pattern and condition are matched, {} is not \
3121 "if this pattern is matched, {} is not initialized",
3130 // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should
3131 // also be accounted for. For now it is fine, as if we don't find *any* relevant
3132 // branching code paths, we point at the places where the binding *is* initialized for
3136 walk_expr(self, ex);