summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_borrowck/src/diagnostics
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_borrowck/src/diagnostics')
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs304
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs53
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/find_all_local_uses.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs115
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs100
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs162
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs18
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs233
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs14
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/var_name.rs25
10 files changed, 677 insertions, 349 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 5e3745f17..e5a36259f 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -6,6 +6,7 @@ use rustc_errors::{
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
};
use rustc_hir as hir;
+use rustc_hir::def::Res;
use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem};
use rustc_infer::infer::TyCtxtInferExt;
@@ -20,7 +21,7 @@ use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty
use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
use rustc_span::def_id::LocalDefId;
use rustc_span::hygiene::DesugaringKind;
-use rustc_span::symbol::sym;
+use rustc_span::symbol::{kw, sym};
use rustc_span::{BytePos, Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt;
@@ -29,6 +30,7 @@ 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::{
borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
@@ -156,7 +158,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
err.span_note(
MultiSpan::from_spans(reinit_spans),
&if reinits <= 3 {
- format!("these {} reinitializations might get skipped", reinits)
+ format!("these {reinits} reinitializations might get skipped")
} else {
format!(
"these 3 reinitializations and {} other{} might get skipped",
@@ -194,7 +196,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if !seen_spans.contains(&move_span) {
if !closure {
- self.suggest_ref_or_clone(mpi, move_span, &mut err, &mut in_pattern);
+ self.suggest_ref_or_clone(
+ mpi,
+ move_span,
+ &mut err,
+ &mut in_pattern,
+ move_spans,
+ );
}
self.explain_captures(
@@ -219,9 +227,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
err.span_label(
span,
format!(
- "value {} here after {}move",
+ "value {} here after {partial_str}move",
desired_action.as_verb_in_past_tense(),
- partial_str
),
);
}
@@ -251,7 +258,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
&format!(
"consider creating a fresh reborrow of {} here",
self.describe_place(moved_place)
- .map(|n| format!("`{}`", n))
+ .map(|n| format!("`{n}`"))
.unwrap_or_else(|| "the mutable reference".to_string()),
),
"&mut *",
@@ -265,7 +272,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
);
let note_msg = match opt_name {
- Some(name) => format!("`{}`", name),
+ Some(name) => format!("`{name}`"),
None => "value".to_owned(),
};
if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, &note_msg) {
@@ -291,9 +298,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
} = use_spans
{
err.note(&format!(
- "{} occurs due to deref coercion to `{}`",
+ "{} occurs due to deref coercion to `{deref_target_ty}`",
desired_action.as_noun(),
- deref_target_ty
));
// Check first whether the source is accessible (issue #87060)
@@ -312,6 +318,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
move_span: Span,
err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
in_pattern: &mut bool,
+ move_spans: UseSpans<'_>,
) {
struct ExpressionFinder<'hir> {
expr_span: Span,
@@ -351,7 +358,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if let Some(hir::Node::Item(hir::Item {
kind: hir::ItemKind::Fn(_, _, body_id),
..
- })) = hir.find(hir.local_def_id_to_hir_id(self.mir_def_id()))
+ })) = hir.find(self.mir_hir_id())
&& let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id)
{
let place = &self.move_data.move_paths[mpi].place;
@@ -387,7 +394,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
let typeck = self.infcx.tcx.typeck(self.mir_def_id());
- let hir_id = hir.get_parent_node(expr.hir_id);
+ let hir_id = hir.parent_id(expr.hir_id);
if let Some(parent) = hir.find(hir_id) {
let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
&& let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
@@ -440,6 +447,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
) = call_expr.kind
{
// Do not suggest `.clone()` in a `for` loop, we already suggest borrowing.
+ } else if let UseSpans::FnSelfUse {
+ kind: CallKind::Normal { .. },
+ ..
+ } = move_spans {
+ // We already suggest cloning for these cases in `explain_captures`.
} else {
self.suggest_cloning(err, ty, move_span);
}
@@ -515,26 +527,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// that are *partially* initialized by assigning to a field of an uninitialized
// binding. We differentiate between them for more accurate wording here.
"isn't fully initialized"
- } else if spans
- .iter()
- .filter(|i| {
- // We filter these to avoid misleading wording in cases like the following,
- // where `x` has an `init`, but it is in the same place we're looking at:
- // ```
- // let x;
- // x += 1;
- // ```
- !i.contains(span)
- // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs`
- && !visitor
- .errors
- .iter()
- .map(|(sp, _)| *sp)
- .any(|sp| span < sp && !sp.contains(span))
- })
- .count()
- == 0
- {
+ } else if !spans.iter().any(|i| {
+ // We filter these to avoid misleading wording in cases like the following,
+ // where `x` has an `init`, but it is in the same place we're looking at:
+ // ```
+ // let x;
+ // x += 1;
+ // ```
+ !i.contains(span)
+ // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs`
+ && !visitor
+ .errors
+ .iter()
+ .map(|(sp, _)| *sp)
+ .any(|sp| span < sp && !sp.contains(span))
+ }) {
show_assign_sugg = true;
"isn't initialized"
} else {
@@ -649,7 +656,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if !assign_value.is_empty() {
err.span_suggestion_verbose(
sugg_span.shrink_to_hi(),
- format!("consider assigning a value"),
+ "consider assigning a value",
format!(" = {}", assign_value),
Applicability::MaybeIncorrect,
);
@@ -666,40 +673,34 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let tcx = self.infcx.tcx;
// Find out if the predicates show that the type is a Fn or FnMut
- let find_fn_kind_from_did = |predicates: ty::EarlyBinder<
- &[(ty::Predicate<'tcx>, Span)],
- >,
- substs| {
- predicates.0.iter().find_map(|(pred, _)| {
- let pred = if let Some(substs) = substs {
- predicates.rebind(*pred).subst(tcx, substs).kind().skip_binder()
- } else {
- pred.kind().skip_binder()
- };
- if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred && pred.self_ty() == ty {
- if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
- return Some(hir::Mutability::Not);
- } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
- return Some(hir::Mutability::Mut);
- }
+ let find_fn_kind_from_did = |(pred, _): (ty::Predicate<'tcx>, _)| {
+ if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred.kind().skip_binder()
+ && pred.self_ty() == ty
+ {
+ if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
+ return Some(hir::Mutability::Not);
+ } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
+ return Some(hir::Mutability::Mut);
}
- None
- })
+ }
+ None
};
// If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
// borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
// These types seem reasonably opaque enough that they could be substituted with their
// borrowed variants in a function body when we see a move error.
- let borrow_level = match ty.kind() {
- ty::Param(_) => find_fn_kind_from_did(
- tcx.bound_explicit_predicates_of(self.mir_def_id().to_def_id())
- .map_bound(|p| p.predicates),
- None,
- ),
- ty::Opaque(did, substs) => {
- find_fn_kind_from_did(tcx.bound_explicit_item_bounds(*did), Some(*substs))
- }
+ let borrow_level = match *ty.kind() {
+ ty::Param(_) => tcx
+ .explicit_predicates_of(self.mir_def_id().to_def_id())
+ .predicates
+ .iter()
+ .copied()
+ .find_map(find_fn_kind_from_did),
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => tcx
+ .bound_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() {
ty::ClosureKind::Fn => Some(hir::Mutability::Not),
ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
@@ -745,7 +746,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
err.span_suggestion_verbose(
span.shrink_to_hi(),
"consider cloning the value if the performance cost is acceptable",
- ".clone()".to_string(),
+ ".clone()",
Applicability::MachineApplicable,
);
}
@@ -943,7 +944,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
(BorrowKind::Mut { .. }, BorrowKind::Shared) => {
first_borrow_desc = "immutable ";
- self.cannot_reborrow_already_borrowed(
+ let mut err = self.cannot_reborrow_already_borrowed(
span,
&desc_place,
&msg_place,
@@ -953,7 +954,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
"immutable",
&msg_borrow,
None,
- )
+ );
+ self.suggest_binding_for_closure_capture_self(
+ &mut err,
+ issued_borrow.borrowed_place,
+ &issued_spans,
+ );
+ err
}
(BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
@@ -1235,6 +1242,138 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
+ fn suggest_binding_for_closure_capture_self(
+ &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();
+
+ // check whether the borrowed place is capturing `self` by mut reference
+ 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 };
+
+ struct ExpressionFinder<'hir> {
+ capture_span: Span,
+ closure_change_spans: Vec<Span>,
+ closure_arg_span: Option<Span>,
+ in_closure: bool,
+ suggest_arg: String,
+ hir: rustc_middle::hir::map::Map<'hir>,
+ closure_local_id: Option<hir::HirId>,
+ closure_call_changes: Vec<(Span, String)>,
+ }
+ impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
+ fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
+ if e.span.contains(self.capture_span) {
+ if let hir::ExprKind::Closure(&hir::Closure {
+ movability: None,
+ body,
+ fn_arg_span,
+ fn_decl: hir::FnDecl{ inputs, .. },
+ ..
+ }) = e.kind &&
+ let Some(hir::Node::Expr(body )) = self.hir.find(body.hir_id) {
+ self.suggest_arg = "this: &Self".to_string();
+ if inputs.len() > 0 {
+ self.suggest_arg.push_str(", ");
+ }
+ self.in_closure = true;
+ self.closure_arg_span = fn_arg_span;
+ self.visit_expr(body);
+ self.in_closure = false;
+ }
+ }
+ if let hir::Expr { kind: hir::ExprKind::Path(path), .. } = e {
+ if let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path &&
+ seg.ident.name == kw::SelfLower && self.in_closure {
+ self.closure_change_spans.push(e.span);
+ }
+ }
+ hir::intravisit::walk_expr(self, e);
+ }
+
+ fn visit_local(&mut self, local: &'hir hir::Local<'hir>) {
+ if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = local.pat &&
+ let Some(init) = local.init
+ {
+ if let hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure {
+ movability: None,
+ ..
+ }), .. } = init &&
+ init.span.contains(self.capture_span) {
+ self.closure_local_id = Some(*hir_id);
+ }
+ }
+ hir::intravisit::walk_local(self, local);
+ }
+
+ fn visit_stmt(&mut self, s: &'hir hir::Stmt<'hir>) {
+ if let hir::StmtKind::Semi(e) = s.kind &&
+ let hir::ExprKind::Call(hir::Expr { kind: hir::ExprKind::Path(path), ..}, args) = e.kind &&
+ let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path &&
+ let Res::Local(hir_id) = seg.res &&
+ Some(hir_id) == self.closure_local_id {
+ let (span, arg_str) = if args.len() > 0 {
+ (args[0].span.shrink_to_lo(), "self, ".to_string())
+ } else {
+ let span = e.span.trim_start(seg.ident.span).unwrap_or(e.span);
+ (span, "(self)".to_string())
+ };
+ self.closure_call_changes.push((span, arg_str));
+ }
+ hir::intravisit::walk_stmt(self, s);
+ }
+ }
+
+ if let Some(hir::Node::ImplItem(
+ hir::ImplItem { kind: hir::ImplItemKind::Fn(_fn_sig, body_id), .. }
+ )) = hir.find(self.mir_hir_id()) &&
+ let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) {
+ let mut finder = ExpressionFinder {
+ capture_span: *capture_kind_span,
+ closure_change_spans: vec![],
+ closure_arg_span: None,
+ in_closure: false,
+ suggest_arg: String::new(),
+ closure_local_id: None,
+ closure_call_changes: vec![],
+ hir,
+ };
+ finder.visit_expr(expr);
+
+ if finder.closure_change_spans.is_empty() || finder.closure_call_changes.is_empty() {
+ return;
+ }
+
+ let mut sugg = vec![];
+ let sm = self.infcx.tcx.sess.source_map();
+
+ if let Some(span) = finder.closure_arg_span {
+ sugg.push((sm.next_point(span.shrink_to_lo()).shrink_to_hi(), finder.suggest_arg));
+ }
+ for span in finder.closure_change_spans {
+ sugg.push((span, "this".to_string()));
+ }
+
+ for (span, suggest) in finder.closure_call_changes {
+ sugg.push((span, suggest));
+ }
+
+ err.multipart_suggestion_verbose(
+ "try explicitly pass `&Self` into the Closure as an argument",
+ sugg,
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+
/// Returns the description of the root place for a conflicting borrow and the full
/// descriptions of the places that caused the conflict.
///
@@ -1418,6 +1557,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// and `move` will not help here.
(
Some(name),
+ BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _),
+ ) => self.report_escaping_closure_capture(
+ borrow_spans,
+ borrow_span,
+ &RegionName {
+ name: self.synthesize_region_name(),
+ source: RegionNameSource::Static,
+ },
+ ConstraintCategory::CallArgument(None),
+ var_or_use_span,
+ &format!("`{}`", name),
+ "block",
+ ),
+ (
+ Some(name),
BorrowExplanation::MustBeValidFor {
category:
category @ (ConstraintCategory::Return(_)
@@ -1436,6 +1590,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
category,
span,
&format!("`{}`", name),
+ "function",
),
(
name,
@@ -1716,7 +1871,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
/// We check that there's a single level of block nesting to ensure always correct
/// suggestions. If we don't, then we only provide a free-form message to avoid
- /// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`.
+ /// misleading users in cases like `tests/ui/nll/borrowed-temporary-error.rs`.
/// We could expand the analysis to suggest hoising all of the relevant parts of
/// the users' code to make the code compile, but that could be too much.
struct NestedStatementVisitor {
@@ -1888,6 +2043,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
Some(err)
}
+ #[instrument(level = "debug", skip(self))]
fn report_escaping_closure_capture(
&mut self,
use_span: UseSpans<'tcx>,
@@ -1896,6 +2052,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
category: ConstraintCategory<'tcx>,
constraint_span: Span,
captured_var: &str,
+ scope: &str,
) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
let tcx = self.infcx.tcx;
let args_span = use_span.args_or_use();
@@ -1926,8 +2083,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
None => "closure",
};
- let mut err =
- self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
+ let mut err = self.cannot_capture_in_long_lived_closure(
+ args_span,
+ kind,
+ captured_var,
+ var_span,
+ scope,
+ );
err.span_suggestion_verbose(
sugg_span,
&format!(
@@ -1949,10 +2111,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
err.note(
"async blocks are not executed immediately and must either take a \
- reference or ownership of outside variables they use",
+ reference or ownership of outside variables they use",
);
} else {
- let msg = format!("function requires argument type to outlive `{}`", fr_name);
+ let msg = format!("{scope} requires argument type to outlive `{fr_name}`");
err.span_note(constraint_span, &msg);
}
}
@@ -2031,7 +2193,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let mut back_edge_stack = Vec::new();
predecessor_locations(self.body, location).for_each(|predecessor| {
- if location.dominates(predecessor, &self.dominators) {
+ if location.dominates(predecessor, self.dominators()) {
back_edge_stack.push(predecessor)
} else {
stack.push(predecessor);
@@ -2143,7 +2305,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let mut has_predecessor = false;
predecessor_locations(self.body, location).for_each(|predecessor| {
- if location.dominates(predecessor, &self.dominators) {
+ if location.dominates(predecessor, self.dominators()) {
back_edge_stack.push(predecessor)
} else {
stack.push(predecessor);
@@ -2669,7 +2831,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// Need to use the `rustc_middle::ty` types to compare against the
// `return_region`. Then use the `rustc_hir` type to get only
// the lifetime span.
- if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
+ if let hir::TyKind::Ref(lifetime, _) = &fn_decl.inputs[index].kind {
// With access to the lifetime, we can get
// the span of it.
arguments.push((*argument, lifetime.ident.span));
@@ -2690,7 +2852,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let return_ty = sig.output().skip_binder();
let mut return_span = fn_decl.output.span();
if let hir::FnRetTy::Return(ty) = &fn_decl.output {
- if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
+ if let hir::TyKind::Ref(lifetime, _) = ty.kind {
return_span = lifetime.ident.span;
}
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 304683618..209574709 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -77,7 +77,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
if borrow_span.map(|sp| !sp.overlaps(var_or_use_span)).unwrap_or(true) {
err.span_label(
var_or_use_span,
- format!("{}borrow later {}", borrow_desc, message),
+ format!("{borrow_desc}borrow later {message}"),
);
}
} else {
@@ -90,7 +90,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
let capture_kind_label = message;
err.span_label(
var_or_use_span,
- format!("{}borrow later {}", borrow_desc, capture_kind_label),
+ format!("{borrow_desc}borrow later {capture_kind_label}"),
);
err.span_label(path_span, path_label);
}
@@ -110,7 +110,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
};
// We can use `var_or_use_span` if either `path_span` is not present, or both spans are the same
if path_span.map(|path_span| path_span == var_or_use_span).unwrap_or(true) {
- err.span_label(var_or_use_span, format!("{}{}", borrow_desc, message));
+ err.span_label(var_or_use_span, format!("{borrow_desc}{message}"));
} else {
// path_span must be `Some` as otherwise the if condition is true
let path_span = path_span.unwrap();
@@ -121,7 +121,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
let capture_kind_label = message;
err.span_label(
var_or_use_span,
- format!("{}borrow later {}", borrow_desc, capture_kind_label),
+ format!("{borrow_desc}borrow later {capture_kind_label}"),
);
err.span_label(path_span, path_label);
}
@@ -160,12 +160,8 @@ impl<'tcx> BorrowExplanation<'tcx> {
match local_names[dropped_local] {
Some(local_name) if !local_decl.from_compiler_desugaring() => {
let message = format!(
- "{B}borrow might be used here, when `{LOC}` is dropped \
- and runs the {DTOR} for {TYPE}",
- B = borrow_desc,
- LOC = local_name,
- TYPE = type_desc,
- DTOR = dtor_desc
+ "{borrow_desc}borrow might be used here, when `{local_name}` is dropped \
+ and runs the {dtor_desc} for {type_desc}",
);
err.span_label(body.source_info(drop_loc).span, message);
@@ -180,18 +176,14 @@ impl<'tcx> BorrowExplanation<'tcx> {
err.span_label(
local_decl.source_info.span,
format!(
- "a temporary with access to the {B}borrow \
+ "a temporary with access to the {borrow_desc}borrow \
is created here ...",
- B = borrow_desc
),
);
let message = format!(
- "... and the {B}borrow might be used here, \
+ "... and the {borrow_desc}borrow might be used here, \
when that temporary is dropped \
- and runs the {DTOR} for {TYPE}",
- B = borrow_desc,
- TYPE = type_desc,
- DTOR = dtor_desc
+ and runs the {dtor_desc} for {type_desc}",
);
err.span_label(body.source_info(drop_loc).span, message);
@@ -249,20 +241,16 @@ impl<'tcx> BorrowExplanation<'tcx> {
err.span_label(
span,
format!(
- "{}requires that `{}` is borrowed for `{}`",
+ "{}requires that `{desc}` is borrowed for `{region_name}`",
category.description(),
- desc,
- region_name,
),
);
} else {
err.span_label(
span,
format!(
- "{}requires that {}borrow lasts for `{}`",
+ "{}requires that {borrow_desc}borrow lasts for `{region_name}`",
category.description(),
- borrow_desc,
- region_name,
),
);
};
@@ -270,7 +258,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
for extra in extra_info {
match extra {
ExtraConstraintInfo::PlaceholderFromPredicate(span) => {
- err.span_note(*span, format!("due to current limitations in the borrow checker, this implies a `'static` lifetime"));
+ err.span_note(*span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
}
}
}
@@ -296,15 +284,14 @@ impl<'tcx> BorrowExplanation<'tcx> {
if region_name.was_named() { region_name.name } else { kw::UnderscoreLifetime };
let msg = format!(
- "you can add a bound to the {}to make it last less than `'static` and match `{}`",
+ "you can add a bound to the {}to make it last less than `'static` and match `{region_name}`",
category.description(),
- region_name,
);
err.span_suggestion_verbose(
span.shrink_to_hi(),
&msg,
- format!(" + {}", suggestable_name),
+ format!(" + {suggestable_name}"),
Applicability::Unspecified,
);
}
@@ -444,6 +431,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
/// First span returned points to the location of the conflicting use
/// Second span if `Some` is returned in the case of closures and points
/// to the use of the path
+ #[instrument(level = "debug", skip(self))]
fn later_use_kind(
&self,
borrow: &BorrowData<'tcx>,
@@ -461,11 +449,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let block = &self.body.basic_blocks[location.block];
let kind = if let Some(&Statement {
- kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), _)),
+ kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), place)),
..
}) = block.statements.get(location.statement_index)
{
- LaterUseKind::FakeLetRead
+ if let Some(l) = place.as_local()
+ && let local_decl = &self.body.local_decls[l]
+ && local_decl.ty.is_closure()
+ {
+ LaterUseKind::ClosureCapture
+ } else {
+ LaterUseKind::FakeLetRead
+ }
} else if self.was_captured_by_trait_object(borrow) {
LaterUseKind::TraitCapture
} else if location.statement_index == block.statements.len() {
diff --git a/compiler/rustc_borrowck/src/diagnostics/find_all_local_uses.rs b/compiler/rustc_borrowck/src/diagnostics/find_all_local_uses.rs
index 498e98343..2c4d953f0 100644
--- a/compiler/rustc_borrowck/src/diagnostics/find_all_local_uses.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/find_all_local_uses.rs
@@ -9,7 +9,7 @@ use rustc_middle::mir::{Body, Local, Location};
/// Find all uses of (including assignments to) a [`Local`].
///
/// Uses `BTreeSet` so output is deterministic.
-pub(super) fn find<'tcx>(body: &Body<'tcx>, local: Local) -> BTreeSet<Location> {
+pub(super) fn find(body: &Body<'_>, local: Local) -> BTreeSet<Location> {
let mut visitor = AllLocalUsesVisitor { for_local: local, uses: BTreeSet::default() };
visitor.visit_body(body);
visitor.uses
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 4e2271a30..1b40b7143 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -6,7 +6,7 @@ use rustc_errors::{Applicability, Diagnostic};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::GeneratorKind;
-use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::{LateBoundRegionConversionTime, TyCtxtInferExt};
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::{
AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand,
@@ -18,7 +18,10 @@ use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
use rustc_span::def_id::LocalDefId;
use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
use rustc_target::abi::VariantIdx;
-use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
+use rustc_trait_selection::traits::{
+ type_known_to_meet_bound_modulo_regions, Obligation, ObligationCause,
+};
use super::borrow_set::BorrowData;
use super::MirBorrowckCtxt;
@@ -400,8 +403,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
move_prefix: &str,
) {
let message = format!(
- "{}move occurs because {} has type `{}`, which does not implement the `Copy` trait",
- move_prefix, place_desc, ty,
+ "{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);
@@ -736,11 +738,11 @@ impl<'tcx> BorrowedContentSource<'tcx> {
BorrowedContentSource::OverloadedDeref(ty) => ty
.ty_adt_def()
.and_then(|adt| match tcx.get_diagnostic_name(adt.did())? {
- name @ (sym::Rc | sym::Arc) => Some(format!("an `{}`", name)),
+ name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")),
_ => None,
})
- .unwrap_or_else(|| format!("dereference of `{}`", ty)),
- BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{}`", ty),
+ .unwrap_or_else(|| format!("dereference of `{ty}`")),
+ BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{ty}`"),
}
}
@@ -766,11 +768,11 @@ impl<'tcx> BorrowedContentSource<'tcx> {
BorrowedContentSource::OverloadedDeref(ty) => ty
.ty_adt_def()
.and_then(|adt| match tcx.get_diagnostic_name(adt.did())? {
- name @ (sym::Rc | sym::Arc) => Some(format!("an `{}`", name)),
+ name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")),
_ => None,
})
- .unwrap_or_else(|| format!("dereference of `{}`", ty)),
- BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{}`", ty),
+ .unwrap_or_else(|| format!("dereference of `{ty}`")),
+ BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{ty}`"),
}
}
@@ -1030,7 +1032,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
let place_name = self
.describe_place(moved_place.as_ref())
- .map(|n| format!("`{}`", n))
+ .map(|n| format!("`{n}`"))
.unwrap_or_else(|| "value".to_owned());
match kind {
CallKind::FnCall { fn_trait_id, .. }
@@ -1039,8 +1041,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
err.span_label(
fn_call_span,
&format!(
- "{} {}moved due to this call{}",
- place_name, partially_str, loop_message
+ "{place_name} {partially_str}moved due to this call{loop_message}",
),
);
err.span_note(
@@ -1053,36 +1054,28 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
err.span_label(
fn_call_span,
&format!(
- "{} {}moved due to usage in operator{}",
- place_name, partially_str, loop_message
+ "{place_name} {partially_str}moved due to usage in operator{loop_message}",
),
);
if self.fn_self_span_reported.insert(fn_span) {
err.span_note(
- // Check whether the source is accessible
- if self.infcx.tcx.sess.source_map().is_span_accessible(self_arg.span) {
- self_arg.span
- } else {
- fn_call_span
- },
+ self_arg.span,
"calling this operator moves the left-hand side",
);
}
}
- CallKind::Normal { self_arg, desugaring, is_option_or_result } => {
+ CallKind::Normal { self_arg, desugaring, method_did } => {
let self_arg = self_arg.unwrap();
+ let tcx = self.infcx.tcx;
if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
- let ty = moved_place.ty(self.body, self.infcx.tcx).ty;
- let suggest = match self.infcx.tcx.get_diagnostic_item(sym::IntoIterator) {
+ 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,
- infcx.tcx.mk_imm_ref(
- infcx.tcx.lifetimes.re_erased,
- infcx.tcx.erase_regions(ty),
- ),
+ tcx.mk_imm_ref(tcx.lifetimes.re_erased, tcx.erase_regions(ty)),
def_id,
DUMMY_SP,
)
@@ -1093,9 +1086,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
err.span_suggestion_verbose(
move_span.shrink_to_lo(),
&format!(
- "consider iterating over a slice of the `{}`'s content to \
+ "consider iterating over a slice of the `{ty}`'s content to \
avoid moving into the `for` loop",
- ty,
),
"&",
Applicability::MaybeIncorrect,
@@ -1105,8 +1097,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
err.span_label(
fn_call_span,
&format!(
- "{} {}moved due to this implicit call to `.into_iter()`{}",
- place_name, partially_str, loop_message
+ "{place_name} {partially_str}moved due to this implicit call to `.into_iter()`{loop_message}",
),
);
// If the moved place was a `&mut` ref, then we can
@@ -1122,7 +1113,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
&format!(
"consider creating a fresh reborrow of {} here",
self.describe_place(moved_place.as_ref())
- .map(|n| format!("`{}`", n))
+ .map(|n| format!("`{n}`"))
.unwrap_or_else(|| "the mutable reference".to_string()),
),
"&mut *",
@@ -1134,19 +1125,67 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
err.span_label(
fn_call_span,
&format!(
- "{} {}moved due to this method call{}",
- place_name, partially_str, loop_message
+ "{place_name} {partially_str}moved due to this method call{loop_message}",
),
);
+ let infcx = tcx.infer_ctxt().build();
+ let ty = tcx.erase_regions(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.replace_bound_vars_with_fresh_vars(
+ fn_call_span,
+ LateBoundRegionConversionTime::FnCall,
+ tcx.fn_sig(method_did).input(0),
+ )
+ && infcx.can_eq(self.param_env, ty, self_ty).is_ok()
+ {
+ err.span_suggestion_verbose(
+ fn_call_span.shrink_to_lo(),
+ "consider reborrowing the `Pin` instead of moving it",
+ "as_mut().".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ if let Some(clone_trait) = tcx.lang_items().clone_trait()
+ && let trait_ref = tcx.mk_trait_ref(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)
+ {
+ err.span_suggestion_verbose(
+ fn_call_span.shrink_to_lo(),
+ "you can `clone` the value and consume it, but this might not be \
+ your desired behavior",
+ "clone().".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
}
// Avoid pointing to the same function in multiple different
// 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!("this function takes ownership of the receiver `self`, which moves {}", place_name)
+ &format!("`{func}` takes ownership of the receiver `self`, which moves {place_name}")
);
}
+ let parent_did = tcx.parent(method_did);
+ let parent_self_ty = (tcx.def_kind(parent_did)
+ == rustc_hir::def::DefKind::Impl)
+ .then_some(parent_did)
+ .and_then(|did| match tcx.type_of(did).kind() {
+ ty::Adt(def, ..) => Some(def.did()),
+ _ => None,
+ });
+ let is_option_or_result = parent_self_ty.map_or(false, |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,
@@ -1161,7 +1200,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if move_span != span || !loop_message.is_empty() {
err.span_label(
move_span,
- format!("value {}moved{} here{}", partially_str, move_msg, loop_message),
+ format!("value {partially_str}moved{move_msg} here{loop_message}"),
);
}
// If the move error occurs due to a loop, don't show
@@ -1169,7 +1208,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if loop_message.is_empty() {
move_spans.var_span_label(
err,
- format!("variable {}moved due to use{}", partially_str, move_spans.describe()),
+ format!("variable {partially_str}moved due to use{}", move_spans.describe()),
"moved",
);
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 5a47f4567..6db3c858a 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -4,7 +4,7 @@ use rustc_middle::ty;
use rustc_mir_dataflow::move_paths::{
IllegalMoveOrigin, IllegalMoveOriginKind, LookupResult, MoveError, MovePathIndex,
};
-use rustc_span::Span;
+use rustc_span::{BytePos, Span};
use crate::diagnostics::{DescribePlaceOpt, UseSpans};
use crate::prefixes::PrefixSet;
@@ -148,7 +148,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
match_span: Span,
statement_span: Span,
) {
- debug!("append_binding_error(match_place={:?}, match_span={:?})", match_place, match_span);
+ debug!(?match_place, ?match_span, "append_binding_error");
let from_simple_let = match_place.is_none();
let match_place = match_place.unwrap_or(move_from);
@@ -160,7 +160,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
if let GroupedMoveError::MovesFromPlace { span, binds_to, .. } = ge
&& match_span == *span
{
- debug!("appending local({:?}) to list", bind_to);
+ debug!("appending local({bind_to:?}) to list");
if !binds_to.is_empty() {
binds_to.push(bind_to);
}
@@ -198,7 +198,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
} = ge
{
if match_span == *span && mpi == *other_mpi {
- debug!("appending local({:?}) to list", bind_to);
+ debug!("appending local({bind_to:?}) to list");
binds_to.push(bind_to);
return;
}
@@ -410,15 +410,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diagnostic, span: Span) {
match error {
GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => {
- if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
- err.span_suggestion(
- span,
- "consider borrowing here",
- format!("&{snippet}"),
- Applicability::Unspecified,
- );
- }
-
+ self.add_borrow_suggestions(err, span);
if binds_to.is_empty() {
let place_ty = move_from.ty(self.body, self.infcx.tcx).ty;
let place_desc = match self.describe_place(move_from.as_ref()) {
@@ -461,39 +453,75 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
}
+ fn add_borrow_suggestions(&self, err: &mut Diagnostic, span: Span) {
+ match self.infcx.tcx.sess.source_map().span_to_snippet(span) {
+ Ok(snippet) if snippet.starts_with('*') => {
+ err.span_suggestion_verbose(
+ span.with_hi(span.lo() + BytePos(1)),
+ "consider removing the dereference here",
+ String::new(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => {
+ err.span_suggestion_verbose(
+ span.shrink_to_lo(),
+ "consider borrowing here",
+ "&".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+
fn add_move_error_suggestions(&self, err: &mut Diagnostic, binds_to: &[Local]) {
- let mut suggestions: Vec<(Span, &str, String)> = Vec::new();
+ let mut suggestions: Vec<(Span, String, String)> = Vec::new();
for local in binds_to {
let bind_to = &self.body.local_decls[*local];
if let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
VarBindingForm { pat_span, .. },
)))) = bind_to.local_info
{
- if let Ok(pat_snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(pat_span)
+ let Ok(pat_snippet) =
+ self.infcx.tcx.sess.source_map().span_to_snippet(pat_span) else { continue; };
+ let Some(stripped) = pat_snippet.strip_prefix('&') else {
+ suggestions.push((
+ bind_to.source_info.span.shrink_to_lo(),
+ "consider borrowing the pattern binding".to_string(),
+ "ref ".to_string(),
+ ));
+ continue;
+ };
+ let inner_pat_snippet = stripped.trim_start();
+ let (pat_span, suggestion, to_remove) = if inner_pat_snippet.starts_with("mut")
+ && inner_pat_snippet["mut".len()..].starts_with(rustc_lexer::is_whitespace)
{
- if let Some(stripped) = pat_snippet.strip_prefix('&') {
- let pat_snippet = stripped.trim_start();
- let (suggestion, to_remove) = if pat_snippet.starts_with("mut")
- && pat_snippet["mut".len()..].starts_with(rustc_lexer::is_whitespace)
- {
- (pat_snippet["mut".len()..].trim_start(), "&mut")
- } else {
- (pat_snippet, "&")
- };
- suggestions.push((pat_span, to_remove, suggestion.to_owned()));
- }
- }
+ let inner_pat_snippet = inner_pat_snippet["mut".len()..].trim_start();
+ let pat_span = pat_span.with_hi(
+ pat_span.lo()
+ + BytePos((pat_snippet.len() - inner_pat_snippet.len()) as u32),
+ );
+ (pat_span, String::new(), "mutable borrow")
+ } else {
+ let pat_span = pat_span.with_hi(
+ pat_span.lo()
+ + BytePos(
+ (pat_snippet.len() - inner_pat_snippet.trim_start().len()) as u32,
+ ),
+ );
+ (pat_span, String::new(), "borrow")
+ };
+ suggestions.push((
+ pat_span,
+ format!("consider removing the {to_remove}"),
+ suggestion.to_string(),
+ ));
}
}
suggestions.sort_unstable_by_key(|&(span, _, _)| span);
suggestions.dedup_by_key(|&mut (span, _, _)| span);
- for (span, to_remove, suggestion) in suggestions {
- err.span_suggestion(
- span,
- &format!("consider removing the `{to_remove}`"),
- suggestion,
- Applicability::MachineApplicable,
- );
+ for (span, msg, suggestion) in suggestions {
+ err.span_suggestion_verbose(span, &msg, suggestion, Applicability::MachineApplicable);
}
}
@@ -521,8 +549,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
if binds_to.len() > 1 {
err.note(
- "move occurs because these variables have types that \
- don't implement the `Copy` trait",
+ "move occurs because these variables have types that don't implement the `Copy` \
+ trait",
);
}
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 3319a8068..45b15c2c5 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -180,6 +180,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
// the verbs used in some diagnostic messages.
let act;
let acted_on;
+ let mut suggest = true;
+ let mut mut_error = None;
+ let mut count = 1;
let span = match error_access {
AccessKind::Mutate => {
@@ -194,15 +197,50 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let borrow_spans = self.borrow_spans(span, location);
let borrow_span = borrow_spans.args_or_use();
- err = self.cannot_borrow_path_as_mutable_because(borrow_span, &item_msg, &reason);
- 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",
- );
+ match the_place_err {
+ PlaceRef { local, projection: [] }
+ if self.body.local_decls[local].can_be_made_mutable() =>
+ {
+ let span = self.body.local_decls[local].source_info.span;
+ mut_error = Some(span);
+ if let Some((buffer, c)) = self.get_buffered_mut_error(span) {
+ // We've encountered a second (or more) attempt to mutably borrow an
+ // immutable binding, so the likely problem is with the binding
+ // declaration, not the use. We collect these in a single diagnostic
+ // and make the binding the primary span of the error.
+ err = buffer;
+ count = c + 1;
+ if count == 2 {
+ err.replace_span_with(span, false);
+ err.span_label(span, "not mutable");
+ }
+ suggest = false;
+ } else {
+ err = self.cannot_borrow_path_as_mutable_because(
+ borrow_span,
+ &item_msg,
+ &reason,
+ );
+ }
+ }
+ _ => {
+ err = self.cannot_borrow_path_as_mutable_because(
+ borrow_span,
+ &item_msg,
+ &reason,
+ );
+ }
+ }
+ 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_span
}
};
@@ -226,7 +264,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
ProjectionElem::Deref,
],
} => {
- err.span_label(span, format!("cannot {ACT}", ACT = act));
+ err.span_label(span, format!("cannot {act}"));
if let Some(span) = get_mut_span_in_struct_field(
self.infcx.tcx,
@@ -252,7 +290,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
.unwrap_or(false) =>
{
let decl = &self.body.local_decls[local];
- err.span_label(span, format!("cannot {ACT}", ACT = act));
+ err.span_label(span, format!("cannot {act}"));
if let Some(mir::Statement {
source_info,
kind:
@@ -276,7 +314,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
pat_span: _,
},
)))) => {
- err.span_note(sp, "the binding is already a mutable borrow");
+ if suggest {
+ err.span_note(sp, "the binding is already a mutable borrow");
+ }
}
_ => {
err.span_note(
@@ -304,20 +344,25 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
} else {
err.span_help(source_info.span, "try removing `&mut` here");
}
- } else if decl.mutability == Mutability::Not
- && !matches!(
+ } else if decl.mutability == Mutability::Not {
+ if matches!(
decl.local_info,
Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(
hir::ImplicitSelfKind::MutRef
- ))))
- )
- {
- err.span_suggestion_verbose(
- decl.source_info.span.shrink_to_lo(),
- "consider making the binding mutable",
- "mut ",
- Applicability::MachineApplicable,
- );
+ ),)))
+ ) {
+ err.note(
+ "as `Self` may be unsized, this call attempts to take `&mut &mut self`",
+ );
+ err.note("however, `&mut self` expands to `self: &mut Self`, therefore `self` cannot be borrowed mutably");
+ } else {
+ err.span_suggestion_verbose(
+ decl.source_info.span.shrink_to_lo(),
+ "consider making the binding mutable",
+ "mut ",
+ Applicability::MachineApplicable,
+ );
+ };
}
}
@@ -333,16 +378,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let local_decl = &self.body.local_decls[local];
assert_eq!(local_decl.mutability, Mutability::Not);
- err.span_label(span, format!("cannot {ACT}", ACT = act));
- err.span_suggestion(
- local_decl.source_info.span,
- "consider changing this to be mutable",
- format!("mut {}", self.local_names[local].unwrap()),
- Applicability::MachineApplicable,
- );
- let tcx = self.infcx.tcx;
- if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() {
- self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
+ if count < 10 {
+ err.span_label(span, format!("cannot {act}"));
+ }
+ if suggest {
+ err.span_suggestion_verbose(
+ local_decl.source_info.span.shrink_to_lo(),
+ "consider changing this to be mutable",
+ "mut ".to_string(),
+ Applicability::MachineApplicable,
+ );
+ let tcx = self.infcx.tcx;
+ if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() {
+ self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
+ }
}
}
@@ -357,7 +406,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let captured_place = &self.upvars[upvar_index.index()].place;
- err.span_label(span, format!("cannot {ACT}", ACT = act));
+ err.span_label(span, format!("cannot {act}"));
let upvar_hir_id = captured_place.get_root_variable();
@@ -397,7 +446,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
.span_to_snippet(span)
.map_or(false, |snippet| snippet.starts_with("&mut ")) =>
{
- err.span_label(span, format!("cannot {ACT}", ACT = act));
+ err.span_label(span, format!("cannot {act}"));
err.span_suggestion(
span,
"try removing `&mut` here",
@@ -409,7 +458,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
PlaceRef { local, projection: [ProjectionElem::Deref] }
if self.body.local_decls[local].is_ref_for_guard() =>
{
- err.span_label(span, format!("cannot {ACT}", ACT = act));
+ err.span_label(span, format!("cannot {act}"));
err.note(
"variables bound in patterns are immutable until the end of the pattern guard",
);
@@ -537,7 +586,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
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(
+ err.span_suggestion_verbose(
err_help_span,
&format!(
"consider changing this to be a mutable {pointer_desc}"
@@ -546,7 +595,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
Applicability::MachineApplicable,
);
} else if let Some(x) = local_trait {
- err.span_suggestion(
+ err.span_suggestion_verbose(
x,
&format!(
"consider changing that to be a mutable {pointer_desc}"
@@ -569,24 +618,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
err.span_label(
span,
format!(
- "`{NAME}` is a `{SIGIL}` {DESC}, \
- so the data it refers to cannot be {ACTED_ON}",
- NAME = name,
- SIGIL = pointer_sigil,
- DESC = pointer_desc,
- ACTED_ON = acted_on
+ "`{name}` is a `{pointer_sigil}` {pointer_desc}, \
+ so the data it refers to cannot be {acted_on}",
),
);
}
_ => {
err.span_label(
span,
- format!(
- "cannot {ACT} through `{SIGIL}` {DESC}",
- ACT = act,
- SIGIL = pointer_sigil,
- DESC = pointer_desc
- ),
+ format!("cannot {act} through `{pointer_sigil}` {pointer_desc}"),
);
}
}
@@ -599,19 +639,19 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => {
- err.span_label(span, format!("cannot {ACT}", ACT = act));
+ err.span_label(span, format!("cannot {act}"));
match opt_source {
Some(BorrowedContentSource::OverloadedDeref(ty)) => {
err.help(&format!(
"trait `DerefMut` is required to modify through a dereference, \
- but it is not implemented for `{ty}`",
+ but it is not implemented for `{ty}`",
));
}
Some(BorrowedContentSource::OverloadedIndex(ty)) => {
err.help(&format!(
"trait `IndexMut` is required to modify indexed content, \
- but it is not implemented for `{ty}`",
+ but it is not implemented for `{ty}`",
));
self.suggest_map_index_mut_alternatives(ty, &mut err, span);
}
@@ -620,11 +660,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
_ => {
- err.span_label(span, format!("cannot {ACT}", ACT = act));
+ err.span_label(span, format!("cannot {act}"));
}
}
- self.buffer_error(err);
+ if let Some(span) = mut_error {
+ self.buffer_mut_error(span, err, count);
+ } else {
+ self.buffer_error(err);
+ }
}
fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diagnostic, span: Span) {
@@ -965,7 +1009,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let hir = self.infcx.tcx.hir();
let closure_id = self.mir_hir_id();
let closure_span = self.infcx.tcx.def_span(self.mir_def_id());
- let fn_call_id = hir.get_parent_node(closure_id);
+ let fn_call_id = hir.parent_id(closure_id);
let node = hir.get(fn_call_id);
let def_id = hir.enclosing_body_owner(fn_call_id);
let mut look_at_return = true;
@@ -1050,7 +1094,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
}
-fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
+pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
match local_decl.local_info.as_deref() {
@@ -1168,7 +1212,7 @@ fn suggest_ampmut<'tcx>(
{
let lt_name = &src[1..ws_pos];
let ty = &src[ws_pos..];
- return (true, highlight_span, format!("&{} mut{}", lt_name, ty));
+ return (true, highlight_span, format!("&{lt_name} mut{ty}"));
}
let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
@@ -1209,7 +1253,7 @@ fn get_mut_span_in_struct_field<'tcx>(
// Now we're dealing with the actual struct that we're going to suggest a change to,
// we can expect a field that is an immutable reference to a type.
&& let hir::Node::Field(field) = node
- && let hir::TyKind::Rptr(lt, hir::MutTy { mutbl: hir::Mutability::Not, ty }) = field.ty.kind
+ && let hir::TyKind::Ref(lt, hir::MutTy { mutbl: hir::Mutability::Not, ty }) = field.ty.kind
{
return Some(lt.ident.span.between(ty.span));
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
index 35c3df768..1eaf0a2f1 100644
--- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
@@ -209,14 +209,14 @@ impl OutlivesSuggestionBuilder {
let mut diag = if suggested.len() == 1 {
mbcx.infcx.tcx.sess.diagnostic().struct_help(&match suggested.last().unwrap() {
SuggestedConstraint::Outlives(a, bs) => {
- let bs: SmallVec<[String; 2]> = bs.iter().map(|r| format!("{}", r)).collect();
- format!("add bound `{}: {}`", a, bs.join(" + "))
+ let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect();
+ format!("add bound `{a}: {}`", bs.join(" + "))
}
SuggestedConstraint::Equal(a, b) => {
- format!("`{}` and `{}` must be the same: replace one with the other", a, b)
+ format!("`{a}` and `{b}` must be the same: replace one with the other")
}
- SuggestedConstraint::Static(a) => format!("replace `{}` with `'static`", a),
+ SuggestedConstraint::Static(a) => format!("replace `{a}` with `'static`"),
})
} else {
// Create a new diagnostic.
@@ -231,18 +231,16 @@ impl OutlivesSuggestionBuilder {
for constraint in suggested {
match constraint {
SuggestedConstraint::Outlives(a, bs) => {
- let bs: SmallVec<[String; 2]> =
- bs.iter().map(|r| format!("{}", r)).collect();
- diag.help(&format!("add bound `{}: {}`", a, bs.join(" + ")));
+ let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect();
+ diag.help(&format!("add bound `{a}: {}`", bs.join(" + ")));
}
SuggestedConstraint::Equal(a, b) => {
diag.help(&format!(
- "`{}` and `{}` must be the same: replace one with the other",
- a, b
+ "`{a}` and `{b}` must be the same: replace one with the other",
));
}
SuggestedConstraint::Static(a) => {
- diag.help(&format!("replace `{}` with `'static`", a));
+ 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 9bc2e79e2..187861ba1 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -4,9 +4,14 @@
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
+use rustc_hir as hir;
+use rustc_hir::def::Res::Def;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor;
-use rustc_hir::{self as hir, Item, ItemKind, Node};
+use rustc_hir::GenericBound::Trait;
+use rustc_hir::QPath::Resolved;
+use rustc_hir::WherePredicate::BoundPredicate;
+use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate};
use rustc_infer::infer::{
error_reporting::nice_region_error::{
self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params,
@@ -18,11 +23,11 @@ use rustc_infer::infer::{
use rustc_middle::hir::place::PlaceBase;
use rustc_middle::mir::{ConstraintCategory, ReturnConstraint};
use rustc_middle::ty::subst::InternalSubsts;
-use rustc_middle::ty::Region;
use rustc_middle::ty::TypeVisitor;
use rustc_middle::ty::{self, RegionVid, Ty};
+use rustc_middle::ty::{Region, TyCtxt};
use rustc_span::symbol::{kw, Ident};
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
use crate::borrowck_errors;
use crate::session_diagnostics::{
@@ -70,7 +75,25 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
///
/// Usually we expect this to either be empty or contain a small number of items, so we can avoid
/// allocation most of the time.
-pub(crate) type RegionErrors<'tcx> = Vec<RegionErrorKind<'tcx>>;
+pub(crate) struct RegionErrors<'tcx>(Vec<RegionErrorKind<'tcx>>, TyCtxt<'tcx>);
+
+impl<'tcx> RegionErrors<'tcx> {
+ pub fn new(tcx: TyCtxt<'tcx>) -> Self {
+ Self(vec![], tcx)
+ }
+ #[track_caller]
+ pub fn push(&mut self, val: impl Into<RegionErrorKind<'tcx>>) {
+ let val = val.into();
+ self.1.sess.delay_span_bug(DUMMY_SP, format!("{val:?}"));
+ self.0.push(val);
+ }
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+ pub fn into_iter(self) -> impl Iterator<Item = RegionErrorKind<'tcx>> {
+ self.0.into_iter()
+ }
+}
#[derive(Clone, Debug)]
pub(crate) enum RegionErrorKind<'tcx> {
@@ -168,12 +191,109 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
false
}
+ // For generic associated types (GATs) which implied 'static requirement
+ // from higher-ranked trait bounds (HRTB). Try to locate span of the trait
+ // and the span which bounded to the trait for adding 'static lifetime suggestion
+ fn suggest_static_lifetime_for_gat_from_hrtb(
+ &self,
+ diag: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+ lower_bound: RegionVid,
+ ) {
+ let mut suggestions = vec![];
+ let hir = self.infcx.tcx.hir();
+
+ // find generic associated types in the given region 'lower_bound'
+ let gat_id_and_generics = self
+ .regioncx
+ .placeholders_contained_in(lower_bound)
+ .map(|placeholder| {
+ if let Some(id) = placeholder.name.get_id()
+ && let Some(placeholder_id) = id.as_local()
+ && let gat_hir_id = hir.local_def_id_to_hir_id(placeholder_id)
+ && let Some(generics_impl) = hir.get_parent(gat_hir_id).generics()
+ {
+ Some((gat_hir_id, generics_impl))
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+ debug!(?gat_id_and_generics);
+
+ // find higher-ranked trait bounds bounded to the generic associated types
+ let mut hrtb_bounds = vec![];
+ gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| {
+ for pred in generics.predicates {
+ let BoundPredicate(
+ WhereBoundPredicate {
+ bound_generic_params,
+ bounds,
+ ..
+ }) = pred else { continue; };
+ if bound_generic_params
+ .iter()
+ .rfind(|bgp| hir.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id)
+ .is_some()
+ {
+ for bound in *bounds {
+ hrtb_bounds.push(bound);
+ }
+ }
+ }
+ });
+ debug!(?hrtb_bounds);
+
+ hrtb_bounds.iter().for_each(|bound| {
+ let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }, _) = bound else { return; };
+ diag.span_note(
+ *trait_span,
+ format!("due to current limitations in the borrow checker, this implies a `'static` lifetime")
+ );
+ let Some(generics_fn) = hir.get_generics(self.body.source.def_id().expect_local()) else { return; };
+ let Def(_, trait_res_defid) = trait_ref.path.res else { return; };
+ debug!(?generics_fn);
+ generics_fn.predicates.iter().for_each(|predicate| {
+ let BoundPredicate(
+ WhereBoundPredicate {
+ span: bounded_span,
+ bounded_ty,
+ bounds,
+ ..
+ }
+ ) = predicate else { return; };
+ bounds.iter().for_each(|bd| {
+ if let Trait(PolyTraitRef { trait_ref: tr_ref, .. }, _) = bd
+ && let Def(_, res_defid) = tr_ref.path.res
+ && res_defid == trait_res_defid // trait id matches
+ && let TyKind::Path(Resolved(_, path)) = bounded_ty.kind
+ && let Def(_, defid) = path.res
+ && generics_fn.params
+ .iter()
+ .rfind(|param| param.def_id.to_def_id() == defid)
+ .is_some() {
+ suggestions.push((bounded_span.shrink_to_hi(), format!(" + 'static")));
+ }
+ });
+ });
+ });
+ if suggestions.len() > 0 {
+ suggestions.dedup();
+ diag.multipart_suggestion_verbose(
+ format!("consider restricting the type parameter to the `'static` lifetime"),
+ suggestions,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+
/// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`.
pub(crate) fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) {
// Iterate through all the errors, producing a diagnostic for each one. The diagnostics are
// buffered in the `MirBorrowckCtxt`.
let mut outlives_suggestion = OutlivesSuggestionBuilder::default();
+ let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> =
+ None;
for nll_error in nll_errors.into_iter() {
match nll_error {
@@ -203,12 +323,21 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
// to report it; we could probably handle it by
// iterating over the universal regions and reporting
// an error that multiple bounds are required.
- self.buffer_error(self.infcx.tcx.sess.create_err(
- GenericDoesNotLiveLongEnough {
+ let mut diag =
+ self.infcx.tcx.sess.create_err(GenericDoesNotLiveLongEnough {
kind: type_test.generic_kind.to_string(),
span: type_test_span,
- },
- ));
+ });
+
+ // Add notes and suggestions for the case of 'static lifetime
+ // implied but not specified when a generic associated types
+ // are from higher-ranked trait bounds
+ self.suggest_static_lifetime_for_gat_from_hrtb(
+ &mut diag,
+ type_test.lower_bound,
+ );
+
+ self.buffer_error(diag);
}
}
@@ -216,13 +345,19 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let named_ty = self.regioncx.name_regions(self.infcx.tcx, hidden_ty);
let named_key = self.regioncx.name_regions(self.infcx.tcx, key);
let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region);
- self.buffer_error(unexpected_hidden_region_diagnostic(
+ let mut diag = unexpected_hidden_region_diagnostic(
self.infcx.tcx,
span,
named_ty,
named_region,
named_key,
- ));
+ );
+ if last_unexpected_hidden_region != Some((span, named_ty, named_key)) {
+ self.buffer_error(diag);
+ last_unexpected_hidden_region = Some((span, named_ty, named_key));
+ } else {
+ diag.delay_as_bug();
+ }
}
RegionErrorKind::BoundUniversalRegionError {
@@ -273,71 +408,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
outlives_suggestion.add_suggestion(self);
}
- fn get_impl_ident_and_self_ty_from_trait(
- &self,
- def_id: DefId,
- trait_objects: &FxIndexSet<DefId>,
- ) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> {
- let tcx = self.infcx.tcx;
- match tcx.hir().get_if_local(def_id) {
- Some(Node::ImplItem(impl_item)) => {
- match tcx.hir().find_by_def_id(tcx.hir().get_parent_item(impl_item.hir_id()).def_id)
- {
- Some(Node::Item(Item {
- kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
- ..
- })) => Some((impl_item.ident, self_ty)),
- _ => None,
- }
- }
- Some(Node::TraitItem(trait_item)) => {
- let trait_did = tcx.hir().get_parent_item(trait_item.hir_id());
- match tcx.hir().find_by_def_id(trait_did.def_id) {
- Some(Node::Item(Item { kind: ItemKind::Trait(..), .. })) => {
- // The method being called is defined in the `trait`, but the `'static`
- // obligation comes from the `impl`. Find that `impl` so that we can point
- // at it in the suggestion.
- let trait_did = trait_did.to_def_id();
- match tcx
- .hir()
- .trait_impls(trait_did)
- .iter()
- .filter_map(|&impl_did| {
- match tcx.hir().get_if_local(impl_did.to_def_id()) {
- Some(Node::Item(Item {
- kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
- ..
- })) if trait_objects.iter().all(|did| {
- // FIXME: we should check `self_ty` against the receiver
- // type in the `UnifyReceiver` context, but for now, use
- // this imperfect proxy. This will fail if there are
- // multiple `impl`s for the same trait like
- // `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`.
- // In that case, only the first one will get suggestions.
- let mut traits = vec![];
- let mut hir_v = HirTraitObjectVisitor(&mut traits, *did);
- hir_v.visit_ty(self_ty);
- !traits.is_empty()
- }) =>
- {
- Some(self_ty)
- }
- _ => None,
- }
- })
- .next()
- {
- Some(self_ty) => Some((trait_item.ident, self_ty)),
- _ => None,
- }
- }
- _ => None,
- }
- }
- _ => None,
- }
- }
-
/// Report an error because the universal region `fr` was required to outlive
/// `outlived_fr` but it is not known to do so. For example:
///
@@ -461,7 +531,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
);
(desc, note)
}
- _ => panic!("Unexpected type {:?}", ty),
+ _ => panic!("Unexpected type {ty:?}"),
};
diag.note(&format!("requirement occurs because of {desc}",));
diag.note(&note);
@@ -472,7 +542,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
for extra in extra_info {
match extra {
ExtraConstraintInfo::PlaceholderFromPredicate(span) => {
- diag.span_note(span, format!("due to current limitations in the borrow checker, this implies a `'static` lifetime"));
+ diag.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
}
}
}
@@ -504,7 +574,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let ErrorConstraintInfo { outlived_fr, span, .. } = errci;
let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty;
- if let ty::Opaque(def_id, _) = *output_ty.kind() {
+ if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *output_ty.kind() {
output_ty = self.infcx.tcx.type_of(def_id)
};
@@ -764,10 +834,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let lifetime = if f.has_name() { fr_name.name } else { kw::UnderscoreLifetime };
let arg = match param.param.pat.simple_ident() {
- Some(simple_ident) => format!("argument `{}`", simple_ident),
+ Some(simple_ident) => format!("argument `{simple_ident}`"),
None => "the argument".to_string(),
};
- let captures = format!("captures data from {}", arg);
+ let captures = format!("captures data from {arg}");
return nice_region_error::suggest_new_region_bound(
self.infcx.tcx,
@@ -777,6 +847,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
Some(arg),
captures,
Some((param.param_ty_span, param.param_ty.to_string())),
+ self.infcx.tcx.is_suitable_region(f).map(|r| r.def_id),
);
}
}
@@ -832,7 +903,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
visitor.visit_ty(param.param_ty);
let Some((ident, self_ty)) =
- self.get_impl_ident_and_self_ty_from_trait(instance.def_id(), &visitor.0) else {return};
+ NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &visitor.0) else { return; };
self.suggest_constrain_dyn_trait_in_impl(diag, &visitor.0, ident, self_ty);
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 171e62d91..9233287cf 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -200,9 +200,9 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
/// increment the counter.
///
/// This is _not_ idempotent. Call `give_region_a_name` when possible.
- fn synthesize_region_name(&self) -> Symbol {
+ pub(crate) fn synthesize_region_name(&self) -> Symbol {
let c = self.next_region_name.replace_with(|counter| *counter + 1);
- Symbol::intern(&format!("'{:?}", c))
+ Symbol::intern(&format!("'{c:?}"))
}
/// Maps from an internal MIR region vid to something that we can
@@ -493,10 +493,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
//
// &
// - let's call the lifetime of this reference `'1`
- (
- ty::Ref(region, referent_ty, _),
- hir::TyKind::Rptr(_lifetime, referent_hir_ty),
- ) => {
+ (ty::Ref(region, referent_ty, _), hir::TyKind::Ref(_lifetime, referent_hir_ty)) => {
if region.to_region_vid() == needle_fr {
// Just grab the first character, the `&`.
let source_map = self.infcx.tcx.sess.source_map();
@@ -622,7 +619,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 {:?} vs {:?}", kind, hir_arg),
+ &format!("unmatched subst and hir arg: found {kind:?} vs {hir_arg:?}"),
);
}
}
@@ -786,8 +783,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
} else {
span_bug!(
hir_ty.span,
- "bounds from lowered return type of async fn did not match expected format: {:?}",
- opaque_ty
+ "bounds from lowered return type of async fn did not match expected format: {opaque_ty:?}",
);
}
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
index b385f95b6..ada3310d8 100644
--- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
@@ -18,7 +18,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
upvars: &[Upvar<'tcx>],
fr: RegionVid,
) -> Option<(Option<Symbol>, Span)> {
- debug!("get_var_name_and_span_for_region(fr={:?})", fr);
+ debug!("get_var_name_and_span_for_region(fr={fr:?})");
assert!(self.universal_regions().is_universal_region(fr));
debug!("get_var_name_and_span_for_region: attempting upvar");
@@ -44,10 +44,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
) -> Option<usize> {
let upvar_index =
self.universal_regions().defining_ty.upvar_tys().position(|upvar_ty| {
- debug!("get_upvar_index_for_region: upvar_ty={:?}", upvar_ty);
+ debug!("get_upvar_index_for_region: upvar_ty={upvar_ty:?}");
tcx.any_free_region_meets(&upvar_ty, |r| {
let r = r.to_region_vid();
- debug!("get_upvar_index_for_region: r={:?} fr={:?}", r, fr);
+ debug!("get_upvar_index_for_region: r={r:?} fr={fr:?}");
r == fr
})
})?;
@@ -55,8 +55,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let upvar_ty = self.universal_regions().defining_ty.upvar_tys().nth(upvar_index);
debug!(
- "get_upvar_index_for_region: found {:?} in upvar {} which has type {:?}",
- fr, upvar_index, upvar_ty,
+ "get_upvar_index_for_region: found {fr:?} in upvar {upvar_index} which has type {upvar_ty:?}",
);
Some(upvar_index)
@@ -71,13 +70,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
upvar_index: usize,
) -> (Symbol, Span) {
let upvar_hir_id = upvars[upvar_index].place.get_root_variable();
- debug!("get_upvar_name_and_span_for_region: upvar_hir_id={:?}", upvar_hir_id);
+ debug!("get_upvar_name_and_span_for_region: upvar_hir_id={upvar_hir_id:?}");
let upvar_name = tcx.hir().name(upvar_hir_id);
let upvar_span = tcx.hir().span(upvar_hir_id);
debug!(
- "get_upvar_name_and_span_for_region: upvar_name={:?} upvar_span={:?}",
- upvar_name, upvar_span
+ "get_upvar_name_and_span_for_region: upvar_name={upvar_name:?} upvar_span={upvar_span:?}",
);
(upvar_name, upvar_span)
@@ -97,15 +95,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let argument_index =
self.universal_regions().unnormalized_input_tys.iter().skip(implicit_inputs).position(
|arg_ty| {
- debug!("get_argument_index_for_region: arg_ty = {:?}", arg_ty);
+ debug!("get_argument_index_for_region: arg_ty = {arg_ty:?}");
tcx.any_free_region_meets(arg_ty, |r| r.to_region_vid() == fr)
},
)?;
debug!(
- "get_argument_index_for_region: found {:?} in argument {} which has type {:?}",
- fr,
- argument_index,
+ "get_argument_index_for_region: found {fr:?} in argument {argument_index} which has type {:?}",
self.universal_regions().unnormalized_input_tys[argument_index],
);
@@ -122,13 +118,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
) -> (Option<Symbol>, Span) {
let implicit_inputs = self.universal_regions().defining_ty.implicit_inputs();
let argument_local = Local::new(implicit_inputs + argument_index + 1);
- debug!("get_argument_name_and_span_for_region: argument_local={:?}", argument_local);
+ debug!("get_argument_name_and_span_for_region: argument_local={argument_local:?}");
let argument_name = local_names[argument_local];
let argument_span = body.local_decls[argument_local].source_info.span;
debug!(
- "get_argument_name_and_span_for_region: argument_name={:?} argument_span={:?}",
- argument_name, argument_span
+ "get_argument_name_and_span_for_region: argument_name={argument_name:?} argument_span={argument_span:?}",
);
(argument_name, argument_span)