about summary refs log tree commit diff
path: root/src/libsyntax/parse
diff options
context:
space:
mode:
authorNick Cameron <ncameron@mozilla.com>2016-02-10 16:11:27 +1300
committerNick Cameron <ncameron@mozilla.com>2016-02-15 09:33:21 +1300
commit847a0d21507faeca6413782c12e9071ca44c97e8 (patch)
treef95d0589b5459ca8ea4fd33da2315c88bd2d9c7e /src/libsyntax/parse
parentffd2a0b9d78191b8a3d97f687077852d15c9b7aa (diff)
downloadrust-847a0d21507faeca6413782c12e9071ca44c97e8.tar.gz
rust-847a0d21507faeca6413782c12e9071ca44c97e8.zip
Some error recovery in the parser
Diffstat (limited to 'src/libsyntax/parse')
-rw-r--r--src/libsyntax/parse/parser.rs182
1 files changed, 150 insertions, 32 deletions
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 14c663b698c..572c51cc2f4 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -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 @@ impl<'a> Parser<'a> {
     /// 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 @@ impl<'a> Parser<'a> {
                                          -> 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 @@ impl<'a> Parser<'a> {
                     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 @@ impl<'a> Parser<'a> {
 
             match f(self) {
                 Ok(t) => v.push(t),
-                Err(mut e) => {
-                    e.emit();
+                Err(e) => {
+                    fe(e);
                     break;
                 }
             }
@@ -1263,7 +1275,7 @@ impl<'a> Parser<'a> {
                                 break;
                             }
                         }
-                        
+
                         return Err(e);
                     }
                 };
@@ -2339,14 +2351,37 @@ impl<'a> Parser<'a> {
 
                             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 @@ impl<'a> Parser<'a> {
                         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 @@ impl<'a> Parser<'a> {
     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 @@ impl<'a> Parser<'a> {
             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 @@ impl<'a> Parser<'a> {
                         }
                         // 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 @@ impl<'a> Parser<'a> {
 
     /// 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 @@ impl<'a> Parser<'a> {
         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 @@ impl<'a> Parser<'a> {
         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 @@ impl<'a> Parser<'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, ()> {
+    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 @@ impl<'a> Parser<'a> {
                 }
             ));
 
+        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))
     }