summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_borrowck/src/diagnostics
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
commit9835e2ae736235810b4ea1c162ca5e65c547e770 (patch)
tree3fcebf40ed70e581d776a8a4c65923e8ec20e026 /compiler/rustc_borrowck/src/diagnostics
parentReleasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff)
downloadrustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz
rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_borrowck/src/diagnostics')
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs437
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs17
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs298
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs55
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs555
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs13
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs22
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/var_name.rs4
10 files changed, 783 insertions, 622 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
index 84f75caa6..f41795d60 100644
--- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
@@ -128,7 +128,7 @@ impl<'tcx> ToUniverseInfo<'tcx>
}
}
-impl<'tcx, F, G> ToUniverseInfo<'tcx> for Canonical<'tcx, type_op::custom::CustomTypeOp<F, G>> {
+impl<'tcx, F> ToUniverseInfo<'tcx> for Canonical<'tcx, type_op::custom::CustomTypeOp<F>> {
fn to_universe_info(self, _base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
// We can't rerun custom type ops.
UniverseInfo::other()
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 75a3dd0c0..15d73ed73 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -1,5 +1,6 @@
+use std::iter;
+
use either::Either;
-use rustc_const_eval::util::CallKind;
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{
@@ -9,8 +10,8 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem};
-use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::ObligationCause;
+use rustc_middle::hir::nested_filter::OnlyBodies;
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::{
self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
@@ -18,20 +19,19 @@ use rustc_middle::mir::{
ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
};
use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty};
+use rustc_middle::util::CallKind;
use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
use rustc_span::def_id::LocalDefId;
use rustc_span::hygiene::DesugaringKind;
-use rustc_span::symbol::{kw, sym};
+use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{BytePos, Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::ObligationCtxt;
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,
@@ -158,7 +158,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
} else if reinits > 1 {
err.span_note(
MultiSpan::from_spans(reinit_spans),
- &if reinits <= 3 {
+ if reinits <= 3 {
format!("these {reinits} reinitializations might get skipped")
} else {
format!(
@@ -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);
@@ -253,7 +253,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// We have a `&mut` ref, we need to reborrow on each iteration (#62112).
err.span_suggestion_verbose(
span.shrink_to_lo(),
- &format!(
+ format!(
"consider creating a fresh reborrow of {} here",
self.describe_place(moved_place)
.map(|n| format!("`{n}`"))
@@ -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 {
@@ -295,7 +304,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
..
} = use_spans
{
- err.note(&format!(
+ err.note(format!(
"{} occurs due to deref coercion to `{deref_target_ty}`",
desired_action.as_noun(),
));
@@ -577,7 +586,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// _ => {} // We don't want to point to this.
// };
// ```
- err.span_label(sp, &label);
+ err.span_label(sp, label);
shown = true;
}
}
@@ -633,11 +642,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else {
return false;
};
- // Regions are already solved, so we must use a fresh InferCtxt,
- // but the type has region variables, so erase those.
- tcx.infer_ctxt()
- .build()
- .type_implements_trait(default_trait, [tcx.erase_regions(ty)], param_env)
+ self.infcx
+ .type_implements_trait(default_trait, [ty], param_env)
.must_apply_modulo_regions()
};
@@ -696,7 +702,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
.copied()
.find_map(find_fn_kind_from_did),
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => tcx
- .bound_explicit_item_bounds(def_id)
+ .explicit_item_bounds(def_id)
.subst_iter_copied(tcx, substs)
.find_map(find_fn_kind_from_did),
ty::Closure(_, substs) => match substs.as_closure().kind() {
@@ -730,13 +736,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
fn suggest_cloning(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
let tcx = self.infcx.tcx;
// Try to find predicates on *generic params* that would allow copying `ty`
- let infcx = tcx.infer_ctxt().build();
-
if let Some(clone_trait_def) = tcx.lang_items().clone_trait()
- && infcx
+ && self.infcx
.type_implements_trait(
clone_trait_def,
- [tcx.erase_regions(ty)],
+ [ty],
self.param_env,
)
.must_apply_modulo_regions()
@@ -760,12 +764,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
.and_then(|def_id| tcx.hir().get_generics(def_id))
else { return; };
// Try to find predicates on *generic params* that would allow copying `ty`
- let infcx = tcx.infer_ctxt().build();
- let ocx = ObligationCtxt::new(&infcx);
+ let ocx = ObligationCtxt::new(&self.infcx);
let copy_did = tcx.require_lang_item(LangItem::Copy, Some(span));
let cause = ObligationCause::misc(span, self.mir_def_id());
- ocx.register_bound(cause, self.param_env, infcx.tcx.erase_regions(ty), copy_did);
+ ocx.register_bound(cause, self.param_env, ty, copy_did);
let errors = ocx.select_all_or_error();
// Only emit suggestion if all required predicates are on generic
@@ -827,11 +830,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 +873,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 },
}
});
@@ -946,7 +953,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
&msg_borrow,
None,
);
- self.suggest_binding_for_closure_capture_self(
+ self.suggest_binding_for_closure_capture_self(&mut err, &issued_spans);
+ self.suggest_using_closure_argument_instead_of_capture(
&mut err,
issued_borrow.borrowed_place,
&issued_spans,
@@ -969,6 +977,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
place,
issued_borrow.borrowed_place,
);
+ self.suggest_using_closure_argument_instead_of_capture(
+ &mut err,
+ issued_borrow.borrowed_place,
+ &issued_spans,
+ );
err
}
@@ -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,37 +1093,53 @@ 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 },
+ }
+ },
);
}
if union_type_name != "" {
- err.note(&format!(
+ err.note(format!(
"{} is a field of the union `{}`, so it overlaps the field {}",
msg_place, union_type_name, msg_borrow,
));
@@ -1199,14 +1238,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
err.span_help(
inner_call_span,
- &format!(
+ format!(
"try adding a local storing this{}...",
if use_span.is_some() { "" } else { " argument" }
),
);
err.span_help(
outer_call_span,
- &format!(
+ format!(
"...and then using that local {}",
if use_span.is_some() { "here" } else { "as the argument to this call" }
),
@@ -1229,22 +1268,160 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
- fn suggest_binding_for_closure_capture_self(
+ /// Suggest using closure argument instead of capture.
+ ///
+ /// For example:
+ /// ```ignore (illustrative)
+ /// struct S;
+ ///
+ /// impl S {
+ /// fn call(&mut self, f: impl Fn(&mut Self)) { /* ... */ }
+ /// fn x(&self) {}
+ /// }
+ ///
+ /// let mut v = S;
+ /// v.call(|this: &mut S| v.x());
+ /// // ^\ ^-- help: try using the closure argument: `this`
+ /// // *-- error: cannot borrow `v` as mutable because it is also borrowed as immutable
+ /// ```
+ fn suggest_using_closure_argument_instead_of_capture(
&self,
err: &mut Diagnostic,
borrowed_place: Place<'tcx>,
issued_spans: &UseSpans<'tcx>,
) {
- let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return };
- let hir = self.infcx.tcx.hir();
+ let &UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return };
+ let tcx = self.infcx.tcx;
+ let hir = tcx.hir();
- // check whether the borrowed place is capturing `self` by mut reference
+ // Get the type of the local that we are trying to borrow
let local = borrowed_place.local;
- let Some(_) = self
- .body
- .local_decls
- .get(local)
- .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) else { return };
+ let local_ty = self.body.local_decls[local].ty;
+
+ // Get the body the error happens in
+ let Some(body_id) = hir.get(self.mir_hir_id()).body_id() else { return };
+
+ let body_expr = hir.body(body_id).value;
+
+ struct ClosureFinder<'hir> {
+ hir: rustc_middle::hir::map::Map<'hir>,
+ borrow_span: Span,
+ res: Option<(&'hir hir::Expr<'hir>, &'hir hir::Closure<'hir>)>,
+ /// The path expression with the `borrow_span` span
+ error_path: Option<(&'hir hir::Expr<'hir>, &'hir hir::QPath<'hir>)>,
+ }
+ impl<'hir> Visitor<'hir> for ClosureFinder<'hir> {
+ type NestedFilter = OnlyBodies;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.hir
+ }
+
+ fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
+ if let hir::ExprKind::Path(qpath) = &ex.kind
+ && ex.span == self.borrow_span
+ {
+ self.error_path = Some((ex, qpath));
+ }
+
+ if let hir::ExprKind::Closure(closure) = ex.kind
+ && ex.span.contains(self.borrow_span)
+ // To support cases like `|| { v.call(|this| v.get()) }`
+ // FIXME: actually support such cases (need to figure out how to move from the capture place to original local)
+ && self.res.as_ref().map_or(true, |(prev_res, _)| prev_res.span.contains(ex.span))
+ {
+ self.res = Some((ex, closure));
+ }
+
+ hir::intravisit::walk_expr(self, ex);
+ }
+ }
+
+ // Find the closure that most tightly wraps `capture_kind_span`
+ let mut finder =
+ ClosureFinder { hir, borrow_span: capture_kind_span, res: None, error_path: None };
+ finder.visit_expr(body_expr);
+ let Some((closure_expr, closure)) = finder.res else { return };
+
+ let typeck_results = tcx.typeck(self.mir_def_id());
+
+ // Check that the parent of the closure is a method call,
+ // with receiver matching with local's type (modulo refs)
+ let parent = hir.parent_id(closure_expr.hir_id);
+ if let hir::Node::Expr(parent) = hir.get(parent) {
+ if let hir::ExprKind::MethodCall(_, recv, ..) = parent.kind {
+ let recv_ty = typeck_results.expr_ty(recv);
+
+ if recv_ty.peel_refs() != local_ty {
+ return;
+ }
+ }
+ }
+
+ // Get closure's arguments
+ let ty::Closure(_, substs) = typeck_results.expr_ty(closure_expr).kind() else { /* hir::Closure can be a generator too */ return };
+ let sig = substs.as_closure().sig();
+ let tupled_params =
+ tcx.erase_late_bound_regions(sig.inputs().iter().next().unwrap().map_bound(|&b| b));
+ let ty::Tuple(params) = tupled_params.kind() else { return };
+
+ // Find the first argument with a matching type, get its name
+ let Some((_, this_name)) = params
+ .iter()
+ .zip(hir.body_param_names(closure.body))
+ .find(|(param_ty, name)|{
+ // FIXME: also support deref for stuff like `Rc` arguments
+ param_ty.peel_refs() == local_ty && name != &Ident::empty()
+ })
+ else { return };
+
+ let spans;
+ if let Some((_path_expr, qpath)) = finder.error_path
+ && let hir::QPath::Resolved(_, path) = qpath
+ && let hir::def::Res::Local(local_id) = path.res
+ {
+ // Find all references to the problematic variable in this closure body
+
+ struct VariableUseFinder {
+ local_id: hir::HirId,
+ spans: Vec<Span>,
+ }
+ impl<'hir> Visitor<'hir> for VariableUseFinder {
+ fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
+ if let hir::ExprKind::Path(qpath) = &ex.kind
+ && let hir::QPath::Resolved(_, path) = qpath
+ && let hir::def::Res::Local(local_id) = path.res
+ && local_id == self.local_id
+ {
+ self.spans.push(ex.span);
+ }
+
+ hir::intravisit::walk_expr(self, ex);
+ }
+ }
+
+ let mut finder = VariableUseFinder { local_id, spans: Vec::new() };
+ finder.visit_expr(hir.body(closure.body).value);
+
+ spans = finder.spans;
+ } else {
+ spans = vec![capture_kind_span];
+ }
+
+ err.multipart_suggestion(
+ "try using the closure argument",
+ iter::zip(spans, iter::repeat(this_name.to_string())).collect(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+
+ fn suggest_binding_for_closure_capture_self(
+ &self,
+ err: &mut Diagnostic,
+ issued_spans: &UseSpans<'tcx>,
+ ) {
+ let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return };
+ let hir = self.infcx.tcx.hir();
struct ExpressionFinder<'hir> {
capture_span: Span,
@@ -1458,34 +1635,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
})
}
- /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
- ///
- /// Depending on the origin of the StorageDeadOrDrop, this may be
- /// reported as either a drop or an illegal mutation of a borrowed value.
- /// The latter is preferred when the this is a drop triggered by a
- /// reassignment, as it's more user friendly to report a problem with the
- /// explicit assignment than the implicit drop.
- #[instrument(level = "debug", skip(self))]
- pub(crate) fn report_storage_dead_or_drop_of_borrowed(
- &mut self,
- location: Location,
- place_span: (Place<'tcx>, Span),
- borrow: &BorrowData<'tcx>,
- ) {
- // It's sufficient to check the last desugaring as Replace is the last
- // one to be applied.
- if let Some(DesugaringKind::Replace) = place_span.1.desugaring_kind() {
- self.report_illegal_mutation_of_borrowed(location, place_span, borrow)
- } else {
- self.report_borrowed_value_does_not_live_long_enough(
- location,
- borrow,
- place_span,
- Some(WriteKind::StorageDeadOrDrop),
- )
- }
- }
-
/// This means that some data referenced by `borrow` needs to live
/// past the point where the StorageDeadOrDrop of `place` occurs.
/// This is usually interpreted as meaning that `place` has too
@@ -1731,9 +1880,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 +2099,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
}
@@ -2029,7 +2184,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let tcx = self.infcx.tcx;
let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
- let return_ty = tcx.erase_regions(return_ty);
// to avoid panics
if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator)
@@ -2099,7 +2253,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
);
err.span_suggestion_verbose(
sugg_span,
- &format!(
+ format!(
"to force the {} to take ownership of {} (and any \
other referenced variables), use the `move` keyword",
kind, captured_var
@@ -2111,7 +2265,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
match category {
ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
let msg = format!("{} is returned here", kind);
- err.span_note(constraint_span, &msg);
+ err.span_note(constraint_span, msg);
}
ConstraintCategory::CallArgument(_) => {
fr_name.highlight_region_name(&mut err);
@@ -2122,7 +2276,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
);
} else {
let msg = format!("{scope} requires argument type to outlive `{fr_name}`");
- err.span_note(constraint_span, &msg);
+ err.span_note(constraint_span, msg);
}
}
_ => bug!(
@@ -2382,11 +2536,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 +2553,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,
@@ -2424,7 +2583,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
Some((method_did, method_substs)),
) = (
&self.body[loan.reserve_location.block].terminator,
- rustc_const_eval::util::find_self_call(
+ rustc_middle::util::find_self_call(
tcx,
self.body,
loan.assigned_place.local,
@@ -2439,7 +2598,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
});
if let Some(Ok(instance)) = deref_target {
let deref_target_ty = instance.ty(tcx, self.param_env);
- err.note(&format!(
+ err.note(format!(
"borrow occurs due to deref coercion to `{}`",
deref_target_ty
));
@@ -2993,7 +3152,7 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
- diag.help(&format!(
+ diag.help(format!(
"use data from the highlighted arguments which match the `{}` lifetime of \
the return type",
region_name,
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 8860395e7..1d430a93a 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -3,7 +3,7 @@
use rustc_errors::{Applicability, Diagnostic};
use rustc_hir as hir;
use rustc_hir::intravisit::Visitor;
-use rustc_index::vec::IndexSlice;
+use rustc_index::IndexSlice;
use rustc_infer::infer::NllRegionVariableOrigin;
use rustc_middle::mir::{
Body, CastKind, ConstraintCategory, FakeReadCause, Local, LocalInfo, Location, Operand, Place,
@@ -90,7 +90,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
{
err.span_label(
pat.span,
- &format!("binding `{ident}` declared here"),
+ format!("binding `{ident}` declared here"),
);
}
}
@@ -118,7 +118,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
let path_span = path_span.unwrap();
// path_span is only present in the case of closure capture
assert!(matches!(later_use_kind, LaterUseKind::ClosureCapture));
- if !borrow_span.map_or(false, |sp| sp.overlaps(var_or_use_span)) {
+ if !borrow_span.is_some_and(|sp| sp.overlaps(var_or_use_span)) {
let path_label = "used here by closure";
let capture_kind_label = message;
err.span_label(
@@ -224,12 +224,9 @@ impl<'tcx> BorrowExplanation<'tcx> {
if info.tail_result_is_ignored {
// #85581: If the first mutable borrow's scope contains
// the second borrow, this suggestion isn't helpful.
- if !multiple_borrow_span
- .map(|(old, new)| {
- old.to(info.span.shrink_to_hi()).contains(new)
- })
- .unwrap_or(false)
- {
+ if !multiple_borrow_span.is_some_and(|(old, new)| {
+ old.to(info.span.shrink_to_hi()).contains(new)
+ }) {
err.span_suggestion_verbose(
info.span.shrink_to_hi(),
"consider adding semicolon after the expression so its \
@@ -323,7 +320,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
err.span_suggestion_verbose(
span.shrink_to_hi(),
- &msg,
+ msg,
format!(" + {suggestable_name}"),
Applicability::Unspecified,
);
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 110354a20..20370e4c6 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -1,13 +1,16 @@
//! Borrow checker diagnostics.
+use crate::session_diagnostics::{
+ CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause,
+ CaptureVarKind, CaptureVarPathUseCause, OnClosureNote,
+};
use itertools::Itertools;
-use rustc_const_eval::util::{call_kind, CallDesugaringKind};
use rustc_errors::{Applicability, Diagnostic};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::GeneratorKind;
-use rustc_index::vec::IndexSlice;
-use rustc_infer::infer::{LateBoundRegionConversionTime, TyCtxtInferExt};
+use rustc_index::IndexSlice;
+use rustc_infer::infer::LateBoundRegionConversionTime;
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::{
AggregateKind, Constant, FakeReadCause, Local, LocalInfo, LocalKind, Location, Operand, Place,
@@ -15,6 +18,7 @@ use rustc_middle::mir::{
};
use rustc_middle::ty::print::Print;
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
+use rustc_middle::util::{call_kind, CallDesugaringKind};
use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
use rustc_span::def_id::LocalDefId;
use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
@@ -45,7 +49,7 @@ pub(crate) use mutability_errors::AccessKind;
pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder;
pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
pub(crate) use region_name::{RegionName, RegionNameSource};
-pub(crate) use rustc_const_eval::util::CallKind;
+pub(crate) use rustc_middle::util::CallKind;
pub(super) struct DescribePlaceOpt {
pub including_downcast: bool,
@@ -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(
@@ -874,7 +843,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}) = &self.body[location.block].terminator
{
let Some((method_did, method_substs)) =
- rustc_const_eval::util::find_self_call(
+ rustc_middle::util::find_self_call(
self.infcx.tcx,
&self.body,
target_temp,
@@ -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 } => {
@@ -1074,35 +1042,27 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
let ty = moved_place.ty(self.body, tcx).ty;
let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) {
- Some(def_id) => {
- let infcx = self.infcx.tcx.infer_ctxt().build();
- type_known_to_meet_bound_modulo_regions(
- &infcx,
- self.param_env,
- tcx.mk_imm_ref(tcx.lifetimes.re_erased, tcx.erase_regions(ty)),
- def_id,
- )
- }
+ Some(def_id) => type_known_to_meet_bound_modulo_regions(
+ &self.infcx,
+ self.param_env,
+ tcx.mk_imm_ref(tcx.lifetimes.re_erased, ty),
+ def_id,
+ ),
_ => 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.
@@ -1113,7 +1073,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if !is_loop_move {
err.span_suggestion_verbose(
move_span.shrink_to_lo(),
- &format!(
+ format!(
"consider creating a fresh reborrow of {} here",
self.describe_place(moved_place.as_ref())
.map(|n| format!("`{n}`"))
@@ -1125,44 +1085,49 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
} else {
- err.span_label(
- fn_call_span,
- &format!(
- "{place_name} {partially_str}moved due to this method call{loop_message}",
- ),
- );
-
- let infcx = tcx.infer_ctxt().build();
+ if let Some((CallDesugaringKind::Await, _)) = desugaring {
+ err.subdiagnostic(CaptureReasonLabel::Await {
+ fn_call_span,
+ place_name: &place_name,
+ is_partial,
+ is_loop_message,
+ });
+ } else {
+ err.subdiagnostic(CaptureReasonLabel::MethodCall {
+ fn_call_span,
+ place_name: &place_name,
+ is_partial,
+ is_loop_message,
+ });
+ }
// Erase and shadow everything that could be passed to the new infcx.
- let ty = tcx.erase_regions(moved_place.ty(self.body, tcx).ty);
- let method_substs = tcx.erase_regions(method_substs);
+ let ty = moved_place.ty(self.body, tcx).ty;
if let ty::Adt(def, substs) = ty.kind()
&& Some(def.did()) == tcx.lang_items().pin_type()
&& let ty::Ref(_, _, hir::Mutability::Mut) = substs.type_at(0).kind()
- && let self_ty = infcx.instantiate_binder_with_fresh_vars(
+ && let self_ty = self.infcx.instantiate_binder_with_fresh_vars(
fn_call_span,
LateBoundRegionConversionTime::FnCall,
tcx.fn_sig(method_did).subst(tcx, method_substs).input(0),
)
- && infcx.can_eq(self.param_env, ty, self_ty)
+ && self.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])
+ && let trait_ref = ty::TraitRef::new(tcx, clone_trait, [ty])
&& let o = Obligation::new(
tcx,
ObligationCause::dummy(),
self.param_env,
ty::Binder::dummy(trait_ref),
)
- && infcx.predicate_must_hold_modulo_regions(&o)
+ && self.infcx.predicate_must_hold_modulo_regions(&o)
{
err.span_suggestion_verbose(
fn_call_span.shrink_to_lo(),
@@ -1177,10 +1142,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 =
@@ -1190,34 +1156,32 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
ty::Adt(def, ..) => Some(def.did()),
_ => None,
});
- let is_option_or_result = parent_self_ty.map_or(false, |def_id| {
+ let is_option_or_result = parent_self_ty.is_some_and(|def_id| {
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 3662bec0c..8b77477a3 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,
+ }
+ });
}
}
}
@@ -518,7 +533,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
suggestions.sort_unstable_by_key(|&(span, _, _)| span);
suggestions.dedup_by_key(|&mut (span, _, _)| span);
for (span, msg, suggestion) in suggestions {
- err.span_suggestion_verbose(span, &msg, suggestion, Applicability::MachineApplicable);
+ err.span_suggestion_verbose(span, msg, suggestion, Applicability::MachineApplicable);
}
}
@@ -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 9d9040096..d0e17bf5a 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -1,4 +1,4 @@
-use rustc_errors::{Applicability, Diagnostic};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::intravisit::Visitor;
use rustc_hir::Node;
@@ -15,8 +15,8 @@ use rustc_span::{sym, BytePos, Span};
use rustc_target::abi::FieldIdx;
use crate::diagnostics::BorrowedContentSource;
+use crate::util::FindAssignments;
use crate::MirBorrowckCtxt;
-use rustc_const_eval::util::collect_writes::FindAssignments;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum AccessKind {
@@ -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
}
@@ -285,8 +289,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
.body
.local_decls
.get(local)
- .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local]))
- .unwrap_or(false) =>
+ .is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) =>
{
let decl = &self.body.local_decls[local];
err.span_label(span, format!("cannot {act}"));
@@ -439,7 +442,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
.sess
.source_map()
.span_to_snippet(span)
- .map_or(false, |snippet| snippet.starts_with("&mut ")) =>
+ .is_ok_and(|snippet| snippet.starts_with("&mut ")) =>
{
err.span_label(span, format!("cannot {act}"));
err.span_suggestion(
@@ -474,179 +477,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
match self.local_names[local] {
Some(name) if !local_decl.from_compiler_desugaring() => {
- let label = match *local_decl.local_info() {
- LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
- let (span, suggestion) =
- suggest_ampmut_self(self.infcx.tcx, local_decl);
- Some((true, span, suggestion))
- }
-
- LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
- binding_mode: ty::BindingMode::BindByValue(_),
- opt_ty_info,
- ..
- })) => {
- // check if the RHS is from desugaring
- let opt_assignment_rhs_span =
- self.body.find_assignments(local).first().map(|&location| {
- if let Some(mir::Statement {
- source_info: _,
- kind:
- mir::StatementKind::Assign(box (
- _,
- mir::Rvalue::Use(mir::Operand::Copy(place)),
- )),
- }) = self.body[location.block]
- .statements
- .get(location.statement_index)
- {
- self.body.local_decls[place.local].source_info.span
- } else {
- self.body.source_info(location).span
- }
- });
- match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
- // on for loops, RHS points to the iterator part
- Some(DesugaringKind::ForLoop) => {
- self.suggest_similar_mut_method_for_for_loop(&mut err);
- err.span_label(opt_assignment_rhs_span.unwrap(), format!(
- "this iterator yields `{pointer_sigil}` {pointer_desc}s",
- ));
- None
- }
- // don't create labels for compiler-generated spans
- Some(_) => None,
- None => {
- let label = if name != kw::SelfLower {
- suggest_ampmut(
- self.infcx.tcx,
- local_decl,
- opt_assignment_rhs_span,
- opt_ty_info,
- )
- } else {
- match local_decl.local_info() {
- LocalInfo::User(mir::BindingForm::Var(
- mir::VarBindingForm {
- opt_ty_info: None, ..
- },
- )) => {
- let (span, sugg) = suggest_ampmut_self(
- self.infcx.tcx,
- local_decl,
- );
- (true, span, sugg)
- }
- // explicit self (eg `self: &'a Self`)
- _ => suggest_ampmut(
- self.infcx.tcx,
- local_decl,
- opt_assignment_rhs_span,
- opt_ty_info,
- ),
- }
- };
- Some(label)
- }
- }
- }
-
- LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
- binding_mode: ty::BindingMode::BindByReference(_),
- ..
- })) => {
- let pattern_span = local_decl.source_info.span;
- suggest_ref_mut(self.infcx.tcx, pattern_span)
- .map(|replacement| (true, pattern_span, replacement))
- }
-
- _ => unreachable!(),
- };
-
- match label {
- Some((true, err_help_span, suggested_code)) => {
- let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
- if !is_trait_sig {
- err.span_suggestion_verbose(
- err_help_span,
- &format!(
- "consider changing this to be a mutable {pointer_desc}"
- ),
- suggested_code,
- Applicability::MachineApplicable,
- );
- } else if let Some(x) = local_trait {
- err.span_suggestion_verbose(
- x,
- &format!(
- "consider changing that to be a mutable {pointer_desc}"
- ),
- suggested_code,
- Applicability::MachineApplicable,
- );
- }
- }
- Some((false, err_label_span, message)) => {
- struct BindingFinder {
- span: Span,
- hir_id: Option<hir::HirId>,
- }
-
- impl<'tcx> Visitor<'tcx> for BindingFinder {
- fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
- if let hir::StmtKind::Local(local) = s.kind {
- if local.pat.span == self.span {
- self.hir_id = Some(local.hir_id);
- }
- }
- hir::intravisit::walk_stmt(self, s);
- }
- }
- let hir_map = self.infcx.tcx.hir();
- let def_id = self.body.source.def_id();
- let hir_id = hir_map.local_def_id_to_hir_id(def_id.expect_local());
- let node = hir_map.find(hir_id);
- let hir_id = if let Some(hir::Node::Item(item)) = node
- && let hir::ItemKind::Fn(.., body_id) = item.kind
- {
- let body = hir_map.body(body_id);
- let mut v = BindingFinder {
- span: err_label_span,
- hir_id: None,
- };
- v.visit_body(body);
- v.hir_id
- } else {
- None
- };
- if let Some(hir_id) = hir_id
- && let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
- {
- let (changing, span, sugg) = match local.ty {
- Some(ty) => ("changing", ty.span, message),
- None => (
- "specifying",
- local.pat.span.shrink_to_hi(),
- format!(": {message}"),
- ),
- };
- err.span_suggestion_verbose(
- span,
- &format!("consider {changing} this binding's type"),
- sugg,
- Applicability::HasPlaceholders,
- );
- } else {
- err.span_label(
- err_label_span,
- &format!(
- "consider changing this binding's type to be: `{message}`"
- ),
- );
- }
- }
- None => {}
- }
err.span_label(
span,
format!(
@@ -654,6 +484,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
so the data it refers to cannot be {acted_on}",
),
);
+
+ self.suggest_make_local_mut(&mut err, local, name);
}
_ => {
err.span_label(
@@ -675,13 +507,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
match opt_source {
Some(BorrowedContentSource::OverloadedDeref(ty)) => {
- err.help(&format!(
+ err.help(format!(
"trait `DerefMut` is required to modify through a dereference, \
but it is not implemented for `{ty}`",
));
}
Some(BorrowedContentSource::OverloadedIndex(ty)) => {
- err.help(&format!(
+ err.help(format!(
"trait `IndexMut` is required to modify indexed content, \
but it is not implemented for `{ty}`",
));
@@ -732,7 +564,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
// val[index] = rv;
// ---------- place
self.err.multipart_suggestions(
- &format!(
+ format!(
"to modify a `{}`, use `.get_mut()`, `.insert()` or the entry API",
self.ty,
),
@@ -784,7 +616,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
{
// val[index].path(args..);
self.err.multipart_suggestion(
- &format!("to modify a `{}` use `.get_mut()`", self.ty),
+ format!("to modify a `{}` use `.get_mut()`", self.ty),
vec![
(
val.span.shrink_to_hi().with_hi(index.span.lo()),
@@ -809,16 +641,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let Some(hir::Node::Item(item)) = node else { return; };
let hir::ItemKind::Fn(.., body_id) = item.kind else { return; };
let body = self.infcx.tcx.hir().body(body_id);
- let mut assign_span = span;
- // Drop desugaring is done at MIR build so it's not in the HIR
- if let Some(DesugaringKind::Replace) = span.desugaring_kind() {
- assign_span.remove_mark();
- }
- let mut v = V { assign_span, err, ty, suggested: false };
+ let mut v = V { assign_span: span, err, ty, suggested: false };
v.visit_body(body);
if !v.suggested {
- err.help(&format!(
+ err.help(format!(
"to modify a `{ty}`, use `.get_mut()`, `.insert()` or the entry API",
));
}
@@ -1127,6 +954,184 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
}
}
+
+ fn suggest_make_local_mut(
+ &self,
+ err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+ local: Local,
+ name: Symbol,
+ ) {
+ let local_decl = &self.body.local_decls[local];
+
+ let (pointer_sigil, pointer_desc) =
+ if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
+
+ let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
+ if is_trait_sig && local_trait.is_none() {
+ return;
+ }
+
+ let decl_span = match local_trait {
+ Some(span) => span,
+ None => local_decl.source_info.span,
+ };
+
+ let label = match *local_decl.local_info() {
+ LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
+ let suggestion = suggest_ampmut_self(self.infcx.tcx, decl_span);
+ Some((true, decl_span, suggestion))
+ }
+
+ LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
+ binding_mode: ty::BindingMode::BindByValue(_),
+ opt_ty_info,
+ ..
+ })) => {
+ // check if the RHS is from desugaring
+ let opt_assignment_rhs_span =
+ self.body.find_assignments(local).first().map(|&location| {
+ if let Some(mir::Statement {
+ source_info: _,
+ kind:
+ mir::StatementKind::Assign(box (
+ _,
+ mir::Rvalue::Use(mir::Operand::Copy(place)),
+ )),
+ }) = self.body[location.block].statements.get(location.statement_index)
+ {
+ self.body.local_decls[place.local].source_info.span
+ } else {
+ self.body.source_info(location).span
+ }
+ });
+ match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
+ // on for loops, RHS points to the iterator part
+ Some(DesugaringKind::ForLoop) => {
+ self.suggest_similar_mut_method_for_for_loop(err);
+ err.span_label(
+ opt_assignment_rhs_span.unwrap(),
+ format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",),
+ );
+ None
+ }
+ // don't create labels for compiler-generated spans
+ Some(_) => None,
+ None => {
+ let label = if name != kw::SelfLower {
+ suggest_ampmut(
+ self.infcx.tcx,
+ local_decl.ty,
+ decl_span,
+ opt_assignment_rhs_span,
+ opt_ty_info,
+ )
+ } else {
+ match local_decl.local_info() {
+ LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
+ opt_ty_info: None,
+ ..
+ })) => {
+ let sugg = suggest_ampmut_self(self.infcx.tcx, decl_span);
+ (true, decl_span, sugg)
+ }
+ // explicit self (eg `self: &'a Self`)
+ _ => suggest_ampmut(
+ self.infcx.tcx,
+ local_decl.ty,
+ decl_span,
+ opt_assignment_rhs_span,
+ opt_ty_info,
+ ),
+ }
+ };
+ Some(label)
+ }
+ }
+ }
+
+ LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
+ binding_mode: ty::BindingMode::BindByReference(_),
+ ..
+ })) => {
+ let pattern_span: Span = local_decl.source_info.span;
+ suggest_ref_mut(self.infcx.tcx, pattern_span)
+ .map(|span| (true, span, "mut ".to_owned()))
+ }
+
+ _ => unreachable!(),
+ };
+
+ match label {
+ Some((true, err_help_span, suggested_code)) => {
+ err.span_suggestion_verbose(
+ err_help_span,
+ format!("consider changing this to be a mutable {pointer_desc}"),
+ suggested_code,
+ Applicability::MachineApplicable,
+ );
+ }
+ Some((false, err_label_span, message)) => {
+ struct BindingFinder {
+ span: Span,
+ hir_id: Option<hir::HirId>,
+ }
+
+ impl<'tcx> Visitor<'tcx> for BindingFinder {
+ fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
+ if let hir::StmtKind::Local(local) = s.kind {
+ if local.pat.span == self.span {
+ self.hir_id = Some(local.hir_id);
+ }
+ }
+ hir::intravisit::walk_stmt(self, s);
+ }
+ }
+ let hir_map = self.infcx.tcx.hir();
+ let def_id = self.body.source.def_id();
+ let hir_id = hir_map.local_def_id_to_hir_id(def_id.expect_local());
+ let node = hir_map.find(hir_id);
+ let hir_id = if let Some(hir::Node::Item(item)) = node
+ && let hir::ItemKind::Fn(.., body_id) = item.kind
+ {
+ let body = hir_map.body(body_id);
+ let mut v = BindingFinder {
+ span: err_label_span,
+ hir_id: None,
+ };
+ v.visit_body(body);
+ v.hir_id
+ } else {
+ None
+ };
+ if let Some(hir_id) = hir_id
+ && let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
+ {
+ let (changing, span, sugg) = match local.ty {
+ Some(ty) => ("changing", ty.span, message),
+ None => (
+ "specifying",
+ local.pat.span.shrink_to_hi(),
+ format!(": {message}"),
+ ),
+ };
+ err.span_suggestion_verbose(
+ span,
+ format!("consider {changing} this binding's type"),
+ sugg,
+ Applicability::HasPlaceholders,
+ );
+ } else {
+ err.span_label(
+ err_label_span,
+ format!(
+ "consider changing this binding's type to be: `{message}`"
+ ),
+ );
+ }
+ }
+ None => {}
+ }
+ }
}
pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
@@ -1143,7 +1148,7 @@ pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<
// suggest removing the `&mut`.
//
// Deliberately fall into this case for all implicit self types,
- // so that we don't fall in to the next case with them.
+ // so that we don't fall into the next case with them.
kind == hir::ImplicitSelfKind::MutRef
}
_ if Some(kw::SelfLower) == local_name => {
@@ -1156,25 +1161,18 @@ pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<
}
}
-fn suggest_ampmut_self<'tcx>(
- tcx: TyCtxt<'tcx>,
- local_decl: &mir::LocalDecl<'tcx>,
-) -> (Span, String) {
- let sp = local_decl.source_info.span;
- (
- sp,
- match tcx.sess.source_map().span_to_snippet(sp) {
- Ok(snippet) => {
- let lt_pos = snippet.find('\'');
- if let Some(lt_pos) = lt_pos {
- format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
- } else {
- "&mut self".to_string()
- }
+fn suggest_ampmut_self<'tcx>(tcx: TyCtxt<'tcx>, span: Span) -> String {
+ match tcx.sess.source_map().span_to_snippet(span) {
+ Ok(snippet) => {
+ let lt_pos = snippet.find('\'');
+ if let Some(lt_pos) = lt_pos {
+ format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
+ } else {
+ "&mut self".to_string()
}
- _ => "&mut self".to_string(),
- },
- )
+ }
+ _ => "&mut self".to_string(),
+ }
}
// When we want to suggest a user change a local variable to be a `&mut`, there
@@ -1194,72 +1192,89 @@ fn suggest_ampmut_self<'tcx>(
// by trying (3.), then (2.) and finally falling back on (1.).
fn suggest_ampmut<'tcx>(
tcx: TyCtxt<'tcx>,
- local_decl: &mir::LocalDecl<'tcx>,
+ decl_ty: Ty<'tcx>,
+ decl_span: Span,
opt_assignment_rhs_span: Option<Span>,
opt_ty_info: Option<Span>,
) -> (bool, Span, String) {
+ // if there is a RHS and it starts with a `&` from it, then check if it is
+ // mutable, and if not, put suggest putting `mut ` to make it mutable.
+ // we don't have to worry about lifetime annotations here because they are
+ // not valid when taking a reference. For example, the following is not valid Rust:
+ //
+ // let x: &i32 = &'a 5;
+ // ^^ lifetime annotation not allowed
+ //
if let Some(assignment_rhs_span) = opt_assignment_rhs_span
&& let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span)
+ && let Some(stripped) = src.strip_prefix('&')
{
- let is_mutbl = |ty: &str| -> bool {
- if let Some(rest) = ty.strip_prefix("mut") {
- match rest.chars().next() {
- // e.g. `&mut x`
- Some(c) if c.is_whitespace() => true,
- // e.g. `&mut(x)`
- Some('(') => true,
- // e.g. `&mut{x}`
- Some('{') => true,
- // e.g. `&mutablevar`
- _ => false,
- }
- } else {
- false
+ let is_mut = if let Some(rest) = stripped.trim_start().strip_prefix("mut") {
+ match rest.chars().next() {
+ // e.g. `&mut x`
+ Some(c) if c.is_whitespace() => true,
+ // e.g. `&mut(x)`
+ Some('(') => true,
+ // e.g. `&mut{x}`
+ Some('{') => true,
+ // e.g. `&mutablevar`
+ _ => false,
}
+ } else {
+ false
};
- if let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace)) {
- let lt_name = &src[1..ws_pos];
- let ty = src[ws_pos..].trim_start();
- if !is_mutbl(ty) {
- return (true, assignment_rhs_span, format!("&{lt_name} mut {ty}"));
- }
- } else if let Some(stripped) = src.strip_prefix('&') {
- let stripped = stripped.trim_start();
- if !is_mutbl(stripped) {
- return (true, assignment_rhs_span, format!("&mut {stripped}"));
- }
+ // if the reference is already mutable then there is nothing we can do
+ // here.
+ if !is_mut {
+ let span = assignment_rhs_span;
+ // shrink the span to just after the `&` in `&variable`
+ let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
+
+ // FIXME(Ezrashaw): returning is bad because we still might want to
+ // update the annotated type, see #106857.
+ return (true, span, "mut ".to_owned());
}
}
- let (suggestability, highlight_span) = match opt_ty_info {
+ let (binding_exists, span) = match opt_ty_info {
// if this is a variable binding with an explicit type,
- // try to highlight that for the suggestion.
+ // then we will suggest changing it to be mutable.
+ // this is `Applicability::MachineApplicable`.
Some(ty_span) => (true, ty_span),
- // otherwise, just highlight the span associated with
- // the (MIR) LocalDecl.
- None => (false, local_decl.source_info.span),
+ // otherwise, we'll suggest *adding* an annotated type, we'll suggest
+ // the RHS's type for that.
+ // this is `Applicability::HasPlaceholders`.
+ None => (false, decl_span),
};
- if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span)
- && let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace))
+ // if the binding already exists and is a reference with a explicit
+ // lifetime, then we can suggest adding ` mut`. this is special-cased from
+ // the path without a explicit lifetime.
+ if let Ok(src) = tcx.sess.source_map().span_to_snippet(span)
+ && src.starts_with("&'")
+ // note that `& 'a T` is invalid so this is correct.
+ && let Some(ws_pos) = src.find(char::is_whitespace)
{
- let lt_name = &src[1..ws_pos];
- let ty = &src[ws_pos..];
- return (true, highlight_span, format!("&{lt_name} mut{ty}"));
- }
+ let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
+ (true, span, " mut".to_owned())
+ // if there is already a binding, we modify it to be `mut`
+ } else if binding_exists {
+ // shrink the span to just after the `&` in `&variable`
+ let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
+ (true, span, "mut ".to_owned())
+ } else {
+ // otherwise, suggest that the user annotates the binding; we provide the
+ // type of the local.
+ let ty_mut = decl_ty.builtin_deref(true).unwrap();
+ assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
- let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
- assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
- (
- suggestability,
- highlight_span,
- if local_decl.ty.is_ref() {
- format!("&mut {}", ty_mut.ty)
- } else {
- format!("*mut {}", ty_mut.ty)
- },
- )
+ (
+ false,
+ span,
+ format!("{}mut {}", if decl_ty.is_ref() {"&"} else {"*"}, ty_mut.ty)
+ )
+ }
}
fn is_closure_or_generator(ty: Ty<'_>) -> bool {
@@ -1296,11 +1311,13 @@ fn get_mut_span_in_struct_field<'tcx>(
}
/// If possible, suggest replacing `ref` with `ref mut`.
-fn suggest_ref_mut(tcx: TyCtxt<'_>, binding_span: Span) -> Option<String> {
- let hi_src = tcx.sess.source_map().span_to_snippet(binding_span).ok()?;
- if hi_src.starts_with("ref") && hi_src["ref".len()..].starts_with(rustc_lexer::is_whitespace) {
- let replacement = format!("ref mut{}", &hi_src["ref".len()..]);
- Some(replacement)
+fn suggest_ref_mut(tcx: TyCtxt<'_>, span: Span) -> Option<Span> {
+ let pattern_str = tcx.sess.source_map().span_to_snippet(span).ok()?;
+ if pattern_str.starts_with("ref")
+ && pattern_str["ref".len()..].starts_with(rustc_lexer::is_whitespace)
+ {
+ let span = span.with_lo(span.lo() + BytePos(4)).shrink_to_lo();
+ Some(span)
} else {
None
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
index d5ece5743..b6eb9ae98 100644
--- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
@@ -125,8 +125,7 @@ impl OutlivesSuggestionBuilder {
|(r, _)| {
self.constraints_to_add
.get(r)
- .map(|r_outlived| r_outlived.as_slice().contains(fr))
- .unwrap_or(false)
+ .is_some_and(|r_outlived| r_outlived.as_slice().contains(fr))
},
);
@@ -171,7 +170,7 @@ impl OutlivesSuggestionBuilder {
if let (Some(fr_name), Some(outlived_fr_name)) = (fr_name, outlived_fr_name)
&& !matches!(outlived_fr_name.source, RegionNameSource::Static)
{
- diag.help(&format!(
+ diag.help(format!(
"consider adding the following bound: `{fr_name}: {outlived_fr_name}`",
));
}
@@ -207,7 +206,7 @@ impl OutlivesSuggestionBuilder {
// If there is exactly one suggestable constraints, then just suggest it. Otherwise, emit a
// list of diagnostics.
let mut diag = if suggested.len() == 1 {
- mbcx.infcx.tcx.sess.diagnostic().struct_help(&match suggested.last().unwrap() {
+ mbcx.infcx.tcx.sess.diagnostic().struct_help(match suggested.last().unwrap() {
SuggestedConstraint::Outlives(a, bs) => {
let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect();
format!("add bound `{a}: {}`", bs.join(" + "))
@@ -232,15 +231,15 @@ impl OutlivesSuggestionBuilder {
match constraint {
SuggestedConstraint::Outlives(a, bs) => {
let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect();
- diag.help(&format!("add bound `{a}: {}`", bs.join(" + ")));
+ diag.help(format!("add bound `{a}: {}`", bs.join(" + ")));
}
SuggestedConstraint::Equal(a, b) => {
- diag.help(&format!(
+ diag.help(format!(
"`{a}` and `{b}` must be the same: replace one with the other",
));
}
SuggestedConstraint::Static(a) => {
- diag.help(&format!("replace `{a}` with `'static`"));
+ diag.help(format!("replace `{a}` with `'static`"));
}
}
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 9fcebeb0a..8ec872e20 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -533,8 +533,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
_ => panic!("Unexpected type {ty:?}"),
};
- diag.note(&format!("requirement occurs because of {desc}",));
- diag.note(&note);
+ diag.note(format!("requirement occurs because of {desc}",));
+ diag.note(note);
diag.help("see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance");
}
}
@@ -845,7 +845,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
return;
}
- let Some((alias_tys, alias_span)) = self
+ let Some((alias_tys, alias_span, lt_addition_span)) = self
.infcx
.tcx
.return_type_impl_or_dyn_traits_with_type_alias(suitable_region.def_id) else { return; };
@@ -858,12 +858,22 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
()
}
if let TyKind::TraitObject(_, lt, _) = alias_ty.kind {
- spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string()));
+ if lt.ident.name == kw::Empty {
+ spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string()));
+ } else {
+ spans_suggs.push((lt.ident.span, "'a".to_string()));
+ }
}
}
- spans_suggs.push((alias_span.shrink_to_hi(), "<'a>".to_string()));
+
+ if let Some(lt_addition_span) = lt_addition_span {
+ spans_suggs.push((lt_addition_span, "'a, ".to_string()));
+ } else {
+ spans_suggs.push((alias_span.shrink_to_hi(), "<'a>".to_string()));
+ }
+
diag.multipart_suggestion_verbose(
- &format!(
+ format!(
"to declare that the trait object {captures}, you can add a lifetime parameter `'a` in the type alias"
),
spans_suggs,
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index f69c4829a..f38e1605f 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -622,7 +622,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
// programs, so we need to use delay_span_bug here. See #82126.
self.infcx.tcx.sess.delay_span_bug(
hir_arg.span(),
- &format!("unmatched subst and hir arg: found {kind:?} vs {hir_arg:?}"),
+ format!("unmatched subst and hir arg: found {kind:?} vs {hir_arg:?}"),
);
}
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
index 376415e3d..98418e237 100644
--- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
@@ -3,7 +3,7 @@
use crate::region_infer::RegionInferenceContext;
use crate::Upvar;
-use rustc_index::vec::{Idx, IndexSlice};
+use rustc_index::IndexSlice;
use rustc_middle::mir::{Body, Local};
use rustc_middle::ty::{RegionVid, TyCtxt};
use rustc_span::source_map::Span;
@@ -117,7 +117,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
argument_index: usize,
) -> (Option<Symbol>, Span) {
let implicit_inputs = self.universal_regions().defining_ty.implicit_inputs();
- let argument_local = Local::new(implicit_inputs + argument_index + 1);
+ let argument_local = Local::from_usize(implicit_inputs + argument_index + 1);
debug!("get_argument_name_and_span_for_region: argument_local={argument_local:?}");
let argument_name = local_names[argument_local];