about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSNCPlay42 <SNCPlay42@gmail.com>2020-06-21 21:37:17 +0100
committerMark Rousskov <mark.simulacrum@gmail.com>2020-09-15 10:36:06 -0400
commit4de9a53d98533868dfa209bfb3604bba92d5e7b1 (patch)
tree0f19e96f09dbdbc43f9d1f7d00aa6aaf9a63c56d
parent90b1f5ae59291dd69d72fad41a22277df19dc953 (diff)
downloadrust-4de9a53d98533868dfa209bfb3604bba92d5e7b1.tar.gz
rust-4de9a53d98533868dfa209bfb3604bba92d5e7b1.zip
improve diagnostics for lifetime after `&mut`
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs28
-rw-r--r--src/test/ui/parser/issue-73568-lifetime-after-mut.rs21
-rw-r--r--src/test/ui/parser/issue-73568-lifetime-after-mut.stderr53
3 files changed, 101 insertions, 1 deletions
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 259764a317d..fc4c62ccbd9 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -276,8 +276,34 @@ impl<'a> Parser<'a> {
     }
 
     fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
-        let opt_lifetime = if self.check_lifetime() { Some(self.expect_lifetime()) } else { None };
+        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();
+        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.
+            // So we can handle this case as an error here, and suggest `'a mut`.
+            // If there *is* a plus next though, handling the error later provides better suggestions
+            // (like adding parentheses)
+            if !self.look_ahead(1, |t| t.is_like_plus()) {
+                let lifetime_span = self.token.span;
+                let span = and_span.to(lifetime_span);
+
+                let mut err = self.struct_span_err(span, "lifetime must precede `mut`");
+                if let Ok(lifetime_src) = self.span_to_snippet(lifetime_span) {
+                    err.span_suggestion(
+                        span,
+                        "place the lifetime before `mut`",
+                        format!("&{} mut", lifetime_src),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+                err.emit();
+
+                opt_lifetime = Some(self.expect_lifetime());
+            }
+        }
         let ty = self.parse_ty_no_plus()?;
         Ok(TyKind::Rptr(opt_lifetime, MutTy { ty, mutbl }))
     }
diff --git a/src/test/ui/parser/issue-73568-lifetime-after-mut.rs b/src/test/ui/parser/issue-73568-lifetime-after-mut.rs
new file mode 100644
index 00000000000..0b10a5f6f4e
--- /dev/null
+++ b/src/test/ui/parser/issue-73568-lifetime-after-mut.rs
@@ -0,0 +1,21 @@
+#![crate_type="lib"]
+fn x<'a>(x: &mut 'a i32){} //~ ERROR lifetime must precede `mut`
+
+macro_rules! mac {
+    ($lt:lifetime) => {
+        fn w<$lt>(w: &mut $lt i32) {}
+        //~^ ERROR lifetime must precede `mut`
+    }
+}
+
+mac!('a);
+
+// avoid false positives
+fn y<'a>(y: &mut 'a + Send) {
+    //~^ ERROR expected a path on the left-hand side of `+`, not `&mut 'a`
+    //~| WARNING trait objects without an explicit `dyn` are deprecated
+    //~| ERROR at least one trait is required for an object type
+    let z = y as &mut 'a + Send;
+    //~^ ERROR expected value, found trait `Send`
+    //~| WARNING trait objects without an explicit `dyn` are deprecated
+}
diff --git a/src/test/ui/parser/issue-73568-lifetime-after-mut.stderr b/src/test/ui/parser/issue-73568-lifetime-after-mut.stderr
new file mode 100644
index 00000000000..abb64f7e490
--- /dev/null
+++ b/src/test/ui/parser/issue-73568-lifetime-after-mut.stderr
@@ -0,0 +1,53 @@
+error: lifetime must precede `mut`
+  --> $DIR/issue-73568-lifetime-after-mut.rs:2:13
+   |
+LL | fn x<'a>(x: &mut 'a i32){}
+   |             ^^^^^^^ help: place the lifetime before `mut`: `&'a mut`
+
+error[E0178]: expected a path on the left-hand side of `+`, not `&mut 'a`
+  --> $DIR/issue-73568-lifetime-after-mut.rs:14:13
+   |
+LL | fn y<'a>(y: &mut 'a + Send) {
+   |             ^^^^^^^^^^^^^^ help: try adding parentheses: `&mut ('a + Send)`
+
+error: lifetime must precede `mut`
+  --> $DIR/issue-73568-lifetime-after-mut.rs:6:22
+   |
+LL |         fn w<$lt>(w: &mut $lt i32) {}
+   |                      ^^^^^^^^ help: place the lifetime before `mut`: `&$lt mut`
+...
+LL | mac!('a);
+   | --------- in this macro invocation
+   |
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0423]: expected value, found trait `Send`
+  --> $DIR/issue-73568-lifetime-after-mut.rs:18:28
+   |
+LL |     let z = y as &mut 'a + Send;
+   |                            ^^^^ not a value
+
+warning: trait objects without an explicit `dyn` are deprecated
+  --> $DIR/issue-73568-lifetime-after-mut.rs:14:18
+   |
+LL | fn y<'a>(y: &mut 'a + Send) {
+   |                  ^^ help: use `dyn`: `dyn 'a`
+   |
+   = note: `#[warn(bare_trait_objects)]` on by default
+
+warning: trait objects without an explicit `dyn` are deprecated
+  --> $DIR/issue-73568-lifetime-after-mut.rs:18:23
+   |
+LL |     let z = y as &mut 'a + Send;
+   |                       ^^ help: use `dyn`: `dyn 'a`
+
+error[E0224]: at least one trait is required for an object type
+  --> $DIR/issue-73568-lifetime-after-mut.rs:14:18
+   |
+LL | fn y<'a>(y: &mut 'a + Send) {
+   |                  ^^
+
+error: aborting due to 5 previous errors; 2 warnings emitted
+
+Some errors have detailed explanations: E0178, E0224, E0423.
+For more information about an error, try `rustc --explain E0178`.