]> git.lizzy.rs Git - rust.git/commitdiff
Some error recovery in the parser
authorNick Cameron <ncameron@mozilla.com>
Wed, 10 Feb 2016 03:11:27 +0000 (16:11 +1300)
committerNick Cameron <ncameron@mozilla.com>
Sun, 14 Feb 2016 20:33:21 +0000 (09:33 +1300)
12 files changed:
src/libsyntax/parse/parser.rs
src/test/compile-fail/issue-30715.rs
src/test/compile-fail/macro-incomplete-parse.rs
src/test/parse-fail/brace-after-qualified-path-in-match.rs
src/test/parse-fail/issue-10636-2.rs
src/test/parse-fail/match-refactor-to-expr.rs
src/test/parse-fail/paren-after-qualified-path-in-match.rs
src/test/parse-fail/pat-lt-bracket-4.rs
src/test/parse-fail/struct-literal-in-for.rs
src/test/parse-fail/struct-literal-in-if.rs
src/test/parse-fail/struct-literal-in-match-discriminant.rs
src/test/parse-fail/struct-literal-in-while.rs

index 14c663b698c23e285245d04e78d56169c05db21a..572c51cc2f4bac4dbc571b605e3f4e3283dc9e95 100644 (file)
@@ -105,6 +105,12 @@ pub enum ParsePub {
     No,
 }
 
+#[derive(Clone, Copy, PartialEq)]
+pub enum SemiColonMode {
+    Break,
+    Ignore,
+}
+
 /// Possibly accept an `token::Interpolated` expression (a pre-parsed expression
 /// dropped into the token stream, which happens while parsing the result of
 /// macro expansion). Placement of these is not as complex as I feared it would
@@ -843,7 +849,10 @@ pub fn parse_seq_to_gt_or_return<T, F>(&mut self,
     /// Eat and discard tokens until one of `kets` is encountered. Respects token trees,
     /// passes through any errors encountered. Used for error recovery.
     pub fn eat_to_tokens(&mut self, kets: &[&token::Token]) {
-        self.parse_seq_to_before_tokens(kets, seq_sep_none(), |p| p.parse_token_tree());
+        self.parse_seq_to_before_tokens(kets,
+                                        seq_sep_none(),
+                                        |p| p.parse_token_tree(),
+                                        |mut e| e.cancel());
     }
 
     /// Parse a sequence, including the closing delimiter. The function
@@ -871,15 +880,18 @@ pub fn parse_seq_to_before_end<T, F>(&mut self,
                                          -> Vec<T>
         where F: FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
     {
-        self.parse_seq_to_before_tokens(&[ket], sep, f)
+        self.parse_seq_to_before_tokens(&[ket], sep, f, |mut e| e.emit())
     }
 
-    pub fn parse_seq_to_before_tokens<T, F>(&mut self,
+    // `fe` is an error handler.
+    fn parse_seq_to_before_tokens<T, F, Fe>(&mut self,
                                             kets: &[&token::Token],
                                             sep: SeqSep,
-                                            mut f: F)
+                                            mut f: F,
+                                            mut fe: Fe)
                                             -> Vec<T>
         where F: FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
+              Fe: FnMut(DiagnosticBuilder)
     {
         let mut first: bool = true;
         let mut v = vec!();
@@ -889,8 +901,8 @@ pub fn parse_seq_to_before_tokens<T, F>(&mut self,
                     if first {
                         first = false;
                     } else {
-                        if let Err(mut e) = self.expect(t) {
-                            e.emit();
+                        if let Err(e) = self.expect(t) {
+                            fe(e);
                             break;
                         }
                     }
@@ -903,8 +915,8 @@ pub fn parse_seq_to_before_tokens<T, F>(&mut self,
 
             match f(self) {
                 Ok(t) => v.push(t),
-                Err(mut e) => {
-                    e.emit();
+                Err(e) => {
+                    fe(e);
                     break;
                 }
             }
@@ -1263,7 +1275,7 @@ pub fn parse_trait_items(&mut self) -> PResult<'a,  Vec<TraitItem>> {
                                 break;
                             }
                         }
-                        
+
                         return Err(e);
                     }
                 };
@@ -2339,14 +2351,37 @@ fn parse_bottom_expr(&mut self) -> PResult<'a, P<Expr>> {
 
                             while self.token != token::CloseDelim(token::Brace) {
                                 if self.eat(&token::DotDot) {
-                                    base = Some(try!(self.parse_expr()));
+                                    match self.parse_expr() {
+                                        Ok(e) => {
+                                            base = Some(e);
+                                        }
+                                        Err(mut e) => {
+                                            e.emit();
+                                            self.recover_stmt();
+                                        }
+                                    }
                                     break;
                                 }
 
-                                fields.push(try!(self.parse_field()));
-                                try!(self.commit_expr(&fields.last().unwrap().expr,
-                                                 &[token::Comma],
-                                                 &[token::CloseDelim(token::Brace)]));
+                                match self.parse_field() {
+                                    Ok(f) => fields.push(f),
+                                    Err(mut e) => {
+                                        e.emit();
+                                        self.recover_stmt();
+                                        break;
+                                    }
+                                }
+
+                                match self.commit_expr(&fields.last().unwrap().expr,
+                                                       &[token::Comma],
+                                                       &[token::CloseDelim(token::Brace)]) {
+                                    Ok(()) => {}
+                                    Err(mut e) => {
+                                        e.emit();
+                                        self.recover_stmt();
+                                        break;
+                                    }
+                                }
                             }
 
                             hi = self.span.hi;
@@ -2748,6 +2783,7 @@ pub fn parse_token_tree(&mut self) -> PResult<'a, TokenTree> {
                         if let Some(&sp) = self.open_braces.last() {
                             err.span_note(sp, "unclosed delimiter");
                         };
+
                         Err(err)
                     },
                     /* we ought to allow different depths of unquotation */
@@ -3195,8 +3231,8 @@ pub fn parse_loop_expr(&mut self, opt_ident: Option<ast::Ident>,
     fn parse_match_expr(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
         let match_span = self.last_span;
         let lo = self.last_span.lo;
-        let discriminant = try!(self.parse_expr_res(
-            Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None));
+        let discriminant = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL,
+                                                    None));
         if let Err(mut e) = self.commit_expr_expecting(&discriminant,
                                                        token::OpenDelim(token::Brace)) {
             if self.token == token::Token::Semi {
@@ -3208,7 +3244,19 @@ fn parse_match_expr(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
             try!(self.parse_inner_attributes()).into_thin_attrs());
         let mut arms: Vec<Arm> = Vec::new();
         while self.token != token::CloseDelim(token::Brace) {
-            arms.push(try!(self.parse_arm()));
+            match self.parse_arm() {
+                Ok(arm) => arms.push(arm),
+                Err(mut e) => {
+                    // Recover by skipping to the end of the block.
+                    e.emit();
+                    self.recover_stmt();
+                    let hi = self.span.hi;
+                    if self.token == token::CloseDelim(token::Brace) {
+                        self.bump();
+                    }
+                    return Ok(self.mk_expr(lo, hi, ExprMatch(discriminant, arms), attrs));
+                }
+            }
         }
         let hi = self.span.hi;
         self.bump();
@@ -3566,7 +3614,11 @@ pub fn parse_pat(&mut self) -> PResult<'a, P<Pat>> {
                         }
                         // Parse struct pattern
                         self.bump();
-                        let (fields, etc) = try!(self.parse_pat_fields());
+                        let (fields, etc) = self.parse_pat_fields().unwrap_or_else(|mut e| {
+                            e.emit();
+                            self.recover_stmt();
+                            (vec![], false)
+                        });
                         self.bump();
                         pat = PatKind::Struct(path, fields, etc);
                       }
@@ -3720,10 +3772,72 @@ fn expected_item_err(&self, attrs: &[Attribute]) {
 
     /// Parse a statement. may include decl.
     pub fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> {
-        Ok(try!(self.parse_stmt_()))
+        Ok(self.parse_stmt_().map(P))
+    }
+
+    // Eat tokens until we can be relatively sure we reached the end of the
+    // statement. This is something of a best-effort heuristic.
+    //
+    // We terminate when we find an unmatched `}` (without consuming it).
+    fn recover_stmt(&mut self) {
+        self.recover_stmt_(SemiColonMode::Ignore)
+    }
+    // If `break_on_semi` is `Break`, then we will stop consuming tokens after
+    // finding (and consuming) a `;` outside of `{}` or `[]` (note that this is
+    // approximate - it can mean we break too early due to macros, but that
+    // shoud only lead to sub-optimal recovery, not inaccurate parsing).
+    fn recover_stmt_(&mut self, break_on_semi: SemiColonMode) {
+        let mut brace_depth = 0;
+        let mut bracket_depth = 0;
+        loop {
+            match self.token {
+                token::OpenDelim(token::DelimToken::Brace) => {
+                    brace_depth += 1;
+                    self.bump();
+                }
+                token::OpenDelim(token::DelimToken::Bracket) => {
+                    bracket_depth += 1;
+                    self.bump();
+                }
+                token::CloseDelim(token::DelimToken::Brace) => {
+                    if brace_depth == 0 {
+                        return;
+                    }
+                    brace_depth -= 1;
+                    self.bump();
+                }
+                token::CloseDelim(token::DelimToken::Bracket) => {
+                    bracket_depth -= 1;
+                    if bracket_depth < 0 {
+                        bracket_depth = 0;
+                    }
+                    self.bump();
+                }
+                token::Eof => return,
+                token::Semi => {
+                    self.bump();
+                    if break_on_semi == SemiColonMode::Break &&
+                       brace_depth == 0 &&
+                       bracket_depth == 0 {
+                        return;
+                    }
+                }
+                _ => {
+                    self.bump()
+                }
+            }
+        }
     }
 
-    fn parse_stmt_(&mut self) -> PResult<'a, Option<Stmt>> {
+    fn parse_stmt_(&mut self) -> Option<Stmt> {
+        self.parse_stmt_without_recovery().unwrap_or_else(|mut e| {
+            e.emit();
+            self.recover_stmt_(SemiColonMode::Break);
+            None
+        })
+    }
+
+    fn parse_stmt_without_recovery(&mut self) -> PResult<'a, Option<Stmt>> {
         maybe_whole!(Some deref self, NtStmt);
 
         let attrs = try!(self.parse_outer_attributes());
@@ -3879,7 +3993,7 @@ fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (Vec<Attribute>, P<Bloc
         let lo = self.span.lo;
         try!(self.expect(&token::OpenDelim(token::Brace)));
         Ok((try!(self.parse_inner_attributes()),
-         try!(self.parse_block_tail(lo, BlockCheckMode::Default))))
+            try!(self.parse_block_tail(lo, BlockCheckMode::Default))))
     }
 
     /// Parse the rest of a block expression or function body
@@ -3889,7 +4003,7 @@ fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult<'a, P<
         let mut expr = None;
 
         while !self.eat(&token::CloseDelim(token::Brace)) {
-            let Spanned {node, span} = if let Some(s) = try!(self.parse_stmt_()) {
+            let Spanned {node, span} = if let Some(s) = self.parse_stmt_() {
                 s
             } else {
                 // Found only `;` or `}`.
@@ -3974,17 +4088,21 @@ fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult<'a, P<
         }))
     }
 
-    fn handle_expression_like_statement(
-            &mut self,
-            e: P<Expr>,
-            span: Span,
-            stmts: &mut Vec<Stmt>,
-            last_block_expr: &mut Option<P<Expr>>) -> PResult<'a, ()> {
+    fn handle_expression_like_statement(&mut self,
+                                        e: P<Expr>,
+                                        span: Span,
+                                        stmts: &mut Vec<Stmt>,
+                                        last_block_expr: &mut Option<P<Expr>>)
+                                        -> PResult<'a, ()> {
         // expression without semicolon
         if classify::expr_requires_semi_to_be_stmt(&e) {
             // Just check for errors and recover; do not eat semicolon yet.
-            try!(self.commit_stmt(&[],
-                             &[token::Semi, token::CloseDelim(token::Brace)]));
+            if let Err(mut e) =
+                self.commit_stmt(&[], &[token::Semi, token::CloseDelim(token::Brace)])
+            {
+                e.emit();
+                self.recover_stmt();
+            }
         }
 
         match self.token {
@@ -4381,13 +4499,13 @@ fn parse_fn_args(&mut self, named_args: bool, allow_variadic: bool)
                 }
             ));
 
+        let args: Vec<_> = args.into_iter().filter_map(|x| x).collect();
+
         if variadic && args.is_empty() {
             self.span_err(sp,
                           "variadic function must be declared with at least one named argument");
         }
 
-        let args = args.into_iter().filter_map(|x| x).collect();
-
         Ok((args, variadic))
     }
 
index 7ad43954010b9b4333e422e3cdab9c47a16ceaad..67f619b4de4f433f79f771dd6fe431cf24c2f584 100644 (file)
@@ -25,7 +25,7 @@ macro_rules! parallel {
 fn main() {
     parallel! {
         for i in 0..n {
-            x += i; //~ ERROR no rules expected the token `+=`
-        }
+            x += i; //~ ERROR expected `:`, found `+=`
+        } //~ ERROR unexpected end of macro invocation
     }
 }
index 32770d9018938b96da18a91a61abc5beeb0e8330..364a7e9cf6d75cb025ea9fc3f4691739ff95d2b2 100644 (file)
@@ -18,7 +18,7 @@ fn bar() {}
 
 macro_rules! ignored_expr {
     () => ( 1,  //~ ERROR unexpected token: `,`
-            2 ) //~ ERROR macro expansion ignores token `2`
+            2 )
 }
 
 macro_rules! ignored_pat {
@@ -28,7 +28,7 @@ macro_rules! ignored_pat {
 ignored_item!(); //~ NOTE caused by the macro expansion here
 
 fn main() {
-    ignored_expr!(); //~ NOTE caused by the macro expansion here
+    ignored_expr!();
     match 1 {
         ignored_pat!() => (), //~ NOTE caused by the macro expansion here
         _ => (),
index 66f462df05ac9d05b04d0ad9f288448efa7c4eb3..ff434d87e1fb2c141955f5f363ad7d4837a56974 100644 (file)
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Z parse-only
+
 fn foo() {
     match x {
         <T as Trait>::Type{key: value} => (),
index f57abf8929e83647f3037c35d749363947ada5e3..41a3b06e6556e9bd1d8ba065256f345de9b1cf90 100644 (file)
@@ -8,8 +8,14 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// FIXME(31528) we emit a bunch of silly errors here due to continuing past the
+// first one. This would be easy-ish to address by better recovery in tokenisation.
+
 // compile-flags: -Z parse-only
 
-pub fn trace_option(option: Option<isize>) {
+pub fn trace_option(option: Option<isize>) { //~ HELP did you mean to close this delimiter?
     option.map(|some| 42; //~ NOTE: unclosed delimiter
+                          //~^ ERROR: expected one of
 } //~ ERROR: incorrect close delimiter
+//~^ ERROR: expected one of
+//~ ERROR: this file contains an un-closed delimiter
index e85fb3c9dd53d9c655e419246304696911fc3386..b99d0493ff7333b2f402829193a766a3ecc1f382 100644 (file)
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Z parse-only
+
 fn main() {
     let foo =
         match //~ NOTE did you mean to remove this `match` keyword?
index d06fd2bb4e704846f4344df39a6b61c40439774f..d3aa4b72b78a55190c1fdadb9e6d982c217a7709 100644 (file)
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Z parse-only
+
 fn foo() {
     match x {
         <T as Trait>::Type(2) => (),
index 3d9b492307a7cd7493b66e6dbfab58b5e5ecd652..a163b38b49270ac25f18c67d055074e39689c038 100644 (file)
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Z parse-only
+
 enum BtNode {
     Node(u32,Box<BtNode>,Box<BtNode>),
     Leaf(u32),
index 107b836d160a5669bbb47bed4b4090795e923205..e57298f72804af423ba7a55a3c6e5e9d91529934 100644 (file)
@@ -23,7 +23,7 @@ fn hi(&self) -> bool {
 fn main() {
     for x in Foo {
         x: 3    //~ ERROR expected type, found `3`
-    }.hi() {
+    }.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{`
         println!("yo");
     }
 }
index b1cccc51d7bb9acc70bb389e09a4a83047d0c277..6bf41b7a450aff70a1c6492c4ce331b7c82e3b5d 100644 (file)
@@ -23,7 +23,7 @@ fn hi(&self) -> bool {
 fn main() {
     if Foo {
         x: 3    //~ ERROR expected type, found `3`
-    }.hi() {
+    }.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{`
         println!("yo");
     }
 }
index 42b3e75bcf6d1a4f92c20769f7e64c473577367e..679f4542824fbd0ff4155e7429add1bd28daac80 100644 (file)
@@ -20,6 +20,6 @@ fn main() {
     } {
         Foo {
             x: x
-        } => {}
+        } => {} //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `=>`
     }
 }
index 1c52dc48ccd1ac125f4271f00b7810006f806e47..b388aac2c54875400a3ea4b0e35c770ec43c2fd5 100644 (file)
@@ -23,7 +23,7 @@ fn hi(&self) -> bool {
 fn main() {
     while Foo {
         x: 3    //~ ERROR expected type, found `3`
-    }.hi() {
+    }.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{`
         println!("yo");
     }
 }