From 9835e2ae736235810b4ea1c162ca5e65c547e770 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 18 May 2024 04:49:50 +0200 Subject: Merging upstream version 1.71.1+dfsg1. Signed-off-by: Daniel Baumann --- .../src/diagnostics/bound_region_errors.rs | 2 +- .../src/diagnostics/conflict_errors.rs | 437 ++++++++++------ .../src/diagnostics/explain_borrow.rs | 17 +- compiler/rustc_borrowck/src/diagnostics/mod.rs | 298 +++++------ .../rustc_borrowck/src/diagnostics/move_errors.rs | 55 +- .../src/diagnostics/mutability_errors.rs | 555 +++++++++++---------- .../src/diagnostics/outlives_suggestion.rs | 13 +- .../src/diagnostics/region_errors.rs | 22 +- .../rustc_borrowck/src/diagnostics/region_name.rs | 2 +- .../rustc_borrowck/src/diagnostics/var_name.rs | 4 +- 10 files changed, 783 insertions(+), 622 deletions(-) (limited to 'compiler/rustc_borrowck/src/diagnostics') 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> { +impl<'tcx, F> ToUniverseInfo<'tcx> for Canonical<'tcx, type_op::custom::CustomTypeOp> { 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: ¬e_msg, + span, + }); } else { - None + err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note { + is_partial_move, + ty, + place: ¬e_msg, + }); }; - self.note_type_does_not_implement_copy(&mut err, ¬e_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, + } + 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, - 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) { + 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, - kind_desc: impl Into, - ) { - 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, - f: impl Fn(Option, Span) -> crate::session_diagnostics::CaptureVarCause, + f: impl FnOnce(Option, 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(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, - } - - 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, + } + + 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) -> 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, opt_ty_info: Option, ) -> (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 { - 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 { + 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(¬e); + diag.note(format!("requirement occurs because of {desc}",)); + diag.note(note); diag.help("see 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, 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]; -- cgit v1.2.3