diff options
Diffstat (limited to 'compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs')
-rw-r--r-- | compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs | 160 |
1 files changed, 140 insertions, 20 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 0ad4abbce..6b5014fa9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1,23 +1,23 @@ +use rustc_errors::{ + Applicability, Diagnostic, DiagnosticBuilder, EmissionGuarantee, ErrorGuaranteed, +}; use rustc_hir as hir; +use rustc_hir::intravisit::Visitor; use rustc_hir::Node; use rustc_middle::hir::map::Map; use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{ hir::place::PlaceBase, - mir::{ - self, BindingForm, ClearCrossCrate, ImplicitSelfKind, Local, LocalDecl, LocalInfo, - LocalKind, Location, - }, + mir::{self, BindingForm, ClearCrossCrate, Local, LocalDecl, LocalInfo, LocalKind, Location}, }; use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::{kw, Symbol}; -use rustc_span::{BytePos, Span}; +use rustc_span::{sym, BytePos, Span}; use crate::diagnostics::BorrowedContentSource; use crate::MirBorrowckCtxt; use rustc_const_eval::util::collect_writes::FindAssignments; -use rustc_errors::{Applicability, Diagnostic}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub(crate) enum AccessKind { @@ -309,7 +309,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { && !matches!( decl.local_info, Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf( - ImplicitSelfKind::MutRef + hir::ImplicitSelfKind::MutRef )))) ) { @@ -364,7 +364,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let Some(Node::Pat(pat)) = self.infcx.tcx.hir().find(upvar_hir_id) && let hir::PatKind::Binding( - hir::BindingAnnotation::Unannotated, + hir::BindingAnnotation::NONE, _, upvar_ident, _, @@ -614,6 +614,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { "trait `IndexMut` is required to modify indexed content, \ but it is not implemented for `{ty}`", )); + self.suggest_map_index_mut_alternatives(ty, &mut err, span); } _ => (), } @@ -627,6 +628,127 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.buffer_error(err); } + fn suggest_map_index_mut_alternatives( + &self, + ty: Ty<'_>, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + span: Span, + ) { + let Some(adt) = ty.ty_adt_def() else { return }; + let did = adt.did(); + if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did) + || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did) + { + struct V<'a, 'b, 'tcx, G: EmissionGuarantee> { + assign_span: Span, + err: &'a mut DiagnosticBuilder<'b, G>, + ty: Ty<'tcx>, + suggested: bool, + } + impl<'a, 'b: 'a, 'hir, 'tcx, G: EmissionGuarantee> Visitor<'hir> for V<'a, 'b, 'tcx, G> { + fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'hir>) { + hir::intravisit::walk_stmt(self, stmt); + let expr = match stmt.kind { + hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr, + hir::StmtKind::Local(hir::Local { init: Some(expr), .. }) => expr, + _ => { + return; + } + }; + if let hir::ExprKind::Assign(place, rv, _sp) = expr.kind + && let hir::ExprKind::Index(val, index) = place.kind + && (expr.span == self.assign_span || place.span == self.assign_span) + { + // val[index] = rv; + // ---------- place + self.err.multipart_suggestions( + &format!( + "to modify a `{}`, use `.get_mut()`, `.insert()` or the entry API", + self.ty, + ), + vec![ + vec![ // val.insert(index, rv); + ( + val.span.shrink_to_hi().with_hi(index.span.lo()), + ".insert(".to_string(), + ), + ( + index.span.shrink_to_hi().with_hi(rv.span.lo()), + ", ".to_string(), + ), + (rv.span.shrink_to_hi(), ")".to_string()), + ], + vec![ // val.get_mut(index).map(|v| { *v = rv; }); + ( + val.span.shrink_to_hi().with_hi(index.span.lo()), + ".get_mut(".to_string(), + ), + ( + index.span.shrink_to_hi().with_hi(place.span.hi()), + ").map(|val| { *val".to_string(), + ), + ( + rv.span.shrink_to_hi(), + "; })".to_string(), + ), + ], + vec![ // let x = val.entry(index).or_insert(rv); + (val.span.shrink_to_lo(), "let val = ".to_string()), + ( + val.span.shrink_to_hi().with_hi(index.span.lo()), + ".entry(".to_string(), + ), + ( + index.span.shrink_to_hi().with_hi(rv.span.lo()), + ").or_insert(".to_string(), + ), + (rv.span.shrink_to_hi(), ")".to_string()), + ], + ].into_iter(), + Applicability::MachineApplicable, + ); + self.suggested = true; + } else if let hir::ExprKind::MethodCall(_path, receiver, _, sp) = expr.kind + && let hir::ExprKind::Index(val, index) = receiver.kind + && expr.span == self.assign_span + { + // val[index].path(args..); + self.err.multipart_suggestion( + &format!("to modify a `{}` use `.get_mut()`", self.ty), + vec![ + ( + val.span.shrink_to_hi().with_hi(index.span.lo()), + ".get_mut(".to_string(), + ), + ( + index.span.shrink_to_hi().with_hi(receiver.span.hi()), + ").map(|val| val".to_string(), + ), + (sp.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ); + self.suggested = true; + } + } + } + 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.as_local().unwrap()); + let node = hir_map.find(hir_id); + 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 v = V { assign_span: span, err, ty, suggested: false }; + v.visit_body(body); + if !v.suggested { + err.help(&format!( + "to modify a `{ty}`, use `.get_mut()`, `.insert()` or the entry API", + )); + } + } + } + /// User cannot make signature of a trait mutable without changing the /// trait. So we find if this error belongs to a trait and if so we move /// suggestion to the trait or disable it if it is out of scope of this crate @@ -786,11 +908,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { [ Expr { kind: - MethodCall( - path_segment, - _args, - span, - ), + MethodCall(path_segment, _, _, span), hir_id, .. }, @@ -810,10 +928,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { _, ) = hir_map.body(fn_body_id).value.kind { - let opt_suggestions = path_segment - .hir_id - .map(|path_hir_id| self.infcx.tcx.typeck(path_hir_id.owner)) - .and_then(|typeck| typeck.type_dependent_def_id(*hir_id)) + let opt_suggestions = self + .infcx + .tcx + .typeck(path_segment.hir_id.owner) + .type_dependent_def_id(*hir_id) .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id)) .map(|def_id| self.infcx.tcx.associated_items(def_id)) .map(|assoc_items| { @@ -851,6 +970,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 node = hir.get(fn_call_id); let def_id = hir.enclosing_body_owner(fn_call_id); @@ -902,7 +1022,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let Some(span) = arg { err.span_label(span, "change this to accept `FnMut` instead of `Fn`"); err.span_label(func.span, "expects `Fn` instead of `FnMut`"); - err.span_label(self.body.span, "in this closure"); + err.span_label(closure_span, "in this closure"); look_at_return = false; } } @@ -928,7 +1048,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { sig.decl.output.span(), "change this to return `FnMut` instead of `Fn`", ); - err.span_label(self.body.span, "in this closure"); + err.span_label(closure_span, "in this closure"); } _ => {} } @@ -952,7 +1072,7 @@ fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symb // // Deliberately fall into this case for all implicit self types, // so that we don't fall in to the next case with them. - *kind == mir::ImplicitSelfKind::MutRef + *kind == hir::ImplicitSelfKind::MutRef } _ if Some(kw::SelfLower) == local_name => { // Otherwise, check if the name is the `self` keyword - in which case |