summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs')
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs304
1 files changed, 233 insertions, 71 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;
}
}