about summary refs log tree commit diff
diff options
context:
space:
mode:
authorlaurent <laurent.mazare@gmail.com>2017-10-30 22:33:57 +0000
committerlaurent <laurent.mazare@gmail.com>2017-10-30 22:33:57 +0000
commit6a62ea682878e9f9e889c44a42f57a7eb1822e0d (patch)
tree0403c267a03c2b212bb845d546339c7268269880
parentf0fe716dbcbf2363ab8f929325d32a17e51039d0 (diff)
downloadrust-6a62ea682878e9f9e889c44a42f57a7eb1822e0d.tar.gz
rust-6a62ea682878e9f9e889c44a42f57a7eb1822e0d.zip
Add a nicer error message for missing in for loop, fixes #40782.
-rw-r--r--src/libsyntax/parse/parser.rs54
-rw-r--r--src/test/ui/issue-40782.rs15
-rw-r--r--src/test/ui/issue-40782.stderr11
3 files changed, 74 insertions, 6 deletions
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index a3a265450ab..37a12801ddc 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -3154,13 +3154,55 @@ impl<'a> Parser<'a> {
         // Parse: `for <src_pat> in <src_expr> <src_loop_block>`
 
         let pat = self.parse_pat()?;
-        self.expect_keyword(keywords::In)?;
-        let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
-        let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?;
-        attrs.extend(iattrs);
+        // Save the state of the parser before parsing 'in'.
+        let parser_snapshot_before_in = self.clone();
+        match self.expect_keyword(keywords::In) {
+            Ok(()) => {
+                let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
+                let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?;
+                attrs.extend(iattrs);
 
-        let hi = self.prev_span;
-        Ok(self.mk_expr(span_lo.to(hi), ExprKind::ForLoop(pat, expr, loop_block, opt_ident), attrs))
+                let hi = self.prev_span;
+                Ok(self.mk_expr(
+                    span_lo.to(hi),
+                    ExprKind::ForLoop(pat, expr, loop_block, opt_ident),
+                    attrs))
+            }
+            Err(mut in_err) => {
+                let parser_snapshot_after_in = self.clone();
+                // Rewind to before attempting to parse the 'in'.
+                mem::replace(self, parser_snapshot_before_in);
+
+                match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None) {
+                    Ok(expr) => {
+                        // Successfully parsed the expr, print a nice error message.
+                        in_err.cancel();
+                        let in_span = parser_snapshot_after_in.span;
+                        let mut err = self.sess.span_diagnostic
+                            .struct_span_err(in_span, "missing `in` in `for` loop");
+                        err.span_label(in_span, "expected `in` here");
+                        let sugg = pprust::to_string(|s| {
+                            s.s.word("for ")?;
+                            s.print_pat(&pat)?;
+                            s.s.word(" in ")?;
+                            s.print_expr(&expr)
+                        });
+                        err.span_suggestion(
+                            in_span,
+                            "try adding `in`",
+                            sugg);
+                        Err(err)
+                    }
+                    Err(mut expr_err) => {
+                        // Couldn't parse as an expr, return original error and parser state.
+                        expr_err.cancel();
+                        mem::replace(self, parser_snapshot_after_in);
+                        Err(in_err)
+                    }
+                }
+            }
+
+            }
     }
 
     /// Parse a 'while' or 'while let' expression ('while' token already eaten)
diff --git a/src/test/ui/issue-40782.rs b/src/test/ui/issue-40782.rs
new file mode 100644
index 00000000000..56ee225105f
--- /dev/null
+++ b/src/test/ui/issue-40782.rs
@@ -0,0 +1,15 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    for i 0..2 {
+    }
+}
+
diff --git a/src/test/ui/issue-40782.stderr b/src/test/ui/issue-40782.stderr
new file mode 100644
index 00000000000..a267c9bf4af
--- /dev/null
+++ b/src/test/ui/issue-40782.stderr
@@ -0,0 +1,11 @@
+error: missing `in` in `for` loop
+  --> $DIR/issue-40782.rs:12:11
+   |
+12 |     for i 0..2 {
+   |           ^
+   |           |
+   |           expected `in` here
+   |           help: try adding `in`: `for i in 0..2`
+
+error: aborting due to previous error
+