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 {reinits} reinitializations might get skipped")
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 {partial_str}move",
229 desired_action.as_verb_in_past_tense(),
234 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
235 let needs_note = match ty.kind() {
236 ty::Closure(id, _) => {
237 let tables = self.infcx.tcx.typeck(id.expect_local());
238 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
240 tables.closure_kind_origins().get(hir_id).is_none()
245 let mpi = self.move_data.moves[move_out_indices[0]].path;
246 let place = &self.move_data.move_paths[mpi].place;
247 let ty = place.ty(self.body, self.infcx.tcx).ty;
249 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
250 // Same for if we're in a loop, see #101119.
251 if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
252 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
253 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
254 err.span_suggestion_verbose(
257 "consider creating a fresh reborrow of {} here",
258 self.describe_place(moved_place)
259 .map(|n| format!("`{n}`"))
260 .unwrap_or_else(|| "the mutable reference".to_string()),
263 Applicability::MachineApplicable,
268 let opt_name = self.describe_place_with_options(
270 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
272 let note_msg = match opt_name {
273 Some(name) => format!("`{name}`"),
274 None => "value".to_owned(),
276 if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) {
277 // Suppress the next suggestion since we don't want to put more bounds onto
278 // something that already has `Fn`-like bounds (or is a closure), so we can't
281 self.suggest_adding_copy_bounds(&mut err, ty, span);
285 let span = if let Some(local) = place.as_local() {
286 Some(self.body.local_decls[local].source_info.span)
290 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
293 if let UseSpans::FnSelfUse {
294 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
299 "{} occurs due to deref coercion to `{deref_target_ty}`",
300 desired_action.as_noun(),
303 // Check first whether the source is accessible (issue #87060)
304 if self.infcx.tcx.sess.source_map().is_span_accessible(deref_target) {
305 err.span_note(deref_target, "deref defined here");
309 self.buffer_move_error(move_out_indices, (used_place, err));
313 fn suggest_ref_or_clone(
317 err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
318 in_pattern: &mut bool,
319 move_spans: UseSpans<'_>,
321 struct ExpressionFinder<'hir> {
323 expr: Option<&'hir hir::Expr<'hir>>,
324 pat: Option<&'hir hir::Pat<'hir>>,
325 parent_pat: Option<&'hir hir::Pat<'hir>>,
327 impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
328 fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
329 if e.span == self.expr_span {
332 hir::intravisit::walk_expr(self, e);
334 fn visit_pat(&mut self, p: &'hir hir::Pat<'hir>) {
335 if p.span == self.expr_span {
338 if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, i, sub) = p.kind {
339 if i.span == self.expr_span || p.span == self.expr_span {
342 // Check if we are in a situation of `ident @ ident` where we want to suggest
343 // `ref ident @ ref ident` or `ref ident @ Struct { ref ident }`.
344 if let Some(subpat) = sub && self.pat.is_none() {
345 self.visit_pat(subpat);
346 if self.pat.is_some() {
347 self.parent_pat = Some(p);
352 hir::intravisit::walk_pat(self, p);
355 let hir = self.infcx.tcx.hir();
356 if let Some(hir::Node::Item(hir::Item {
357 kind: hir::ItemKind::Fn(_, _, body_id),
359 })) = hir.find(hir.local_def_id_to_hir_id(self.mir_def_id()))
360 && let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id)
362 let place = &self.move_data.move_paths[mpi].place;
363 let span = place.as_local()
364 .map(|local| self.body.local_decls[local].source_info.span);
365 let mut finder = ExpressionFinder {
366 expr_span: move_span,
371 finder.visit_expr(expr);
372 if let Some(span) = span && let Some(expr) = finder.expr {
373 for (_, expr) in hir.parent_iter(expr.hir_id) {
374 if let hir::Node::Expr(expr) = expr {
375 if expr.span.contains(span) {
376 // If the let binding occurs within the same loop, then that
377 // loop isn't relevant, like in the following, the outermost `loop`
378 // doesn't play into `x` being moved.
381 // let x = String::new();
389 if let hir::ExprKind::Loop(.., loop_span) = expr.kind {
390 err.span_label(loop_span, "inside of this loop");
394 let typeck = self.infcx.tcx.typeck(self.mir_def_id());
395 let hir_id = hir.parent_id(expr.hir_id);
396 if let Some(parent) = hir.find(hir_id) {
397 let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
398 && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
399 && let Some(def_id) = typeck.type_dependent_def_id(parent_expr.hir_id)
401 (def_id.as_local(), args, 1)
402 } else if let hir::Node::Expr(parent_expr) = parent
403 && let hir::ExprKind::Call(call, args) = parent_expr.kind
404 && let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind()
406 (def_id.as_local(), args, 0)
410 if let Some(def_id) = def_id
411 && let Some(node) = hir.find(hir.local_def_id_to_hir_id(def_id))
412 && let Some(fn_sig) = node.fn_sig()
413 && let Some(ident) = node.ident()
414 && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
415 && let Some(arg) = fn_sig.decl.inputs.get(pos + offset)
417 let mut span: MultiSpan = arg.span.into();
418 span.push_span_label(
420 "this parameter takes ownership of the value".to_string(),
422 let descr = match node.fn_kind() {
423 Some(hir::intravisit::FnKind::ItemFn(..)) | None => "function",
424 Some(hir::intravisit::FnKind::Method(..)) => "method",
425 Some(hir::intravisit::FnKind::Closure) => "closure",
427 span.push_span_label(
429 format!("in this {descr}"),
434 "consider changing this parameter type in {descr} `{ident}` to \
435 borrow instead if owning the value isn't necessary",
439 let place = &self.move_data.move_paths[mpi].place;
440 let ty = place.ty(self.body, self.infcx.tcx).ty;
441 if let hir::Node::Expr(parent_expr) = parent
442 && let hir::ExprKind::Call(call_expr, _) = parent_expr.kind
443 && let hir::ExprKind::Path(
444 hir::QPath::LangItem(LangItem::IntoIterIntoIter, _, _)
447 // Do not suggest `.clone()` in a `for` loop, we already suggest borrowing.
448 } else if let UseSpans::FnSelfUse {
449 kind: CallKind::Normal { .. },
452 // We already suggest cloning for these cases in `explain_captures`.
454 self.suggest_cloning(err, ty, move_span);
458 if let Some(pat) = finder.pat {
460 let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())];
461 if let Some(pat) = finder.parent_pat {
462 sugg.insert(0, (pat.span.shrink_to_lo(), "ref ".to_string()));
464 err.multipart_suggestion_verbose(
465 "borrow this binding in the pattern to avoid moving the value",
467 Applicability::MachineApplicable,
473 fn report_use_of_uninitialized(
476 used_place: PlaceRef<'tcx>,
477 moved_place: PlaceRef<'tcx>,
478 desired_action: InitializationRequiringAction,
480 use_spans: UseSpans<'tcx>,
481 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
482 // We need all statements in the body where the binding was assigned to to later find all
483 // the branching code paths where the binding *wasn't* assigned to.
484 let inits = &self.move_data.init_path_map[mpi];
485 let move_path = &self.move_data.move_paths[mpi];
486 let decl_span = self.body.local_decls[move_path.place.local].source_info.span;
487 let mut spans = vec![];
488 for init_idx in inits {
489 let init = &self.move_data.inits[*init_idx];
490 let span = init.span(&self.body);
491 if !span.is_dummy() {
496 let (name, desc) = match self.describe_place_with_options(
498 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
500 Some(name) => (format!("`{name}`"), format!("`{name}` ")),
501 None => ("the variable".to_string(), String::new()),
503 let path = match self.describe_place_with_options(
505 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
507 Some(name) => format!("`{name}`"),
508 None => "value".to_string(),
511 // We use the statements were the binding was initialized, and inspect the HIR to look
512 // for the branching codepaths that aren't covered, to point at them.
513 let map = self.infcx.tcx.hir();
514 let body_id = map.body_owned_by(self.mir_def_id());
515 let body = map.body(body_id);
517 let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] };
518 visitor.visit_body(&body);
520 let mut show_assign_sugg = false;
521 let isnt_initialized = if let InitializationRequiringAction::PartialAssignment
522 | InitializationRequiringAction::Assignment = desired_action
524 // The same error is emitted for bindings that are *sometimes* initialized and the ones
525 // that are *partially* initialized by assigning to a field of an uninitialized
526 // binding. We differentiate between them for more accurate wording here.
527 "isn't fully initialized"
528 } else if !spans.iter().any(|i| {
529 // We filter these to avoid misleading wording in cases like the following,
530 // where `x` has an `init`, but it is in the same place we're looking at:
536 // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs`
541 .any(|sp| span < sp && !sp.contains(span))
543 show_assign_sugg = true;
546 "is possibly-uninitialized"
549 let used = desired_action.as_general_verb_in_past_tense();
551 struct_span_err!(self, span, E0381, "{used} binding {desc}{isnt_initialized}");
552 use_spans.var_path_only_subdiag(&mut err, desired_action);
554 if let InitializationRequiringAction::PartialAssignment
555 | InitializationRequiringAction::Assignment = desired_action
558 "partial initialization isn't supported, fully initialize the binding with a \
559 default value and mutate it, or use `std::mem::MaybeUninit`",
562 err.span_label(span, format!("{path} {used} here but it {isnt_initialized}"));
564 let mut shown = false;
565 for (sp, label) in visitor.errors {
566 if sp < span && !sp.overlaps(span) {
567 // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention
568 // match arms coming after the primary span because they aren't relevant:
572 // _ if { x = 2; true } => {}
577 // _ => {} // We don't want to point to this.
580 err.span_label(sp, &label);
586 if *sp < span && !sp.overlaps(span) {
587 err.span_label(*sp, "binding initialized here in some conditions");
592 err.span_label(decl_span, "binding declared here but left uninitialized");
593 if show_assign_sugg {
596 sugg_span: Option<Span>,
599 impl<'v> Visitor<'v> for LetVisitor {
600 fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
601 if self.sugg_span.is_some() {
604 if let hir::StmtKind::Local(hir::Local {
605 span, ty, init: None, ..
606 }) = &ex.kind && span.contains(self.decl_span) {
607 self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span));
609 hir::intravisit::walk_stmt(self, ex);
613 let mut visitor = LetVisitor { decl_span, sugg_span: None };
614 visitor.visit_body(&body);
615 if let Some(span) = visitor.sugg_span {
616 self.suggest_assign_value(&mut err, moved_place, span);
622 fn suggest_assign_value(
624 err: &mut Diagnostic,
625 moved_place: PlaceRef<'tcx>,
628 let ty = moved_place.ty(self.body, self.infcx.tcx).ty;
629 debug!("ty: {:?}, kind: {:?}", ty, ty.kind());
631 let tcx = self.infcx.tcx;
632 let implements_default = |ty, param_env| {
633 let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else {
636 // Regions are already solved, so we must use a fresh InferCtxt,
637 // but the type has region variables, so erase those.
640 .type_implements_trait(default_trait, [tcx.erase_regions(ty)], param_env)
641 .must_apply_modulo_regions()
644 let assign_value = match ty.kind() {
646 ty::Float(_) => "0.0",
647 ty::Int(_) | ty::Uint(_) => "0",
648 ty::Never | ty::Error(_) => "",
649 ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => "vec![]",
650 ty::Adt(_, _) if implements_default(ty, self.param_env) => "Default::default()",
654 if !assign_value.is_empty() {
655 err.span_suggestion_verbose(
656 sugg_span.shrink_to_hi(),
657 "consider assigning a value",
658 format!(" = {}", assign_value),
659 Applicability::MaybeIncorrect,
664 fn suggest_borrow_fn_like(
666 err: &mut Diagnostic,
668 move_sites: &[MoveSite],
671 let tcx = self.infcx.tcx;
673 // Find out if the predicates show that the type is a Fn or FnMut
674 let find_fn_kind_from_did = |predicates: ty::EarlyBinder<
675 &[(ty::Predicate<'tcx>, Span)],
678 predicates.0.iter().find_map(|(pred, _)| {
679 let pred = if let Some(substs) = substs {
680 predicates.rebind(*pred).subst(tcx, substs).kind().skip_binder()
682 pred.kind().skip_binder()
684 if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred && pred.self_ty() == ty {
685 if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
686 return Some(hir::Mutability::Not);
687 } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
688 return Some(hir::Mutability::Mut);
695 // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
696 // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
697 // These types seem reasonably opaque enough that they could be substituted with their
698 // borrowed variants in a function body when we see a move error.
699 let borrow_level = match ty.kind() {
700 ty::Param(_) => find_fn_kind_from_did(
701 tcx.bound_explicit_predicates_of(self.mir_def_id().to_def_id())
702 .map_bound(|p| p.predicates),
705 ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
706 find_fn_kind_from_did(tcx.bound_explicit_item_bounds(*def_id), Some(*substs))
708 ty::Closure(_, substs) => match substs.as_closure().kind() {
709 ty::ClosureKind::Fn => Some(hir::Mutability::Not),
710 ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
716 let Some(borrow_level) = borrow_level else { return false; };
717 let sugg = move_sites
720 let move_out = self.move_data.moves[(*move_site).moi];
721 let moved_place = &self.move_data.move_paths[move_out.path].place;
722 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
723 let move_span = move_spans.args_or_use();
724 let suggestion = borrow_level.ref_prefix_str().to_owned();
725 (move_span.shrink_to_lo(), suggestion)
728 err.multipart_suggestion_verbose(
729 format!("consider {}borrowing {value_name}", borrow_level.mutably_str()),
731 Applicability::MaybeIncorrect,
736 fn suggest_cloning(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
737 let tcx = self.infcx.tcx;
738 // Try to find predicates on *generic params* that would allow copying `ty`
739 let infcx = tcx.infer_ctxt().build();
741 if let Some(clone_trait_def) = tcx.lang_items().clone_trait()
743 .type_implements_trait(
745 [tcx.erase_regions(ty)],
748 .must_apply_modulo_regions()
750 err.span_suggestion_verbose(
752 "consider cloning the value if the performance cost is acceptable",
754 Applicability::MachineApplicable,
759 fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
760 let tcx = self.infcx.tcx;
761 let generics = tcx.generics_of(self.mir_def_id());
763 let Some(hir_generics) = tcx
764 .typeck_root_def_id(self.mir_def_id().to_def_id())
766 .and_then(|def_id| tcx.hir().get_generics(def_id))
768 // Try to find predicates on *generic params* that would allow copying `ty`
769 let infcx = tcx.infer_ctxt().build();
770 let copy_did = infcx.tcx.require_lang_item(LangItem::Copy, Some(span));
771 let cause = ObligationCause::new(
774 rustc_infer::traits::ObligationCauseCode::MiscObligation,
776 let errors = rustc_trait_selection::traits::fully_solve_bound(
780 // Erase any region vids from the type, which may not be resolved
781 infcx.tcx.erase_regions(ty),
785 // Only emit suggestion if all required predicates are on generic
786 let predicates: Result<Vec<_>, _> = errors
788 .map(|err| match err.obligation.predicate.kind().skip_binder() {
789 PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
790 match predicate.self_ty().kind() {
791 ty::Param(param_ty) => Ok((
792 generics.type_param(param_ty, tcx),
793 predicate.trait_ref.print_only_trait_path().to_string(),
802 if let Ok(predicates) = predicates {
803 suggest_constraining_type_params(
809 .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
814 pub(crate) fn report_move_out_while_borrowed(
817 (place, span): (Place<'tcx>, Span),
818 borrow: &BorrowData<'tcx>,
821 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
822 location, place, span, borrow
824 let value_msg = self.describe_any_place(place.as_ref());
825 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
827 let borrow_spans = self.retrieve_borrow_spans(borrow);
828 let borrow_span = borrow_spans.args_or_use();
830 let move_spans = self.move_spans(place.as_ref(), location);
831 let span = move_spans.args_or_use();
833 let mut err = self.cannot_move_when_borrowed(
836 &self.describe_any_place(place.as_ref()),
841 borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow);
843 move_spans.var_span_label(
845 format!("move occurs due to use{}", move_spans.describe()),
849 self.explain_why_borrow_contains_point(location, borrow, None)
850 .add_explanation_to_diagnostic(
859 self.buffer_error(err);
862 pub(crate) fn report_use_while_mutably_borrowed(
865 (place, _span): (Place<'tcx>, Span),
866 borrow: &BorrowData<'tcx>,
867 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
868 let borrow_spans = self.retrieve_borrow_spans(borrow);
869 let borrow_span = borrow_spans.args_or_use();
871 // Conflicting borrows are reported separately, so only check for move
873 let use_spans = self.move_spans(place.as_ref(), location);
874 let span = use_spans.var_or_use();
876 // 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
877 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
878 let mut err = self.cannot_use_when_mutably_borrowed(
880 &self.describe_any_place(place.as_ref()),
882 &self.describe_any_place(borrow.borrowed_place.as_ref()),
884 borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| {
885 use crate::session_diagnostics::CaptureVarCause::*;
886 let place = &borrow.borrowed_place;
887 let desc_place = self.describe_any_place(place.as_ref());
889 Some(_) => BorrowUsePlaceGenerator { place: desc_place, var_span },
890 None => BorrowUsePlaceClosure { place: desc_place, var_span },
894 self.explain_why_borrow_contains_point(location, borrow, None)
895 .add_explanation_to_diagnostic(
907 pub(crate) fn report_conflicting_borrow(
910 (place, span): (Place<'tcx>, Span),
911 gen_borrow_kind: BorrowKind,
912 issued_borrow: &BorrowData<'tcx>,
913 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
914 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
915 let issued_span = issued_spans.args_or_use();
917 let borrow_spans = self.borrow_spans(span, location);
918 let span = borrow_spans.args_or_use();
920 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
926 let (desc_place, msg_place, msg_borrow, union_type_name) =
927 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
929 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
930 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
932 // FIXME: supply non-"" `opt_via` when appropriate
933 let first_borrow_desc;
934 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
935 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
936 first_borrow_desc = "mutable ";
937 self.cannot_reborrow_already_borrowed(
949 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
950 first_borrow_desc = "immutable ";
951 self.cannot_reborrow_already_borrowed(
964 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
965 first_borrow_desc = "first ";
966 let mut err = self.cannot_mutably_borrow_multiply(
974 self.suggest_split_at_mut_if_applicable(
977 issued_borrow.borrowed_place,
982 (BorrowKind::Unique, BorrowKind::Unique) => {
983 first_borrow_desc = "first ";
984 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
987 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
988 if let Some(immutable_section_description) =
989 self.classify_immutable_section(issued_borrow.assigned_place)
991 let mut err = self.cannot_mutate_in_immutable_section(
995 immutable_section_description,
998 borrow_spans.var_span_label(
1001 "borrow occurs due to use of {}{}",
1003 borrow_spans.describe(),
1010 first_borrow_desc = "immutable ";
1011 self.cannot_reborrow_already_borrowed(
1025 (BorrowKind::Unique, _) => {
1026 first_borrow_desc = "first ";
1027 self.cannot_uniquely_borrow_by_one_closure(
1039 (BorrowKind::Shared, BorrowKind::Unique) => {
1040 first_borrow_desc = "first ";
1041 self.cannot_reborrow_already_uniquely_borrowed(
1054 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
1055 first_borrow_desc = "first ";
1056 self.cannot_reborrow_already_uniquely_borrowed(
1069 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
1071 BorrowKind::Shallow,
1072 BorrowKind::Mut { .. }
1073 | BorrowKind::Unique
1074 | BorrowKind::Shared
1075 | BorrowKind::Shallow,
1076 ) => unreachable!(),
1079 if issued_spans == borrow_spans {
1080 borrow_spans.var_span_label(
1082 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
1083 gen_borrow_kind.describe_mutability(),
1086 let borrow_place = &issued_borrow.borrowed_place;
1087 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
1088 issued_spans.var_span_label(
1091 "first borrow occurs due to use of {}{}",
1093 issued_spans.describe(),
1095 issued_borrow.kind.describe_mutability(),
1098 borrow_spans.var_span_label(
1101 "second borrow occurs due to use of {}{}",
1103 borrow_spans.describe(),
1105 gen_borrow_kind.describe_mutability(),
1109 if union_type_name != "" {
1111 "{} is a field of the union `{}`, so it overlaps the field {}",
1112 msg_place, union_type_name, msg_borrow,
1116 explanation.add_explanation_to_diagnostic(
1123 Some((issued_span, span)),
1126 self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation);
1131 #[instrument(level = "debug", skip(self, err))]
1132 fn suggest_using_local_if_applicable(
1134 err: &mut Diagnostic,
1136 issued_borrow: &BorrowData<'tcx>,
1137 explanation: BorrowExplanation<'tcx>,
1139 let used_in_call = matches!(
1141 BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _)
1144 debug!("not later used in call");
1149 if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation {
1155 let outer_call_loc =
1156 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
1159 issued_borrow.reserve_location
1161 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
1163 let inner_param_location = location;
1164 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
1165 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
1168 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
1170 "`inner_param_location` {:?} is not for an assignment: {:?}",
1171 inner_param_location, inner_param_stmt
1175 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
1176 let Some((inner_call_loc, inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
1177 let Either::Right(term) = self.body.stmt_at(loc) else {
1178 debug!("{:?} is a statement, so it can't be a call", loc);
1181 let TerminatorKind::Call { args, .. } = &term.kind else {
1182 debug!("not a call: {:?}", term);
1185 debug!("checking call args for uses of inner_param: {:?}", args);
1186 if args.contains(&Operand::Move(inner_param)) {
1192 debug!("no uses of inner_param found as a by-move call arg");
1195 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
1197 let inner_call_span = inner_call_term.source_info.span;
1198 let outer_call_span = match use_span {
1200 None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span,
1202 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
1203 // FIXME: This stops the suggestion in some cases where it should be emitted.
1204 // Fix the spans for those cases so it's emitted correctly.
1206 "outer span {:?} does not strictly contain inner span {:?}",
1207 outer_call_span, inner_call_span
1214 "try adding a local storing this{}...",
1215 if use_span.is_some() { "" } else { " argument" }
1221 "...and then using that local {}",
1222 if use_span.is_some() { "here" } else { "as the argument to this call" }
1227 fn suggest_split_at_mut_if_applicable(
1229 err: &mut Diagnostic,
1231 borrowed_place: Place<'tcx>,
1233 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
1234 (&place.projection[..], &borrowed_place.projection[..])
1237 "consider using `.split_at_mut(position)` or similar method to obtain \
1238 two mutable non-overlapping sub-slices",
1243 /// Returns the description of the root place for a conflicting borrow and the full
1244 /// descriptions of the places that caused the conflict.
1246 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
1247 /// attempted while a shared borrow is live, then this function will return:
1252 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
1253 /// a shared borrow of another field `x.y`, then this function will return:
1255 /// ("x", "x.z", "x.y")
1258 /// In the more complex union case, where the union is a field of a struct, then if a mutable
1259 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
1260 /// another field `x.u.y`, then this function will return:
1262 /// ("x.u", "x.u.z", "x.u.y")
1265 /// This is used when creating error messages like below:
1268 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
1269 /// mutable (via `a.u.s.b`) [E0502]
1271 pub(crate) fn describe_place_for_conflicting_borrow(
1273 first_borrowed_place: Place<'tcx>,
1274 second_borrowed_place: Place<'tcx>,
1275 ) -> (String, String, String, String) {
1276 // Define a small closure that we can use to check if the type of a place
1278 let union_ty = |place_base| {
1279 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
1280 // using a type annotation in the closure argument instead leads to a lifetime error.
1281 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
1282 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
1285 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
1286 // code duplication (particularly around returning an empty description in the failure
1290 // If we have a conflicting borrow of the same place, then we don't want to add
1291 // an extraneous "via x.y" to our diagnostics, so filter out this case.
1292 first_borrowed_place != second_borrowed_place
1295 // We're going to want to traverse the first borrowed place to see if we can find
1296 // field access to a union. If we find that, then we will keep the place of the
1297 // union being accessed and the field that was being accessed so we can check the
1298 // second borrowed place for the same union and an access to a different field.
1299 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
1301 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
1302 return Some((place_base, field));
1309 .and_then(|(target_base, target_field)| {
1310 // With the place of a union and a field access into it, we traverse the second
1311 // borrowed place and look for an access to a different field of the same union.
1312 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
1313 if let ProjectionElem::Field(field, _) = elem {
1314 if let Some(union_ty) = union_ty(place_base) {
1315 if field != target_field && place_base == target_base {
1317 self.describe_any_place(place_base),
1318 self.describe_any_place(first_borrowed_place.as_ref()),
1319 self.describe_any_place(second_borrowed_place.as_ref()),
1320 union_ty.to_string(),
1328 .unwrap_or_else(|| {
1329 // If we didn't find a field access into a union, or both places match, then
1330 // only return the description of the first place.
1332 self.describe_any_place(first_borrowed_place.as_ref()),
1340 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
1342 /// This means that some data referenced by `borrow` needs to live
1343 /// past the point where the StorageDeadOrDrop of `place` occurs.
1344 /// This is usually interpreted as meaning that `place` has too
1345 /// short a lifetime. (But sometimes it is more useful to report
1346 /// it as a more direct conflict between the execution of a
1347 /// `Drop::drop` with an aliasing borrow.)
1348 #[instrument(level = "debug", skip(self))]
1349 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
1352 borrow: &BorrowData<'tcx>,
1353 place_span: (Place<'tcx>, Span),
1354 kind: Option<WriteKind>,
1356 let drop_span = place_span.1;
1358 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1360 let borrow_spans = self.retrieve_borrow_spans(borrow);
1361 let borrow_span = borrow_spans.var_or_use_path_span();
1363 assert!(root_place.projection.is_empty());
1364 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1366 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1368 if self.access_place_error_reported.contains(&(
1369 Place { local: root_place.local, projection: root_place_projection },
1373 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1379 self.access_place_error_reported.insert((
1380 Place { local: root_place.local, projection: root_place_projection },
1384 let borrowed_local = borrow.borrowed_place.local;
1385 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1387 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1388 self.buffer_error(err);
1392 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1393 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1395 // If a borrow of path `B` conflicts with drop of `D` (and
1396 // we're not in the uninteresting case where `B` is a
1397 // prefix of `D`), then report this as a more interesting
1398 // destructor conflict.
1399 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1400 self.report_borrow_conflicts_with_destructor(
1401 location, borrow, place_span, kind, dropped_ty,
1407 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1409 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1410 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1412 debug!(?place_desc, ?explanation);
1414 let err = match (place_desc, explanation) {
1415 // If the outlives constraint comes from inside the closure,
1420 // Box::new(|| y) as Box<Fn() -> &'static i32>
1422 // then just use the normal error. The closure isn't escaping
1423 // and `move` will not help here.
1426 BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _),
1427 ) => self.report_escaping_closure_capture(
1431 name: self.synthesize_region_name(),
1432 source: RegionNameSource::Static,
1434 ConstraintCategory::CallArgument(None),
1436 &format!("`{}`", name),
1441 BorrowExplanation::MustBeValidFor {
1443 category @ (ConstraintCategory::Return(_)
1444 | ConstraintCategory::CallArgument(_)
1445 | ConstraintCategory::OpaqueType),
1446 from_closure: false,
1451 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1452 .report_escaping_closure_capture(
1458 &format!("`{}`", name),
1463 BorrowExplanation::MustBeValidFor {
1464 category: ConstraintCategory::Assignment,
1465 from_closure: false,
1468 source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
1474 ) => self.report_escaping_data(borrow_span, &name, upvar_span, upvar_name, span),
1475 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1483 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1493 self.buffer_error(err);
1496 fn report_local_value_does_not_live_long_enough(
1500 borrow: &BorrowData<'tcx>,
1502 borrow_spans: UseSpans<'tcx>,
1503 explanation: BorrowExplanation<'tcx>,
1504 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1506 "report_local_value_does_not_live_long_enough(\
1507 {:?}, {:?}, {:?}, {:?}, {:?}\
1509 location, name, borrow, drop_span, borrow_spans
1512 let borrow_span = borrow_spans.var_or_use_path_span();
1513 if let BorrowExplanation::MustBeValidFor {
1517 from_closure: false,
1521 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1526 opt_place_desc.as_ref(),
1532 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1534 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1535 let region_name = annotation.emit(self, &mut err);
1539 format!("`{}` would have to be valid for `{}`...", name, region_name),
1542 let fn_hir_id = self.mir_hir_id();
1546 "...but `{}` will be dropped here, when the {} returns",
1551 .opt_name(fn_hir_id)
1552 .map(|name| format!("function `{}`", name))
1553 .unwrap_or_else(|| {
1557 .typeck(self.mir_def_id())
1558 .node_type(fn_hir_id)
1561 ty::Closure(..) => "enclosing closure",
1562 ty::Generator(..) => "enclosing generator",
1563 kind => bug!("expected closure or generator, found {:?}", kind),
1571 "functions cannot return a borrow to data owned within the function's scope, \
1572 functions can only return borrows to data passed as arguments",
1575 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1576 references-and-borrowing.html#dangling-references>",
1579 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1581 explanation.add_explanation_to_diagnostic(
1592 err.span_label(borrow_span, "borrowed value does not live long enough");
1593 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1595 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1597 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1599 explanation.add_explanation_to_diagnostic(
1613 fn report_borrow_conflicts_with_destructor(
1616 borrow: &BorrowData<'tcx>,
1617 (place, drop_span): (Place<'tcx>, Span),
1618 kind: Option<WriteKind>,
1619 dropped_ty: Ty<'tcx>,
1622 "report_borrow_conflicts_with_destructor(\
1623 {:?}, {:?}, ({:?}, {:?}), {:?}\
1625 location, borrow, place, drop_span, kind,
1628 let borrow_spans = self.retrieve_borrow_spans(borrow);
1629 let borrow_span = borrow_spans.var_or_use();
1631 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1633 let what_was_dropped = match self.describe_place(place.as_ref()) {
1634 Some(name) => format!("`{}`", name),
1635 None => String::from("temporary value"),
1638 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1639 Some(borrowed) => format!(
1640 "here, drop of {D} needs exclusive access to `{B}`, \
1641 because the type `{T}` implements the `Drop` trait",
1642 D = what_was_dropped,
1647 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1648 D = what_was_dropped,
1652 err.span_label(drop_span, label);
1654 // Only give this note and suggestion if they could be relevant.
1656 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1658 BorrowExplanation::UsedLater { .. }
1659 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1660 err.note("consider using a `let` binding to create a longer lived value");
1665 explanation.add_explanation_to_diagnostic(
1675 self.buffer_error(err);
1678 fn report_thread_local_value_does_not_live_long_enough(
1682 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1684 "report_thread_local_value_does_not_live_long_enough(\
1687 drop_span, borrow_span
1690 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1694 "thread-local variables cannot be borrowed beyond the end of the function",
1696 err.span_label(drop_span, "end of enclosing function is here");
1701 #[instrument(level = "debug", skip(self))]
1702 fn report_temporary_value_does_not_live_long_enough(
1705 borrow: &BorrowData<'tcx>,
1707 borrow_spans: UseSpans<'tcx>,
1709 explanation: BorrowExplanation<'tcx>,
1710 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1711 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1714 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1725 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1726 err.span_label(proper_span, "creates a temporary value which is freed while still in use");
1727 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1730 BorrowExplanation::UsedLater(..)
1731 | BorrowExplanation::UsedLaterInLoop(..)
1732 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1733 // Only give this note and suggestion if it could be relevant.
1734 let sm = self.infcx.tcx.sess.source_map();
1735 let mut suggested = false;
1736 let msg = "consider using a `let` binding to create a longer lived value";
1738 /// We check that there's a single level of block nesting to ensure always correct
1739 /// suggestions. If we don't, then we only provide a free-form message to avoid
1740 /// misleading users in cases like `tests/ui/nll/borrowed-temporary-error.rs`.
1741 /// We could expand the analysis to suggest hoising all of the relevant parts of
1742 /// the users' code to make the code compile, but that could be too much.
1743 struct NestedStatementVisitor {
1749 impl<'tcx> Visitor<'tcx> for NestedStatementVisitor {
1750 fn visit_block(&mut self, block: &hir::Block<'tcx>) {
1752 walk_block(self, block);
1755 fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) {
1756 if self.span == expr.span {
1757 self.found = self.current;
1759 walk_expr(self, expr);
1762 let source_info = self.body.source_info(location);
1763 if let Some(scope) = self.body.source_scopes.get(source_info.scope)
1764 && let ClearCrossCrate::Set(scope_data) = &scope.local_data
1765 && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root)
1766 && let Some(id) = node.body_id()
1767 && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind
1769 for stmt in block.stmts {
1770 let mut visitor = NestedStatementVisitor {
1775 visitor.visit_stmt(stmt);
1776 if visitor.found == 0
1777 && stmt.span.contains(proper_span)
1778 && let Some(p) = sm.span_to_margin(stmt.span)
1779 && let Ok(s) = sm.span_to_snippet(proper_span)
1781 let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
1782 err.multipart_suggestion_verbose(
1785 (stmt.span.shrink_to_lo(), addition),
1786 (proper_span, "binding".to_string()),
1788 Applicability::MaybeIncorrect,
1801 explanation.add_explanation_to_diagnostic(
1811 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1813 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1818 fn try_report_cannot_return_reference_to_local(
1820 borrow: &BorrowData<'tcx>,
1823 category: ConstraintCategory<'tcx>,
1824 opt_place_desc: Option<&String>,
1825 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1826 let return_kind = match category {
1827 ConstraintCategory::Return(_) => "return",
1828 ConstraintCategory::Yield => "yield",
1832 // FIXME use a better heuristic than Spans
1833 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1839 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1840 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1841 match self.body.local_kind(local) {
1842 LocalKind::ReturnPointer | LocalKind::Temp => {
1843 bug!("temporary or return pointer with a name")
1845 LocalKind::Var => "local variable ",
1847 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1849 "variable captured by `move` "
1851 LocalKind::Arg => "function parameter ",
1857 format!("{}`{}`", local_kind, place_desc),
1858 format!("`{}` is borrowed here", place_desc),
1862 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1863 let local = root_place.local;
1864 match self.body.local_kind(local) {
1865 LocalKind::ReturnPointer | LocalKind::Temp => {
1866 ("temporary value".to_string(), "temporary value created here".to_string())
1869 "function parameter".to_string(),
1870 "function parameter borrowed here".to_string(),
1873 ("local binding".to_string(), "local binding introduced here".to_string())
1878 let mut err = self.cannot_return_reference_to_local(
1885 if return_span != borrow_span {
1886 err.span_label(borrow_span, note);
1888 let tcx = self.infcx.tcx;
1890 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1891 let return_ty = tcx.erase_regions(return_ty);
1894 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator)
1897 .type_implements_trait(iter_trait, [return_ty], self.param_env)
1898 .must_apply_modulo_regions()
1900 err.span_suggestion_hidden(
1901 return_span.shrink_to_hi(),
1902 "use `.collect()` to allocate the iterator",
1903 ".collect::<Vec<_>>()",
1904 Applicability::MaybeIncorrect,
1912 #[instrument(level = "debug", skip(self))]
1913 fn report_escaping_closure_capture(
1915 use_span: UseSpans<'tcx>,
1917 fr_name: &RegionName,
1918 category: ConstraintCategory<'tcx>,
1919 constraint_span: Span,
1922 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1923 let tcx = self.infcx.tcx;
1924 let args_span = use_span.args_or_use();
1926 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1928 if string.starts_with("async ") {
1929 let pos = args_span.lo() + BytePos(6);
1930 (args_span.with_lo(pos).with_hi(pos), "move ")
1931 } else if string.starts_with("async|") {
1932 let pos = args_span.lo() + BytePos(5);
1933 (args_span.with_lo(pos).with_hi(pos), " move")
1935 (args_span.shrink_to_lo(), "move ")
1938 Err(_) => (args_span, "move |<args>| <body>"),
1940 let kind = match use_span.generator_kind() {
1941 Some(generator_kind) => match generator_kind {
1942 GeneratorKind::Async(async_kind) => match async_kind {
1943 AsyncGeneratorKind::Block => "async block",
1944 AsyncGeneratorKind::Closure => "async closure",
1945 _ => bug!("async block/closure expected, but async function found."),
1947 GeneratorKind::Gen => "generator",
1952 let mut err = self.cannot_capture_in_long_lived_closure(
1959 err.span_suggestion_verbose(
1962 "to force the {} to take ownership of {} (and any \
1963 other referenced variables), use the `move` keyword",
1967 Applicability::MachineApplicable,
1971 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1972 let msg = format!("{} is returned here", kind);
1973 err.span_note(constraint_span, &msg);
1975 ConstraintCategory::CallArgument(_) => {
1976 fr_name.highlight_region_name(&mut err);
1977 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1979 "async blocks are not executed immediately and must either take a \
1980 reference or ownership of outside variables they use",
1983 let msg = format!("{scope} requires argument type to outlive `{fr_name}`");
1984 err.span_note(constraint_span, &msg);
1988 "report_escaping_closure_capture called with unexpected constraint \
1997 fn report_escaping_data(
2000 name: &Option<String>,
2004 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
2005 let tcx = self.infcx.tcx;
2007 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
2010 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
2014 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
2017 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
2019 if let Some(name) = name {
2022 format!("reference to `{}` escapes the {} body here", name, escapes_from),
2027 format!("reference escapes the {} body here", escapes_from),
2034 fn get_moved_indexes(
2038 ) -> (Vec<MoveSite>, Vec<Location>) {
2039 fn predecessor_locations<'tcx, 'a>(
2040 body: &'a mir::Body<'tcx>,
2042 ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
2043 if location.statement_index == 0 {
2044 let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
2045 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
2047 Either::Right(std::iter::once(Location {
2048 statement_index: location.statement_index - 1,
2054 let mut mpis = vec![mpi];
2055 let move_paths = &self.move_data.move_paths;
2056 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
2058 let mut stack = Vec::new();
2059 let mut back_edge_stack = Vec::new();
2061 predecessor_locations(self.body, location).for_each(|predecessor| {
2062 if location.dominates(predecessor, &self.dominators) {
2063 back_edge_stack.push(predecessor)
2065 stack.push(predecessor);
2069 let mut reached_start = false;
2071 /* Check if the mpi is initialized as an argument */
2072 let mut is_argument = false;
2073 for arg in self.body.args_iter() {
2074 let path = self.move_data.rev_lookup.find_local(arg);
2075 if mpis.contains(&path) {
2080 let mut visited = FxHashSet::default();
2081 let mut move_locations = FxHashSet::default();
2082 let mut reinits = vec![];
2083 let mut result = vec![];
2085 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
2087 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
2088 location, is_back_edge
2091 if !visited.insert(location) {
2097 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
2098 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
2099 // this analysis only tries to find moves explicitly
2100 // written by the user, so we ignore the move-outs
2101 // created by `StorageDead` and at the beginning
2104 // If we are found a use of a.b.c which was in error, then we want to look for
2105 // moves not only of a.b.c but also a.b and a.
2107 // Note that the moves data already includes "parent" paths, so we don't have to
2108 // worry about the other case: that is, if there is a move of a.b.c, it is already
2109 // marked as a move of a.b and a as well, so we will generate the correct errors
2111 for moi in &self.move_data.loc_map[location] {
2112 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
2113 let path = self.move_data.moves[*moi].path;
2114 if mpis.contains(&path) {
2116 "report_use_of_moved_or_uninitialized: found {:?}",
2117 move_paths[path].place
2119 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
2120 move_locations.insert(location);
2122 // Strictly speaking, we could continue our DFS here. There may be
2123 // other moves that can reach the point of error. But it is kind of
2124 // confusing to highlight them.
2132 // drop(a); // <-- current point of error
2135 // Because we stop the DFS here, we only highlight `let c = a`,
2136 // and not `let b = a`. We will of course also report an error at
2137 // `let c = a` which highlights `let b = a` as the move.
2144 let mut any_match = false;
2145 for ii in &self.move_data.init_loc_map[location] {
2146 let init = self.move_data.inits[*ii];
2148 InitKind::Deep | InitKind::NonPanicPathOnly => {
2149 if mpis.contains(&init.path) {
2153 InitKind::Shallow => {
2154 if mpi == init.path {
2161 reinits.push(location);
2167 while let Some(location) = stack.pop() {
2168 if dfs_iter(&mut result, location, false) {
2172 let mut has_predecessor = false;
2173 predecessor_locations(self.body, location).for_each(|predecessor| {
2174 if location.dominates(predecessor, &self.dominators) {
2175 back_edge_stack.push(predecessor)
2177 stack.push(predecessor);
2179 has_predecessor = true;
2182 if !has_predecessor {
2183 reached_start = true;
2186 if (is_argument || !reached_start) && result.is_empty() {
2187 /* Process back edges (moves in future loop iterations) only if
2188 the move path is definitely initialized upon loop entry,
2189 to avoid spurious "in previous iteration" errors.
2190 During DFS, if there's a path from the error back to the start
2191 of the function with no intervening init or move, then the
2192 move path may be uninitialized at loop entry.
2194 while let Some(location) = back_edge_stack.pop() {
2195 if dfs_iter(&mut result, location, true) {
2199 predecessor_locations(self.body, location)
2200 .for_each(|predecessor| back_edge_stack.push(predecessor));
2204 // Check if we can reach these reinits from a move location.
2205 let reinits_reachable = reinits
2208 let mut visited = FxHashSet::default();
2209 let mut stack = vec![*reinit];
2210 while let Some(location) = stack.pop() {
2211 if !visited.insert(location) {
2214 if move_locations.contains(&location) {
2217 stack.extend(predecessor_locations(self.body, location));
2221 .collect::<Vec<Location>>();
2222 (result, reinits_reachable)
2225 pub(crate) fn report_illegal_mutation_of_borrowed(
2228 (place, span): (Place<'tcx>, Span),
2229 loan: &BorrowData<'tcx>,
2231 let loan_spans = self.retrieve_borrow_spans(loan);
2232 let loan_span = loan_spans.args_or_use();
2234 let descr_place = self.describe_any_place(place.as_ref());
2235 if loan.kind == BorrowKind::Shallow {
2236 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
2237 let mut err = self.cannot_mutate_in_immutable_section(
2244 loan_spans.var_span_label(
2246 format!("borrow occurs due to use{}", loan_spans.describe()),
2247 loan.kind.describe_mutability(),
2250 self.buffer_error(err);
2256 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
2258 loan_spans.var_span_label(
2260 format!("borrow occurs due to use{}", loan_spans.describe()),
2261 loan.kind.describe_mutability(),
2264 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
2274 self.explain_deref_coercion(loan, &mut err);
2276 self.buffer_error(err);
2279 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
2280 let tcx = self.infcx.tcx;
2282 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
2283 Some((method_did, method_substs)),
2285 &self.body[loan.reserve_location.block].terminator,
2286 rustc_const_eval::util::find_self_call(
2289 loan.assigned_place.local,
2290 loan.reserve_location.block,
2293 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
2295 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
2296 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
2299 if let Some(Ok(instance)) = deref_target {
2300 let deref_target_ty = instance.ty(tcx, self.param_env);
2302 "borrow occurs due to deref coercion to `{}`",
2305 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
2311 /// Reports an illegal reassignment; for example, an assignment to
2312 /// (part of) a non-`mut` local that occurs potentially after that
2313 /// local has already been initialized. `place` is the path being
2314 /// assigned; `err_place` is a place providing a reason why
2315 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
2316 /// assignment to `x.f`).
2317 pub(crate) fn report_illegal_reassignment(
2319 _location: Location,
2320 (place, span): (Place<'tcx>, Span),
2321 assigned_span: Span,
2322 err_place: Place<'tcx>,
2324 let (from_arg, local_decl, local_name) = match err_place.as_local() {
2326 self.body.local_kind(local) == LocalKind::Arg,
2327 Some(&self.body.local_decls[local]),
2328 self.local_names[local],
2330 None => (false, None, None),
2333 // If root local is initialized immediately (everything apart from let
2334 // PATTERN;) then make the error refer to that local, rather than the
2335 // place being assigned later.
2336 let (place_description, assigned_span) = match local_decl {
2339 Some(box LocalInfo::User(
2340 ClearCrossCrate::Clear
2341 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
2342 opt_match_place: None,
2346 | Some(box LocalInfo::StaticRef { .. })
2350 | None => (self.describe_any_place(place.as_ref()), assigned_span),
2351 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
2354 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
2355 let msg = if from_arg {
2356 "cannot assign to immutable argument"
2358 "cannot assign twice to immutable variable"
2360 if span != assigned_span && !from_arg {
2361 err.span_label(assigned_span, format!("first assignment to {}", place_description));
2363 if let Some(decl) = local_decl
2364 && let Some(name) = local_name
2365 && decl.can_be_made_mutable()
2367 err.span_suggestion(
2368 decl.source_info.span,
2369 "consider making this binding mutable",
2370 format!("mut {}", name),
2371 Applicability::MachineApplicable,
2374 err.span_label(span, msg);
2375 self.buffer_error(err);
2378 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
2379 let tcx = self.infcx.tcx;
2380 let (kind, _place_ty) = place.projection.iter().fold(
2381 (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)),
2382 |(kind, place_ty), &elem| {
2385 ProjectionElem::Deref => match kind {
2386 StorageDeadOrDrop::LocalStorageDead
2387 | StorageDeadOrDrop::BoxedStorageDead => {
2389 place_ty.ty.is_box(),
2390 "Drop of value behind a reference or raw pointer"
2392 StorageDeadOrDrop::BoxedStorageDead
2394 StorageDeadOrDrop::Destructor(_) => kind,
2396 ProjectionElem::OpaqueCast { .. }
2397 | ProjectionElem::Field(..)
2398 | ProjectionElem::Downcast(..) => {
2399 match place_ty.ty.kind() {
2400 ty::Adt(def, _) if def.has_dtor(tcx) => {
2401 // Report the outermost adt with a destructor
2403 StorageDeadOrDrop::Destructor(_) => kind,
2404 StorageDeadOrDrop::LocalStorageDead
2405 | StorageDeadOrDrop::BoxedStorageDead => {
2406 StorageDeadOrDrop::Destructor(place_ty.ty)
2413 ProjectionElem::ConstantIndex { .. }
2414 | ProjectionElem::Subslice { .. }
2415 | ProjectionElem::Index(_) => kind,
2417 place_ty.projection_ty(tcx, elem),
2424 /// Describe the reason for the fake borrow that was assigned to `place`.
2425 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
2426 use rustc_middle::mir::visit::Visitor;
2427 struct FakeReadCauseFinder<'tcx> {
2429 cause: Option<FakeReadCause>,
2431 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
2432 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
2434 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
2435 if *place == self.place =>
2437 self.cause = Some(*cause);
2443 let mut visitor = FakeReadCauseFinder { place, cause: None };
2444 visitor.visit_body(&self.body);
2445 match visitor.cause {
2446 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2447 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2452 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2453 /// borrow of local value that does not live long enough.
2454 fn annotate_argument_and_return_for_borrow(
2456 borrow: &BorrowData<'tcx>,
2457 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2458 // Define a fallback for when we can't match a closure.
2460 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2464 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2466 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2468 self.infcx.tcx.fn_sig(self.mir_def_id()),
2475 // In order to determine whether we need to annotate, we need to check whether the reserve
2476 // place was an assignment into a temporary.
2478 // If it was, we check whether or not that temporary is eventually assigned into the return
2479 // place. If it was, we can add annotations about the function's return type and arguments
2480 // and it'll make sense.
2481 let location = borrow.reserve_location;
2482 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2483 if let Some(Statement { kind: StatementKind::Assign(box (reservation, _)), .. }) =
2484 &self.body[location.block].statements.get(location.statement_index)
2486 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2487 // Check that the initial assignment of the reserve location is into a temporary.
2488 let mut target = match reservation.as_local() {
2489 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2493 // Next, look through the rest of the block, checking if we are assigning the
2494 // `target` (that is, the place that contains our borrow) to anything.
2495 let mut annotated_closure = None;
2496 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2498 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2501 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2502 if let Some(assigned_to) = place.as_local() {
2504 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2508 // Check if our `target` was captured by a closure.
2509 if let Rvalue::Aggregate(
2510 box AggregateKind::Closure(def_id, substs),
2514 for operand in operands {
2515 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2519 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2523 // Find the local from the operand.
2524 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2528 if assigned_from_local != target {
2532 // If a closure captured our `target` and then assigned
2533 // into a place then we should annotate the closure in
2534 // case it ends up being assigned into the return place.
2536 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2538 "annotate_argument_and_return_for_borrow: \
2539 annotated_closure={:?} assigned_from_local={:?} \
2541 annotated_closure, assigned_from_local, assigned_to
2544 if assigned_to == mir::RETURN_PLACE {
2545 // If it was assigned directly into the return place, then
2547 return annotated_closure;
2549 // Otherwise, update the target.
2550 target = assigned_to;
2554 // If none of our closure's operands matched, then skip to the next
2559 // Otherwise, look at other types of assignment.
2560 let assigned_from = match rvalue {
2561 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2562 Rvalue::Use(operand) => match operand {
2563 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2571 "annotate_argument_and_return_for_borrow: \
2572 assigned_from={:?}",
2576 // Find the local from the rvalue.
2577 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2579 "annotate_argument_and_return_for_borrow: \
2580 assigned_from_local={:?}",
2581 assigned_from_local,
2584 // Check if our local matches the target - if so, we've assigned our
2585 // borrow to a new place.
2586 if assigned_from_local != target {
2590 // If we assigned our `target` into a new place, then we should
2591 // check if it was the return place.
2593 "annotate_argument_and_return_for_borrow: \
2594 assigned_from_local={:?} assigned_to={:?}",
2595 assigned_from_local, assigned_to
2597 if assigned_to == mir::RETURN_PLACE {
2598 // If it was then return the annotated closure if there was one,
2599 // else, annotate this function.
2600 return annotated_closure.or_else(fallback);
2603 // If we didn't assign into the return place, then we just update
2605 target = assigned_to;
2610 // Check the terminator if we didn't find anything in the statements.
2611 let terminator = &self.body[location.block].terminator();
2613 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2616 if let TerminatorKind::Call { destination, target: Some(_), args, .. } =
2619 if let Some(assigned_to) = destination.as_local() {
2621 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2624 for operand in args {
2625 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2629 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2633 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2635 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2636 assigned_from_local,
2639 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2640 return annotated_closure.or_else(fallback);
2648 // If we haven't found an assignment into the return place, then we need not add
2650 debug!("annotate_argument_and_return_for_borrow: none found");
2654 /// Annotate the first argument and return type of a function signature if they are
2659 sig: ty::PolyFnSig<'tcx>,
2660 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2661 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2662 let is_closure = self.infcx.tcx.is_closure(did.to_def_id());
2663 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
2664 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2666 // We need to work out which arguments to highlight. We do this by looking
2667 // at the return type, where there are three cases:
2669 // 1. If there are named arguments, then we should highlight the return type and
2670 // highlight any of the arguments that are also references with that lifetime.
2671 // If there are no arguments that have the same lifetime as the return type,
2672 // then don't highlight anything.
2673 // 2. The return type is a reference with an anonymous lifetime. If this is
2674 // the case, then we can take advantage of (and teach) the lifetime elision
2677 // We know that an error is being reported. So the arguments and return type
2678 // must satisfy the elision rules. Therefore, if there is a single argument
2679 // then that means the return type and first (and only) argument have the same
2680 // lifetime and the borrow isn't meeting that, we can highlight the argument
2683 // If there are multiple arguments then the first argument must be self (else
2684 // it would not satisfy the elision rules), so we can highlight self and the
2686 // 3. The return type is not a reference. In this case, we don't highlight
2688 let return_ty = sig.output();
2689 match return_ty.skip_binder().kind() {
2690 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2691 // This is case 1 from above, return type is a named reference so we need to
2692 // search for relevant arguments.
2693 let mut arguments = Vec::new();
2694 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2695 if let ty::Ref(argument_region, _, _) = argument.kind() {
2696 if argument_region == return_region {
2697 // Need to use the `rustc_middle::ty` types to compare against the
2698 // `return_region`. Then use the `rustc_hir` type to get only
2699 // the lifetime span.
2700 if let hir::TyKind::Ref(lifetime, _) = &fn_decl.inputs[index].kind {
2701 // With access to the lifetime, we can get
2703 arguments.push((*argument, lifetime.ident.span));
2705 bug!("ty type is a ref but hir type is not");
2711 // We need to have arguments. This shouldn't happen, but it's worth checking.
2712 if arguments.is_empty() {
2716 // We use a mix of the HIR and the Ty types to get information
2717 // as the HIR doesn't have full types for closure arguments.
2718 let return_ty = sig.output().skip_binder();
2719 let mut return_span = fn_decl.output.span();
2720 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2721 if let hir::TyKind::Ref(lifetime, _) = ty.kind {
2722 return_span = lifetime.ident.span;
2726 Some(AnnotatedBorrowFnSignature::NamedFunction {
2732 ty::Ref(_, _, _) if is_closure => {
2733 // This is case 2 from above but only for closures, return type is anonymous
2734 // reference so we select
2735 // the first argument.
2736 let argument_span = fn_decl.inputs.first()?.span;
2737 let argument_ty = sig.inputs().skip_binder().first()?;
2739 // Closure arguments are wrapped in a tuple, so we need to get the first
2741 if let ty::Tuple(elems) = argument_ty.kind() {
2742 let &argument_ty = elems.first()?;
2743 if let ty::Ref(_, _, _) = argument_ty.kind() {
2744 return Some(AnnotatedBorrowFnSignature::Closure {
2753 ty::Ref(_, _, _) => {
2754 // This is also case 2 from above but for functions, return type is still an
2755 // anonymous reference so we select the first argument.
2756 let argument_span = fn_decl.inputs.first()?.span;
2757 let argument_ty = *sig.inputs().skip_binder().first()?;
2759 let return_span = fn_decl.output.span();
2760 let return_ty = sig.output().skip_binder();
2762 // We expect the first argument to be a reference.
2763 match argument_ty.kind() {
2764 ty::Ref(_, _, _) => {}
2768 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2776 // This is case 3 from above, return type is not a reference so don't highlight
2785 enum AnnotatedBorrowFnSignature<'tcx> {
2787 arguments: Vec<(Ty<'tcx>, Span)>,
2788 return_ty: Ty<'tcx>,
2792 argument_ty: Ty<'tcx>,
2793 argument_span: Span,
2794 return_ty: Ty<'tcx>,
2798 argument_ty: Ty<'tcx>,
2799 argument_span: Span,
2803 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2804 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2806 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2808 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2811 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2814 cx.get_region_name_for_ty(argument_ty, 0)
2816 &AnnotatedBorrowFnSignature::AnonymousFunction {
2822 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2823 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2825 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2826 let types_equal = return_ty_name == argument_ty_name;
2831 if types_equal { "also " } else { "" },
2837 "argument and return type have the same lifetime due to lifetime elision rules",
2840 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2841 lifetime-syntax.html#lifetime-elision>",
2844 cx.get_region_name_for_ty(return_ty, 0)
2846 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2847 // Region of return type and arguments checked to be the same earlier.
2848 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2849 for (_, argument_span) in arguments {
2850 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2853 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2856 "use data from the highlighted arguments which match the `{}` lifetime of \
2867 /// Detect whether one of the provided spans is a statement nested within the top-most visited expr
2868 struct ReferencedStatementsVisitor<'a>(&'a [Span], bool);
2870 impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> {
2871 fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
2873 hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => {
2881 /// Given a set of spans representing statements initializing the relevant binding, visit all the
2882 /// function expressions looking for branching code paths that *do not* initialize the binding.
2883 struct ConditionVisitor<'b> {
2886 errors: Vec<(Span, String)>,
2889 impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
2890 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
2892 hir::ExprKind::If(cond, body, None) => {
2893 // `if` expressions with no `else` that initialize the binding might be missing an
2895 let mut v = ReferencedStatementsVisitor(self.spans, false);
2901 "if this `if` condition is `false`, {} is not initialized",
2906 ex.span.shrink_to_hi(),
2907 format!("an `else` arm might be missing here, initializing {}", self.name),
2911 hir::ExprKind::If(cond, body, Some(other)) => {
2912 // `if` expressions where the binding is only initialized in one of the two arms
2913 // might be missing a binding initialization.
2914 let mut a = ReferencedStatementsVisitor(self.spans, false);
2916 let mut b = ReferencedStatementsVisitor(self.spans, false);
2917 b.visit_expr(other);
2919 (true, true) | (false, false) => {}
2921 if other.span.is_desugaring(DesugaringKind::WhileLoop) {
2925 "if this condition isn't met and the `while` loop runs 0 \
2926 times, {} is not initialized",
2932 body.span.shrink_to_hi().until(other.span),
2934 "if the `if` condition is `false` and this `else` arm is \
2935 executed, {} is not initialized",
2945 "if this condition is `true`, {} is not initialized",
2952 hir::ExprKind::Match(e, arms, loop_desugar) => {
2953 // If the binding is initialized in one of the match arms, then the other match
2954 // arms might be missing an initialization.
2955 let results: Vec<bool> = arms
2958 let mut v = ReferencedStatementsVisitor(self.spans, false);
2963 if results.iter().any(|x| *x) && !results.iter().all(|x| *x) {
2964 for (arm, seen) in arms.iter().zip(results) {
2966 if loop_desugar == hir::MatchSource::ForLoopDesugar {
2970 "if the `for` loop runs 0 times, {} is not initialized",
2974 } else if let Some(guard) = &arm.guard {
2976 arm.pat.span.to(guard.body().span),
2978 "if this pattern and condition are matched, {} is not \
2987 "if this pattern is matched, {} is not initialized",
2996 // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should
2997 // also be accounted for. For now it is fine, as if we don't find *any* relevant
2998 // branching code paths, we point at the places where the binding *is* initialized for
3002 walk_expr(self, ex);