about summary refs log tree commit diff
path: root/src/libsyntax/parse
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-07-24 10:26:32 +0200
committerMazdak Farrokhzad <twingoow@gmail.com>2019-07-28 20:43:09 +0200
commitdfad725be540137e0bc3022fe5341378e4690b9b (patch)
treef8b4f5d3561be6ae57395dabea7b244f1e5a8b78 /src/libsyntax/parse
parent023525dbda35748a10713471b948974b68a1c2cc (diff)
downloadrust-dfad725be540137e0bc3022fe5341378e4690b9b.tar.gz
rust-dfad725be540137e0bc3022fe5341378e4690b9b.zip
Recover 'for ( $pat in $expr ) $block'.
Diffstat (limited to 'src/libsyntax/parse')
-rw-r--r--src/libsyntax/parse/diagnostics.rs44
-rw-r--r--src/libsyntax/parse/parser.rs11
2 files changed, 55 insertions, 0 deletions
diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs
index f4fc87506f3..e9dcfa81343 100644
--- a/src/libsyntax/parse/diagnostics.rs
+++ b/src/libsyntax/parse/diagnostics.rs
@@ -923,6 +923,50 @@ impl<'a> Parser<'a> {
         }
     }
 
+    /// Recover a situation like `for ( $pat in $expr )`
+    /// and suggest writing `for $pat in $expr` instead.
+    ///
+    /// This should be called before parsing the `$block`.
+    crate fn recover_parens_around_for_head(
+        &mut self,
+        pat: P<Pat>,
+        expr: &Expr,
+        begin_paren: Option<Span>,
+    ) -> P<Pat> {
+        match (&self.token.kind, begin_paren) {
+            (token::CloseDelim(token::Paren), Some(begin_par_sp)) => {
+                self.bump();
+
+                let pat_str = self
+                    .sess
+                    .source_map()
+                    // Remove the `(` from the span of the pattern:
+                    .span_to_snippet(pat.span.trim_start(begin_par_sp).unwrap())
+                    .unwrap_or_else(|_| pprust::pat_to_string(&pat));
+
+                self.struct_span_err(self.prev_span, "unexpected closing `)`")
+                    .span_label(begin_par_sp, "opening `(`")
+                    .span_suggestion(
+                        begin_par_sp.to(self.prev_span),
+                        "remove parenthesis in `for` loop",
+                        format!("{} in {}", pat_str, pprust::expr_to_string(&expr)),
+                        // With e.g. `for (x) in y)` this would replace `(x) in y)`
+                        // with `x) in y)` which is syntactically invalid.
+                        // However, this is prevented before we get here.
+                        Applicability::MachineApplicable,
+                    )
+                    .emit();
+
+                // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint.
+                pat.and_then(|pat| match pat.node {
+                    PatKind::Paren(pat) => pat,
+                    _ => P(pat),
+                })
+            }
+            _ => pat,
+        }
+    }
+
     crate fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool {
         self.token.is_ident() &&
             if let ast::ExprKind::Path(..) = node { true } else { false } &&
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 8f8ed411180..42030acf9df 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -3275,6 +3275,14 @@ impl<'a> Parser<'a> {
                           mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
         // Parse: `for <src_pat> in <src_expr> <src_loop_block>`
 
+        // Record whether we are about to parse `for (`.
+        // This is used below for recovery in case of `for ( $stuff ) $block`
+        // in which case we will suggest `for $stuff $block`.
+        let begin_paren = match self.token.kind {
+            token::OpenDelim(token::Paren) => Some(self.token.span),
+            _ => None,
+        };
+
         let pat = self.parse_top_level_pat()?;
         if !self.eat_keyword(kw::In) {
             let in_span = self.prev_span.between(self.token.span);
@@ -3290,6 +3298,9 @@ impl<'a> Parser<'a> {
         let in_span = self.prev_span;
         self.check_for_for_in_in_typo(in_span);
         let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
+
+        let pat = self.recover_parens_around_for_head(pat, &expr, begin_paren);
+
         let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?;
         attrs.extend(iattrs);