about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlyssa Verkade <alyssaverkade@gmail.com>2022-01-22 15:35:09 -0800
committerAlyssa Verkade <alyssaverkade@gmail.com>2022-01-22 16:21:16 -0800
commitb885700c7b1f8e9ba26e1f930dcb55ef56130b28 (patch)
treea0fa150b9abe48451760706884d99b53a1329f49
parentecf72996eda4f8af19b0ca7235c6f62e0245a313 (diff)
downloadrust-b885700c7b1f8e9ba26e1f930dcb55ef56130b28.tar.gz
rust-b885700c7b1f8e9ba26e1f930dcb55ef56130b28.zip
[borrowck] Fix help on mutating &self in async fns
Previously, when rustc was provided an async function that tried to
mutate through a shared reference to an implicit self (as shown in the
ui test), rustc would suggest modifying the parameter signature
to `&mut` + the fully qualified name of the ty (in the case of the repro
`S`). If a user modified their code to match the suggestion, the
compiler would not accept it.

This commit modifies the suggestion so that when rustc is provided the
ui test that is also attached in this commit, it suggests (correctly)
`&mut self`. We try to be careful about distinguishing between implicit
and explicit self annotations, since the latter seem to be handled
correctly already.

Fixes rust-lang/rust#93093
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs32
-rw-r--r--src/test/ui/borrowck/issue-93093.rs14
-rw-r--r--src/test/ui/borrowck/issue-93093.stderr12
3 files changed, 52 insertions, 6 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 8f4e574fbd6..a0b269bc44a 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -488,12 +488,32 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                                     // don't create labels for compiler-generated spans
                                     Some(_) => None,
                                     None => {
-                                        let (span, suggestion) = suggest_ampmut(
-                                            self.infcx.tcx,
-                                            local_decl,
-                                            opt_assignment_rhs_span,
-                                            *opt_ty_info,
-                                        );
+                                        let (span, suggestion) = if name != kw::SelfLower {
+                                            suggest_ampmut(
+                                                self.infcx.tcx,
+                                                local_decl,
+                                                opt_assignment_rhs_span,
+                                                *opt_ty_info,
+                                            )
+                                        } else {
+                                            match local_decl.local_info.as_deref() {
+                                                Some(LocalInfo::User(ClearCrossCrate::Set(
+                                                    mir::BindingForm::Var(mir::VarBindingForm {
+                                                        opt_ty_info: None,
+                                                        ..
+                                                    }),
+                                                ))) => {
+                                                    suggest_ampmut_self(self.infcx.tcx, local_decl)
+                                                }
+                                                // explicit self (eg `self: &'a Self`)
+                                                _ => suggest_ampmut(
+                                                    self.infcx.tcx,
+                                                    local_decl,
+                                                    opt_assignment_rhs_span,
+                                                    *opt_ty_info,
+                                                ),
+                                            }
+                                        };
                                         Some((true, span, suggestion))
                                     }
                                 }
diff --git a/src/test/ui/borrowck/issue-93093.rs b/src/test/ui/borrowck/issue-93093.rs
new file mode 100644
index 00000000000..f4db5ecafac
--- /dev/null
+++ b/src/test/ui/borrowck/issue-93093.rs
@@ -0,0 +1,14 @@
+// edition:2018
+struct S {
+    foo: usize,
+}
+impl S {
+    async fn bar(&self) { //~ HELP consider changing this to be a mutable reference
+        //~| SUGGESTION &mut self
+        self.foo += 1; //~ ERROR cannot assign to `self.foo`, which is behind a `&` reference [E0594]
+    }
+}
+
+fn main() {
+    S { foo: 1 }.bar();
+}
diff --git a/src/test/ui/borrowck/issue-93093.stderr b/src/test/ui/borrowck/issue-93093.stderr
new file mode 100644
index 00000000000..031128af476
--- /dev/null
+++ b/src/test/ui/borrowck/issue-93093.stderr
@@ -0,0 +1,12 @@
+error[E0594]: cannot assign to `self.foo`, which is behind a `&` reference
+  --> $DIR/issue-93093.rs:8:9
+   |
+LL |     async fn bar(&self) {
+   |                  ----- help: consider changing this to be a mutable reference: `&mut self`
+LL |
+LL |         self.foo += 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`.