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
/// 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
-> 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!();
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;
}
}
match f(self) {
Ok(t) => v.push(t),
- Err(mut e) => {
- e.emit();
+ Err(e) => {
+ fe(e);
break;
}
}
break;
}
}
-
+
return Err(e);
}
};
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;
if let Some(&sp) = self.open_braces.last() {
err.span_note(sp, "unclosed delimiter");
};
+
Err(err)
},
/* we ought to allow different depths of unquotation */
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 {
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();
}
// 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);
}
/// 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());
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
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 `}`.
}))
}
- 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 {
}
));
+ 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))
}