Skip to content

Commit a111297

Browse files
committed
Auto merge of #32479 - eddyb:eof-not-even-twice, r=nikomatsakis
Prevent bumping the parser past the EOF. Makes `Parser::bump` after EOF into an ICE, forcing callers to avoid repeated EOF bumps. This ICE is intended to break infinite loops where EOF wasn't stopping the loop. For example, the handling of EOF in `parse_trait_items`' recovery loop fixes #32446. But even without this specific fix, the ICE is triggered, which helps diagnosis and UX. This is a `[breaking-change]` for plugins authors who eagerly eat multiple EOFs. See docopt/docopt.rs#171 for such an example and the necessary fix.
2 parents cad964a + 221d0fb commit a111297

File tree

6 files changed

+49
-14
lines changed

6 files changed

+49
-14
lines changed

src/libsyntax/parse/parser.rs

+28-9
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ pub struct Parser<'a> {
254254
/// the previous token or None (only stashed sometimes).
255255
pub last_token: Option<Box<token::Token>>,
256256
last_token_interpolated: bool,
257+
last_token_eof: bool,
257258
pub buffer: [TokenAndSpan; 4],
258259
pub buffer_start: isize,
259260
pub buffer_end: isize,
@@ -366,6 +367,7 @@ impl<'a> Parser<'a> {
366367
last_span: span,
367368
last_token: None,
368369
last_token_interpolated: false,
370+
last_token_eof: false,
369371
buffer: [
370372
placeholder.clone(),
371373
placeholder.clone(),
@@ -955,7 +957,9 @@ impl<'a> Parser<'a> {
955957
{
956958
self.expect(bra)?;
957959
let result = self.parse_seq_to_before_end(ket, sep, f);
958-
self.bump();
960+
if self.token == *ket {
961+
self.bump();
962+
}
959963
Ok(result)
960964
}
961965

@@ -998,6 +1002,15 @@ impl<'a> Parser<'a> {
9981002

9991003
/// Advance the parser by one token
10001004
pub fn bump(&mut self) {
1005+
if self.last_token_eof {
1006+
// Bumping after EOF is a bad sign, usually an infinite loop.
1007+
self.bug("attempted to bump the parser past EOF (may be stuck in a loop)");
1008+
}
1009+
1010+
if self.token == token::Eof {
1011+
self.last_token_eof = true;
1012+
}
1013+
10011014
self.last_span = self.span;
10021015
// Stash token for error recovery (sometimes; clone is not necessarily cheap).
10031016
self.last_token = if self.token.is_ident() ||
@@ -1281,15 +1294,21 @@ impl<'a> Parser<'a> {
12811294
Ok(cua) => cua,
12821295
Err(e) => {
12831296
loop {
1284-
p.bump();
1285-
if p.token == token::Semi {
1286-
p.bump();
1287-
break;
1288-
}
1297+
match p.token {
1298+
token::Eof => break,
1299+
1300+
token::CloseDelim(token::Brace) |
1301+
token::Semi => {
1302+
p.bump();
1303+
break;
1304+
}
1305+
1306+
token::OpenDelim(token::Brace) => {
1307+
p.parse_token_tree()?;
1308+
break;
1309+
}
12891310

1290-
if p.token == token::OpenDelim(token::DelimToken::Brace) {
1291-
p.parse_token_tree()?;
1292-
break;
1311+
_ => p.bump()
12931312
}
12941313
}
12951314

src/test/compile-fail/issue-10636-2.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,5 @@
1414
pub fn trace_option(option: Option<isize>) {
1515
option.map(|some| 42; //~ NOTE: unclosed delimiter
1616
//~^ ERROR: expected one of
17-
//~^^ ERROR: mismatched types
1817
} //~ ERROR: incorrect close delimiter
19-
//~^ ERROR: expected one of
18+
//~^ ERROR: unexpected token

src/test/compile-fail/token-error-correct-3.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ pub mod raw {
2121
if !is_directory(path.as_ref()) { //~ ERROR: unresolved name `is_directory`
2222
callback(path.as_ref(); //~ NOTE: unclosed delimiter
2323
//~^ ERROR: expected one of
24-
fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: expected one of
24+
fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: mismatched types
2525
} else { //~ ERROR: incorrect close delimiter: `}`
26+
//~^ ERROR: expected one of
2627
Ok(false);
2728
}
2829

src/test/parse-fail/issue-32446.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -Z parse-only
12+
13+
fn main() {}
14+
15+
// This used to end up in an infite loop trying to bump past EOF.
16+
trait T { ... } //~ ERROR

src/test/parse-fail/pat-lt-bracket-6.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010

1111
fn main() {
1212
let Test(&desc[..]) = x; //~ error: expected one of `,` or `@`, found `[`
13-
//~^ ERROR expected one of `:`, `;`, or `=`, found `..`
13+
//~^ ERROR expected one of `:`, `;`, `=`, or `@`, found `[`
1414
}

src/test/parse-fail/pat-lt-bracket-7.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010

1111
fn main() {
1212
for thing(x[]) in foo {} //~ error: expected one of `,` or `@`, found `[`
13-
//~^ ERROR: expected `in`, found `]`
13+
//~^ ERROR expected one of `@` or `in`, found `[`
1414
}

0 commit comments

Comments
 (0)