mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-05 11:48:30 +00:00
Auto merge of #31555 - nrc:err-recover, r=pnkfelix
Recover from missing brackets in the parser
This commit is contained in:
commit
f9543a9b74
@ -105,6 +105,12 @@ pub enum ParsePub {
|
|||||||
No,
|
No,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
pub enum SemiColonMode {
|
||||||
|
Break,
|
||||||
|
Ignore,
|
||||||
|
}
|
||||||
|
|
||||||
/// Possibly accept an `token::Interpolated` expression (a pre-parsed expression
|
/// Possibly accept an `token::Interpolated` expression (a pre-parsed expression
|
||||||
/// dropped into the token stream, which happens while parsing the result of
|
/// 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
|
/// macro expansion). Placement of these is not as complex as I feared it would
|
||||||
@ -598,7 +604,7 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
/// Check if the next token is `tok`, and return `true` if so.
|
/// Check if the next token is `tok`, and return `true` if so.
|
||||||
///
|
///
|
||||||
/// This method is will automatically add `tok` to `expected_tokens` if `tok` is not
|
/// This method will automatically add `tok` to `expected_tokens` if `tok` is not
|
||||||
/// encountered.
|
/// encountered.
|
||||||
pub fn check(&mut self, tok: &token::Token) -> bool {
|
pub fn check(&mut self, tok: &token::Token) -> bool {
|
||||||
let is_present = self.token == *tok;
|
let is_present = self.token == *tok;
|
||||||
@ -840,6 +846,15 @@ impl<'a> Parser<'a> {
|
|||||||
return Ok((v, returned));
|
return Ok((v, returned));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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(),
|
||||||
|
|mut e| e.cancel());
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a sequence, including the closing delimiter. The function
|
/// Parse a sequence, including the closing delimiter. The function
|
||||||
/// f must consume tokens until reaching the next separator or
|
/// f must consume tokens until reaching the next separator or
|
||||||
/// closing bracket.
|
/// closing bracket.
|
||||||
@ -850,7 +865,7 @@ impl<'a> Parser<'a> {
|
|||||||
-> PResult<'a, Vec<T>> where
|
-> PResult<'a, Vec<T>> where
|
||||||
F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
||||||
{
|
{
|
||||||
let val = try!(self.parse_seq_to_before_end(ket, sep, f));
|
let val = self.parse_seq_to_before_end(ket, sep, f);
|
||||||
self.bump();
|
self.bump();
|
||||||
Ok(val)
|
Ok(val)
|
||||||
}
|
}
|
||||||
@ -861,24 +876,53 @@ impl<'a> Parser<'a> {
|
|||||||
pub fn parse_seq_to_before_end<T, F>(&mut self,
|
pub fn parse_seq_to_before_end<T, F>(&mut self,
|
||||||
ket: &token::Token,
|
ket: &token::Token,
|
||||||
sep: SeqSep,
|
sep: SeqSep,
|
||||||
mut f: F)
|
f: F)
|
||||||
-> PResult<'a, Vec<T>> where
|
-> Vec<T>
|
||||||
F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
||||||
|
{
|
||||||
|
self.parse_seq_to_before_tokens(&[ket], sep, f, |mut e| e.emit())
|
||||||
|
}
|
||||||
|
|
||||||
|
// `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 fe: Fe)
|
||||||
|
-> Vec<T>
|
||||||
|
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
||||||
|
Fe: FnMut(DiagnosticBuilder)
|
||||||
{
|
{
|
||||||
let mut first: bool = true;
|
let mut first: bool = true;
|
||||||
let mut v = vec!();
|
let mut v = vec!();
|
||||||
while self.token != *ket {
|
while !kets.contains(&&self.token) {
|
||||||
match sep.sep {
|
match sep.sep {
|
||||||
Some(ref t) => {
|
Some(ref t) => {
|
||||||
if first { first = false; }
|
if first {
|
||||||
else { try!(self.expect(t)); }
|
first = false;
|
||||||
}
|
} else {
|
||||||
_ => ()
|
if let Err(e) = self.expect(t) {
|
||||||
|
fe(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
if sep.trailing_sep_allowed && kets.iter().any(|k| self.check(k)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
match f(self) {
|
||||||
|
Ok(t) => v.push(t),
|
||||||
|
Err(e) => {
|
||||||
|
fe(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if sep.trailing_sep_allowed && self.check(ket) { break; }
|
|
||||||
v.push(try!(f(self)));
|
|
||||||
}
|
}
|
||||||
return Ok(v);
|
|
||||||
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a sequence, including the closing delimiter. The function
|
/// Parse a sequence, including the closing delimiter. The function
|
||||||
@ -893,7 +937,7 @@ impl<'a> Parser<'a> {
|
|||||||
F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
||||||
{
|
{
|
||||||
try!(self.expect(bra));
|
try!(self.expect(bra));
|
||||||
let result = try!(self.parse_seq_to_before_end(ket, sep, f));
|
let result = self.parse_seq_to_before_end(ket, sep, f);
|
||||||
self.bump();
|
self.bump();
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
@ -929,7 +973,7 @@ impl<'a> Parser<'a> {
|
|||||||
{
|
{
|
||||||
let lo = self.span.lo;
|
let lo = self.span.lo;
|
||||||
try!(self.expect(bra));
|
try!(self.expect(bra));
|
||||||
let result = try!(self.parse_seq_to_before_end(ket, sep, f));
|
let result = self.parse_seq_to_before_end(ket, sep, f);
|
||||||
let hi = self.span.hi;
|
let hi = self.span.hi;
|
||||||
self.bump();
|
self.bump();
|
||||||
Ok(spanned(lo, hi, result))
|
Ok(spanned(lo, hi, result))
|
||||||
@ -1216,7 +1260,25 @@ impl<'a> Parser<'a> {
|
|||||||
};
|
};
|
||||||
(ident, TraitItemKind::Const(ty, default))
|
(ident, TraitItemKind::Const(ty, default))
|
||||||
} else {
|
} else {
|
||||||
let (constness, unsafety, abi) = try!(p.parse_fn_front_matter());
|
let (constness, unsafety, abi) = match p.parse_fn_front_matter() {
|
||||||
|
Ok(cua) => cua,
|
||||||
|
Err(e) => {
|
||||||
|
loop {
|
||||||
|
p.bump();
|
||||||
|
if p.token == token::Semi {
|
||||||
|
p.bump();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.token == token::OpenDelim(token::DelimToken::Brace) {
|
||||||
|
try!(p.parse_token_tree());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let ident = try!(p.parse_ident());
|
let ident = try!(p.parse_ident());
|
||||||
let mut generics = try!(p.parse_generics());
|
let mut generics = try!(p.parse_generics());
|
||||||
@ -2289,14 +2351,37 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
while self.token != token::CloseDelim(token::Brace) {
|
while self.token != token::CloseDelim(token::Brace) {
|
||||||
if self.eat(&token::DotDot) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
fields.push(try!(self.parse_field()));
|
match self.parse_field() {
|
||||||
try!(self.commit_expr(&fields.last().unwrap().expr,
|
Ok(f) => fields.push(f),
|
||||||
&[token::Comma],
|
Err(mut e) => {
|
||||||
&[token::CloseDelim(token::Brace)]));
|
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;
|
hi = self.span.hi;
|
||||||
@ -2641,45 +2726,16 @@ impl<'a> Parser<'a> {
|
|||||||
// yet.
|
// yet.
|
||||||
maybe_whole!(deref self, NtTT);
|
maybe_whole!(deref self, NtTT);
|
||||||
|
|
||||||
// this is the fall-through for the 'match' below.
|
|
||||||
// invariants: the current token is not a left-delimiter,
|
|
||||||
// not an EOF, and not the desired right-delimiter (if
|
|
||||||
// it were, parse_seq_to_before_end would have prevented
|
|
||||||
// reaching this point.
|
|
||||||
fn parse_non_delim_tt_tok<'b>(p: &mut Parser<'b>) -> PResult<'b, TokenTree> {
|
|
||||||
maybe_whole!(deref p, NtTT);
|
|
||||||
match p.token {
|
|
||||||
token::CloseDelim(_) => {
|
|
||||||
let token_str = p.this_token_to_string();
|
|
||||||
let mut err = p.fatal(
|
|
||||||
&format!("incorrect close delimiter: `{}`", token_str));
|
|
||||||
// This is a conservative error: only report the last unclosed delimiter. The
|
|
||||||
// previous unclosed delimiters could actually be closed! The parser just hasn't
|
|
||||||
// gotten to them yet.
|
|
||||||
if let Some(&sp) = p.open_braces.last() {
|
|
||||||
err.span_note(sp, "unclosed delimiter");
|
|
||||||
};
|
|
||||||
Err(err)
|
|
||||||
},
|
|
||||||
/* we ought to allow different depths of unquotation */
|
|
||||||
token::Dollar | token::SubstNt(..) if p.quote_depth > 0 => {
|
|
||||||
p.parse_unquoted()
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
Ok(TokenTree::Token(p.span, p.bump_and_get()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.token {
|
match self.token {
|
||||||
token::Eof => {
|
token::Eof => {
|
||||||
let open_braces = self.open_braces.clone();
|
|
||||||
let mut err: DiagnosticBuilder<'a> =
|
let mut err: DiagnosticBuilder<'a> =
|
||||||
self.fatal("this file contains an un-closed delimiter");
|
self.diagnostic().struct_span_err(self.span,
|
||||||
for sp in &open_braces {
|
"this file contains an un-closed delimiter");
|
||||||
|
for sp in &self.open_braces {
|
||||||
err.span_help(*sp, "did you mean to close this delimiter?");
|
err.span_help(*sp, "did you mean to close this delimiter?");
|
||||||
}
|
}
|
||||||
return Err(err);
|
|
||||||
|
Err(err)
|
||||||
},
|
},
|
||||||
token::OpenDelim(delim) => {
|
token::OpenDelim(delim) => {
|
||||||
// The span for beginning of the delimited section
|
// The span for beginning of the delimited section
|
||||||
@ -2691,11 +2747,9 @@ impl<'a> Parser<'a> {
|
|||||||
self.bump();
|
self.bump();
|
||||||
|
|
||||||
// Parse the token trees within the delimiters
|
// Parse the token trees within the delimiters
|
||||||
let tts = try!(self.parse_seq_to_before_end(
|
let tts = self.parse_seq_to_before_end(&token::CloseDelim(delim),
|
||||||
&token::CloseDelim(delim),
|
seq_sep_none(),
|
||||||
seq_sep_none(),
|
|p| p.parse_token_tree());
|
||||||
|p| p.parse_token_tree()
|
|
||||||
));
|
|
||||||
|
|
||||||
// Parse the close delimiter.
|
// Parse the close delimiter.
|
||||||
let close_span = self.span;
|
let close_span = self.span;
|
||||||
@ -2712,7 +2766,35 @@ impl<'a> Parser<'a> {
|
|||||||
close_span: close_span,
|
close_span: close_span,
|
||||||
})))
|
})))
|
||||||
},
|
},
|
||||||
_ => parse_non_delim_tt_tok(self),
|
_ => {
|
||||||
|
// invariants: the current token is not a left-delimiter,
|
||||||
|
// not an EOF, and not the desired right-delimiter (if
|
||||||
|
// it were, parse_seq_to_before_end would have prevented
|
||||||
|
// reaching this point.
|
||||||
|
maybe_whole!(deref self, NtTT);
|
||||||
|
match self.token {
|
||||||
|
token::CloseDelim(_) => {
|
||||||
|
let token_str = self.this_token_to_string();
|
||||||
|
let mut err = self.diagnostic().struct_span_err(self.span,
|
||||||
|
&format!("incorrect close delimiter: `{}`", token_str));
|
||||||
|
// This is a conservative error: only report the last unclosed delimiter.
|
||||||
|
// The previous unclosed delimiters could actually be closed! The parser
|
||||||
|
// just hasn't gotten to them yet.
|
||||||
|
if let Some(&sp) = self.open_braces.last() {
|
||||||
|
err.span_note(sp, "unclosed delimiter");
|
||||||
|
};
|
||||||
|
|
||||||
|
Err(err)
|
||||||
|
},
|
||||||
|
/* we ought to allow different depths of unquotation */
|
||||||
|
token::Dollar | token::SubstNt(..) if self.quote_depth > 0 => {
|
||||||
|
self.parse_unquoted()
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
Ok(TokenTree::Token(self.span, self.bump_and_get()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3149,8 +3231,8 @@ impl<'a> Parser<'a> {
|
|||||||
fn parse_match_expr(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
|
fn parse_match_expr(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
|
||||||
let match_span = self.last_span;
|
let match_span = self.last_span;
|
||||||
let lo = self.last_span.lo;
|
let lo = self.last_span.lo;
|
||||||
let discriminant = try!(self.parse_expr_res(
|
let discriminant = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL,
|
||||||
Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None));
|
None));
|
||||||
if let Err(mut e) = self.commit_expr_expecting(&discriminant,
|
if let Err(mut e) = self.commit_expr_expecting(&discriminant,
|
||||||
token::OpenDelim(token::Brace)) {
|
token::OpenDelim(token::Brace)) {
|
||||||
if self.token == token::Token::Semi {
|
if self.token == token::Token::Semi {
|
||||||
@ -3162,7 +3244,19 @@ impl<'a> Parser<'a> {
|
|||||||
try!(self.parse_inner_attributes()).into_thin_attrs());
|
try!(self.parse_inner_attributes()).into_thin_attrs());
|
||||||
let mut arms: Vec<Arm> = Vec::new();
|
let mut arms: Vec<Arm> = Vec::new();
|
||||||
while self.token != token::CloseDelim(token::Brace) {
|
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, ExprKind::Match(discriminant, arms), attrs));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let hi = self.span.hi;
|
let hi = self.span.hi;
|
||||||
self.bump();
|
self.bump();
|
||||||
@ -3520,7 +3614,11 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
// Parse struct pattern
|
// Parse struct pattern
|
||||||
self.bump();
|
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();
|
self.bump();
|
||||||
pat = PatKind::Struct(path, fields, etc);
|
pat = PatKind::Struct(path, fields, etc);
|
||||||
}
|
}
|
||||||
@ -3674,10 +3772,72 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
/// Parse a statement. may include decl.
|
/// Parse a statement. may include decl.
|
||||||
pub fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> {
|
pub fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> {
|
||||||
Ok(try!(self.parse_stmt_()))
|
Ok(self.parse_stmt_())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_stmt_(&mut self) -> PResult<'a, Option<Stmt>> {
|
// 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) -> 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);
|
maybe_whole!(Some deref self, NtStmt);
|
||||||
|
|
||||||
let attrs = try!(self.parse_outer_attributes());
|
let attrs = try!(self.parse_outer_attributes());
|
||||||
@ -3833,7 +3993,7 @@ impl<'a> Parser<'a> {
|
|||||||
let lo = self.span.lo;
|
let lo = self.span.lo;
|
||||||
try!(self.expect(&token::OpenDelim(token::Brace)));
|
try!(self.expect(&token::OpenDelim(token::Brace)));
|
||||||
Ok((try!(self.parse_inner_attributes()),
|
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
|
/// Parse the rest of a block expression or function body
|
||||||
@ -3843,7 +4003,7 @@ impl<'a> Parser<'a> {
|
|||||||
let mut expr = None;
|
let mut expr = None;
|
||||||
|
|
||||||
while !self.eat(&token::CloseDelim(token::Brace)) {
|
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
|
s
|
||||||
} else {
|
} else {
|
||||||
// Found only `;` or `}`.
|
// Found only `;` or `}`.
|
||||||
@ -3928,17 +4088,21 @@ impl<'a> Parser<'a> {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_expression_like_statement(
|
fn handle_expression_like_statement(&mut self,
|
||||||
&mut self,
|
e: P<Expr>,
|
||||||
e: P<Expr>,
|
span: Span,
|
||||||
span: Span,
|
stmts: &mut Vec<Stmt>,
|
||||||
stmts: &mut Vec<Stmt>,
|
last_block_expr: &mut Option<P<Expr>>)
|
||||||
last_block_expr: &mut Option<P<Expr>>) -> PResult<'a, ()> {
|
-> PResult<'a, ()> {
|
||||||
// expression without semicolon
|
// expression without semicolon
|
||||||
if classify::expr_requires_semi_to_be_stmt(&e) {
|
if classify::expr_requires_semi_to_be_stmt(&e) {
|
||||||
// Just check for errors and recover; do not eat semicolon yet.
|
// Just check for errors and recover; do not eat semicolon yet.
|
||||||
try!(self.commit_stmt(&[],
|
if let Err(mut e) =
|
||||||
&[token::Semi, token::CloseDelim(token::Brace)]));
|
self.commit_stmt(&[], &[token::Semi, token::CloseDelim(token::Brace)])
|
||||||
|
{
|
||||||
|
e.emit();
|
||||||
|
self.recover_stmt();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.token {
|
match self.token {
|
||||||
@ -4171,8 +4335,8 @@ impl<'a> Parser<'a> {
|
|||||||
fn forbid_lifetime(&mut self) -> PResult<'a, ()> {
|
fn forbid_lifetime(&mut self) -> PResult<'a, ()> {
|
||||||
if self.token.is_lifetime() {
|
if self.token.is_lifetime() {
|
||||||
let span = self.span;
|
let span = self.span;
|
||||||
return Err(self.span_fatal(span, "lifetime parameters must be declared \
|
return Err(self.diagnostic().struct_span_err(span, "lifetime parameters must be \
|
||||||
prior to type parameters"))
|
declared prior to type parameters"))
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -4300,7 +4464,8 @@ impl<'a> Parser<'a> {
|
|||||||
fn parse_fn_args(&mut self, named_args: bool, allow_variadic: bool)
|
fn parse_fn_args(&mut self, named_args: bool, allow_variadic: bool)
|
||||||
-> PResult<'a, (Vec<Arg> , bool)> {
|
-> PResult<'a, (Vec<Arg> , bool)> {
|
||||||
let sp = self.span;
|
let sp = self.span;
|
||||||
let mut args: Vec<Option<Arg>> =
|
let mut variadic = false;
|
||||||
|
let args: Vec<Option<Arg>> =
|
||||||
try!(self.parse_unspanned_seq(
|
try!(self.parse_unspanned_seq(
|
||||||
&token::OpenDelim(token::Paren),
|
&token::OpenDelim(token::Paren),
|
||||||
&token::CloseDelim(token::Paren),
|
&token::CloseDelim(token::Paren),
|
||||||
@ -4311,38 +4476,36 @@ impl<'a> Parser<'a> {
|
|||||||
if allow_variadic {
|
if allow_variadic {
|
||||||
if p.token != token::CloseDelim(token::Paren) {
|
if p.token != token::CloseDelim(token::Paren) {
|
||||||
let span = p.span;
|
let span = p.span;
|
||||||
return Err(p.span_fatal(span,
|
p.span_err(span,
|
||||||
"`...` must be last in argument list for variadic function"))
|
"`...` must be last in argument list for variadic function");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let span = p.span;
|
let span = p.span;
|
||||||
return Err(p.span_fatal(span,
|
p.span_err(span,
|
||||||
"only foreign functions are allowed to be variadic"))
|
"only foreign functions are allowed to be variadic");
|
||||||
}
|
}
|
||||||
|
variadic = true;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
Ok(Some(try!(p.parse_arg_general(named_args))))
|
match p.parse_arg_general(named_args) {
|
||||||
|
Ok(arg) => Ok(Some(arg)),
|
||||||
|
Err(mut e) => {
|
||||||
|
e.emit();
|
||||||
|
p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]);
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
let variadic = match args.pop() {
|
let args: Vec<_> = args.into_iter().filter_map(|x| x).collect();
|
||||||
Some(None) => true,
|
|
||||||
Some(x) => {
|
|
||||||
// Need to put back that last arg
|
|
||||||
args.push(x);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
None => false
|
|
||||||
};
|
|
||||||
|
|
||||||
if variadic && args.is_empty() {
|
if variadic && args.is_empty() {
|
||||||
self.span_err(sp,
|
self.span_err(sp,
|
||||||
"variadic function must be declared with at least one named argument");
|
"variadic function must be declared with at least one named argument");
|
||||||
}
|
}
|
||||||
|
|
||||||
let args = args.into_iter().map(|x| x.unwrap()).collect();
|
|
||||||
|
|
||||||
Ok((args, variadic))
|
Ok((args, variadic))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4519,11 +4682,11 @@ impl<'a> Parser<'a> {
|
|||||||
token::Comma => {
|
token::Comma => {
|
||||||
self.bump();
|
self.bump();
|
||||||
let sep = seq_sep_trailing_allowed(token::Comma);
|
let sep = seq_sep_trailing_allowed(token::Comma);
|
||||||
let mut fn_inputs = try!(self.parse_seq_to_before_end(
|
let mut fn_inputs = self.parse_seq_to_before_end(
|
||||||
&token::CloseDelim(token::Paren),
|
&token::CloseDelim(token::Paren),
|
||||||
sep,
|
sep,
|
||||||
parse_arg_fn
|
parse_arg_fn
|
||||||
));
|
);
|
||||||
fn_inputs.insert(0, Arg::new_self(explicit_self_sp, mutbl_self, $self_id));
|
fn_inputs.insert(0, Arg::new_self(explicit_self_sp, mutbl_self, $self_id));
|
||||||
fn_inputs
|
fn_inputs
|
||||||
}
|
}
|
||||||
@ -4542,8 +4705,7 @@ impl<'a> Parser<'a> {
|
|||||||
let fn_inputs = match explicit_self {
|
let fn_inputs = match explicit_self {
|
||||||
SelfKind::Static => {
|
SelfKind::Static => {
|
||||||
let sep = seq_sep_trailing_allowed(token::Comma);
|
let sep = seq_sep_trailing_allowed(token::Comma);
|
||||||
try!(self.parse_seq_to_before_end(&token::CloseDelim(token::Paren),
|
self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), sep, parse_arg_fn)
|
||||||
sep, parse_arg_fn))
|
|
||||||
}
|
}
|
||||||
SelfKind::Value(id) => parse_remaining_arguments!(id),
|
SelfKind::Value(id) => parse_remaining_arguments!(id),
|
||||||
SelfKind::Region(_,_,id) => parse_remaining_arguments!(id),
|
SelfKind::Region(_,_,id) => parse_remaining_arguments!(id),
|
||||||
@ -4574,11 +4736,11 @@ impl<'a> Parser<'a> {
|
|||||||
} else {
|
} else {
|
||||||
try!(self.expect(&token::BinOp(token::Or)));
|
try!(self.expect(&token::BinOp(token::Or)));
|
||||||
try!(self.parse_obsolete_closure_kind());
|
try!(self.parse_obsolete_closure_kind());
|
||||||
let args = try!(self.parse_seq_to_before_end(
|
let args = self.parse_seq_to_before_end(
|
||||||
&token::BinOp(token::Or),
|
&token::BinOp(token::Or),
|
||||||
seq_sep_trailing_allowed(token::Comma),
|
seq_sep_trailing_allowed(token::Comma),
|
||||||
|p| p.parse_fn_block_arg()
|
|p| p.parse_fn_block_arg()
|
||||||
));
|
);
|
||||||
self.bump();
|
self.bump();
|
||||||
args
|
args
|
||||||
}
|
}
|
||||||
@ -4740,8 +4902,8 @@ impl<'a> Parser<'a> {
|
|||||||
// eat a matched-delimiter token tree:
|
// eat a matched-delimiter token tree:
|
||||||
let delim = try!(self.expect_open_delim());
|
let delim = try!(self.expect_open_delim());
|
||||||
let tts = try!(self.parse_seq_to_end(&token::CloseDelim(delim),
|
let tts = try!(self.parse_seq_to_end(&token::CloseDelim(delim),
|
||||||
seq_sep_none(),
|
seq_sep_none(),
|
||||||
|p| p.parse_token_tree()));
|
|p| p.parse_token_tree()));
|
||||||
let m_ = Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT };
|
let m_ = Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT };
|
||||||
let m: ast::Mac = codemap::Spanned { node: m_,
|
let m: ast::Mac = codemap::Spanned { node: m_,
|
||||||
span: mk_sp(lo,
|
span: mk_sp(lo,
|
||||||
@ -5800,8 +5962,8 @@ impl<'a> Parser<'a> {
|
|||||||
// eat a matched-delimiter token tree:
|
// eat a matched-delimiter token tree:
|
||||||
let delim = try!(self.expect_open_delim());
|
let delim = try!(self.expect_open_delim());
|
||||||
let tts = try!(self.parse_seq_to_end(&token::CloseDelim(delim),
|
let tts = try!(self.parse_seq_to_end(&token::CloseDelim(delim),
|
||||||
seq_sep_none(),
|
seq_sep_none(),
|
||||||
|p| p.parse_token_tree()));
|
|p| p.parse_token_tree()));
|
||||||
// single-variant-enum... :
|
// single-variant-enum... :
|
||||||
let m = Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT };
|
let m = Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT };
|
||||||
let m: ast::Mac = codemap::Spanned { node: m,
|
let m: ast::Mac = codemap::Spanned { node: m,
|
||||||
|
@ -25,7 +25,7 @@ macro_rules! parallel {
|
|||||||
fn main() {
|
fn main() {
|
||||||
parallel! {
|
parallel! {
|
||||||
for i in 0..n {
|
for i in 0..n {
|
||||||
x += i; //~ ERROR no rules expected the token `+=`
|
x += i; //~ ERROR expected `:`, found `+=`
|
||||||
}
|
} //~ ERROR unexpected end of macro invocation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ macro_rules! ignored_item {
|
|||||||
|
|
||||||
macro_rules! ignored_expr {
|
macro_rules! ignored_expr {
|
||||||
() => ( 1, //~ ERROR unexpected token: `,`
|
() => ( 1, //~ ERROR unexpected token: `,`
|
||||||
2 ) //~ ERROR macro expansion ignores token `2`
|
2 )
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! ignored_pat {
|
macro_rules! ignored_pat {
|
||||||
@ -28,7 +28,7 @@ macro_rules! ignored_pat {
|
|||||||
ignored_item!(); //~ NOTE caused by the macro expansion here
|
ignored_item!(); //~ NOTE caused by the macro expansion here
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
ignored_expr!(); //~ NOTE caused by the macro expansion here
|
ignored_expr!();
|
||||||
match 1 {
|
match 1 {
|
||||||
ignored_pat!() => (), //~ NOTE caused by the macro expansion here
|
ignored_pat!() => (), //~ NOTE caused by the macro expansion here
|
||||||
_ => (),
|
_ => (),
|
||||||
|
22
src/test/compile-fail/parser-recovery-1.rs
Normal file
22
src/test/compile-fail/parser-recovery-1.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
// Test that we can recover from missing braces in the parser.
|
||||||
|
|
||||||
|
trait Foo {
|
||||||
|
fn bar() {
|
||||||
|
let x = foo(); //~ ERROR unresolved name `foo`
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = y.; //~ ERROR unexpected token
|
||||||
|
//~^ ERROR unresolved name `y`
|
||||||
|
} //~ ERROR this file contains an un-closed delimiter
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||||
// file at the top-level directory of this distribution and at
|
// file at the top-level directory of this distribution and at
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
//
|
//
|
||||||
@ -8,11 +8,15 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
// compile-flags: -Z parse-only
|
// Test that we can recover from mismatched braces in the parser.
|
||||||
|
|
||||||
extern {
|
trait Foo {
|
||||||
fn printf(...); //~ ERROR: variadic function must be declared with at least one named argument
|
fn bar() {
|
||||||
fn printf(..., foo: isize); //~ ERROR: `...` must be last in argument list for variadic function
|
let x = foo(); //~ ERROR unresolved name `foo`
|
||||||
|
) //~ ERROR incorrect close delimiter: `)`
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {
|
||||||
|
let x = y.; //~ ERROR unexpected token
|
||||||
|
//~^ ERROR unresolved name `y`
|
||||||
|
}
|
@ -8,6 +8,8 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
// compile-flags: -Z parse-only
|
||||||
|
|
||||||
fn foo() {
|
fn foo() {
|
||||||
match x {
|
match x {
|
||||||
<T as Trait>::Type{key: value} => (),
|
<T as Trait>::Type{key: value} => (),
|
||||||
|
@ -8,8 +8,14 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// 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
|
// 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
|
option.map(|some| 42; //~ NOTE: unclosed delimiter
|
||||||
|
//~^ ERROR: expected one of
|
||||||
} //~ ERROR: incorrect close delimiter
|
} //~ ERROR: incorrect close delimiter
|
||||||
|
//~^ ERROR: expected one of
|
||||||
|
//~ ERROR: this file contains an un-closed delimiter
|
||||||
|
@ -12,3 +12,4 @@
|
|||||||
|
|
||||||
fn bar<'a, T>(x: mymodule::X<'a, T, 'b, 'c>) {}
|
fn bar<'a, T>(x: mymodule::X<'a, T, 'b, 'c>) {}
|
||||||
//~^ ERROR lifetime parameters must be declared prior to type parameters
|
//~^ ERROR lifetime parameters must be declared prior to type parameters
|
||||||
|
//~^^ ERROR unexpected token
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
|
|
||||||
fn foo() { //~ HELP did you mean to close this delimiter?
|
fn foo() { //~ HELP did you mean to close this delimiter?
|
||||||
match Some(x) {
|
match Some(x) {
|
||||||
Some(y) { panic!(); }
|
Some(y) => { panic!(); }
|
||||||
None { panic!(); }
|
None => { panic!(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bar() {
|
fn bar() {
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
// compile-flags: -Z parse-only
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let foo =
|
let foo =
|
||||||
match //~ NOTE did you mean to remove this `match` keyword?
|
match //~ NOTE did you mean to remove this `match` keyword?
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
// compile-flags: -Z parse-only
|
||||||
|
|
||||||
fn foo() {
|
fn foo() {
|
||||||
match x {
|
match x {
|
||||||
<T as Trait>::Type(2) => (),
|
<T as Trait>::Type(2) => (),
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
// compile-flags: -Z parse-only
|
||||||
|
|
||||||
enum BtNode {
|
enum BtNode {
|
||||||
Node(u32,Box<BtNode>,Box<BtNode>),
|
Node(u32,Box<BtNode>,Box<BtNode>),
|
||||||
Leaf(u32),
|
Leaf(u32),
|
||||||
|
@ -10,4 +10,5 @@
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let Test(&desc[..]) = x; //~ error: expected one of `,` or `@`, found `[`
|
let Test(&desc[..]) = x; //~ error: expected one of `,` or `@`, found `[`
|
||||||
|
//~^ ERROR expected one of `:`, `;`, or `=`, found `..`
|
||||||
}
|
}
|
||||||
|
@ -9,5 +9,6 @@
|
|||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
for thing(x[]) {} //~ error: expected one of `,` or `@`, found `[`
|
for thing(x[]) in foo {} //~ error: expected one of `,` or `@`, found `[`
|
||||||
|
//~^ ERROR: expected `in`, found `]`
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ impl Foo {
|
|||||||
fn main() {
|
fn main() {
|
||||||
for x in Foo {
|
for x in Foo {
|
||||||
x: 3 //~ ERROR expected type, found `3`
|
x: 3 //~ ERROR expected type, found `3`
|
||||||
}.hi() {
|
}.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{`
|
||||||
println!("yo");
|
println!("yo");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ impl Foo {
|
|||||||
fn main() {
|
fn main() {
|
||||||
if Foo {
|
if Foo {
|
||||||
x: 3 //~ ERROR expected type, found `3`
|
x: 3 //~ ERROR expected type, found `3`
|
||||||
}.hi() {
|
}.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{`
|
||||||
println!("yo");
|
println!("yo");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,6 @@ fn main() {
|
|||||||
} {
|
} {
|
||||||
Foo {
|
Foo {
|
||||||
x: x
|
x: x
|
||||||
} => {}
|
} => {} //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `=>`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ impl Foo {
|
|||||||
fn main() {
|
fn main() {
|
||||||
while Foo {
|
while Foo {
|
||||||
x: 3 //~ ERROR expected type, found `3`
|
x: 3 //~ ERROR expected type, found `3`
|
||||||
}.hi() {
|
}.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{`
|
||||||
println!("yo");
|
println!("yo");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user