about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs22
-rw-r--r--src/test/ui/parser/recover-ref-dyn-mut.rs9
-rw-r--r--src/test/ui/parser/recover-ref-dyn-mut.stderr15
3 files changed, 45 insertions, 1 deletions
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index de5a5632600..1fbf01b1b97 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -393,7 +393,7 @@ impl<'a> Parser<'a> {
         let and_span = self.prev_token.span;
         let mut opt_lifetime =
             if self.check_lifetime() { Some(self.expect_lifetime()) } else { None };
-        let mutbl = self.parse_mutability();
+        let mut mutbl = self.parse_mutability();
         if self.token.is_lifetime() && mutbl == Mutability::Mut && opt_lifetime.is_none() {
             // A lifetime is invalid here: it would be part of a bare trait bound, which requires
             // it to be followed by a plus, but we disallow plus in the pointee type.
@@ -417,6 +417,26 @@ impl<'a> Parser<'a> {
 
                 opt_lifetime = Some(self.expect_lifetime());
             }
+        } else if self.token.is_keyword(kw::Dyn)
+            && mutbl == Mutability::Not
+            && self.look_ahead(1, |t| t.is_keyword(kw::Mut))
+        {
+            // We have `&dyn mut ...`, which is invalid and should be `&mut dyn ...`.
+            let span = and_span.to(self.look_ahead(1, |t| t.span));
+            let mut err = self.struct_span_err(span, "`mut` must precede `dyn`");
+            err.span_suggestion(
+                span,
+                "place `mut` before `dyn`",
+                "&mut dyn".to_string(),
+                Applicability::MachineApplicable,
+            );
+            err.emit();
+
+            // Recovery
+            mutbl = Mutability::Mut;
+            let (dyn_tok, dyn_tok_sp) = (self.token.clone(), self.token_spacing);
+            self.bump();
+            self.bump_with((dyn_tok, dyn_tok_sp));
         }
         let ty = self.parse_ty_no_plus()?;
         Ok(TyKind::Rptr(opt_lifetime, MutTy { ty, mutbl }))
diff --git a/src/test/ui/parser/recover-ref-dyn-mut.rs b/src/test/ui/parser/recover-ref-dyn-mut.rs
new file mode 100644
index 00000000000..3016275cc0f
--- /dev/null
+++ b/src/test/ui/parser/recover-ref-dyn-mut.rs
@@ -0,0 +1,9 @@
+// Test that the parser detects `&dyn mut`, offers a help message, and
+// recovers.
+
+fn main() {
+    let r: &dyn mut Trait;
+    //~^ ERROR: `mut` must precede `dyn`
+    //~| HELP: place `mut` before `dyn`
+    //~| ERROR: cannot find trait `Trait` in this scope [E0405]
+}
diff --git a/src/test/ui/parser/recover-ref-dyn-mut.stderr b/src/test/ui/parser/recover-ref-dyn-mut.stderr
new file mode 100644
index 00000000000..c048c8ea1b0
--- /dev/null
+++ b/src/test/ui/parser/recover-ref-dyn-mut.stderr
@@ -0,0 +1,15 @@
+error: `mut` must precede `dyn`
+  --> $DIR/recover-ref-dyn-mut.rs:5:12
+   |
+LL |     let r: &dyn mut Trait;
+   |            ^^^^^^^^ help: place `mut` before `dyn`: `&mut dyn`
+
+error[E0405]: cannot find trait `Trait` in this scope
+  --> $DIR/recover-ref-dyn-mut.rs:5:21
+   |
+LL |     let r: &dyn mut Trait;
+   |                     ^^^^^ not found in this scope
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0405`.