diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs index 88122777d2e..88ba112c8d5 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs @@ -6,7 +6,7 @@ use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{ hir::place::PlaceBase, - mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, Location}, + mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, LocalKind, Location}, }; use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::{kw, Symbol}; @@ -424,15 +424,108 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { match label { Some((true, err_help_span, suggested_code)) => { - err.span_suggestion( - err_help_span, - &format!( - "consider changing this to be a mutable {}", - pointer_desc - ), - suggested_code, - Applicability::MachineApplicable, - ); + /// 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 + let (is_trait_sig, local_trait) = { + if self.body.local_kind(local) != LocalKind::Arg { + (false, None) + } else { + let hir_map = self.infcx.tcx.hir(); + let my_hir = hir_map.local_def_id_to_hir_id( + self.body.source.def_id().as_local().unwrap(), + ); + match hir_map.find(hir_map.get_parent_node(my_hir)) { + Some(Node::Item(hir::Item { + kind: + hir::ItemKind::Impl(hir::Impl { + of_trait: + Some(hir::TraitRef { + path: + hir::Path { + res: + hir::def::Res::Def(_, td), + .. + }, + .. + }), + .. + }), + .. + })) => { + (true, td.as_local().and_then(|tld| { + let h = hir_map.local_def_id_to_hir_id(tld); + match hir_map.find(h) { + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Trait( + _, _, _, _, + items + ), + .. + })) => { + let mut f_in_trait_opt = None; + for hir::TraitItemRef { id: fi, kind: k, .. } in *items { + let hi = fi.hir_id(); + if !matches!(k, hir::AssocItemKind::Fn { .. }) { + continue; + } + if hir_map.name(hi) != hir_map.name(my_hir) { + continue; + } + f_in_trait_opt = Some(hi); + break; + } + f_in_trait_opt.and_then(|f_in_trait| { + match hir_map.find(f_in_trait) { + Some(Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(hir::FnSig { + decl: hir::FnDecl { + inputs, + .. + }, + .. + }, _), + .. + })) => { + let hir::Ty { span, .. } = inputs[local.index() - 1]; + Some(span) + }, + _ => None, + } + }) + //Some(hir_map.span(h)) + } + _ => None + } + })) + } + _ => (false, None), + } + } + }; + if !is_trait_sig { + err.span_suggestion( + 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( + x, + &format!( + "consider changing that to be a mutable {}", + pointer_desc + ), + suggested_code, + Applicability::MachineApplicable, + ); + } } Some((false, err_label_span, message)) => { err.span_label(err_label_span, &message); diff --git a/src/test/ui/suggestions/issue-68049-1.rs b/src/test/ui/suggestions/issue-68049-1.rs new file mode 100644 index 00000000000..9b9ae429aeb --- /dev/null +++ b/src/test/ui/suggestions/issue-68049-1.rs @@ -0,0 +1,16 @@ +use std::alloc::{GlobalAlloc, Layout}; + +struct Test(u32); + +unsafe impl GlobalAlloc for Test { + unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { + self.0 += 1; + 0 as *mut u8 + } + + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) { + unimplemented!(); + } +} + +fn main() { } diff --git a/src/test/ui/suggestions/issue-68049-1.stderr b/src/test/ui/suggestions/issue-68049-1.stderr new file mode 100644 index 00000000000..32367d2d0cf --- /dev/null +++ b/src/test/ui/suggestions/issue-68049-1.stderr @@ -0,0 +1,9 @@ +error[E0594]: cannot assign to `self.0` which is behind a `&` reference + --> $DIR/issue-68049-1.rs:7:9 + | +LL | self.0 += 1; + | ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be written + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0594`. diff --git a/src/test/ui/suggestions/issue-68049-2.rs b/src/test/ui/suggestions/issue-68049-2.rs new file mode 100644 index 00000000000..22bb6b85d6f --- /dev/null +++ b/src/test/ui/suggestions/issue-68049-2.rs @@ -0,0 +1,21 @@ +trait Hello { + fn example(&self, input: &i32); // should suggest here +} + +struct Test1(i32); + +impl Hello for Test1 { + fn example(&self, input: &i32) { // should not suggest here + *input = self.0; + } +} + +struct Test2(i32); + +impl Hello for Test2 { + fn example(&self, input: &i32) { // should not suggest here + self.0 += *input; + } +} + +fn main() { } diff --git a/src/test/ui/suggestions/issue-68049-2.stderr b/src/test/ui/suggestions/issue-68049-2.stderr new file mode 100644 index 00000000000..f10a83c68a8 --- /dev/null +++ b/src/test/ui/suggestions/issue-68049-2.stderr @@ -0,0 +1,21 @@ +error[E0594]: cannot assign to `*input` which is behind a `&` reference + --> $DIR/issue-68049-2.rs:9:7 + | +LL | fn example(&self, input: &i32); // should suggest here + | ---- help: consider changing that to be a mutable reference: `&mut i32` +... +LL | *input = self.0; + | ^^^^^^^^^^^^^^^ `input` is a `&` reference, so the data it refers to cannot be written + +error[E0594]: cannot assign to `self.0` which is behind a `&` reference + --> $DIR/issue-68049-2.rs:17:5 + | +LL | fn example(&self, input: &i32); // should suggest here + | ----- help: consider changing that to be a mutable reference: `&mut self` +... +LL | self.0 += *input; + | ^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be written + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0594`.