]> git.lizzy.rs Git - rust.git/commitdiff
recover on `for<'a> |...| body` closures.
authorMazdak Farrokhzad <twingoow@gmail.com>
Sat, 21 Mar 2020 07:32:55 +0000 (08:32 +0100)
committerMazdak Farrokhzad <twingoow@gmail.com>
Sat, 21 Mar 2020 08:54:03 +0000 (09:54 +0100)
src/librustc_parse/parser/expr.rs
src/librustc_parse/parser/generics.rs
src/librustc_parse/parser/item.rs
src/test/ui/parser/recover-quantified-closure.rs [new file with mode: 0644]
src/test/ui/parser/recover-quantified-closure.stderr [new file with mode: 0644]

index c65e99842c5ddf3c63353bed158521c57f6ef081..957bdd082bb62e86eabf8a0707020ff777b5b293 100644 (file)
@@ -925,8 +925,17 @@ fn parse_bottom_expr(&mut self) -> PResult<'a, P<Expr>> {
             self.parse_closure_expr(attrs)
         } else if self.eat_keyword(kw::If) {
             self.parse_if_expr(attrs)
-        } else if self.eat_keyword(kw::For) {
-            self.parse_for_expr(None, self.prev_token.span, attrs)
+        } else if self.check_keyword(kw::For) {
+            if self.choose_generics_over_qpath(1) {
+                // NOTE(Centril, eddyb): DO NOT REMOVE! Beyond providing parser recovery,
+                // this is an insurance policy in case we allow qpaths in (tuple-)struct patterns.
+                // When `for <Foo as Bar>::Proj in $expr $block` is wanted,
+                // you can disambiguate in favor of a pattern with `(...)`.
+                self.recover_quantified_closure_expr(attrs)
+            } else {
+                assert!(self.eat_keyword(kw::For));
+                self.parse_for_expr(None, self.prev_token.span, attrs)
+            }
         } else if self.eat_keyword(kw::While) {
             self.parse_while_expr(None, self.prev_token.span, attrs)
         } else if let Some(label) = self.eat_label() {
@@ -1416,6 +1425,26 @@ pub(super) fn parse_block_expr(
         Ok(self.mk_expr(blk.span, ExprKind::Block(blk, opt_label), attrs))
     }
 
+    /// Recover on an explicitly quantified closure expression, e.g., `for<'a> |x: &'a u8| *x + 1`.
+    fn recover_quantified_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+        let lo = self.token.span;
+        let _ = self.parse_late_bound_lifetime_defs()?;
+        let span_for = lo.to(self.prev_token.span);
+        let closure = self.parse_closure_expr(attrs)?;
+
+        self.struct_span_err(span_for, "cannot introduce explicit parameters for a closure")
+            .span_label(closure.span, "the parameters are attached to this closure")
+            .span_suggestion(
+                span_for,
+                "remove the parameters",
+                String::new(),
+                Applicability::MachineApplicable,
+            )
+            .emit();
+
+        Ok(self.mk_expr_err(lo.to(closure.span)))
+    }
+
     /// Parses a closure expression (e.g., `move |args| expr`).
     fn parse_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
         let lo = self.token.span;
index 59fd5f7c4be1fc55f414cdb6dce37c985048f1c2..3442c5081c18f879c07ddded71faf7cb3fe6f750 100644 (file)
@@ -181,7 +181,7 @@ pub(super) fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> {
         // We are considering adding generics to the `where` keyword as an alternative higher-rank
         // parameter syntax (as in `where<'a>` or `where<T>`. To avoid that being a breaking
         // change we parse those generics now, but report an error.
-        if self.choose_generics_over_qpath() {
+        if self.choose_generics_over_qpath(0) {
             let generics = self.parse_generics()?;
             self.struct_span_err(
                 generics.span,
@@ -257,7 +257,7 @@ fn parse_ty_where_predicate(&mut self) -> PResult<'a, ast::WherePredicate> {
         }
     }
 
-    pub(super) fn choose_generics_over_qpath(&self) -> bool {
+    pub(super) fn choose_generics_over_qpath(&self, start: usize) -> bool {
         // There's an ambiguity between generic parameters and qualified paths in impls.
         // If we see `<` it may start both, so we have to inspect some following tokens.
         // The following combinations can only start generics,
@@ -274,15 +274,12 @@ pub(super) fn choose_generics_over_qpath(&self) -> bool {
         // we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
         // because this is what almost always expected in practice, qualified paths in impls
         // (`impl <Type>::AssocTy { ... }`) aren't even allowed by type checker at the moment.
-        self.token == token::Lt
-            && (self.look_ahead(1, |t| t == &token::Pound || t == &token::Gt)
-                || self.look_ahead(1, |t| t.is_lifetime() || t.is_ident())
-                    && self.look_ahead(2, |t| {
-                        t == &token::Gt
-                            || t == &token::Comma
-                            || t == &token::Colon
-                            || t == &token::Eq
+        self.look_ahead(start, |t| t == &token::Lt)
+            && (self.look_ahead(start + 1, |t| t == &token::Pound || t == &token::Gt)
+                || self.look_ahead(start + 1, |t| t.is_lifetime() || t.is_ident())
+                    && self.look_ahead(start + 2, |t| {
+                        matches!(t.kind, token::Gt | token::Comma | token::Colon | token::Eq)
                     })
-                || self.is_keyword_ahead(1, &[kw::Const]))
+                || self.is_keyword_ahead(start + 1, &[kw::Const]))
     }
 }
index 9d70f606f3ef4339e80e6c619cd1048282bbd8d6..42c4bac3b8db4b2f4b8cb5cf45c20f81623297c8 100644 (file)
@@ -458,7 +458,7 @@ fn parse_item_impl(
         self.expect_keyword(kw::Impl)?;
 
         // First, parse generic parameters if necessary.
-        let mut generics = if self.choose_generics_over_qpath() {
+        let mut generics = if self.choose_generics_over_qpath(0) {
             self.parse_generics()?
         } else {
             let mut generics = Generics::default();
diff --git a/src/test/ui/parser/recover-quantified-closure.rs b/src/test/ui/parser/recover-quantified-closure.rs
new file mode 100644 (file)
index 0000000..3813247
--- /dev/null
@@ -0,0 +1,10 @@
+fn main() {
+    for<'a> |x: &'a u8| *x + 1;
+    //~^ ERROR cannot introduce explicit parameters for a closure
+}
+
+enum Foo { Bar }
+fn foo(x: impl Iterator<Item = Foo>) {
+    for <Foo>::Bar in x {}
+    //~^ ERROR expected one of `move`, `static`, `|`
+}
diff --git a/src/test/ui/parser/recover-quantified-closure.stderr b/src/test/ui/parser/recover-quantified-closure.stderr
new file mode 100644 (file)
index 0000000..0f01132
--- /dev/null
@@ -0,0 +1,16 @@
+error: cannot introduce explicit parameters for a closure
+  --> $DIR/recover-quantified-closure.rs:2:5
+   |
+LL |     for<'a> |x: &'a u8| *x + 1;
+   |     ^^^^^^^ ------------------ the parameters are attached to this closure
+   |     |
+   |     help: remove the parameters
+
+error: expected one of `move`, `static`, `|`, or `||`, found `::`
+  --> $DIR/recover-quantified-closure.rs:8:14
+   |
+LL |     for <Foo>::Bar in x {}
+   |              ^^ expected one of `move`, `static`, `|`, or `||`
+
+error: aborting due to 2 previous errors
+