about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2023-04-17 18:13:33 +0200
committerGitHub <noreply@github.com>2023-04-17 18:13:33 +0200
commit91fe1174185c6914c4d5a746281ad9ef0f72f9cf (patch)
tree2851c83a0e8744b70492c99071074bf410c14560
parente49122fb1ca87a6c3e3c22abb315fc75cfe8daed (diff)
parentbdf0e74777dd92aed40c19a1b20eb08ac1c845a8 (diff)
downloadrust-91fe1174185c6914c4d5a746281ad9ef0f72f9cf.tar.gz
rust-91fe1174185c6914c4d5a746281ad9ef0f72f9cf.zip
Rollup merge of #104055 - AndyJado:bck_errors, r=davidtwco
Migrate diagnostics in `rustc_borrowck`

sorry for making a new PR, [#103559](https://github.com/rust-lang/rust/pull/103559) and [#103960](https://github.com/rust-lang/rust/pull/103960).

I am crawling, joyfully.
-rw-r--r--compiler/rustc_borrowck/messages.ftl150
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs188
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs246
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs53
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs20
-rw-r--r--compiler/rustc_borrowck/src/session_diagnostics.rs207
6 files changed, 612 insertions, 252 deletions
diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl
index a3b6b5e8138..0b8123c9703 100644
--- a/compiler/rustc_borrowck/messages.ftl
+++ b/compiler/rustc_borrowck/messages.ftl
@@ -56,18 +56,6 @@ borrowck_returned_lifetime_short =
 borrowck_used_impl_require_static =
     the used `impl` has a `'static` requirement
 
-borrowck_capture_kind_label =
-    capture is {$kind_desc} because of use here
-
-borrowck_var_borrow_by_use_place_in_generator =
-    borrow occurs due to use of {$place} in closure in generator
-
-borrowck_var_borrow_by_use_place_in_closure =
-    borrow occurs due to use of {$place} in closure
-
-borrowck_var_borrow_by_use_place =
-    borrow occurs due to use of {$place}
-
 borrowck_borrow_due_to_use_generator =
     borrow occurs due to use in generator
 
@@ -101,12 +89,63 @@ borrowck_capture_mut =
 borrowck_capture_move =
     capture is moved because of use here
 
+borrowck_var_borrow_by_use_place_in_generator =
+    {$is_single_var ->
+        *[true] borrow occurs
+        [false] borrows occur
+    } due to use of {$place} in generator
+
+borrowck_var_borrow_by_use_place_in_closure =
+    {$is_single_var ->
+        *[true] borrow occurs
+        [false] borrows occur
+    } due to use of {$place} in closure
+
+borrowck_var_borrow_by_use_in_generator =
+    borrow occurs due to use in generator
+
+borrowck_var_borrow_by_use_in_closure =
+    borrow occurs due to use in closure
+
 borrowck_var_move_by_use_place_in_generator =
     move occurs due to use of {$place} in generator
 
 borrowck_var_move_by_use_place_in_closure =
     move occurs due to use of {$place} in closure
 
+borrowck_var_move_by_use_in_generator =
+    move occurs due to use in generator
+
+borrowck_var_move_by_use_in_closure =
+    move occurs due to use in closure
+
+borrowck_partial_var_move_by_use_in_generator =
+    variable {$is_partial ->
+        [true] partially moved
+        *[false] moved
+    } due to use in generator
+
+borrowck_partial_var_move_by_use_in_closure =
+    variable {$is_partial ->
+        [true] partially moved
+        *[false] moved
+    } due to use in closure
+
+borrowck_var_first_borrow_by_use_place_in_generator =
+    first borrow occurs due to use of {$place} in generator
+
+borrowck_var_first_borrow_by_use_place_in_closure =
+    first borrow occurs due to use of {$place} in closure
+
+borrowck_var_second_borrow_by_use_place_in_generator =
+    second borrow occurs due to use of {$place} in generator
+
+borrowck_var_second_borrow_by_use_place_in_closure =
+    second borrow occurs due to use of {$place} in closure
+
+borrowck_var_mutable_borrow_by_use_place_in_closure =
+    mutable borrow occurs due to use of {$place} in closure
+
 borrowck_cannot_move_when_borrowed =
     cannot move out of {$place ->
         [value] value
@@ -127,3 +166,90 @@ borrowck_opaque_type_non_generic_param =
         [true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
         *[other] this generic parameter must be used with a generic {$kind} parameter
     }
+
+borrowck_moved_due_to_call =
+    {$place_name} {$is_partial ->
+        [true] partially moved
+        *[false] moved
+    } due to this {$is_loop_message ->
+        [true] call, in previous iteration of loop
+        *[false] call
+    }
+
+borrowck_moved_due_to_usage_in_operator =
+    {$place_name} {$is_partial ->
+        [true] partially moved
+        *[false] moved
+    } due to usage in {$is_loop_message ->
+        [true] operator, in previous iteration of loop
+        *[false] operator
+    }
+
+borrowck_moved_due_to_implicit_into_iter_call =
+    {$place_name} {$is_partial ->
+        [true] partially moved
+        *[false] moved
+    } due to this implicit call to {$is_loop_message ->
+        [true] `.into_iter()`, in previous iteration of loop
+        *[false] `.into_iter()`
+    }
+
+borrowck_moved_due_to_method_call =
+    {$place_name} {$is_partial ->
+        [true] partially moved
+        *[false] moved
+    } due to this method {$is_loop_message ->
+        [true] call, in previous iteration of loop
+        *[false] call
+    }
+
+borrowck_value_moved_here =
+    value {$is_partial ->
+        [true] partially moved
+        *[false] moved
+    } {$is_move_msg ->
+        [true] into closure here
+        *[false] here
+    }{$is_loop_message ->
+        [true] , in previous iteration of loop
+        *[false] {""}
+    }
+
+borrowck_consider_borrow_type_contents =
+    help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents
+
+borrowck_moved_a_fn_once_in_call =
+    this value implements `FnOnce`, which causes it to be moved when called
+
+borrowck_calling_operator_moves_lhs =
+    calling this operator moves the left-hand side
+
+borrowck_func_take_self_moved_place =
+    `{$func}` takes ownership of the receiver `self`, which moves {$place_name}
+
+borrowck_suggest_iterate_over_slice =
+    consider iterating over a slice of the `{$ty}`'s content to avoid moving into the `for` loop
+
+borrowck_suggest_create_freash_reborrow =
+    consider reborrowing the `Pin` instead of moving it
+
+borrowck_value_capture_here =
+    value captured {$is_within ->
+        [true] here by generator
+        *[false] here
+    }
+
+borrowck_move_out_place_here =
+    {$place} is moved here
+
+borrowck_closure_invoked_twice =
+    closure cannot be invoked more than once because it moves the variable `{$place_name}` out of its environment
+
+borrowck_closure_moved_twice =
+    closure cannot be moved more than once as it is not `Copy` due to moving the variable `{$place_name}` out of its environment
+
+borrowck_ty_no_impl_copy =
+    {$is_partial_move ->
+        [true] partial move
+        *[false] move
+    } occurs because {$place} has type `{$ty}`, which does not implement the `Copy` trait
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 6259ae47e89..a4a3c738f47 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -30,8 +30,8 @@ use crate::borrow_set::TwoPhaseActivation;
 use crate::borrowck_errors;
 
 use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;
-use crate::diagnostics::find_all_local_uses;
 use crate::diagnostics::mutability_errors::mut_borrow_of_mutable_ref;
+use crate::diagnostics::{find_all_local_uses, CapturedMessageOpt};
 use crate::{
     borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
     InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
@@ -183,13 +183,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
                 let move_span = move_spans.args_or_use();
 
-                let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
+                let is_move_msg = move_spans.for_closure();
 
-                let loop_message = if location == move_out.source || move_site.traversed_back_edge {
-                    ", in previous iteration of loop"
-                } else {
-                    ""
-                };
+                let is_loop_message = location == move_out.source || move_site.traversed_back_edge;
 
                 if location == move_out.source {
                     is_loop_move = true;
@@ -206,17 +202,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         );
                     }
 
+                    let msg_opt = CapturedMessageOpt {
+                        is_partial_move,
+                        is_loop_message,
+                        is_move_msg,
+                        is_loop_move,
+                        maybe_reinitialized_locations_is_empty: maybe_reinitialized_locations
+                            .is_empty(),
+                    };
                     self.explain_captures(
                         &mut err,
                         span,
                         move_span,
                         move_spans,
                         *moved_place,
-                        partially_str,
-                        loop_message,
-                        move_msg,
-                        is_loop_move,
-                        maybe_reinitialized_locations.is_empty(),
+                        msg_opt,
                     );
                 }
                 seen_spans.insert(move_span);
@@ -282,12 +282,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             }
 
             if needs_note {
-                let span = if let Some(local) = place.as_local() {
-                    Some(self.body.local_decls[local].source_info.span)
+                if let Some(local) = place.as_local() {
+                    let span = self.body.local_decls[local].source_info.span;
+                    err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
+                        is_partial_move,
+                        ty,
+                        place: &note_msg,
+                        span,
+                    });
                 } else {
-                    None
+                    err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note {
+                        is_partial_move,
+                        ty,
+                        place: &note_msg,
+                    });
                 };
-                self.note_type_does_not_implement_copy(&mut err, &note_msg, ty, span, partial_str);
             }
 
             if let UseSpans::FnSelfUse {
@@ -827,11 +836,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
         borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow);
 
-        move_spans.var_span_label(
-            &mut err,
-            format!("move occurs due to use{}", move_spans.describe()),
-            "moved",
-        );
+        move_spans.var_subdiag(None, &mut err, None, |kind, var_span| {
+            use crate::session_diagnostics::CaptureVarCause::*;
+            match kind {
+                Some(_) => MoveUseInGenerator { var_span },
+                None => MoveUseInClosure { var_span },
+            }
+        });
 
         self.explain_why_borrow_contains_point(location, borrow, None)
             .add_explanation_to_diagnostic(
@@ -868,13 +879,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             borrow_span,
             &self.describe_any_place(borrow.borrowed_place.as_ref()),
         );
-        borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| {
+        borrow_spans.var_subdiag(None, &mut err, Some(borrow.kind), |kind, var_span| {
             use crate::session_diagnostics::CaptureVarCause::*;
             let place = &borrow.borrowed_place;
             let desc_place = self.describe_any_place(place.as_ref());
             match kind {
-                Some(_) => BorrowUsePlaceGenerator { place: desc_place, var_span },
-                None => BorrowUsePlaceClosure { place: desc_place, var_span },
+                Some(_) => {
+                    BorrowUsePlaceGenerator { place: desc_place, var_span, is_single_var: true }
+                }
+                None => BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: true },
             }
         });
 
@@ -988,16 +1001,26 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         immutable_section_description,
                         "mutably borrow",
                     );
-                    borrow_spans.var_span_label(
+                    borrow_spans.var_subdiag(
+                        None,
                         &mut err,
-                        format!(
-                            "borrow occurs due to use of {}{}",
-                            desc_place,
-                            borrow_spans.describe(),
-                        ),
-                        "immutable",
+                        Some(BorrowKind::Unique),
+                        |kind, var_span| {
+                            use crate::session_diagnostics::CaptureVarCause::*;
+                            match kind {
+                                Some(_) => BorrowUsePlaceGenerator {
+                                    place: desc_place,
+                                    var_span,
+                                    is_single_var: true,
+                                },
+                                None => BorrowUsePlaceClosure {
+                                    place: desc_place,
+                                    var_span,
+                                    is_single_var: true,
+                                },
+                            }
+                        },
                     );
-
                     return err;
                 } else {
                     first_borrow_desc = "immutable ";
@@ -1070,32 +1093,48 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         };
 
         if issued_spans == borrow_spans {
-            borrow_spans.var_span_label(
-                &mut err,
-                format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
-                gen_borrow_kind.describe_mutability(),
-            );
+            borrow_spans.var_subdiag(None, &mut err, Some(gen_borrow_kind), |kind, var_span| {
+                use crate::session_diagnostics::CaptureVarCause::*;
+                match kind {
+                    Some(_) => BorrowUsePlaceGenerator {
+                        place: desc_place,
+                        var_span,
+                        is_single_var: false,
+                    },
+                    None => {
+                        BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: false }
+                    }
+                }
+            });
         } else {
-            let borrow_place = &issued_borrow.borrowed_place;
-            let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
-            issued_spans.var_span_label(
+            issued_spans.var_subdiag(
+                Some(&self.infcx.tcx.sess.parse_sess.span_diagnostic),
                 &mut err,
-                format!(
-                    "first borrow occurs due to use of {}{}",
-                    borrow_place_desc,
-                    issued_spans.describe(),
-                ),
-                issued_borrow.kind.describe_mutability(),
+                Some(issued_borrow.kind),
+                |kind, var_span| {
+                    use crate::session_diagnostics::CaptureVarCause::*;
+                    let borrow_place = &issued_borrow.borrowed_place;
+                    let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
+                    match kind {
+                        Some(_) => {
+                            FirstBorrowUsePlaceGenerator { place: borrow_place_desc, var_span }
+                        }
+                        None => FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span },
+                    }
+                },
             );
 
-            borrow_spans.var_span_label(
+            borrow_spans.var_subdiag(
+                Some(&self.infcx.tcx.sess.parse_sess.span_diagnostic),
                 &mut err,
-                format!(
-                    "second borrow occurs due to use of {}{}",
-                    desc_place,
-                    borrow_spans.describe(),
-                ),
-                gen_borrow_kind.describe_mutability(),
+                Some(gen_borrow_kind),
+                |kind, var_span| {
+                    use crate::session_diagnostics::CaptureVarCause::*;
+                    match kind {
+                        Some(_) => SecondBorrowUsePlaceGenerator { place: desc_place, var_span },
+                        None => SecondBorrowUsePlaceClosure { place: desc_place, var_span },
+                    }
+                },
             );
         }
 
@@ -1731,9 +1770,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             err.span_label(borrow_span, "borrowed value does not live long enough");
             err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
 
-            let within = if borrow_spans.for_generator() { " by generator" } else { "" };
-
-            borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
+            borrow_spans.args_subdiag(&mut err, |args_span| {
+                crate::session_diagnostics::CaptureArgLabel::Capture {
+                    is_within: borrow_spans.for_generator(),
+                    args_span,
+                }
+            });
 
             explanation.add_explanation_to_diagnostic(
                 self.infcx.tcx,
@@ -1947,9 +1989,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             None,
         );
 
-        let within = if borrow_spans.for_generator() { " by generator" } else { "" };
-
-        borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
+        borrow_spans.args_subdiag(&mut err, |args_span| {
+            crate::session_diagnostics::CaptureArgLabel::Capture {
+                is_within: borrow_spans.for_generator(),
+                args_span,
+            }
+        });
 
         err
     }
@@ -2382,11 +2427,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     section,
                     "assign",
                 );
-                loan_spans.var_span_label(
-                    &mut err,
-                    format!("borrow occurs due to use{}", loan_spans.describe()),
-                    loan.kind.describe_mutability(),
-                );
+
+                loan_spans.var_subdiag(None, &mut err, Some(loan.kind), |kind, var_span| {
+                    use crate::session_diagnostics::CaptureVarCause::*;
+                    match kind {
+                        Some(_) => BorrowUseInGenerator { var_span },
+                        None => BorrowUseInClosure { var_span },
+                    }
+                });
 
                 self.buffer_error(err);
 
@@ -2396,11 +2444,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
         let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
 
-        loan_spans.var_span_label(
-            &mut err,
-            format!("borrow occurs due to use{}", loan_spans.describe()),
-            loan.kind.describe_mutability(),
-        );
+        loan_spans.var_subdiag(None, &mut err, Some(loan.kind), |kind, var_span| {
+            use crate::session_diagnostics::CaptureVarCause::*;
+            match kind {
+                Some(_) => BorrowUseInGenerator { var_span },
+                None => BorrowUseInClosure { var_span },
+            }
+        });
 
         self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
             self.infcx.tcx,
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index dd003b5b040..4a85df9f8c0 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -1,5 +1,9 @@
 //! Borrow checker diagnostics.
 
+use crate::session_diagnostics::{
+    CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause,
+    CaptureVarKind, CaptureVarPathUseCause, OnClosureNote,
+};
 use itertools::Itertools;
 use rustc_errors::{Applicability, Diagnostic};
 use rustc_hir as hir;
@@ -117,13 +121,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() {
                         let did = did.expect_local();
                         if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
-                            diag.span_note(
-                                *span,
-                                &format!(
-                                    "closure cannot be invoked more than once because it moves the \
-                                    variable `{}` out of its environment",
-                                    ty::place_to_string_for_capture(self.infcx.tcx, hir_place)
-                                ),
+                            diag.eager_subdiagnostic(
+                                &self.infcx.tcx.sess.parse_sess.span_diagnostic,
+                                OnClosureNote::InvokedTwice {
+                                    place_name: &ty::place_to_string_for_capture(
+                                        self.infcx.tcx,
+                                        hir_place,
+                                    ),
+                                    span: *span,
+                                },
                             );
                             return true;
                         }
@@ -137,13 +143,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() {
                 let did = did.expect_local();
                 if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
-                    diag.span_note(
-                        *span,
-                        &format!(
-                            "closure cannot be moved more than once as it is not `Copy` due to \
-                             moving the variable `{}` out of its environment",
-                            ty::place_to_string_for_capture(self.infcx.tcx, hir_place)
-                        ),
+                    diag.eager_subdiagnostic(
+                        &self.infcx.tcx.sess.parse_sess.span_diagnostic,
+                        OnClosureNote::MovedTwice {
+                            place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
+                            span: *span,
+                        },
                     );
                     return true;
                 }
@@ -380,25 +385,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         }
     }
 
-    /// Add a note that a type does not implement `Copy`
-    pub(super) fn note_type_does_not_implement_copy(
-        &self,
-        err: &mut Diagnostic,
-        place_desc: &str,
-        ty: Ty<'tcx>,
-        span: Option<Span>,
-        move_prefix: &str,
-    ) {
-        let message = format!(
-            "{move_prefix}move occurs because {place_desc} has type `{ty}`, which does not implement the `Copy` trait",
-        );
-        if let Some(span) = span {
-            err.span_label(span, message);
-        } else {
-            err.note(&message);
-        }
-    }
-
     pub(super) fn borrowed_content_source(
         &self,
         deref_base: PlaceRef<'tcx>,
@@ -582,9 +568,13 @@ impl UseSpans<'_> {
     }
 
     /// Add a span label to the arguments of the closure, if it exists.
-    pub(super) fn args_span_label(self, err: &mut Diagnostic, message: impl Into<String>) {
+    pub(super) fn args_subdiag(
+        self,
+        err: &mut Diagnostic,
+        f: impl FnOnce(Span) -> CaptureArgLabel,
+    ) {
         if let UseSpans::ClosureUse { args_span, .. } = self {
-            err.span_label(args_span, message);
+            err.subdiagnostic(f(args_span));
         }
     }
 
@@ -595,8 +585,8 @@ impl UseSpans<'_> {
         err: &mut Diagnostic,
         action: crate::InitializationRequiringAction,
     ) {
-        use crate::session_diagnostics::CaptureVarPathUseCause::*;
         use crate::InitializationRequiringAction::*;
+        use CaptureVarPathUseCause::*;
         if let UseSpans::ClosureUse { generator_kind, path_span, .. } = self {
             match generator_kind {
                 Some(_) => {
@@ -619,34 +609,14 @@ impl UseSpans<'_> {
         }
     }
 
-    /// Add a span label to the use of the captured variable, if it exists.
-    pub(super) fn var_span_label(
-        self,
-        err: &mut Diagnostic,
-        message: impl Into<String>,
-        kind_desc: impl Into<String>,
-    ) {
-        if let UseSpans::ClosureUse { capture_kind_span, path_span, .. } = self {
-            if capture_kind_span == path_span {
-                err.span_label(capture_kind_span, message);
-            } else {
-                let capture_kind_label =
-                    format!("capture is {} because of use here", kind_desc.into());
-                let path_label = message;
-                err.span_label(capture_kind_span, capture_kind_label);
-                err.span_label(path_span, path_label);
-            }
-        }
-    }
-
     /// Add a subdiagnostic to the use of the captured variable, if it exists.
     pub(super) fn var_subdiag(
         self,
+        handler: Option<&rustc_errors::Handler>,
         err: &mut Diagnostic,
         kind: Option<rustc_middle::mir::BorrowKind>,
-        f: impl Fn(Option<GeneratorKind>, Span) -> crate::session_diagnostics::CaptureVarCause,
+        f: impl FnOnce(Option<GeneratorKind>, Span) -> CaptureVarCause,
     ) {
-        use crate::session_diagnostics::CaptureVarKind::*;
         if let UseSpans::ClosureUse { generator_kind, capture_kind_span, path_span, .. } = self {
             if capture_kind_span != path_span {
                 err.subdiagnostic(match kind {
@@ -654,17 +624,21 @@ impl UseSpans<'_> {
                         rustc_middle::mir::BorrowKind::Shared
                         | rustc_middle::mir::BorrowKind::Shallow
                         | rustc_middle::mir::BorrowKind::Unique => {
-                            Immute { kind_span: capture_kind_span }
+                            CaptureVarKind::Immut { kind_span: capture_kind_span }
                         }
 
                         rustc_middle::mir::BorrowKind::Mut { .. } => {
-                            Mut { kind_span: capture_kind_span }
+                            CaptureVarKind::Mut { kind_span: capture_kind_span }
                         }
                     },
-                    None => Move { kind_span: capture_kind_span },
+                    None => CaptureVarKind::Move { kind_span: capture_kind_span },
                 });
             };
-            err.subdiagnostic(f(generator_kind, path_span));
+            let diag = f(generator_kind, path_span);
+            match handler {
+                Some(hd) => err.eager_subdiagnostic(hd, diag),
+                None => err.subdiagnostic(diag),
+            };
         }
     }
 
@@ -684,20 +658,6 @@ impl UseSpans<'_> {
         }
     }
 
-    /// Describe the span associated with a use of a place.
-    pub(super) fn describe(&self) -> &str {
-        match *self {
-            UseSpans::ClosureUse { generator_kind, .. } => {
-                if generator_kind.is_some() {
-                    " in generator"
-                } else {
-                    " in closure"
-                }
-            }
-            _ => "",
-        }
-    }
-
     pub(super) fn or_else<F>(self, if_other: F) -> Self
     where
         F: FnOnce() -> Self,
@@ -788,6 +748,15 @@ impl<'tcx> BorrowedContentSource<'tcx> {
     }
 }
 
+///helper struct for explain_captures()
+struct CapturedMessageOpt {
+    is_partial_move: bool,
+    is_loop_message: bool,
+    is_move_msg: bool,
+    is_loop_move: bool,
+    maybe_reinitialized_locations_is_empty: bool,
+}
+
 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     /// Finds the spans associated to a move or copy of move_place at location.
     pub(super) fn move_spans(
@@ -1027,12 +996,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         move_span: Span,
         move_spans: UseSpans<'tcx>,
         moved_place: Place<'tcx>,
-        partially_str: &str,
-        loop_message: &str,
-        move_msg: &str,
-        is_loop_move: bool,
-        maybe_reinitialized_locations_is_empty: bool,
+        msg_opt: CapturedMessageOpt,
     ) {
+        let CapturedMessageOpt {
+            is_partial_move: is_partial,
+            is_loop_message,
+            is_move_msg,
+            is_loop_move,
+            maybe_reinitialized_locations_is_empty,
+        } = msg_opt;
         if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
             let place_name = self
                 .describe_place(moved_place.as_ref())
@@ -1042,30 +1014,26 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 CallKind::FnCall { fn_trait_id, .. }
                     if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() =>
                 {
-                    err.span_label(
+                    err.subdiagnostic(CaptureReasonLabel::Call {
                         fn_call_span,
-                        &format!(
-                            "{place_name} {partially_str}moved due to this call{loop_message}",
-                        ),
-                    );
-                    err.span_note(
-                        var_span,
-                        "this value implements `FnOnce`, which causes it to be moved when called",
-                    );
+                        place_name: &place_name,
+                        is_partial,
+                        is_loop_message,
+                    });
+                    err.subdiagnostic(CaptureReasonNote::FnOnceMoveInCall { var_span });
                 }
                 CallKind::Operator { self_arg, .. } => {
                     let self_arg = self_arg.unwrap();
-                    err.span_label(
+                    err.subdiagnostic(CaptureReasonLabel::OperatorUse {
                         fn_call_span,
-                        &format!(
-                            "{place_name} {partially_str}moved due to usage in operator{loop_message}",
-                        ),
-                    );
+                        place_name: &place_name,
+                        is_partial,
+                        is_loop_message,
+                    });
                     if self.fn_self_span_reported.insert(fn_span) {
-                        err.span_note(
-                            self_arg.span,
-                            "calling this operator moves the left-hand side",
-                        );
+                        err.subdiagnostic(CaptureReasonNote::LhsMoveByOperator {
+                            span: self_arg.span,
+                        });
                     }
                 }
                 CallKind::Normal { self_arg, desugaring, method_did, method_substs } => {
@@ -1086,23 +1054,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             _ => false,
                         };
                         if suggest {
-                            err.span_suggestion_verbose(
-                                move_span.shrink_to_lo(),
-                                &format!(
-                                    "consider iterating over a slice of the `{ty}`'s content to \
-                                     avoid moving into the `for` loop",
-                                ),
-                                "&",
-                                Applicability::MaybeIncorrect,
-                            );
+                            err.subdiagnostic(CaptureReasonSuggest::IterateSlice {
+                                ty,
+                                span: move_span.shrink_to_lo(),
+                            });
                         }
 
-                        err.span_label(
+                        err.subdiagnostic(CaptureReasonLabel::ImplicitCall {
                             fn_call_span,
-                            &format!(
-                                "{place_name} {partially_str}moved due to this implicit call to `.into_iter()`{loop_message}",
-                            ),
-                        );
+                            place_name: &place_name,
+                            is_partial,
+                            is_loop_message,
+                        });
                         // If the moved place was a `&mut` ref, then we can
                         // suggest to reborrow it where it was moved, so it
                         // will still be valid by the time we get to the usage.
@@ -1125,13 +1088,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             }
                         }
                     } else {
-                        err.span_label(
+                        err.subdiagnostic(CaptureReasonLabel::MethodCall {
                             fn_call_span,
-                            &format!(
-                                "{place_name} {partially_str}moved due to this method call{loop_message}",
-                            ),
-                        );
-
+                            place_name: &place_name,
+                            is_partial,
+                            is_loop_message,
+                        });
                         let infcx = tcx.infer_ctxt().build();
                         // Erase and shadow everything that could be passed to the new infcx.
                         let ty = tcx.erase_regions(moved_place.ty(self.body, tcx).ty);
@@ -1147,12 +1109,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             )
                             && infcx.can_eq(self.param_env, ty, self_ty)
                         {
-                            err.span_suggestion_verbose(
-                                fn_call_span.shrink_to_lo(),
-                                "consider reborrowing the `Pin` instead of moving it",
-                                "as_mut().".to_string(),
-                                Applicability::MaybeIncorrect,
-                            );
+                            err.eager_subdiagnostic(
+                                &self.infcx.tcx.sess.parse_sess.span_diagnostic,
+                                CaptureReasonSuggest::FreshReborrow {
+                                    span: fn_call_span.shrink_to_lo(),
+                                });
                         }
                         if let Some(clone_trait) = tcx.lang_items().clone_trait()
                             && let trait_ref = tcx.mk_trait_ref(clone_trait, [ty])
@@ -1177,10 +1138,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     // error messages.
                     if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
                         let func = tcx.def_path_str(method_did);
-                        err.span_note(
-                            self_arg.span,
-                            &format!("`{func}` takes ownership of the receiver `self`, which moves {place_name}")
-                        );
+                        err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
+                            func,
+                            place_name,
+                            span: self_arg.span,
+                        });
                     }
                     let parent_did = tcx.parent(method_did);
                     let parent_self_ty =
@@ -1194,30 +1156,28 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
                     });
                     if is_option_or_result && maybe_reinitialized_locations_is_empty {
-                        err.span_label(
-                            var_span,
-                            "help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents",
-                        );
+                        err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
                     }
                 }
                 // Other desugarings takes &self, which cannot cause a move
                 _ => {}
             }
         } else {
-            if move_span != span || !loop_message.is_empty() {
-                err.span_label(
+            if move_span != span || is_loop_message {
+                err.subdiagnostic(CaptureReasonLabel::MovedHere {
                     move_span,
-                    format!("value {partially_str}moved{move_msg} here{loop_message}"),
-                );
+                    is_partial,
+                    is_move_msg,
+                    is_loop_message,
+                });
             }
             // If the move error occurs due to a loop, don't show
             // another message for the same span
-            if loop_message.is_empty() {
-                move_spans.var_span_label(
-                    err,
-                    format!("variable {partially_str}moved due to use{}", move_spans.describe()),
-                    "moved",
-                );
+            if !is_loop_message {
+                move_spans.var_subdiag(None, err, None, |kind, var_span| match kind {
+                    Some(_) => CaptureVarCause::PartialMoveUseInGenerator { var_span, is_partial },
+                    None => CaptureVarCause::PartialMoveUseInClosure { var_span, is_partial },
+                })
             }
         }
     }
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 3662bec0c76..67af96a71e3 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -6,6 +6,7 @@ use rustc_mir_dataflow::move_paths::{
 };
 use rustc_span::{BytePos, Span};
 
+use crate::diagnostics::CapturedMessageOpt;
 use crate::diagnostics::{DescribePlaceOpt, UseSpans};
 use crate::prefixes::PrefixSet;
 use crate::MirBorrowckCtxt;
@@ -397,10 +398,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 }
             }
         };
+        let msg_opt = CapturedMessageOpt {
+            is_partial_move: false,
+            is_loop_message: false,
+            is_move_msg: false,
+            is_loop_move: false,
+            maybe_reinitialized_locations_is_empty: true,
+        };
         if let Some(use_spans) = use_spans {
-            self.explain_captures(
-                &mut err, span, span, use_spans, move_place, "", "", "", false, true,
-            );
+            self.explain_captures(&mut err, span, span, use_spans, move_place, msg_opt);
         }
         err
     }
@@ -416,13 +422,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                         None => "value".to_string(),
                     };
 
-                    self.note_type_does_not_implement_copy(
-                        err,
-                        &place_desc,
-                        place_ty,
-                        Some(span),
-                        "",
-                    );
+                    err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
+                        is_partial_move: false,
+                        ty: place_ty,
+                        place: &place_desc,
+                        span,
+                    });
                 } else {
                     binds_to.sort();
                     binds_to.dedup();
@@ -444,9 +449,19 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     Some(desc) => format!("`{desc}`"),
                     None => "value".to_string(),
                 };
-                self.note_type_does_not_implement_copy(err, &place_desc, place_ty, Some(span), "");
+                err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
+                    is_partial_move: false,
+                    ty: place_ty,
+                    place: &place_desc,
+                    span,
+                });
 
-                use_spans.args_span_label(err, format!("{place_desc} is moved here"));
+                use_spans.args_subdiag(err, |args_span| {
+                    crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
+                        place: place_desc,
+                        args_span,
+                    }
+                });
             }
         }
     }
@@ -534,13 +549,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             }
 
             if binds_to.len() == 1 {
-                self.note_type_does_not_implement_copy(
-                    err,
-                    &format!("`{}`", self.local_names[*local].unwrap()),
-                    bind_to.ty,
-                    Some(binding_span),
-                    "",
-                );
+                let place_desc = &format!("`{}`", self.local_names[*local].unwrap());
+                err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
+                    is_partial_move: false,
+                    ty: bind_to.ty,
+                    place: &place_desc,
+                    span: binding_span,
+                });
             }
         }
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 9c907147e5a..eb5f166548d 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -231,14 +231,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     }
                 }
                 if suggest {
-                    borrow_spans.var_span_label(
-                        &mut err,
-                        format!(
-                            "mutable borrow occurs due to use of {} in closure",
-                            self.describe_any_place(access_place.as_ref()),
-                        ),
-                        "mutable",
-                    );
+                    borrow_spans.var_subdiag(
+                    None,
+                    &mut err,
+                    Some(mir::BorrowKind::Mut { allow_two_phase_borrow: false }),
+                    |_kind, var_span| {
+                        let place = self.describe_any_place(access_place.as_ref());
+                        crate::session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure {
+                            place,
+                            var_span,
+                        }
+                    },
+                );
                 }
                 borrow_span
             }
diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs
index a3678929099..bb95101845f 100644
--- a/compiler/rustc_borrowck/src/session_diagnostics.rs
+++ b/compiler/rustc_borrowck/src/session_diagnostics.rs
@@ -184,7 +184,7 @@ pub(crate) enum CaptureVarPathUseCause {
 #[derive(Subdiagnostic)]
 pub(crate) enum CaptureVarKind {
     #[label(borrowck_capture_immute)]
-    Immute {
+    Immut {
         #[primary_span]
         kind_span: Span,
     },
@@ -204,16 +204,80 @@ pub(crate) enum CaptureVarKind {
 pub(crate) enum CaptureVarCause {
     #[label(borrowck_var_borrow_by_use_place_in_generator)]
     BorrowUsePlaceGenerator {
+        is_single_var: bool,
         place: String,
         #[primary_span]
         var_span: Span,
     },
     #[label(borrowck_var_borrow_by_use_place_in_closure)]
     BorrowUsePlaceClosure {
+        is_single_var: bool,
         place: String,
         #[primary_span]
         var_span: Span,
     },
+    #[label(borrowck_var_borrow_by_use_in_generator)]
+    BorrowUseInGenerator {
+        #[primary_span]
+        var_span: Span,
+    },
+    #[label(borrowck_var_borrow_by_use_in_closure)]
+    BorrowUseInClosure {
+        #[primary_span]
+        var_span: Span,
+    },
+    #[label(borrowck_var_move_by_use_in_generator)]
+    MoveUseInGenerator {
+        #[primary_span]
+        var_span: Span,
+    },
+    #[label(borrowck_var_move_by_use_in_closure)]
+    MoveUseInClosure {
+        #[primary_span]
+        var_span: Span,
+    },
+    #[label(borrowck_var_first_borrow_by_use_place_in_generator)]
+    FirstBorrowUsePlaceGenerator {
+        place: String,
+        #[primary_span]
+        var_span: Span,
+    },
+    #[label(borrowck_var_first_borrow_by_use_place_in_closure)]
+    FirstBorrowUsePlaceClosure {
+        place: String,
+        #[primary_span]
+        var_span: Span,
+    },
+    #[label(borrowck_var_second_borrow_by_use_place_in_generator)]
+    SecondBorrowUsePlaceGenerator {
+        place: String,
+        #[primary_span]
+        var_span: Span,
+    },
+    #[label(borrowck_var_second_borrow_by_use_place_in_closure)]
+    SecondBorrowUsePlaceClosure {
+        place: String,
+        #[primary_span]
+        var_span: Span,
+    },
+    #[label(borrowck_var_mutable_borrow_by_use_place_in_closure)]
+    MutableBorrowUsePlaceClosure {
+        place: String,
+        #[primary_span]
+        var_span: Span,
+    },
+    #[label(borrowck_partial_var_move_by_use_in_generator)]
+    PartialMoveUseInGenerator {
+        #[primary_span]
+        var_span: Span,
+        is_partial: bool,
+    },
+    #[label(borrowck_partial_var_move_by_use_in_closure)]
+    PartialMoveUseInClosure {
+        #[primary_span]
+        var_span: Span,
+        is_partial: bool,
+    },
 }
 
 #[derive(Diagnostic)]
@@ -239,3 +303,144 @@ pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> {
     #[label]
     pub param_span: Span,
 }
+
+#[derive(Subdiagnostic)]
+pub(crate) enum CaptureReasonLabel<'a> {
+    #[label(borrowck_moved_due_to_call)]
+    Call {
+        #[primary_span]
+        fn_call_span: Span,
+        place_name: &'a str,
+        is_partial: bool,
+        is_loop_message: bool,
+    },
+    #[label(borrowck_moved_due_to_usage_in_operator)]
+    OperatorUse {
+        #[primary_span]
+        fn_call_span: Span,
+        place_name: &'a str,
+        is_partial: bool,
+        is_loop_message: bool,
+    },
+    #[label(borrowck_moved_due_to_implicit_into_iter_call)]
+    ImplicitCall {
+        #[primary_span]
+        fn_call_span: Span,
+        place_name: &'a str,
+        is_partial: bool,
+        is_loop_message: bool,
+    },
+    #[label(borrowck_moved_due_to_method_call)]
+    MethodCall {
+        #[primary_span]
+        fn_call_span: Span,
+        place_name: &'a str,
+        is_partial: bool,
+        is_loop_message: bool,
+    },
+    #[label(borrowck_value_moved_here)]
+    MovedHere {
+        #[primary_span]
+        move_span: Span,
+        is_partial: bool,
+        is_move_msg: bool,
+        is_loop_message: bool,
+    },
+    #[label(borrowck_consider_borrow_type_contents)]
+    BorrowContent {
+        #[primary_span]
+        var_span: Span,
+    },
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum CaptureReasonNote {
+    #[note(borrowck_moved_a_fn_once_in_call)]
+    FnOnceMoveInCall {
+        #[primary_span]
+        var_span: Span,
+    },
+    #[note(borrowck_calling_operator_moves_lhs)]
+    LhsMoveByOperator {
+        #[primary_span]
+        span: Span,
+    },
+    #[note(borrowck_func_take_self_moved_place)]
+    FuncTakeSelf {
+        func: String,
+        place_name: String,
+        #[primary_span]
+        span: Span,
+    },
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum CaptureReasonSuggest<'tcx> {
+    #[suggestion(
+        borrowck_suggest_iterate_over_slice,
+        applicability = "maybe-incorrect",
+        code = "&",
+        style = "verbose"
+    )]
+    IterateSlice {
+        ty: Ty<'tcx>,
+        #[primary_span]
+        span: Span,
+    },
+    #[suggestion(
+        borrowck_suggest_create_freash_reborrow,
+        applicability = "maybe-incorrect",
+        code = "as_mut().",
+        style = "verbose"
+    )]
+    FreshReborrow {
+        #[primary_span]
+        span: Span,
+    },
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum CaptureArgLabel {
+    #[label(borrowck_value_capture_here)]
+    Capture {
+        is_within: bool,
+        #[primary_span]
+        args_span: Span,
+    },
+    #[label(borrowck_move_out_place_here)]
+    MoveOutPlace {
+        place: String,
+        #[primary_span]
+        args_span: Span,
+    },
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum OnClosureNote<'a> {
+    #[note(borrowck_closure_invoked_twice)]
+    InvokedTwice {
+        place_name: &'a str,
+        #[primary_span]
+        span: Span,
+    },
+    #[note(borrowck_closure_moved_twice)]
+    MovedTwice {
+        place_name: &'a str,
+        #[primary_span]
+        span: Span,
+    },
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum TypeNoCopy<'a, 'tcx> {
+    #[label(borrowck_ty_no_impl_copy)]
+    Label {
+        is_partial_move: bool,
+        ty: Ty<'tcx>,
+        place: &'a str,
+        #[primary_span]
+        span: Span,
+    },
+    #[note(borrowck_ty_no_impl_copy)]
+    Note { is_partial_move: bool, ty: Ty<'tcx>, place: &'a str },
+}