2 use rustc_const_eval::util::CallKind;
3 use rustc_data_structures::captures::Captures;
4 use rustc_data_structures::fx::FxHashSet;
6 struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
9 use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
10 use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem};
11 use rustc_infer::infer::TyCtxtInferExt;
12 use rustc_infer::traits::ObligationCause;
13 use rustc_middle::mir::tcx::PlaceTy;
14 use rustc_middle::mir::{
15 self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
16 FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
17 ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
19 use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty};
20 use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
21 use rustc_span::def_id::LocalDefId;
22 use rustc_span::hygiene::DesugaringKind;
23 use rustc_span::symbol::sym;
24 use rustc_span::{BytePos, Span, Symbol};
25 use rustc_trait_selection::infer::InferCtxtExt;
27 use crate::borrow_set::TwoPhaseActivation;
28 use crate::borrowck_errors;
30 use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;
31 use crate::diagnostics::find_all_local_uses;
33 borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
34 InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
38 explain_borrow::{BorrowExplanation, LaterUseKind},
39 DescribePlaceOpt, RegionName, RegionNameSource, UseSpans,
44 /// Index of the "move out" that we found. The `MoveData` can
45 /// then tell us where the move occurred.
48 /// `true` if we traversed a back edge while walking from the point
49 /// of error to the move site.
50 traversed_back_edge: bool,
53 /// Which case a StorageDeadOrDrop is for.
54 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
55 enum StorageDeadOrDrop<'tcx> {
61 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
62 pub(crate) fn report_use_of_moved_or_uninitialized(
65 desired_action: InitializationRequiringAction,
66 (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),
70 "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
71 moved_place={:?} used_place={:?} span={:?} mpi={:?}",
72 location, desired_action, moved_place, used_place, span, mpi
76 self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
77 let span = use_spans.args_or_use();
79 let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
81 "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
82 move_site_vec, use_spans
84 let move_out_indices: Vec<_> =
85 move_site_vec.iter().map(|move_site| move_site.moi).collect();
87 if move_out_indices.is_empty() {
88 let root_place = PlaceRef { projection: &[], ..used_place };
90 if !self.uninitialized_error_reported.insert(root_place) {
92 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
98 let err = self.report_use_of_uninitialized(
106 self.buffer_error(err);
108 if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) {
109 if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) {
111 "report_use_of_moved_or_uninitialized place: error suppressed mois={:?}",
118 let is_partial_move = move_site_vec.iter().any(|move_site| {
119 let move_out = self.move_data.moves[(*move_site).moi];
120 let moved_place = &self.move_data.move_paths[move_out.path].place;
121 // `*(_1)` where `_1` is a `Box` is actually a move out.
122 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]
123 && self.body.local_decls[moved_place.local].ty.is_box();
126 && used_place != moved_place.as_ref()
127 && used_place.is_prefix_of(moved_place.as_ref())
130 let partial_str = if is_partial_move { "partial " } else { "" };
131 let partially_str = if is_partial_move { "partially " } else { "" };
133 let mut err = self.cannot_act_on_moved_value(
135 desired_action.as_noun(),
137 self.describe_place_with_options(
139 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
143 let reinit_spans = maybe_reinitialized_locations
147 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
150 .collect::<Vec<Span>>();
152 let reinits = maybe_reinitialized_locations.len();
154 err.span_label(reinit_spans[0], "this reinitialization might get skipped");
155 } else if reinits > 1 {
157 MultiSpan::from_spans(reinit_spans),
159 format!("these {} reinitializations might get skipped", reinits)
162 "these 3 reinitializations and {} other{} might get skipped",
164 if reinits == 4 { "" } else { "s" }
170 let closure = self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
172 let mut is_loop_move = false;
173 let mut in_pattern = false;
174 let mut seen_spans = FxHashSet::default();
176 for move_site in &move_site_vec {
177 let move_out = self.move_data.moves[(*move_site).moi];
178 let moved_place = &self.move_data.move_paths[move_out.path].place;
180 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
181 let move_span = move_spans.args_or_use();
183 let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
185 let loop_message = if location == move_out.source || move_site.traversed_back_edge {
186 ", in previous iteration of loop"
191 if location == move_out.source {
195 if !seen_spans.contains(&move_span) {
197 self.suggest_ref_or_clone(
206 self.explain_captures(
216 maybe_reinitialized_locations.is_empty(),
219 seen_spans.insert(move_span);
222 use_spans.var_path_only_subdiag(&mut err, desired_action);
228 "value {} here after {}move",
229 desired_action.as_verb_in_past_tense(),
235 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
236 let needs_note = match ty.kind() {
237 ty::Closure(id, _) => {
238 let tables = self.infcx.tcx.typeck(id.expect_local());
239 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
241 tables.closure_kind_origins().get(hir_id).is_none()
246 let mpi = self.move_data.moves[move_out_indices[0]].path;
247 let place = &self.move_data.move_paths[mpi].place;
248 let ty = place.ty(self.body, self.infcx.tcx).ty;
250 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
251 // Same for if we're in a loop, see #101119.
252 if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
253 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
254 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
255 err.span_suggestion_verbose(
258 "consider creating a fresh reborrow of {} here",
259 self.describe_place(moved_place)
260 .map(|n| format!("`{}`", n))
261 .unwrap_or_else(|| "the mutable reference".to_string()),
264 Applicability::MachineApplicable,
269 let opt_name = self.describe_place_with_options(
271 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
273 let note_msg = match opt_name {
274 Some(name) => format!("`{}`", name),
275 None => "value".to_owned(),
277 if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) {
278 // Suppress the next suggestion since we don't want to put more bounds onto
279 // something that already has `Fn`-like bounds (or is a closure), so we can't
282 self.suggest_adding_copy_bounds(&mut err, ty, span);
286 let span = if let Some(local) = place.as_local() {
287 Some(self.body.local_decls[local].source_info.span)
291 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
294 if let UseSpans::FnSelfUse {
295 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
300 "{} occurs due to deref coercion to `{}`",
301 desired_action.as_noun(),
305 // Check first whether the source is accessible (issue #87060)
306 if self.infcx.tcx.sess.source_map().is_span_accessible(deref_target) {
307 err.span_note(deref_target, "deref defined here");
311 self.buffer_move_error(move_out_indices, (used_place, err));
315 fn suggest_ref_or_clone(
319 err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
320 in_pattern: &mut bool,
321 move_spans: UseSpans<'_>,
323 struct ExpressionFinder<'hir> {
325 expr: Option<&'hir hir::Expr<'hir>>,
326 pat: Option<&'hir hir::Pat<'hir>>,
327 parent_pat: Option<&'hir hir::Pat<'hir>>,
329 impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
330 fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
331 if e.span == self.expr_span {
334 hir::intravisit::walk_expr(self, e);
336 fn visit_pat(&mut self, p: &'hir hir::Pat<'hir>) {
337 if p.span == self.expr_span {
340 if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, i, sub) = p.kind {
341 if i.span == self.expr_span || p.span == self.expr_span {
344 // Check if we are in a situation of `ident @ ident` where we want to suggest
345 // `ref ident @ ref ident` or `ref ident @ Struct { ref ident }`.
346 if let Some(subpat) = sub && self.pat.is_none() {
347 self.visit_pat(subpat);
348 if self.pat.is_some() {
349 self.parent_pat = Some(p);
354 hir::intravisit::walk_pat(self, p);
357 let hir = self.infcx.tcx.hir();
358 if let Some(hir::Node::Item(hir::Item {
359 kind: hir::ItemKind::Fn(_, _, body_id),
361 })) = hir.find(hir.local_def_id_to_hir_id(self.mir_def_id()))
362 && let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id)
364 let place = &self.move_data.move_paths[mpi].place;
365 let span = place.as_local()
366 .map(|local| self.body.local_decls[local].source_info.span);
367 let mut finder = ExpressionFinder {
368 expr_span: move_span,
373 finder.visit_expr(expr);
374 if let Some(span) = span && let Some(expr) = finder.expr {
375 for (_, expr) in hir.parent_iter(expr.hir_id) {
376 if let hir::Node::Expr(expr) = expr {
377 if expr.span.contains(span) {
378 // If the let binding occurs within the same loop, then that
379 // loop isn't relevant, like in the following, the outermost `loop`
380 // doesn't play into `x` being moved.
383 // let x = String::new();
391 if let hir::ExprKind::Loop(.., loop_span) = expr.kind {
392 err.span_label(loop_span, "inside of this loop");
396 let typeck = self.infcx.tcx.typeck(self.mir_def_id());
397 let hir_id = hir.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 = |predicates: ty::EarlyBinder<
677 &[(ty::Predicate<'tcx>, Span)],
680 predicates.0.iter().find_map(|(pred, _)| {
681 let pred = if let Some(substs) = substs {
682 predicates.rebind(*pred).subst(tcx, substs).kind().skip_binder()
684 pred.kind().skip_binder()
686 if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred && pred.self_ty() == ty {
687 if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
688 return Some(hir::Mutability::Not);
689 } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
690 return Some(hir::Mutability::Mut);
697 // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
698 // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
699 // These types seem reasonably opaque enough that they could be substituted with their
700 // borrowed variants in a function body when we see a move error.
701 let borrow_level = match ty.kind() {
702 ty::Param(_) => find_fn_kind_from_did(
703 tcx.bound_explicit_predicates_of(self.mir_def_id().to_def_id())
704 .map_bound(|p| p.predicates),
707 ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
708 find_fn_kind_from_did(tcx.bound_explicit_item_bounds(*def_id), Some(*substs))
710 ty::Closure(_, substs) => match substs.as_closure().kind() {
711 ty::ClosureKind::Fn => Some(hir::Mutability::Not),
712 ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
718 let Some(borrow_level) = borrow_level else { return false; };
719 let sugg = move_sites
722 let move_out = self.move_data.moves[(*move_site).moi];
723 let moved_place = &self.move_data.move_paths[move_out.path].place;
724 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
725 let move_span = move_spans.args_or_use();
726 let suggestion = borrow_level.ref_prefix_str().to_owned();
727 (move_span.shrink_to_lo(), suggestion)
730 err.multipart_suggestion_verbose(
731 format!("consider {}borrowing {value_name}", borrow_level.mutably_str()),
733 Applicability::MaybeIncorrect,
738 fn suggest_cloning(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
739 let tcx = self.infcx.tcx;
740 // Try to find predicates on *generic params* that would allow copying `ty`
741 let infcx = tcx.infer_ctxt().build();
743 if let Some(clone_trait_def) = tcx.lang_items().clone_trait()
745 .type_implements_trait(
747 [tcx.erase_regions(ty)],
750 .must_apply_modulo_regions()
752 err.span_suggestion_verbose(
754 "consider cloning the value if the performance cost is acceptable",
756 Applicability::MachineApplicable,
761 fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
762 let tcx = self.infcx.tcx;
763 let generics = tcx.generics_of(self.mir_def_id());
765 let Some(hir_generics) = tcx
766 .typeck_root_def_id(self.mir_def_id().to_def_id())
768 .and_then(|def_id| tcx.hir().get_generics(def_id))
770 // Try to find predicates on *generic params* that would allow copying `ty`
771 let infcx = tcx.infer_ctxt().build();
772 let copy_did = infcx.tcx.require_lang_item(LangItem::Copy, Some(span));
773 let cause = ObligationCause::new(
776 rustc_infer::traits::ObligationCauseCode::MiscObligation,
778 let errors = rustc_trait_selection::traits::fully_solve_bound(
782 // Erase any region vids from the type, which may not be resolved
783 infcx.tcx.erase_regions(ty),
787 // Only emit suggestion if all required predicates are on generic
788 let predicates: Result<Vec<_>, _> = errors
790 .map(|err| match err.obligation.predicate.kind().skip_binder() {
791 PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
792 match predicate.self_ty().kind() {
793 ty::Param(param_ty) => Ok((
794 generics.type_param(param_ty, tcx),
795 predicate.trait_ref.print_only_trait_path().to_string(),
804 if let Ok(predicates) = predicates {
805 suggest_constraining_type_params(
811 .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
816 pub(crate) fn report_move_out_while_borrowed(
819 (place, span): (Place<'tcx>, Span),
820 borrow: &BorrowData<'tcx>,
823 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
824 location, place, span, borrow
826 let value_msg = self.describe_any_place(place.as_ref());
827 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
829 let borrow_spans = self.retrieve_borrow_spans(borrow);
830 let borrow_span = borrow_spans.args_or_use();
832 let move_spans = self.move_spans(place.as_ref(), location);
833 let span = move_spans.args_or_use();
835 let mut err = self.cannot_move_when_borrowed(
838 &self.describe_any_place(place.as_ref()),
843 borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow);
845 move_spans.var_span_label(
847 format!("move occurs due to use{}", move_spans.describe()),
851 self.explain_why_borrow_contains_point(location, borrow, None)
852 .add_explanation_to_diagnostic(
861 self.buffer_error(err);
864 pub(crate) fn report_use_while_mutably_borrowed(
867 (place, _span): (Place<'tcx>, Span),
868 borrow: &BorrowData<'tcx>,
869 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
870 let borrow_spans = self.retrieve_borrow_spans(borrow);
871 let borrow_span = borrow_spans.args_or_use();
873 // Conflicting borrows are reported separately, so only check for move
875 let use_spans = self.move_spans(place.as_ref(), location);
876 let span = use_spans.var_or_use();
878 // 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
879 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
880 let mut err = self.cannot_use_when_mutably_borrowed(
882 &self.describe_any_place(place.as_ref()),
884 &self.describe_any_place(borrow.borrowed_place.as_ref()),
886 borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| {
887 use crate::session_diagnostics::CaptureVarCause::*;
888 let place = &borrow.borrowed_place;
889 let desc_place = self.describe_any_place(place.as_ref());
891 Some(_) => BorrowUsePlaceGenerator { place: desc_place, var_span },
892 None => BorrowUsePlaceClosure { place: desc_place, var_span },
896 self.explain_why_borrow_contains_point(location, borrow, None)
897 .add_explanation_to_diagnostic(
909 pub(crate) fn report_conflicting_borrow(
912 (place, span): (Place<'tcx>, Span),
913 gen_borrow_kind: BorrowKind,
914 issued_borrow: &BorrowData<'tcx>,
915 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
916 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
917 let issued_span = issued_spans.args_or_use();
919 let borrow_spans = self.borrow_spans(span, location);
920 let span = borrow_spans.args_or_use();
922 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
928 let (desc_place, msg_place, msg_borrow, union_type_name) =
929 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
931 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
932 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
934 // FIXME: supply non-"" `opt_via` when appropriate
935 let first_borrow_desc;
936 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
937 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
938 first_borrow_desc = "mutable ";
939 self.cannot_reborrow_already_borrowed(
951 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
952 first_borrow_desc = "immutable ";
953 self.cannot_reborrow_already_borrowed(
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 /// Returns the description of the root place for a conflicting borrow and the full
1246 /// descriptions of the places that caused the conflict.
1248 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
1249 /// attempted while a shared borrow is live, then this function will return:
1254 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
1255 /// a shared borrow of another field `x.y`, then this function will return:
1257 /// ("x", "x.z", "x.y")
1260 /// In the more complex union case, where the union is a field of a struct, then if a mutable
1261 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
1262 /// another field `x.u.y`, then this function will return:
1264 /// ("x.u", "x.u.z", "x.u.y")
1267 /// This is used when creating error messages like below:
1270 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
1271 /// mutable (via `a.u.s.b`) [E0502]
1273 pub(crate) fn describe_place_for_conflicting_borrow(
1275 first_borrowed_place: Place<'tcx>,
1276 second_borrowed_place: Place<'tcx>,
1277 ) -> (String, String, String, String) {
1278 // Define a small closure that we can use to check if the type of a place
1280 let union_ty = |place_base| {
1281 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
1282 // using a type annotation in the closure argument instead leads to a lifetime error.
1283 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
1284 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
1287 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
1288 // code duplication (particularly around returning an empty description in the failure
1292 // If we have a conflicting borrow of the same place, then we don't want to add
1293 // an extraneous "via x.y" to our diagnostics, so filter out this case.
1294 first_borrowed_place != second_borrowed_place
1297 // We're going to want to traverse the first borrowed place to see if we can find
1298 // field access to a union. If we find that, then we will keep the place of the
1299 // union being accessed and the field that was being accessed so we can check the
1300 // second borrowed place for the same union and an access to a different field.
1301 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
1303 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
1304 return Some((place_base, field));
1311 .and_then(|(target_base, target_field)| {
1312 // With the place of a union and a field access into it, we traverse the second
1313 // borrowed place and look for an access to a different field of the same union.
1314 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
1315 if let ProjectionElem::Field(field, _) = elem {
1316 if let Some(union_ty) = union_ty(place_base) {
1317 if field != target_field && place_base == target_base {
1319 self.describe_any_place(place_base),
1320 self.describe_any_place(first_borrowed_place.as_ref()),
1321 self.describe_any_place(second_borrowed_place.as_ref()),
1322 union_ty.to_string(),
1330 .unwrap_or_else(|| {
1331 // If we didn't find a field access into a union, or both places match, then
1332 // only return the description of the first place.
1334 self.describe_any_place(first_borrowed_place.as_ref()),
1342 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
1344 /// This means that some data referenced by `borrow` needs to live
1345 /// past the point where the StorageDeadOrDrop of `place` occurs.
1346 /// This is usually interpreted as meaning that `place` has too
1347 /// short a lifetime. (But sometimes it is more useful to report
1348 /// it as a more direct conflict between the execution of a
1349 /// `Drop::drop` with an aliasing borrow.)
1350 #[instrument(level = "debug", skip(self))]
1351 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
1354 borrow: &BorrowData<'tcx>,
1355 place_span: (Place<'tcx>, Span),
1356 kind: Option<WriteKind>,
1358 let drop_span = place_span.1;
1360 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1362 let borrow_spans = self.retrieve_borrow_spans(borrow);
1363 let borrow_span = borrow_spans.var_or_use_path_span();
1365 assert!(root_place.projection.is_empty());
1366 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1368 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1370 if self.access_place_error_reported.contains(&(
1371 Place { local: root_place.local, projection: root_place_projection },
1375 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1381 self.access_place_error_reported.insert((
1382 Place { local: root_place.local, projection: root_place_projection },
1386 let borrowed_local = borrow.borrowed_place.local;
1387 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1389 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1390 self.buffer_error(err);
1394 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1395 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1397 // If a borrow of path `B` conflicts with drop of `D` (and
1398 // we're not in the uninteresting case where `B` is a
1399 // prefix of `D`), then report this as a more interesting
1400 // destructor conflict.
1401 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1402 self.report_borrow_conflicts_with_destructor(
1403 location, borrow, place_span, kind, dropped_ty,
1409 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1411 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1412 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1414 debug!(?place_desc, ?explanation);
1416 let err = match (place_desc, explanation) {
1417 // If the outlives constraint comes from inside the closure,
1422 // Box::new(|| y) as Box<Fn() -> &'static i32>
1424 // then just use the normal error. The closure isn't escaping
1425 // and `move` will not help here.
1428 BorrowExplanation::MustBeValidFor {
1430 category @ (ConstraintCategory::Return(_)
1431 | ConstraintCategory::CallArgument(_)
1432 | ConstraintCategory::OpaqueType),
1433 from_closure: false,
1438 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1439 .report_escaping_closure_capture(
1445 &format!("`{}`", name),
1449 BorrowExplanation::MustBeValidFor {
1450 category: ConstraintCategory::Assignment,
1451 from_closure: false,
1454 source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
1460 ) => self.report_escaping_data(borrow_span, &name, upvar_span, upvar_name, span),
1461 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1469 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1479 self.buffer_error(err);
1482 fn report_local_value_does_not_live_long_enough(
1486 borrow: &BorrowData<'tcx>,
1488 borrow_spans: UseSpans<'tcx>,
1489 explanation: BorrowExplanation<'tcx>,
1490 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1492 "report_local_value_does_not_live_long_enough(\
1493 {:?}, {:?}, {:?}, {:?}, {:?}\
1495 location, name, borrow, drop_span, borrow_spans
1498 let borrow_span = borrow_spans.var_or_use_path_span();
1499 if let BorrowExplanation::MustBeValidFor {
1503 from_closure: false,
1507 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1512 opt_place_desc.as_ref(),
1518 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1520 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1521 let region_name = annotation.emit(self, &mut err);
1525 format!("`{}` would have to be valid for `{}`...", name, region_name),
1528 let fn_hir_id = self.mir_hir_id();
1532 "...but `{}` will be dropped here, when the {} returns",
1537 .opt_name(fn_hir_id)
1538 .map(|name| format!("function `{}`", name))
1539 .unwrap_or_else(|| {
1543 .typeck(self.mir_def_id())
1544 .node_type(fn_hir_id)
1547 ty::Closure(..) => "enclosing closure",
1548 ty::Generator(..) => "enclosing generator",
1549 kind => bug!("expected closure or generator, found {:?}", kind),
1557 "functions cannot return a borrow to data owned within the function's scope, \
1558 functions can only return borrows to data passed as arguments",
1561 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1562 references-and-borrowing.html#dangling-references>",
1565 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1567 explanation.add_explanation_to_diagnostic(
1578 err.span_label(borrow_span, "borrowed value does not live long enough");
1579 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1581 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1583 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1585 explanation.add_explanation_to_diagnostic(
1599 fn report_borrow_conflicts_with_destructor(
1602 borrow: &BorrowData<'tcx>,
1603 (place, drop_span): (Place<'tcx>, Span),
1604 kind: Option<WriteKind>,
1605 dropped_ty: Ty<'tcx>,
1608 "report_borrow_conflicts_with_destructor(\
1609 {:?}, {:?}, ({:?}, {:?}), {:?}\
1611 location, borrow, place, drop_span, kind,
1614 let borrow_spans = self.retrieve_borrow_spans(borrow);
1615 let borrow_span = borrow_spans.var_or_use();
1617 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1619 let what_was_dropped = match self.describe_place(place.as_ref()) {
1620 Some(name) => format!("`{}`", name),
1621 None => String::from("temporary value"),
1624 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1625 Some(borrowed) => format!(
1626 "here, drop of {D} needs exclusive access to `{B}`, \
1627 because the type `{T}` implements the `Drop` trait",
1628 D = what_was_dropped,
1633 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1634 D = what_was_dropped,
1638 err.span_label(drop_span, label);
1640 // Only give this note and suggestion if they could be relevant.
1642 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1644 BorrowExplanation::UsedLater { .. }
1645 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1646 err.note("consider using a `let` binding to create a longer lived value");
1651 explanation.add_explanation_to_diagnostic(
1661 self.buffer_error(err);
1664 fn report_thread_local_value_does_not_live_long_enough(
1668 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1670 "report_thread_local_value_does_not_live_long_enough(\
1673 drop_span, borrow_span
1676 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1680 "thread-local variables cannot be borrowed beyond the end of the function",
1682 err.span_label(drop_span, "end of enclosing function is here");
1687 #[instrument(level = "debug", skip(self))]
1688 fn report_temporary_value_does_not_live_long_enough(
1691 borrow: &BorrowData<'tcx>,
1693 borrow_spans: UseSpans<'tcx>,
1695 explanation: BorrowExplanation<'tcx>,
1696 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1697 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1700 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1711 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1712 err.span_label(proper_span, "creates a temporary value which is freed while still in use");
1713 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1716 BorrowExplanation::UsedLater(..)
1717 | BorrowExplanation::UsedLaterInLoop(..)
1718 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1719 // Only give this note and suggestion if it could be relevant.
1720 let sm = self.infcx.tcx.sess.source_map();
1721 let mut suggested = false;
1722 let msg = "consider using a `let` binding to create a longer lived value";
1724 /// We check that there's a single level of block nesting to ensure always correct
1725 /// suggestions. If we don't, then we only provide a free-form message to avoid
1726 /// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`.
1727 /// We could expand the analysis to suggest hoising all of the relevant parts of
1728 /// the users' code to make the code compile, but that could be too much.
1729 struct NestedStatementVisitor {
1735 impl<'tcx> Visitor<'tcx> for NestedStatementVisitor {
1736 fn visit_block(&mut self, block: &hir::Block<'tcx>) {
1738 walk_block(self, block);
1741 fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) {
1742 if self.span == expr.span {
1743 self.found = self.current;
1745 walk_expr(self, expr);
1748 let source_info = self.body.source_info(location);
1749 if let Some(scope) = self.body.source_scopes.get(source_info.scope)
1750 && let ClearCrossCrate::Set(scope_data) = &scope.local_data
1751 && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root)
1752 && let Some(id) = node.body_id()
1753 && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind
1755 for stmt in block.stmts {
1756 let mut visitor = NestedStatementVisitor {
1761 visitor.visit_stmt(stmt);
1762 if visitor.found == 0
1763 && stmt.span.contains(proper_span)
1764 && let Some(p) = sm.span_to_margin(stmt.span)
1765 && let Ok(s) = sm.span_to_snippet(proper_span)
1767 let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
1768 err.multipart_suggestion_verbose(
1771 (stmt.span.shrink_to_lo(), addition),
1772 (proper_span, "binding".to_string()),
1774 Applicability::MaybeIncorrect,
1787 explanation.add_explanation_to_diagnostic(
1797 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1799 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1804 fn try_report_cannot_return_reference_to_local(
1806 borrow: &BorrowData<'tcx>,
1809 category: ConstraintCategory<'tcx>,
1810 opt_place_desc: Option<&String>,
1811 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1812 let return_kind = match category {
1813 ConstraintCategory::Return(_) => "return",
1814 ConstraintCategory::Yield => "yield",
1818 // FIXME use a better heuristic than Spans
1819 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1825 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1826 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1827 match self.body.local_kind(local) {
1828 LocalKind::ReturnPointer | LocalKind::Temp => {
1829 bug!("temporary or return pointer with a name")
1831 LocalKind::Var => "local variable ",
1833 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1835 "variable captured by `move` "
1837 LocalKind::Arg => "function parameter ",
1843 format!("{}`{}`", local_kind, place_desc),
1844 format!("`{}` is borrowed here", place_desc),
1848 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1849 let local = root_place.local;
1850 match self.body.local_kind(local) {
1851 LocalKind::ReturnPointer | LocalKind::Temp => {
1852 ("temporary value".to_string(), "temporary value created here".to_string())
1855 "function parameter".to_string(),
1856 "function parameter borrowed here".to_string(),
1859 ("local binding".to_string(), "local binding introduced here".to_string())
1864 let mut err = self.cannot_return_reference_to_local(
1871 if return_span != borrow_span {
1872 err.span_label(borrow_span, note);
1874 let tcx = self.infcx.tcx;
1876 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1877 let return_ty = tcx.erase_regions(return_ty);
1880 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator)
1883 .type_implements_trait(iter_trait, [return_ty], self.param_env)
1884 .must_apply_modulo_regions()
1886 err.span_suggestion_hidden(
1887 return_span.shrink_to_hi(),
1888 "use `.collect()` to allocate the iterator",
1889 ".collect::<Vec<_>>()",
1890 Applicability::MaybeIncorrect,
1898 fn report_escaping_closure_capture(
1900 use_span: UseSpans<'tcx>,
1902 fr_name: &RegionName,
1903 category: ConstraintCategory<'tcx>,
1904 constraint_span: Span,
1906 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1907 let tcx = self.infcx.tcx;
1908 let args_span = use_span.args_or_use();
1910 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1912 if string.starts_with("async ") {
1913 let pos = args_span.lo() + BytePos(6);
1914 (args_span.with_lo(pos).with_hi(pos), "move ")
1915 } else if string.starts_with("async|") {
1916 let pos = args_span.lo() + BytePos(5);
1917 (args_span.with_lo(pos).with_hi(pos), " move")
1919 (args_span.shrink_to_lo(), "move ")
1922 Err(_) => (args_span, "move |<args>| <body>"),
1924 let kind = match use_span.generator_kind() {
1925 Some(generator_kind) => match generator_kind {
1926 GeneratorKind::Async(async_kind) => match async_kind {
1927 AsyncGeneratorKind::Block => "async block",
1928 AsyncGeneratorKind::Closure => "async closure",
1929 _ => bug!("async block/closure expected, but async function found."),
1931 GeneratorKind::Gen => "generator",
1937 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1938 err.span_suggestion_verbose(
1941 "to force the {} to take ownership of {} (and any \
1942 other referenced variables), use the `move` keyword",
1946 Applicability::MachineApplicable,
1950 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1951 let msg = format!("{} is returned here", kind);
1952 err.span_note(constraint_span, &msg);
1954 ConstraintCategory::CallArgument(_) => {
1955 fr_name.highlight_region_name(&mut err);
1956 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1958 "async blocks are not executed immediately and must either take a \
1959 reference or ownership of outside variables they use",
1962 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1963 err.span_note(constraint_span, &msg);
1967 "report_escaping_closure_capture called with unexpected constraint \
1976 fn report_escaping_data(
1979 name: &Option<String>,
1983 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1984 let tcx = self.infcx.tcx;
1986 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1989 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1993 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1996 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1998 if let Some(name) = name {
2001 format!("reference to `{}` escapes the {} body here", name, escapes_from),
2006 format!("reference escapes the {} body here", escapes_from),
2013 fn get_moved_indexes(
2017 ) -> (Vec<MoveSite>, Vec<Location>) {
2018 fn predecessor_locations<'tcx, 'a>(
2019 body: &'a mir::Body<'tcx>,
2021 ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
2022 if location.statement_index == 0 {
2023 let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
2024 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
2026 Either::Right(std::iter::once(Location {
2027 statement_index: location.statement_index - 1,
2033 let mut mpis = vec![mpi];
2034 let move_paths = &self.move_data.move_paths;
2035 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
2037 let mut stack = Vec::new();
2038 let mut back_edge_stack = Vec::new();
2040 predecessor_locations(self.body, location).for_each(|predecessor| {
2041 if location.dominates(predecessor, &self.dominators) {
2042 back_edge_stack.push(predecessor)
2044 stack.push(predecessor);
2048 let mut reached_start = false;
2050 /* Check if the mpi is initialized as an argument */
2051 let mut is_argument = false;
2052 for arg in self.body.args_iter() {
2053 let path = self.move_data.rev_lookup.find_local(arg);
2054 if mpis.contains(&path) {
2059 let mut visited = FxHashSet::default();
2060 let mut move_locations = FxHashSet::default();
2061 let mut reinits = vec![];
2062 let mut result = vec![];
2064 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
2066 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
2067 location, is_back_edge
2070 if !visited.insert(location) {
2076 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
2077 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
2078 // this analysis only tries to find moves explicitly
2079 // written by the user, so we ignore the move-outs
2080 // created by `StorageDead` and at the beginning
2083 // If we are found a use of a.b.c which was in error, then we want to look for
2084 // moves not only of a.b.c but also a.b and a.
2086 // Note that the moves data already includes "parent" paths, so we don't have to
2087 // worry about the other case: that is, if there is a move of a.b.c, it is already
2088 // marked as a move of a.b and a as well, so we will generate the correct errors
2090 for moi in &self.move_data.loc_map[location] {
2091 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
2092 let path = self.move_data.moves[*moi].path;
2093 if mpis.contains(&path) {
2095 "report_use_of_moved_or_uninitialized: found {:?}",
2096 move_paths[path].place
2098 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
2099 move_locations.insert(location);
2101 // Strictly speaking, we could continue our DFS here. There may be
2102 // other moves that can reach the point of error. But it is kind of
2103 // confusing to highlight them.
2111 // drop(a); // <-- current point of error
2114 // Because we stop the DFS here, we only highlight `let c = a`,
2115 // and not `let b = a`. We will of course also report an error at
2116 // `let c = a` which highlights `let b = a` as the move.
2123 let mut any_match = false;
2124 for ii in &self.move_data.init_loc_map[location] {
2125 let init = self.move_data.inits[*ii];
2127 InitKind::Deep | InitKind::NonPanicPathOnly => {
2128 if mpis.contains(&init.path) {
2132 InitKind::Shallow => {
2133 if mpi == init.path {
2140 reinits.push(location);
2146 while let Some(location) = stack.pop() {
2147 if dfs_iter(&mut result, location, false) {
2151 let mut has_predecessor = false;
2152 predecessor_locations(self.body, location).for_each(|predecessor| {
2153 if location.dominates(predecessor, &self.dominators) {
2154 back_edge_stack.push(predecessor)
2156 stack.push(predecessor);
2158 has_predecessor = true;
2161 if !has_predecessor {
2162 reached_start = true;
2165 if (is_argument || !reached_start) && result.is_empty() {
2166 /* Process back edges (moves in future loop iterations) only if
2167 the move path is definitely initialized upon loop entry,
2168 to avoid spurious "in previous iteration" errors.
2169 During DFS, if there's a path from the error back to the start
2170 of the function with no intervening init or move, then the
2171 move path may be uninitialized at loop entry.
2173 while let Some(location) = back_edge_stack.pop() {
2174 if dfs_iter(&mut result, location, true) {
2178 predecessor_locations(self.body, location)
2179 .for_each(|predecessor| back_edge_stack.push(predecessor));
2183 // Check if we can reach these reinits from a move location.
2184 let reinits_reachable = reinits
2187 let mut visited = FxHashSet::default();
2188 let mut stack = vec![*reinit];
2189 while let Some(location) = stack.pop() {
2190 if !visited.insert(location) {
2193 if move_locations.contains(&location) {
2196 stack.extend(predecessor_locations(self.body, location));
2200 .collect::<Vec<Location>>();
2201 (result, reinits_reachable)
2204 pub(crate) fn report_illegal_mutation_of_borrowed(
2207 (place, span): (Place<'tcx>, Span),
2208 loan: &BorrowData<'tcx>,
2210 let loan_spans = self.retrieve_borrow_spans(loan);
2211 let loan_span = loan_spans.args_or_use();
2213 let descr_place = self.describe_any_place(place.as_ref());
2214 if loan.kind == BorrowKind::Shallow {
2215 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
2216 let mut err = self.cannot_mutate_in_immutable_section(
2223 loan_spans.var_span_label(
2225 format!("borrow occurs due to use{}", loan_spans.describe()),
2226 loan.kind.describe_mutability(),
2229 self.buffer_error(err);
2235 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
2237 loan_spans.var_span_label(
2239 format!("borrow occurs due to use{}", loan_spans.describe()),
2240 loan.kind.describe_mutability(),
2243 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
2253 self.explain_deref_coercion(loan, &mut err);
2255 self.buffer_error(err);
2258 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
2259 let tcx = self.infcx.tcx;
2261 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
2262 Some((method_did, method_substs)),
2264 &self.body[loan.reserve_location.block].terminator,
2265 rustc_const_eval::util::find_self_call(
2268 loan.assigned_place.local,
2269 loan.reserve_location.block,
2272 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
2274 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
2275 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
2278 if let Some(Ok(instance)) = deref_target {
2279 let deref_target_ty = instance.ty(tcx, self.param_env);
2281 "borrow occurs due to deref coercion to `{}`",
2284 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
2290 /// Reports an illegal reassignment; for example, an assignment to
2291 /// (part of) a non-`mut` local that occurs potentially after that
2292 /// local has already been initialized. `place` is the path being
2293 /// assigned; `err_place` is a place providing a reason why
2294 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
2295 /// assignment to `x.f`).
2296 pub(crate) fn report_illegal_reassignment(
2298 _location: Location,
2299 (place, span): (Place<'tcx>, Span),
2300 assigned_span: Span,
2301 err_place: Place<'tcx>,
2303 let (from_arg, local_decl, local_name) = match err_place.as_local() {
2305 self.body.local_kind(local) == LocalKind::Arg,
2306 Some(&self.body.local_decls[local]),
2307 self.local_names[local],
2309 None => (false, None, None),
2312 // If root local is initialized immediately (everything apart from let
2313 // PATTERN;) then make the error refer to that local, rather than the
2314 // place being assigned later.
2315 let (place_description, assigned_span) = match local_decl {
2318 Some(box LocalInfo::User(
2319 ClearCrossCrate::Clear
2320 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
2321 opt_match_place: None,
2325 | Some(box LocalInfo::StaticRef { .. })
2329 | None => (self.describe_any_place(place.as_ref()), assigned_span),
2330 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
2333 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
2334 let msg = if from_arg {
2335 "cannot assign to immutable argument"
2337 "cannot assign twice to immutable variable"
2339 if span != assigned_span && !from_arg {
2340 err.span_label(assigned_span, format!("first assignment to {}", place_description));
2342 if let Some(decl) = local_decl
2343 && let Some(name) = local_name
2344 && decl.can_be_made_mutable()
2346 err.span_suggestion(
2347 decl.source_info.span,
2348 "consider making this binding mutable",
2349 format!("mut {}", name),
2350 Applicability::MachineApplicable,
2353 err.span_label(span, msg);
2354 self.buffer_error(err);
2357 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
2358 let tcx = self.infcx.tcx;
2359 let (kind, _place_ty) = place.projection.iter().fold(
2360 (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)),
2361 |(kind, place_ty), &elem| {
2364 ProjectionElem::Deref => match kind {
2365 StorageDeadOrDrop::LocalStorageDead
2366 | StorageDeadOrDrop::BoxedStorageDead => {
2368 place_ty.ty.is_box(),
2369 "Drop of value behind a reference or raw pointer"
2371 StorageDeadOrDrop::BoxedStorageDead
2373 StorageDeadOrDrop::Destructor(_) => kind,
2375 ProjectionElem::OpaqueCast { .. }
2376 | ProjectionElem::Field(..)
2377 | ProjectionElem::Downcast(..) => {
2378 match place_ty.ty.kind() {
2379 ty::Adt(def, _) if def.has_dtor(tcx) => {
2380 // Report the outermost adt with a destructor
2382 StorageDeadOrDrop::Destructor(_) => kind,
2383 StorageDeadOrDrop::LocalStorageDead
2384 | StorageDeadOrDrop::BoxedStorageDead => {
2385 StorageDeadOrDrop::Destructor(place_ty.ty)
2392 ProjectionElem::ConstantIndex { .. }
2393 | ProjectionElem::Subslice { .. }
2394 | ProjectionElem::Index(_) => kind,
2396 place_ty.projection_ty(tcx, elem),
2403 /// Describe the reason for the fake borrow that was assigned to `place`.
2404 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
2405 use rustc_middle::mir::visit::Visitor;
2406 struct FakeReadCauseFinder<'tcx> {
2408 cause: Option<FakeReadCause>,
2410 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
2411 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
2413 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
2414 if *place == self.place =>
2416 self.cause = Some(*cause);
2422 let mut visitor = FakeReadCauseFinder { place, cause: None };
2423 visitor.visit_body(&self.body);
2424 match visitor.cause {
2425 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2426 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2431 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2432 /// borrow of local value that does not live long enough.
2433 fn annotate_argument_and_return_for_borrow(
2435 borrow: &BorrowData<'tcx>,
2436 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2437 // Define a fallback for when we can't match a closure.
2439 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2443 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2445 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2447 self.infcx.tcx.fn_sig(self.mir_def_id()),
2454 // In order to determine whether we need to annotate, we need to check whether the reserve
2455 // place was an assignment into a temporary.
2457 // If it was, we check whether or not that temporary is eventually assigned into the return
2458 // place. If it was, we can add annotations about the function's return type and arguments
2459 // and it'll make sense.
2460 let location = borrow.reserve_location;
2461 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2462 if let Some(Statement { kind: StatementKind::Assign(box (reservation, _)), .. }) =
2463 &self.body[location.block].statements.get(location.statement_index)
2465 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2466 // Check that the initial assignment of the reserve location is into a temporary.
2467 let mut target = match reservation.as_local() {
2468 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2472 // Next, look through the rest of the block, checking if we are assigning the
2473 // `target` (that is, the place that contains our borrow) to anything.
2474 let mut annotated_closure = None;
2475 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2477 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2480 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2481 if let Some(assigned_to) = place.as_local() {
2483 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2487 // Check if our `target` was captured by a closure.
2488 if let Rvalue::Aggregate(
2489 box AggregateKind::Closure(def_id, substs),
2493 for operand in operands {
2494 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2498 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2502 // Find the local from the operand.
2503 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2507 if assigned_from_local != target {
2511 // If a closure captured our `target` and then assigned
2512 // into a place then we should annotate the closure in
2513 // case it ends up being assigned into the return place.
2515 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2517 "annotate_argument_and_return_for_borrow: \
2518 annotated_closure={:?} assigned_from_local={:?} \
2520 annotated_closure, assigned_from_local, assigned_to
2523 if assigned_to == mir::RETURN_PLACE {
2524 // If it was assigned directly into the return place, then
2526 return annotated_closure;
2528 // Otherwise, update the target.
2529 target = assigned_to;
2533 // If none of our closure's operands matched, then skip to the next
2538 // Otherwise, look at other types of assignment.
2539 let assigned_from = match rvalue {
2540 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2541 Rvalue::Use(operand) => match operand {
2542 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2550 "annotate_argument_and_return_for_borrow: \
2551 assigned_from={:?}",
2555 // Find the local from the rvalue.
2556 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2558 "annotate_argument_and_return_for_borrow: \
2559 assigned_from_local={:?}",
2560 assigned_from_local,
2563 // Check if our local matches the target - if so, we've assigned our
2564 // borrow to a new place.
2565 if assigned_from_local != target {
2569 // If we assigned our `target` into a new place, then we should
2570 // check if it was the return place.
2572 "annotate_argument_and_return_for_borrow: \
2573 assigned_from_local={:?} assigned_to={:?}",
2574 assigned_from_local, assigned_to
2576 if assigned_to == mir::RETURN_PLACE {
2577 // If it was then return the annotated closure if there was one,
2578 // else, annotate this function.
2579 return annotated_closure.or_else(fallback);
2582 // If we didn't assign into the return place, then we just update
2584 target = assigned_to;
2589 // Check the terminator if we didn't find anything in the statements.
2590 let terminator = &self.body[location.block].terminator();
2592 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2595 if let TerminatorKind::Call { destination, target: Some(_), args, .. } =
2598 if let Some(assigned_to) = destination.as_local() {
2600 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2603 for operand in args {
2604 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2608 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2612 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2614 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2615 assigned_from_local,
2618 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2619 return annotated_closure.or_else(fallback);
2627 // If we haven't found an assignment into the return place, then we need not add
2629 debug!("annotate_argument_and_return_for_borrow: none found");
2633 /// Annotate the first argument and return type of a function signature if they are
2638 sig: ty::PolyFnSig<'tcx>,
2639 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2640 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2641 let is_closure = self.infcx.tcx.is_closure(did.to_def_id());
2642 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
2643 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2645 // We need to work out which arguments to highlight. We do this by looking
2646 // at the return type, where there are three cases:
2648 // 1. If there are named arguments, then we should highlight the return type and
2649 // highlight any of the arguments that are also references with that lifetime.
2650 // If there are no arguments that have the same lifetime as the return type,
2651 // then don't highlight anything.
2652 // 2. The return type is a reference with an anonymous lifetime. If this is
2653 // the case, then we can take advantage of (and teach) the lifetime elision
2656 // We know that an error is being reported. So the arguments and return type
2657 // must satisfy the elision rules. Therefore, if there is a single argument
2658 // then that means the return type and first (and only) argument have the same
2659 // lifetime and the borrow isn't meeting that, we can highlight the argument
2662 // If there are multiple arguments then the first argument must be self (else
2663 // it would not satisfy the elision rules), so we can highlight self and the
2665 // 3. The return type is not a reference. In this case, we don't highlight
2667 let return_ty = sig.output();
2668 match return_ty.skip_binder().kind() {
2669 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2670 // This is case 1 from above, return type is a named reference so we need to
2671 // search for relevant arguments.
2672 let mut arguments = Vec::new();
2673 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2674 if let ty::Ref(argument_region, _, _) = argument.kind() {
2675 if argument_region == return_region {
2676 // Need to use the `rustc_middle::ty` types to compare against the
2677 // `return_region`. Then use the `rustc_hir` type to get only
2678 // the lifetime span.
2679 if let hir::TyKind::Ref(lifetime, _) = &fn_decl.inputs[index].kind {
2680 // With access to the lifetime, we can get
2682 arguments.push((*argument, lifetime.ident.span));
2684 bug!("ty type is a ref but hir type is not");
2690 // We need to have arguments. This shouldn't happen, but it's worth checking.
2691 if arguments.is_empty() {
2695 // We use a mix of the HIR and the Ty types to get information
2696 // as the HIR doesn't have full types for closure arguments.
2697 let return_ty = sig.output().skip_binder();
2698 let mut return_span = fn_decl.output.span();
2699 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2700 if let hir::TyKind::Ref(lifetime, _) = ty.kind {
2701 return_span = lifetime.ident.span;
2705 Some(AnnotatedBorrowFnSignature::NamedFunction {
2711 ty::Ref(_, _, _) if is_closure => {
2712 // This is case 2 from above but only for closures, return type is anonymous
2713 // reference so we select
2714 // the first argument.
2715 let argument_span = fn_decl.inputs.first()?.span;
2716 let argument_ty = sig.inputs().skip_binder().first()?;
2718 // Closure arguments are wrapped in a tuple, so we need to get the first
2720 if let ty::Tuple(elems) = argument_ty.kind() {
2721 let &argument_ty = elems.first()?;
2722 if let ty::Ref(_, _, _) = argument_ty.kind() {
2723 return Some(AnnotatedBorrowFnSignature::Closure {
2732 ty::Ref(_, _, _) => {
2733 // This is also case 2 from above but for functions, return type is still an
2734 // anonymous reference so we select the first argument.
2735 let argument_span = fn_decl.inputs.first()?.span;
2736 let argument_ty = *sig.inputs().skip_binder().first()?;
2738 let return_span = fn_decl.output.span();
2739 let return_ty = sig.output().skip_binder();
2741 // We expect the first argument to be a reference.
2742 match argument_ty.kind() {
2743 ty::Ref(_, _, _) => {}
2747 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2755 // This is case 3 from above, return type is not a reference so don't highlight
2764 enum AnnotatedBorrowFnSignature<'tcx> {
2766 arguments: Vec<(Ty<'tcx>, Span)>,
2767 return_ty: Ty<'tcx>,
2771 argument_ty: Ty<'tcx>,
2772 argument_span: Span,
2773 return_ty: Ty<'tcx>,
2777 argument_ty: Ty<'tcx>,
2778 argument_span: Span,
2782 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2783 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2785 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2787 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2790 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2793 cx.get_region_name_for_ty(argument_ty, 0)
2795 &AnnotatedBorrowFnSignature::AnonymousFunction {
2801 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2802 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2804 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2805 let types_equal = return_ty_name == argument_ty_name;
2810 if types_equal { "also " } else { "" },
2816 "argument and return type have the same lifetime due to lifetime elision rules",
2819 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2820 lifetime-syntax.html#lifetime-elision>",
2823 cx.get_region_name_for_ty(return_ty, 0)
2825 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2826 // Region of return type and arguments checked to be the same earlier.
2827 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2828 for (_, argument_span) in arguments {
2829 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2832 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2835 "use data from the highlighted arguments which match the `{}` lifetime of \
2846 /// Detect whether one of the provided spans is a statement nested within the top-most visited expr
2847 struct ReferencedStatementsVisitor<'a>(&'a [Span], bool);
2849 impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> {
2850 fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
2852 hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => {
2860 /// Given a set of spans representing statements initializing the relevant binding, visit all the
2861 /// function expressions looking for branching code paths that *do not* initialize the binding.
2862 struct ConditionVisitor<'b> {
2865 errors: Vec<(Span, String)>,
2868 impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
2869 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
2871 hir::ExprKind::If(cond, body, None) => {
2872 // `if` expressions with no `else` that initialize the binding might be missing an
2874 let mut v = ReferencedStatementsVisitor(self.spans, false);
2880 "if this `if` condition is `false`, {} is not initialized",
2885 ex.span.shrink_to_hi(),
2886 format!("an `else` arm might be missing here, initializing {}", self.name),
2890 hir::ExprKind::If(cond, body, Some(other)) => {
2891 // `if` expressions where the binding is only initialized in one of the two arms
2892 // might be missing a binding initialization.
2893 let mut a = ReferencedStatementsVisitor(self.spans, false);
2895 let mut b = ReferencedStatementsVisitor(self.spans, false);
2896 b.visit_expr(other);
2898 (true, true) | (false, false) => {}
2900 if other.span.is_desugaring(DesugaringKind::WhileLoop) {
2904 "if this condition isn't met and the `while` loop runs 0 \
2905 times, {} is not initialized",
2911 body.span.shrink_to_hi().until(other.span),
2913 "if the `if` condition is `false` and this `else` arm is \
2914 executed, {} is not initialized",
2924 "if this condition is `true`, {} is not initialized",
2931 hir::ExprKind::Match(e, arms, loop_desugar) => {
2932 // If the binding is initialized in one of the match arms, then the other match
2933 // arms might be missing an initialization.
2934 let results: Vec<bool> = arms
2937 let mut v = ReferencedStatementsVisitor(self.spans, false);
2942 if results.iter().any(|x| *x) && !results.iter().all(|x| *x) {
2943 for (arm, seen) in arms.iter().zip(results) {
2945 if loop_desugar == hir::MatchSource::ForLoopDesugar {
2949 "if the `for` loop runs 0 times, {} is not initialized",
2953 } else if let Some(guard) = &arm.guard {
2955 arm.pat.span.to(guard.body().span),
2957 "if this pattern and condition are matched, {} is not \
2966 "if this pattern is matched, {} is not initialized",
2975 // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should
2976 // also be accounted for. For now it is fine, as if we don't find *any* relevant
2977 // branching code paths, we point at the places where the binding *is* initialized for
2981 walk_expr(self, ex);