about summary refs log tree commit diff
path: root/src/librustc_parse/parser/expr.rs
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-03-22 15:48:32 +0100
committerGitHub <noreply@github.com>2020-03-22 15:48:32 +0100
commitea44d71f9bc76d2136e20553259d43f55f282db1 (patch)
tree792b0ffe15867a29de688c9a94547b8bea5e8ff7 /src/librustc_parse/parser/expr.rs
parent9890d9a9d0e76cecbb6cfad41c0901f248430877 (diff)
parent4d30b92e3e57cf606a25c807a9e4ab2b7a4d1064 (diff)
downloadrust-ea44d71f9bc76d2136e20553259d43f55f282db1.tar.gz
rust-ea44d71f9bc76d2136e20553259d43f55f282db1.zip
Rollup merge of #70209 - Centril:recover-quant-closure, r=petrochenkov
parser: recover on `for<'a> |...| body` closures

When encountering `for` and `<` is 1 token ahead, interpret this as an explicitly quantified generic closure and recover, rather than attempting to parse a `for` loop. This provides both improved diagnostics as well as an insurance policy for the ability to use this as the syntax for generic closures in the future.

As requested by r? @eddyb
Diffstat (limited to 'src/librustc_parse/parser/expr.rs')
-rw-r--r--src/librustc_parse/parser/expr.rs33
1 files changed, 31 insertions, 2 deletions
diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs
index b993857681a..b205a4b3222 100644
--- a/src/librustc_parse/parser/expr.rs
+++ b/src/librustc_parse/parser/expr.rs
@@ -925,8 +925,17 @@ impl<'a> Parser<'a> {
             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() {
@@ -1417,6 +1426,26 @@ impl<'a> Parser<'a> {
         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;