about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-05-11 07:44:16 +0000
committerbors <bors@rust-lang.org>2021-05-11 07:44:16 +0000
commit382f748f23979e37e3e012b090e5a0313463f182 (patch)
treea46c0160d8d555947f70025c70cd25a3c93ae020
parentfe62c6e2958abfe54a9410a24a5750baf4c157e0 (diff)
parent1f20966e90cd964842054c284fb0ec5e8de6df7b (diff)
downloadrust-382f748f23979e37e3e012b090e5a0313463f182.tar.gz
rust-382f748f23979e37e3e012b090e5a0313463f182.zip
Auto merge of #85100 - HKalbasi:issue-68049-fix, r=Aaron1011
Fix invalid suggestion of changing impl trait signature

Fix #68049
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs92
-rw-r--r--src/test/ui/suggestions/issue-68049-1.rs16
-rw-r--r--src/test/ui/suggestions/issue-68049-1.stderr9
-rw-r--r--src/test/ui/suggestions/issue-68049-2.rs21
-rw-r--r--src/test/ui/suggestions/issue-68049-2.stderr21
5 files changed, 149 insertions, 10 deletions
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..1e2714a2c1b 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,28 @@ 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,
-                                );
+                                let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
+                                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);
@@ -503,6 +516,65 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         err.buffer(&mut self.errors_buffer);
     }
 
+    /// 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
+    fn is_error_in_trait(&self, local: Local) -> (bool, Option<Span>) {
+        if self.body.local_kind(local) != LocalKind::Arg {
+            return (false, None);
+        }
+        let hir_map = self.infcx.tcx.hir();
+        let my_def = self.body.source.def_id();
+        let my_hir = hir_map.local_def_id_to_hir_id(my_def.as_local().unwrap());
+        let td = if let Some(a) =
+            self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
+        {
+            a
+        } else {
+            return (false, None);
+        };
+        (
+            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,
+                        })
+                    }
+                    _ => None,
+                }
+            }),
+        )
+    }
+
     // point to span of upvar making closure call require mutable borrow
     fn show_mutating_upvar(
         &self,
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..0acb7b1bf2b
--- /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; //~ ERROR cannot assign
+        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..1c3430c14e9
--- /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; //~ ERROR cannot assign
+  }
+}
+
+struct Test2(i32);
+
+impl Hello for Test2 {
+  fn example(&self, input: &i32) { // should not suggest here
+    self.0 += *input; //~ ERROR cannot assign
+  }
+}
+
+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`.