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(mpi, move_span, &mut err, &mut in_pattern);
200 self.explain_captures(
210 maybe_reinitialized_locations.is_empty(),
213 seen_spans.insert(move_span);
216 use_spans.var_path_only_subdiag(&mut err, desired_action);
222 "value {} here after {}move",
223 desired_action.as_verb_in_past_tense(),
229 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
230 let needs_note = match ty.kind() {
231 ty::Closure(id, _) => {
232 let tables = self.infcx.tcx.typeck(id.expect_local());
233 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
235 tables.closure_kind_origins().get(hir_id).is_none()
240 let mpi = self.move_data.moves[move_out_indices[0]].path;
241 let place = &self.move_data.move_paths[mpi].place;
242 let ty = place.ty(self.body, self.infcx.tcx).ty;
244 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
245 // Same for if we're in a loop, see #101119.
246 if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
247 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
248 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
249 err.span_suggestion_verbose(
252 "consider creating a fresh reborrow of {} here",
253 self.describe_place(moved_place)
254 .map(|n| format!("`{}`", n))
255 .unwrap_or_else(|| "the mutable reference".to_string()),
258 Applicability::MachineApplicable,
263 let opt_name = self.describe_place_with_options(
265 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
267 let note_msg = match opt_name {
268 Some(ref name) => format!("`{}`", name),
269 None => "value".to_owned(),
271 if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) {
272 // Suppress the next suggestion since we don't want to put more bounds onto
273 // something that already has `Fn`-like bounds (or is a closure), so we can't
276 self.suggest_adding_copy_bounds(&mut err, ty, span);
280 let span = if let Some(local) = place.as_local() {
281 Some(self.body.local_decls[local].source_info.span)
285 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
288 if let UseSpans::FnSelfUse {
289 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
294 "{} occurs due to deref coercion to `{}`",
295 desired_action.as_noun(),
299 // Check first whether the source is accessible (issue #87060)
300 if self.infcx.tcx.sess.source_map().is_span_accessible(deref_target) {
301 err.span_note(deref_target, "deref defined here");
305 self.buffer_move_error(move_out_indices, (used_place, err));
309 fn suggest_ref_or_clone(
313 err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
314 in_pattern: &mut bool,
316 struct ExpressionFinder<'hir> {
318 expr: Option<&'hir hir::Expr<'hir>>,
319 pat: Option<&'hir hir::Pat<'hir>>,
320 parent_pat: Option<&'hir hir::Pat<'hir>>,
322 impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
323 fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
324 if e.span == self.expr_span {
327 hir::intravisit::walk_expr(self, e);
329 fn visit_pat(&mut self, p: &'hir hir::Pat<'hir>) {
330 if p.span == self.expr_span {
333 if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, i, sub) = p.kind {
334 if i.span == self.expr_span || p.span == self.expr_span {
337 // Check if we are in a situation of `ident @ ident` where we want to suggest
338 // `ref ident @ ref ident` or `ref ident @ Struct { ref ident }`.
339 if let Some(subpat) = sub && self.pat.is_none() {
340 self.visit_pat(subpat);
341 if self.pat.is_some() {
342 self.parent_pat = Some(p);
347 hir::intravisit::walk_pat(self, p);
350 let hir = self.infcx.tcx.hir();
351 if let Some(hir::Node::Item(hir::Item {
352 kind: hir::ItemKind::Fn(_, _, body_id),
354 })) = hir.find(hir.local_def_id_to_hir_id(self.mir_def_id()))
355 && let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id)
357 let place = &self.move_data.move_paths[mpi].place;
358 let span = place.as_local()
359 .map(|local| self.body.local_decls[local].source_info.span);
360 let mut finder = ExpressionFinder {
361 expr_span: move_span,
366 finder.visit_expr(expr);
367 if let Some(span) = span && let Some(expr) = finder.expr {
368 for (_, expr) in hir.parent_iter(expr.hir_id) {
369 if let hir::Node::Expr(expr) = expr {
370 if expr.span.contains(span) {
371 // If the let binding occurs within the same loop, then that
372 // loop isn't relevant, like in the following, the outermost `loop`
373 // doesn't play into `x` being moved.
376 // let x = String::new();
384 if let hir::ExprKind::Loop(.., loop_span) = expr.kind {
385 err.span_label(loop_span, "inside of this loop");
389 let typeck = self.infcx.tcx.typeck(self.mir_def_id());
390 let hir_id = hir.get_parent_node(expr.hir_id);
391 if let Some(parent) = hir.find(hir_id) {
392 let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
393 && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
394 && let Some(def_id) = typeck.type_dependent_def_id(parent_expr.hir_id)
396 (def_id.as_local(), args, 1)
397 } else if let hir::Node::Expr(parent_expr) = parent
398 && let hir::ExprKind::Call(call, args) = parent_expr.kind
399 && let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind()
401 (def_id.as_local(), args, 0)
405 if let Some(def_id) = def_id
406 && let Some(node) = hir.find(hir.local_def_id_to_hir_id(def_id))
407 && let Some(fn_sig) = node.fn_sig()
408 && let Some(ident) = node.ident()
409 && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
410 && let Some(arg) = fn_sig.decl.inputs.get(pos + offset)
412 let mut span: MultiSpan = arg.span.into();
413 span.push_span_label(
415 "this parameter takes ownership of the value".to_string(),
417 let descr = match node.fn_kind() {
418 Some(hir::intravisit::FnKind::ItemFn(..)) | None => "function",
419 Some(hir::intravisit::FnKind::Method(..)) => "method",
420 Some(hir::intravisit::FnKind::Closure) => "closure",
422 span.push_span_label(
424 format!("in this {descr}"),
429 "consider changing this parameter type in {descr} `{ident}` to \
430 borrow instead if owning the value isn't necessary",
434 let place = &self.move_data.move_paths[mpi].place;
435 let ty = place.ty(self.body, self.infcx.tcx).ty;
436 if let hir::Node::Expr(parent_expr) = parent
437 && let hir::ExprKind::Call(call_expr, _) = parent_expr.kind
438 && let hir::ExprKind::Path(
439 hir::QPath::LangItem(LangItem::IntoIterIntoIter, _, _)
442 // Do not suggest `.clone()` in a `for` loop, we already suggest borrowing.
444 self.suggest_cloning(err, ty, move_span);
448 if let Some(pat) = finder.pat {
450 let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())];
451 if let Some(pat) = finder.parent_pat {
452 sugg.insert(0, (pat.span.shrink_to_lo(), "ref ".to_string()));
454 err.multipart_suggestion_verbose(
455 "borrow this binding in the pattern to avoid moving the value",
457 Applicability::MachineApplicable,
463 fn report_use_of_uninitialized(
466 used_place: PlaceRef<'tcx>,
467 moved_place: PlaceRef<'tcx>,
468 desired_action: InitializationRequiringAction,
470 use_spans: UseSpans<'tcx>,
471 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
472 // We need all statements in the body where the binding was assigned to to later find all
473 // the branching code paths where the binding *wasn't* assigned to.
474 let inits = &self.move_data.init_path_map[mpi];
475 let move_path = &self.move_data.move_paths[mpi];
476 let decl_span = self.body.local_decls[move_path.place.local].source_info.span;
477 let mut spans = vec![];
478 for init_idx in inits {
479 let init = &self.move_data.inits[*init_idx];
480 let span = init.span(&self.body);
481 if !span.is_dummy() {
486 let (name, desc) = match self.describe_place_with_options(
488 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
490 Some(name) => (format!("`{name}`"), format!("`{name}` ")),
491 None => ("the variable".to_string(), String::new()),
493 let path = match self.describe_place_with_options(
495 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
497 Some(name) => format!("`{name}`"),
498 None => "value".to_string(),
501 // We use the statements were the binding was initialized, and inspect the HIR to look
502 // for the branching codepaths that aren't covered, to point at them.
503 let map = self.infcx.tcx.hir();
504 let body_id = map.body_owned_by(self.mir_def_id());
505 let body = map.body(body_id);
507 let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] };
508 visitor.visit_body(&body);
510 let mut show_assign_sugg = false;
511 let isnt_initialized = if let InitializationRequiringAction::PartialAssignment
512 | InitializationRequiringAction::Assignment = desired_action
514 // The same error is emitted for bindings that are *sometimes* initialized and the ones
515 // that are *partially* initialized by assigning to a field of an uninitialized
516 // binding. We differentiate between them for more accurate wording here.
517 "isn't fully initialized"
521 // We filter these to avoid misleading wording in cases like the following,
522 // where `x` has an `init`, but it is in the same place we're looking at:
528 // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs`
533 .any(|sp| span < sp && !sp.contains(span))
538 show_assign_sugg = true;
541 "is possibly-uninitialized"
544 let used = desired_action.as_general_verb_in_past_tense();
546 struct_span_err!(self, span, E0381, "{used} binding {desc}{isnt_initialized}");
547 use_spans.var_path_only_subdiag(&mut err, desired_action);
549 if let InitializationRequiringAction::PartialAssignment
550 | InitializationRequiringAction::Assignment = desired_action
553 "partial initialization isn't supported, fully initialize the binding with a \
554 default value and mutate it, or use `std::mem::MaybeUninit`",
557 err.span_label(span, format!("{path} {used} here but it {isnt_initialized}"));
559 let mut shown = false;
560 for (sp, label) in visitor.errors {
561 if sp < span && !sp.overlaps(span) {
562 // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention
563 // match arms coming after the primary span because they aren't relevant:
567 // _ if { x = 2; true } => {}
572 // _ => {} // We don't want to point to this.
575 err.span_label(sp, &label);
581 if *sp < span && !sp.overlaps(span) {
582 err.span_label(*sp, "binding initialized here in some conditions");
587 err.span_label(decl_span, "binding declared here but left uninitialized");
588 if show_assign_sugg {
591 sugg_span: Option<Span>,
594 impl<'v> Visitor<'v> for LetVisitor {
595 fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
596 if self.sugg_span.is_some() {
599 if let hir::StmtKind::Local(hir::Local {
600 span, ty, init: None, ..
601 }) = &ex.kind && span.contains(self.decl_span) {
602 self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span));
604 hir::intravisit::walk_stmt(self, ex);
608 let mut visitor = LetVisitor { decl_span, sugg_span: None };
609 visitor.visit_body(&body);
610 if let Some(span) = visitor.sugg_span {
611 self.suggest_assign_value(&mut err, moved_place, span);
617 fn suggest_assign_value(
619 err: &mut Diagnostic,
620 moved_place: PlaceRef<'tcx>,
623 let ty = moved_place.ty(self.body, self.infcx.tcx).ty;
624 debug!("ty: {:?}, kind: {:?}", ty, ty.kind());
626 let tcx = self.infcx.tcx;
627 let implements_default = |ty, param_env| {
628 let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else {
631 // Regions are already solved, so we must use a fresh InferCtxt,
632 // but the type has region variables, so erase those.
635 .type_implements_trait(default_trait, [tcx.erase_regions(ty)], param_env)
636 .must_apply_modulo_regions()
639 let assign_value = match ty.kind() {
641 ty::Float(_) => "0.0",
642 ty::Int(_) | ty::Uint(_) => "0",
643 ty::Never | ty::Error(_) => "",
644 ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => "vec![]",
645 ty::Adt(_, _) if implements_default(ty, self.param_env) => "Default::default()",
649 if !assign_value.is_empty() {
650 err.span_suggestion_verbose(
651 sugg_span.shrink_to_hi(),
652 format!("consider assigning a value"),
653 format!(" = {}", assign_value),
654 Applicability::MaybeIncorrect,
659 fn suggest_borrow_fn_like(
661 err: &mut Diagnostic,
663 move_sites: &[MoveSite],
666 let tcx = self.infcx.tcx;
668 // Find out if the predicates show that the type is a Fn or FnMut
669 let find_fn_kind_from_did = |predicates: ty::EarlyBinder<
670 &[(ty::Predicate<'tcx>, Span)],
673 predicates.0.iter().find_map(|(pred, _)| {
674 let pred = if let Some(substs) = substs {
675 predicates.rebind(*pred).subst(tcx, substs).kind().skip_binder()
677 pred.kind().skip_binder()
679 if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred && pred.self_ty() == ty {
680 if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
681 return Some(hir::Mutability::Not);
682 } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
683 return Some(hir::Mutability::Mut);
690 // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
691 // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
692 // These types seem reasonably opaque enough that they could be substituted with their
693 // borrowed variants in a function body when we see a move error.
694 let borrow_level = match ty.kind() {
695 ty::Param(_) => find_fn_kind_from_did(
696 tcx.bound_explicit_predicates_of(self.mir_def_id().to_def_id())
697 .map_bound(|p| p.predicates),
700 ty::Opaque(did, substs) => {
701 find_fn_kind_from_did(tcx.bound_explicit_item_bounds(*did), Some(*substs))
703 ty::Closure(_, substs) => match substs.as_closure().kind() {
704 ty::ClosureKind::Fn => Some(hir::Mutability::Not),
705 ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
711 let Some(borrow_level) = borrow_level else { return false; };
712 let sugg = move_sites
715 let move_out = self.move_data.moves[(*move_site).moi];
716 let moved_place = &self.move_data.move_paths[move_out.path].place;
717 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
718 let move_span = move_spans.args_or_use();
719 let suggestion = borrow_level.ref_prefix_str().to_owned();
720 (move_span.shrink_to_lo(), suggestion)
723 err.multipart_suggestion_verbose(
724 format!("consider {}borrowing {value_name}", borrow_level.mutably_str()),
726 Applicability::MaybeIncorrect,
731 fn suggest_cloning(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
732 let tcx = self.infcx.tcx;
733 // Try to find predicates on *generic params* that would allow copying `ty`
734 let infcx = tcx.infer_ctxt().build();
736 if let Some(clone_trait_def) = tcx.lang_items().clone_trait()
738 .type_implements_trait(
740 [tcx.erase_regions(ty)],
743 .must_apply_modulo_regions()
745 err.span_suggestion_verbose(
747 "consider cloning the value if the performance cost is acceptable",
748 ".clone()".to_string(),
749 Applicability::MachineApplicable,
754 fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
755 let tcx = self.infcx.tcx;
756 let generics = tcx.generics_of(self.mir_def_id());
758 let Some(hir_generics) = tcx
759 .typeck_root_def_id(self.mir_def_id().to_def_id())
761 .and_then(|def_id| tcx.hir().get_generics(def_id))
763 // Try to find predicates on *generic params* that would allow copying `ty`
764 let infcx = tcx.infer_ctxt().build();
765 let copy_did = infcx.tcx.require_lang_item(LangItem::Copy, Some(span));
766 let cause = ObligationCause::new(
769 rustc_infer::traits::ObligationCauseCode::MiscObligation,
771 let errors = rustc_trait_selection::traits::fully_solve_bound(
775 // Erase any region vids from the type, which may not be resolved
776 infcx.tcx.erase_regions(ty),
780 // Only emit suggestion if all required predicates are on generic
781 let predicates: Result<Vec<_>, _> = errors
783 .map(|err| match err.obligation.predicate.kind().skip_binder() {
784 PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
785 match predicate.self_ty().kind() {
786 ty::Param(param_ty) => Ok((
787 generics.type_param(param_ty, tcx),
788 predicate.trait_ref.print_only_trait_path().to_string(),
797 if let Ok(predicates) = predicates {
798 suggest_constraining_type_params(
804 .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
809 pub(crate) fn report_move_out_while_borrowed(
812 (place, span): (Place<'tcx>, Span),
813 borrow: &BorrowData<'tcx>,
816 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
817 location, place, span, borrow
819 let value_msg = self.describe_any_place(place.as_ref());
820 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
822 let borrow_spans = self.retrieve_borrow_spans(borrow);
823 let borrow_span = borrow_spans.args_or_use();
825 let move_spans = self.move_spans(place.as_ref(), location);
826 let span = move_spans.args_or_use();
828 let mut err = self.cannot_move_when_borrowed(
831 &self.describe_any_place(place.as_ref()),
836 borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow);
838 move_spans.var_span_label(
840 format!("move occurs due to use{}", move_spans.describe()),
844 self.explain_why_borrow_contains_point(location, borrow, None)
845 .add_explanation_to_diagnostic(
854 self.buffer_error(err);
857 pub(crate) fn report_use_while_mutably_borrowed(
860 (place, _span): (Place<'tcx>, Span),
861 borrow: &BorrowData<'tcx>,
862 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
863 let borrow_spans = self.retrieve_borrow_spans(borrow);
864 let borrow_span = borrow_spans.args_or_use();
866 // Conflicting borrows are reported separately, so only check for move
868 let use_spans = self.move_spans(place.as_ref(), location);
869 let span = use_spans.var_or_use();
871 // If the attempted use is in a closure then we do not care about the path span of the place we are currently trying to use
872 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
873 let mut err = self.cannot_use_when_mutably_borrowed(
875 &self.describe_any_place(place.as_ref()),
877 &self.describe_any_place(borrow.borrowed_place.as_ref()),
879 borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| {
880 use crate::session_diagnostics::CaptureVarCause::*;
881 let place = &borrow.borrowed_place;
882 let desc_place = self.describe_any_place(place.as_ref());
884 Some(_) => BorrowUsePlaceGenerator { place: desc_place, var_span },
885 None => BorrowUsePlaceClosure { place: desc_place, var_span },
889 self.explain_why_borrow_contains_point(location, borrow, None)
890 .add_explanation_to_diagnostic(
902 pub(crate) fn report_conflicting_borrow(
905 (place, span): (Place<'tcx>, Span),
906 gen_borrow_kind: BorrowKind,
907 issued_borrow: &BorrowData<'tcx>,
908 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
909 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
910 let issued_span = issued_spans.args_or_use();
912 let borrow_spans = self.borrow_spans(span, location);
913 let span = borrow_spans.args_or_use();
915 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
921 let (desc_place, msg_place, msg_borrow, union_type_name) =
922 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
924 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
925 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
927 // FIXME: supply non-"" `opt_via` when appropriate
928 let first_borrow_desc;
929 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
930 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
931 first_borrow_desc = "mutable ";
932 self.cannot_reborrow_already_borrowed(
944 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
945 first_borrow_desc = "immutable ";
946 self.cannot_reborrow_already_borrowed(
959 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
960 first_borrow_desc = "first ";
961 let mut err = self.cannot_mutably_borrow_multiply(
969 self.suggest_split_at_mut_if_applicable(
972 issued_borrow.borrowed_place,
977 (BorrowKind::Unique, BorrowKind::Unique) => {
978 first_borrow_desc = "first ";
979 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
982 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
983 if let Some(immutable_section_description) =
984 self.classify_immutable_section(issued_borrow.assigned_place)
986 let mut err = self.cannot_mutate_in_immutable_section(
990 immutable_section_description,
993 borrow_spans.var_span_label(
996 "borrow occurs due to use of {}{}",
998 borrow_spans.describe(),
1005 first_borrow_desc = "immutable ";
1006 self.cannot_reborrow_already_borrowed(
1020 (BorrowKind::Unique, _) => {
1021 first_borrow_desc = "first ";
1022 self.cannot_uniquely_borrow_by_one_closure(
1034 (BorrowKind::Shared, BorrowKind::Unique) => {
1035 first_borrow_desc = "first ";
1036 self.cannot_reborrow_already_uniquely_borrowed(
1049 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
1050 first_borrow_desc = "first ";
1051 self.cannot_reborrow_already_uniquely_borrowed(
1064 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
1066 BorrowKind::Shallow,
1067 BorrowKind::Mut { .. }
1068 | BorrowKind::Unique
1069 | BorrowKind::Shared
1070 | BorrowKind::Shallow,
1071 ) => unreachable!(),
1074 if issued_spans == borrow_spans {
1075 borrow_spans.var_span_label(
1077 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
1078 gen_borrow_kind.describe_mutability(),
1081 let borrow_place = &issued_borrow.borrowed_place;
1082 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
1083 issued_spans.var_span_label(
1086 "first borrow occurs due to use of {}{}",
1088 issued_spans.describe(),
1090 issued_borrow.kind.describe_mutability(),
1093 borrow_spans.var_span_label(
1096 "second borrow occurs due to use of {}{}",
1098 borrow_spans.describe(),
1100 gen_borrow_kind.describe_mutability(),
1104 if union_type_name != "" {
1106 "{} is a field of the union `{}`, so it overlaps the field {}",
1107 msg_place, union_type_name, msg_borrow,
1111 explanation.add_explanation_to_diagnostic(
1118 Some((issued_span, span)),
1121 self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation);
1126 #[instrument(level = "debug", skip(self, err))]
1127 fn suggest_using_local_if_applicable(
1129 err: &mut Diagnostic,
1131 issued_borrow: &BorrowData<'tcx>,
1132 explanation: BorrowExplanation<'tcx>,
1134 let used_in_call = matches!(
1136 BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _)
1139 debug!("not later used in call");
1144 if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation {
1150 let outer_call_loc =
1151 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
1154 issued_borrow.reserve_location
1156 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
1158 let inner_param_location = location;
1159 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
1160 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
1163 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
1165 "`inner_param_location` {:?} is not for an assignment: {:?}",
1166 inner_param_location, inner_param_stmt
1170 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
1171 let Some((inner_call_loc, inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
1172 let Either::Right(term) = self.body.stmt_at(loc) else {
1173 debug!("{:?} is a statement, so it can't be a call", loc);
1176 let TerminatorKind::Call { args, .. } = &term.kind else {
1177 debug!("not a call: {:?}", term);
1180 debug!("checking call args for uses of inner_param: {:?}", args);
1181 if args.contains(&Operand::Move(inner_param)) {
1187 debug!("no uses of inner_param found as a by-move call arg");
1190 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
1192 let inner_call_span = inner_call_term.source_info.span;
1193 let outer_call_span = match use_span {
1195 None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span,
1197 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
1198 // FIXME: This stops the suggestion in some cases where it should be emitted.
1199 // Fix the spans for those cases so it's emitted correctly.
1201 "outer span {:?} does not strictly contain inner span {:?}",
1202 outer_call_span, inner_call_span
1209 "try adding a local storing this{}...",
1210 if use_span.is_some() { "" } else { " argument" }
1216 "...and then using that local {}",
1217 if use_span.is_some() { "here" } else { "as the argument to this call" }
1222 fn suggest_split_at_mut_if_applicable(
1224 err: &mut Diagnostic,
1226 borrowed_place: Place<'tcx>,
1228 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
1229 (&place.projection[..], &borrowed_place.projection[..])
1232 "consider using `.split_at_mut(position)` or similar method to obtain \
1233 two mutable non-overlapping sub-slices",
1238 /// Returns the description of the root place for a conflicting borrow and the full
1239 /// descriptions of the places that caused the conflict.
1241 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
1242 /// attempted while a shared borrow is live, then this function will return:
1247 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
1248 /// a shared borrow of another field `x.y`, then this function will return:
1250 /// ("x", "x.z", "x.y")
1253 /// In the more complex union case, where the union is a field of a struct, then if a mutable
1254 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
1255 /// another field `x.u.y`, then this function will return:
1257 /// ("x.u", "x.u.z", "x.u.y")
1260 /// This is used when creating error messages like below:
1263 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
1264 /// mutable (via `a.u.s.b`) [E0502]
1266 pub(crate) fn describe_place_for_conflicting_borrow(
1268 first_borrowed_place: Place<'tcx>,
1269 second_borrowed_place: Place<'tcx>,
1270 ) -> (String, String, String, String) {
1271 // Define a small closure that we can use to check if the type of a place
1273 let union_ty = |place_base| {
1274 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
1275 // using a type annotation in the closure argument instead leads to a lifetime error.
1276 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
1277 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
1280 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
1281 // code duplication (particularly around returning an empty description in the failure
1285 // If we have a conflicting borrow of the same place, then we don't want to add
1286 // an extraneous "via x.y" to our diagnostics, so filter out this case.
1287 first_borrowed_place != second_borrowed_place
1290 // We're going to want to traverse the first borrowed place to see if we can find
1291 // field access to a union. If we find that, then we will keep the place of the
1292 // union being accessed and the field that was being accessed so we can check the
1293 // second borrowed place for the same union and an access to a different field.
1294 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
1296 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
1297 return Some((place_base, field));
1304 .and_then(|(target_base, target_field)| {
1305 // With the place of a union and a field access into it, we traverse the second
1306 // borrowed place and look for an access to a different field of the same union.
1307 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
1308 if let ProjectionElem::Field(field, _) = elem {
1309 if let Some(union_ty) = union_ty(place_base) {
1310 if field != target_field && place_base == target_base {
1312 self.describe_any_place(place_base),
1313 self.describe_any_place(first_borrowed_place.as_ref()),
1314 self.describe_any_place(second_borrowed_place.as_ref()),
1315 union_ty.to_string(),
1323 .unwrap_or_else(|| {
1324 // If we didn't find a field access into a union, or both places match, then
1325 // only return the description of the first place.
1327 self.describe_any_place(first_borrowed_place.as_ref()),
1335 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
1337 /// This means that some data referenced by `borrow` needs to live
1338 /// past the point where the StorageDeadOrDrop of `place` occurs.
1339 /// This is usually interpreted as meaning that `place` has too
1340 /// short a lifetime. (But sometimes it is more useful to report
1341 /// it as a more direct conflict between the execution of a
1342 /// `Drop::drop` with an aliasing borrow.)
1343 #[instrument(level = "debug", skip(self))]
1344 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
1347 borrow: &BorrowData<'tcx>,
1348 place_span: (Place<'tcx>, Span),
1349 kind: Option<WriteKind>,
1351 let drop_span = place_span.1;
1353 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1355 let borrow_spans = self.retrieve_borrow_spans(borrow);
1356 let borrow_span = borrow_spans.var_or_use_path_span();
1358 assert!(root_place.projection.is_empty());
1359 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1361 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1363 if self.access_place_error_reported.contains(&(
1364 Place { local: root_place.local, projection: root_place_projection },
1368 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1374 self.access_place_error_reported.insert((
1375 Place { local: root_place.local, projection: root_place_projection },
1379 let borrowed_local = borrow.borrowed_place.local;
1380 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1382 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1383 self.buffer_error(err);
1387 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1388 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1390 // If a borrow of path `B` conflicts with drop of `D` (and
1391 // we're not in the uninteresting case where `B` is a
1392 // prefix of `D`), then report this as a more interesting
1393 // destructor conflict.
1394 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1395 self.report_borrow_conflicts_with_destructor(
1396 location, borrow, place_span, kind, dropped_ty,
1402 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1404 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1405 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1407 debug!(?place_desc, ?explanation);
1409 let err = match (place_desc, explanation) {
1410 // If the outlives constraint comes from inside the closure,
1415 // Box::new(|| y) as Box<Fn() -> &'static i32>
1417 // then just use the normal error. The closure isn't escaping
1418 // and `move` will not help here.
1421 BorrowExplanation::MustBeValidFor {
1423 category @ (ConstraintCategory::Return(_)
1424 | ConstraintCategory::CallArgument(_)
1425 | ConstraintCategory::OpaqueType),
1426 from_closure: false,
1431 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1432 .report_escaping_closure_capture(
1438 &format!("`{}`", name),
1442 BorrowExplanation::MustBeValidFor {
1443 category: ConstraintCategory::Assignment,
1444 from_closure: false,
1447 source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
1453 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1454 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1462 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1472 self.buffer_error(err);
1475 fn report_local_value_does_not_live_long_enough(
1479 borrow: &BorrowData<'tcx>,
1481 borrow_spans: UseSpans<'tcx>,
1482 explanation: BorrowExplanation<'tcx>,
1483 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1485 "report_local_value_does_not_live_long_enough(\
1486 {:?}, {:?}, {:?}, {:?}, {:?}\
1488 location, name, borrow, drop_span, borrow_spans
1491 let borrow_span = borrow_spans.var_or_use_path_span();
1492 if let BorrowExplanation::MustBeValidFor {
1496 from_closure: false,
1500 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1505 opt_place_desc.as_ref(),
1511 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1513 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1514 let region_name = annotation.emit(self, &mut err);
1518 format!("`{}` would have to be valid for `{}`...", name, region_name),
1521 let fn_hir_id = self.mir_hir_id();
1525 "...but `{}` will be dropped here, when the {} returns",
1530 .opt_name(fn_hir_id)
1531 .map(|name| format!("function `{}`", name))
1532 .unwrap_or_else(|| {
1536 .typeck(self.mir_def_id())
1537 .node_type(fn_hir_id)
1540 ty::Closure(..) => "enclosing closure",
1541 ty::Generator(..) => "enclosing generator",
1542 kind => bug!("expected closure or generator, found {:?}", kind),
1550 "functions cannot return a borrow to data owned within the function's scope, \
1551 functions can only return borrows to data passed as arguments",
1554 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1555 references-and-borrowing.html#dangling-references>",
1558 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1560 explanation.add_explanation_to_diagnostic(
1571 err.span_label(borrow_span, "borrowed value does not live long enough");
1572 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1574 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1576 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1578 explanation.add_explanation_to_diagnostic(
1592 fn report_borrow_conflicts_with_destructor(
1595 borrow: &BorrowData<'tcx>,
1596 (place, drop_span): (Place<'tcx>, Span),
1597 kind: Option<WriteKind>,
1598 dropped_ty: Ty<'tcx>,
1601 "report_borrow_conflicts_with_destructor(\
1602 {:?}, {:?}, ({:?}, {:?}), {:?}\
1604 location, borrow, place, drop_span, kind,
1607 let borrow_spans = self.retrieve_borrow_spans(borrow);
1608 let borrow_span = borrow_spans.var_or_use();
1610 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1612 let what_was_dropped = match self.describe_place(place.as_ref()) {
1613 Some(name) => format!("`{}`", name),
1614 None => String::from("temporary value"),
1617 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1618 Some(borrowed) => format!(
1619 "here, drop of {D} needs exclusive access to `{B}`, \
1620 because the type `{T}` implements the `Drop` trait",
1621 D = what_was_dropped,
1626 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1627 D = what_was_dropped,
1631 err.span_label(drop_span, label);
1633 // Only give this note and suggestion if they could be relevant.
1635 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1637 BorrowExplanation::UsedLater { .. }
1638 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1639 err.note("consider using a `let` binding to create a longer lived value");
1644 explanation.add_explanation_to_diagnostic(
1654 self.buffer_error(err);
1657 fn report_thread_local_value_does_not_live_long_enough(
1661 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1663 "report_thread_local_value_does_not_live_long_enough(\
1666 drop_span, borrow_span
1669 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1673 "thread-local variables cannot be borrowed beyond the end of the function",
1675 err.span_label(drop_span, "end of enclosing function is here");
1680 #[instrument(level = "debug", skip(self))]
1681 fn report_temporary_value_does_not_live_long_enough(
1684 borrow: &BorrowData<'tcx>,
1686 borrow_spans: UseSpans<'tcx>,
1688 explanation: BorrowExplanation<'tcx>,
1689 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1690 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1693 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1704 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1705 err.span_label(proper_span, "creates a temporary value which is freed while still in use");
1706 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1709 BorrowExplanation::UsedLater(..)
1710 | BorrowExplanation::UsedLaterInLoop(..)
1711 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1712 // Only give this note and suggestion if it could be relevant.
1713 let sm = self.infcx.tcx.sess.source_map();
1714 let mut suggested = false;
1715 let msg = "consider using a `let` binding to create a longer lived value";
1717 /// We check that there's a single level of block nesting to ensure always correct
1718 /// suggestions. If we don't, then we only provide a free-form message to avoid
1719 /// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`.
1720 /// We could expand the analysis to suggest hoising all of the relevant parts of
1721 /// the users' code to make the code compile, but that could be too much.
1722 struct NestedStatementVisitor {
1728 impl<'tcx> Visitor<'tcx> for NestedStatementVisitor {
1729 fn visit_block(&mut self, block: &hir::Block<'tcx>) {
1731 walk_block(self, block);
1734 fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) {
1735 if self.span == expr.span {
1736 self.found = self.current;
1738 walk_expr(self, expr);
1741 let source_info = self.body.source_info(location);
1742 if let Some(scope) = self.body.source_scopes.get(source_info.scope)
1743 && let ClearCrossCrate::Set(scope_data) = &scope.local_data
1744 && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root)
1745 && let Some(id) = node.body_id()
1746 && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind
1748 for stmt in block.stmts {
1749 let mut visitor = NestedStatementVisitor {
1754 visitor.visit_stmt(stmt);
1755 if visitor.found == 0
1756 && stmt.span.contains(proper_span)
1757 && let Some(p) = sm.span_to_margin(stmt.span)
1758 && let Ok(s) = sm.span_to_snippet(proper_span)
1760 let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
1761 err.multipart_suggestion_verbose(
1764 (stmt.span.shrink_to_lo(), addition),
1765 (proper_span, "binding".to_string()),
1767 Applicability::MaybeIncorrect,
1780 explanation.add_explanation_to_diagnostic(
1790 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1792 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1797 fn try_report_cannot_return_reference_to_local(
1799 borrow: &BorrowData<'tcx>,
1802 category: ConstraintCategory<'tcx>,
1803 opt_place_desc: Option<&String>,
1804 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1805 let return_kind = match category {
1806 ConstraintCategory::Return(_) => "return",
1807 ConstraintCategory::Yield => "yield",
1811 // FIXME use a better heuristic than Spans
1812 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1818 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1819 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1820 match self.body.local_kind(local) {
1821 LocalKind::ReturnPointer | LocalKind::Temp => {
1822 bug!("temporary or return pointer with a name")
1824 LocalKind::Var => "local variable ",
1826 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1828 "variable captured by `move` "
1830 LocalKind::Arg => "function parameter ",
1836 format!("{}`{}`", local_kind, place_desc),
1837 format!("`{}` is borrowed here", place_desc),
1841 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1842 let local = root_place.local;
1843 match self.body.local_kind(local) {
1844 LocalKind::ReturnPointer | LocalKind::Temp => {
1845 ("temporary value".to_string(), "temporary value created here".to_string())
1848 "function parameter".to_string(),
1849 "function parameter borrowed here".to_string(),
1852 ("local binding".to_string(), "local binding introduced here".to_string())
1857 let mut err = self.cannot_return_reference_to_local(
1864 if return_span != borrow_span {
1865 err.span_label(borrow_span, note);
1867 let tcx = self.infcx.tcx;
1869 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1870 let return_ty = tcx.erase_regions(return_ty);
1873 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator)
1876 .type_implements_trait(iter_trait, [return_ty], self.param_env)
1877 .must_apply_modulo_regions()
1879 err.span_suggestion_hidden(
1880 return_span.shrink_to_hi(),
1881 "use `.collect()` to allocate the iterator",
1882 ".collect::<Vec<_>>()",
1883 Applicability::MaybeIncorrect,
1891 fn report_escaping_closure_capture(
1893 use_span: UseSpans<'tcx>,
1895 fr_name: &RegionName,
1896 category: ConstraintCategory<'tcx>,
1897 constraint_span: Span,
1899 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1900 let tcx = self.infcx.tcx;
1901 let args_span = use_span.args_or_use();
1903 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1905 if string.starts_with("async ") {
1906 let pos = args_span.lo() + BytePos(6);
1907 (args_span.with_lo(pos).with_hi(pos), "move ")
1908 } else if string.starts_with("async|") {
1909 let pos = args_span.lo() + BytePos(5);
1910 (args_span.with_lo(pos).with_hi(pos), " move")
1912 (args_span.shrink_to_lo(), "move ")
1915 Err(_) => (args_span, "move |<args>| <body>"),
1917 let kind = match use_span.generator_kind() {
1918 Some(generator_kind) => match generator_kind {
1919 GeneratorKind::Async(async_kind) => match async_kind {
1920 AsyncGeneratorKind::Block => "async block",
1921 AsyncGeneratorKind::Closure => "async closure",
1922 _ => bug!("async block/closure expected, but async function found."),
1924 GeneratorKind::Gen => "generator",
1930 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1931 err.span_suggestion_verbose(
1934 "to force the {} to take ownership of {} (and any \
1935 other referenced variables), use the `move` keyword",
1939 Applicability::MachineApplicable,
1943 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1944 let msg = format!("{} is returned here", kind);
1945 err.span_note(constraint_span, &msg);
1947 ConstraintCategory::CallArgument(_) => {
1948 fr_name.highlight_region_name(&mut err);
1949 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1951 "async blocks are not executed immediately and must either take a \
1952 reference or ownership of outside variables they use",
1955 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1956 err.span_note(constraint_span, &msg);
1960 "report_escaping_closure_capture called with unexpected constraint \
1969 fn report_escaping_data(
1972 name: &Option<String>,
1976 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1977 let tcx = self.infcx.tcx;
1979 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1982 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1986 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1989 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1991 if let Some(name) = name {
1994 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1999 format!("reference escapes the {} body here", escapes_from),
2006 fn get_moved_indexes(
2010 ) -> (Vec<MoveSite>, Vec<Location>) {
2011 fn predecessor_locations<'tcx, 'a>(
2012 body: &'a mir::Body<'tcx>,
2014 ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
2015 if location.statement_index == 0 {
2016 let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
2017 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
2019 Either::Right(std::iter::once(Location {
2020 statement_index: location.statement_index - 1,
2026 let mut mpis = vec![mpi];
2027 let move_paths = &self.move_data.move_paths;
2028 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
2030 let mut stack = Vec::new();
2031 let mut back_edge_stack = Vec::new();
2033 predecessor_locations(self.body, location).for_each(|predecessor| {
2034 if location.dominates(predecessor, &self.dominators) {
2035 back_edge_stack.push(predecessor)
2037 stack.push(predecessor);
2041 let mut reached_start = false;
2043 /* Check if the mpi is initialized as an argument */
2044 let mut is_argument = false;
2045 for arg in self.body.args_iter() {
2046 let path = self.move_data.rev_lookup.find_local(arg);
2047 if mpis.contains(&path) {
2052 let mut visited = FxHashSet::default();
2053 let mut move_locations = FxHashSet::default();
2054 let mut reinits = vec![];
2055 let mut result = vec![];
2057 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
2059 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
2060 location, is_back_edge
2063 if !visited.insert(location) {
2069 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
2070 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
2071 // this analysis only tries to find moves explicitly
2072 // written by the user, so we ignore the move-outs
2073 // created by `StorageDead` and at the beginning
2076 // If we are found a use of a.b.c which was in error, then we want to look for
2077 // moves not only of a.b.c but also a.b and a.
2079 // Note that the moves data already includes "parent" paths, so we don't have to
2080 // worry about the other case: that is, if there is a move of a.b.c, it is already
2081 // marked as a move of a.b and a as well, so we will generate the correct errors
2083 for moi in &self.move_data.loc_map[location] {
2084 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
2085 let path = self.move_data.moves[*moi].path;
2086 if mpis.contains(&path) {
2088 "report_use_of_moved_or_uninitialized: found {:?}",
2089 move_paths[path].place
2091 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
2092 move_locations.insert(location);
2094 // Strictly speaking, we could continue our DFS here. There may be
2095 // other moves that can reach the point of error. But it is kind of
2096 // confusing to highlight them.
2104 // drop(a); // <-- current point of error
2107 // Because we stop the DFS here, we only highlight `let c = a`,
2108 // and not `let b = a`. We will of course also report an error at
2109 // `let c = a` which highlights `let b = a` as the move.
2116 let mut any_match = false;
2117 for ii in &self.move_data.init_loc_map[location] {
2118 let init = self.move_data.inits[*ii];
2120 InitKind::Deep | InitKind::NonPanicPathOnly => {
2121 if mpis.contains(&init.path) {
2125 InitKind::Shallow => {
2126 if mpi == init.path {
2133 reinits.push(location);
2139 while let Some(location) = stack.pop() {
2140 if dfs_iter(&mut result, location, false) {
2144 let mut has_predecessor = false;
2145 predecessor_locations(self.body, location).for_each(|predecessor| {
2146 if location.dominates(predecessor, &self.dominators) {
2147 back_edge_stack.push(predecessor)
2149 stack.push(predecessor);
2151 has_predecessor = true;
2154 if !has_predecessor {
2155 reached_start = true;
2158 if (is_argument || !reached_start) && result.is_empty() {
2159 /* Process back edges (moves in future loop iterations) only if
2160 the move path is definitely initialized upon loop entry,
2161 to avoid spurious "in previous iteration" errors.
2162 During DFS, if there's a path from the error back to the start
2163 of the function with no intervening init or move, then the
2164 move path may be uninitialized at loop entry.
2166 while let Some(location) = back_edge_stack.pop() {
2167 if dfs_iter(&mut result, location, true) {
2171 predecessor_locations(self.body, location)
2172 .for_each(|predecessor| back_edge_stack.push(predecessor));
2176 // Check if we can reach these reinits from a move location.
2177 let reinits_reachable = reinits
2180 let mut visited = FxHashSet::default();
2181 let mut stack = vec![*reinit];
2182 while let Some(location) = stack.pop() {
2183 if !visited.insert(location) {
2186 if move_locations.contains(&location) {
2189 stack.extend(predecessor_locations(self.body, location));
2193 .collect::<Vec<Location>>();
2194 (result, reinits_reachable)
2197 pub(crate) fn report_illegal_mutation_of_borrowed(
2200 (place, span): (Place<'tcx>, Span),
2201 loan: &BorrowData<'tcx>,
2203 let loan_spans = self.retrieve_borrow_spans(loan);
2204 let loan_span = loan_spans.args_or_use();
2206 let descr_place = self.describe_any_place(place.as_ref());
2207 if loan.kind == BorrowKind::Shallow {
2208 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
2209 let mut err = self.cannot_mutate_in_immutable_section(
2216 loan_spans.var_span_label(
2218 format!("borrow occurs due to use{}", loan_spans.describe()),
2219 loan.kind.describe_mutability(),
2222 self.buffer_error(err);
2228 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
2230 loan_spans.var_span_label(
2232 format!("borrow occurs due to use{}", loan_spans.describe()),
2233 loan.kind.describe_mutability(),
2236 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
2246 self.explain_deref_coercion(loan, &mut err);
2248 self.buffer_error(err);
2251 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
2252 let tcx = self.infcx.tcx;
2254 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
2255 Some((method_did, method_substs)),
2257 &self.body[loan.reserve_location.block].terminator,
2258 rustc_const_eval::util::find_self_call(
2261 loan.assigned_place.local,
2262 loan.reserve_location.block,
2265 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
2267 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
2268 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
2271 if let Some(Ok(instance)) = deref_target {
2272 let deref_target_ty = instance.ty(tcx, self.param_env);
2274 "borrow occurs due to deref coercion to `{}`",
2277 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
2283 /// Reports an illegal reassignment; for example, an assignment to
2284 /// (part of) a non-`mut` local that occurs potentially after that
2285 /// local has already been initialized. `place` is the path being
2286 /// assigned; `err_place` is a place providing a reason why
2287 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
2288 /// assignment to `x.f`).
2289 pub(crate) fn report_illegal_reassignment(
2291 _location: Location,
2292 (place, span): (Place<'tcx>, Span),
2293 assigned_span: Span,
2294 err_place: Place<'tcx>,
2296 let (from_arg, local_decl, local_name) = match err_place.as_local() {
2298 self.body.local_kind(local) == LocalKind::Arg,
2299 Some(&self.body.local_decls[local]),
2300 self.local_names[local],
2302 None => (false, None, None),
2305 // If root local is initialized immediately (everything apart from let
2306 // PATTERN;) then make the error refer to that local, rather than the
2307 // place being assigned later.
2308 let (place_description, assigned_span) = match local_decl {
2311 Some(box LocalInfo::User(
2312 ClearCrossCrate::Clear
2313 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
2314 opt_match_place: None,
2318 | Some(box LocalInfo::StaticRef { .. })
2322 | None => (self.describe_any_place(place.as_ref()), assigned_span),
2323 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
2326 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
2327 let msg = if from_arg {
2328 "cannot assign to immutable argument"
2330 "cannot assign twice to immutable variable"
2332 if span != assigned_span && !from_arg {
2333 err.span_label(assigned_span, format!("first assignment to {}", place_description));
2335 if let Some(decl) = local_decl
2336 && let Some(name) = local_name
2337 && decl.can_be_made_mutable()
2339 err.span_suggestion(
2340 decl.source_info.span,
2341 "consider making this binding mutable",
2342 format!("mut {}", name),
2343 Applicability::MachineApplicable,
2346 err.span_label(span, msg);
2347 self.buffer_error(err);
2350 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
2351 let tcx = self.infcx.tcx;
2352 let (kind, _place_ty) = place.projection.iter().fold(
2353 (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)),
2354 |(kind, place_ty), &elem| {
2357 ProjectionElem::Deref => match kind {
2358 StorageDeadOrDrop::LocalStorageDead
2359 | StorageDeadOrDrop::BoxedStorageDead => {
2361 place_ty.ty.is_box(),
2362 "Drop of value behind a reference or raw pointer"
2364 StorageDeadOrDrop::BoxedStorageDead
2366 StorageDeadOrDrop::Destructor(_) => kind,
2368 ProjectionElem::OpaqueCast { .. }
2369 | ProjectionElem::Field(..)
2370 | ProjectionElem::Downcast(..) => {
2371 match place_ty.ty.kind() {
2372 ty::Adt(def, _) if def.has_dtor(tcx) => {
2373 // Report the outermost adt with a destructor
2375 StorageDeadOrDrop::Destructor(_) => kind,
2376 StorageDeadOrDrop::LocalStorageDead
2377 | StorageDeadOrDrop::BoxedStorageDead => {
2378 StorageDeadOrDrop::Destructor(place_ty.ty)
2385 ProjectionElem::ConstantIndex { .. }
2386 | ProjectionElem::Subslice { .. }
2387 | ProjectionElem::Index(_) => kind,
2389 place_ty.projection_ty(tcx, elem),
2396 /// Describe the reason for the fake borrow that was assigned to `place`.
2397 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
2398 use rustc_middle::mir::visit::Visitor;
2399 struct FakeReadCauseFinder<'tcx> {
2401 cause: Option<FakeReadCause>,
2403 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
2404 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
2406 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
2407 if *place == self.place =>
2409 self.cause = Some(*cause);
2415 let mut visitor = FakeReadCauseFinder { place, cause: None };
2416 visitor.visit_body(&self.body);
2417 match visitor.cause {
2418 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2419 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2424 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2425 /// borrow of local value that does not live long enough.
2426 fn annotate_argument_and_return_for_borrow(
2428 borrow: &BorrowData<'tcx>,
2429 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2430 // Define a fallback for when we can't match a closure.
2432 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2436 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2438 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2440 self.infcx.tcx.fn_sig(self.mir_def_id()),
2447 // In order to determine whether we need to annotate, we need to check whether the reserve
2448 // place was an assignment into a temporary.
2450 // If it was, we check whether or not that temporary is eventually assigned into the return
2451 // place. If it was, we can add annotations about the function's return type and arguments
2452 // and it'll make sense.
2453 let location = borrow.reserve_location;
2454 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2455 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
2456 &self.body[location.block].statements.get(location.statement_index)
2458 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2459 // Check that the initial assignment of the reserve location is into a temporary.
2460 let mut target = match reservation.as_local() {
2461 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2465 // Next, look through the rest of the block, checking if we are assigning the
2466 // `target` (that is, the place that contains our borrow) to anything.
2467 let mut annotated_closure = None;
2468 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2470 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2473 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2474 if let Some(assigned_to) = place.as_local() {
2476 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2480 // Check if our `target` was captured by a closure.
2481 if let Rvalue::Aggregate(
2482 box AggregateKind::Closure(def_id, substs),
2486 for operand in operands {
2487 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2491 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2495 // Find the local from the operand.
2496 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2500 if assigned_from_local != target {
2504 // If a closure captured our `target` and then assigned
2505 // into a place then we should annotate the closure in
2506 // case it ends up being assigned into the return place.
2508 self.annotate_fn_sig(def_id, substs.as_closure().sig());
2510 "annotate_argument_and_return_for_borrow: \
2511 annotated_closure={:?} assigned_from_local={:?} \
2513 annotated_closure, assigned_from_local, assigned_to
2516 if assigned_to == mir::RETURN_PLACE {
2517 // If it was assigned directly into the return place, then
2519 return annotated_closure;
2521 // Otherwise, update the target.
2522 target = assigned_to;
2526 // If none of our closure's operands matched, then skip to the next
2531 // Otherwise, look at other types of assignment.
2532 let assigned_from = match rvalue {
2533 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2534 Rvalue::Use(operand) => match operand {
2535 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2543 "annotate_argument_and_return_for_borrow: \
2544 assigned_from={:?}",
2548 // Find the local from the rvalue.
2549 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2551 "annotate_argument_and_return_for_borrow: \
2552 assigned_from_local={:?}",
2553 assigned_from_local,
2556 // Check if our local matches the target - if so, we've assigned our
2557 // borrow to a new place.
2558 if assigned_from_local != target {
2562 // If we assigned our `target` into a new place, then we should
2563 // check if it was the return place.
2565 "annotate_argument_and_return_for_borrow: \
2566 assigned_from_local={:?} assigned_to={:?}",
2567 assigned_from_local, assigned_to
2569 if assigned_to == mir::RETURN_PLACE {
2570 // If it was then return the annotated closure if there was one,
2571 // else, annotate this function.
2572 return annotated_closure.or_else(fallback);
2575 // If we didn't assign into the return place, then we just update
2577 target = assigned_to;
2582 // Check the terminator if we didn't find anything in the statements.
2583 let terminator = &self.body[location.block].terminator();
2585 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2588 if let TerminatorKind::Call { destination, target: Some(_), args, .. } =
2591 if let Some(assigned_to) = destination.as_local() {
2593 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2596 for operand in args {
2597 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2601 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2605 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2607 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2608 assigned_from_local,
2611 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2612 return annotated_closure.or_else(fallback);
2620 // If we haven't found an assignment into the return place, then we need not add
2622 debug!("annotate_argument_and_return_for_borrow: none found");
2626 /// Annotate the first argument and return type of a function signature if they are
2631 sig: ty::PolyFnSig<'tcx>,
2632 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2633 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2634 let is_closure = self.infcx.tcx.is_closure(did.to_def_id());
2635 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
2636 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2638 // We need to work out which arguments to highlight. We do this by looking
2639 // at the return type, where there are three cases:
2641 // 1. If there are named arguments, then we should highlight the return type and
2642 // highlight any of the arguments that are also references with that lifetime.
2643 // If there are no arguments that have the same lifetime as the return type,
2644 // then don't highlight anything.
2645 // 2. The return type is a reference with an anonymous lifetime. If this is
2646 // the case, then we can take advantage of (and teach) the lifetime elision
2649 // We know that an error is being reported. So the arguments and return type
2650 // must satisfy the elision rules. Therefore, if there is a single argument
2651 // then that means the return type and first (and only) argument have the same
2652 // lifetime and the borrow isn't meeting that, we can highlight the argument
2655 // If there are multiple arguments then the first argument must be self (else
2656 // it would not satisfy the elision rules), so we can highlight self and the
2658 // 3. The return type is not a reference. In this case, we don't highlight
2660 let return_ty = sig.output();
2661 match return_ty.skip_binder().kind() {
2662 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2663 // This is case 1 from above, return type is a named reference so we need to
2664 // search for relevant arguments.
2665 let mut arguments = Vec::new();
2666 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2667 if let ty::Ref(argument_region, _, _) = argument.kind() {
2668 if argument_region == return_region {
2669 // Need to use the `rustc_middle::ty` types to compare against the
2670 // `return_region`. Then use the `rustc_hir` type to get only
2671 // the lifetime span.
2672 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2673 // With access to the lifetime, we can get
2675 arguments.push((*argument, lifetime.ident.span));
2677 bug!("ty type is a ref but hir type is not");
2683 // We need to have arguments. This shouldn't happen, but it's worth checking.
2684 if arguments.is_empty() {
2688 // We use a mix of the HIR and the Ty types to get information
2689 // as the HIR doesn't have full types for closure arguments.
2690 let return_ty = sig.output().skip_binder();
2691 let mut return_span = fn_decl.output.span();
2692 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2693 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2694 return_span = lifetime.ident.span;
2698 Some(AnnotatedBorrowFnSignature::NamedFunction {
2704 ty::Ref(_, _, _) if is_closure => {
2705 // This is case 2 from above but only for closures, return type is anonymous
2706 // reference so we select
2707 // the first argument.
2708 let argument_span = fn_decl.inputs.first()?.span;
2709 let argument_ty = sig.inputs().skip_binder().first()?;
2711 // Closure arguments are wrapped in a tuple, so we need to get the first
2713 if let ty::Tuple(elems) = argument_ty.kind() {
2714 let &argument_ty = elems.first()?;
2715 if let ty::Ref(_, _, _) = argument_ty.kind() {
2716 return Some(AnnotatedBorrowFnSignature::Closure {
2725 ty::Ref(_, _, _) => {
2726 // This is also case 2 from above but for functions, return type is still an
2727 // anonymous reference so we select the first argument.
2728 let argument_span = fn_decl.inputs.first()?.span;
2729 let argument_ty = *sig.inputs().skip_binder().first()?;
2731 let return_span = fn_decl.output.span();
2732 let return_ty = sig.output().skip_binder();
2734 // We expect the first argument to be a reference.
2735 match argument_ty.kind() {
2736 ty::Ref(_, _, _) => {}
2740 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2748 // This is case 3 from above, return type is not a reference so don't highlight
2757 enum AnnotatedBorrowFnSignature<'tcx> {
2759 arguments: Vec<(Ty<'tcx>, Span)>,
2760 return_ty: Ty<'tcx>,
2764 argument_ty: Ty<'tcx>,
2765 argument_span: Span,
2766 return_ty: Ty<'tcx>,
2770 argument_ty: Ty<'tcx>,
2771 argument_span: Span,
2775 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2776 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2778 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2780 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2783 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2786 cx.get_region_name_for_ty(argument_ty, 0)
2788 &AnnotatedBorrowFnSignature::AnonymousFunction {
2794 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2795 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2797 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2798 let types_equal = return_ty_name == argument_ty_name;
2803 if types_equal { "also " } else { "" },
2809 "argument and return type have the same lifetime due to lifetime elision rules",
2812 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2813 lifetime-syntax.html#lifetime-elision>",
2816 cx.get_region_name_for_ty(return_ty, 0)
2818 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2819 // Region of return type and arguments checked to be the same earlier.
2820 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2821 for (_, argument_span) in arguments {
2822 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2825 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2828 "use data from the highlighted arguments which match the `{}` lifetime of \
2839 /// Detect whether one of the provided spans is a statement nested within the top-most visited expr
2840 struct ReferencedStatementsVisitor<'a>(&'a [Span], bool);
2842 impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> {
2843 fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
2845 hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => {
2853 /// Given a set of spans representing statements initializing the relevant binding, visit all the
2854 /// function expressions looking for branching code paths that *do not* initialize the binding.
2855 struct ConditionVisitor<'b> {
2858 errors: Vec<(Span, String)>,
2861 impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
2862 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
2864 hir::ExprKind::If(cond, body, None) => {
2865 // `if` expressions with no `else` that initialize the binding might be missing an
2867 let mut v = ReferencedStatementsVisitor(self.spans, false);
2873 "if this `if` condition is `false`, {} is not initialized",
2878 ex.span.shrink_to_hi(),
2879 format!("an `else` arm might be missing here, initializing {}", self.name),
2883 hir::ExprKind::If(cond, body, Some(other)) => {
2884 // `if` expressions where the binding is only initialized in one of the two arms
2885 // might be missing a binding initialization.
2886 let mut a = ReferencedStatementsVisitor(self.spans, false);
2888 let mut b = ReferencedStatementsVisitor(self.spans, false);
2889 b.visit_expr(other);
2891 (true, true) | (false, false) => {}
2893 if other.span.is_desugaring(DesugaringKind::WhileLoop) {
2897 "if this condition isn't met and the `while` loop runs 0 \
2898 times, {} is not initialized",
2904 body.span.shrink_to_hi().until(other.span),
2906 "if the `if` condition is `false` and this `else` arm is \
2907 executed, {} is not initialized",
2917 "if this condition is `true`, {} is not initialized",
2924 hir::ExprKind::Match(e, arms, loop_desugar) => {
2925 // If the binding is initialized in one of the match arms, then the other match
2926 // arms might be missing an initialization.
2927 let results: Vec<bool> = arms
2930 let mut v = ReferencedStatementsVisitor(self.spans, false);
2935 if results.iter().any(|x| *x) && !results.iter().all(|x| *x) {
2936 for (arm, seen) in arms.iter().zip(results) {
2938 if loop_desugar == hir::MatchSource::ForLoopDesugar {
2942 "if the `for` loop runs 0 times, {} is not initialized",
2946 } else if let Some(guard) = &arm.guard {
2948 arm.pat.span.to(guard.body().span),
2950 "if this pattern and condition are matched, {} is not \
2959 "if this pattern is matched, {} is not initialized",
2968 // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should
2969 // also be accounted for. For now it is fine, as if we don't find *any* relevant
2970 // branching code paths, we point at the places where the binding *is* initialized for
2974 walk_expr(self, ex);