Skip to content

Commit

Permalink
Recover from item trailing semicolon
Browse files Browse the repository at this point in the history
  • Loading branch information
estebank committed Jan 14, 2019
1 parent c2d381d commit 3874c77
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 30 deletions.
59 changes: 35 additions & 24 deletions src/libsyntax/parse/parser.rs
Expand Up @@ -6408,41 +6408,52 @@ impl<'a> Parser<'a> {
}
}

fn maybe_consume_incorrect_semicolon(&mut self, items: &[P<Item>]) -> bool {
if self.eat(&token::Semi) {
let mut err = self.struct_span_err(self.prev_span, "expected item, found `;`");
err.span_suggestion_short_with_applicability(
self.prev_span,
"remove this semicolon",
String::new(),
Applicability::MachineApplicable,
);
if !items.is_empty() {
let previous_item = &items[items.len()-1];
let previous_item_kind_name = match previous_item.node {
// say "braced struct" because tuple-structs and
// braceless-empty-struct declarations do take a semicolon
ItemKind::Struct(..) => Some("braced struct"),
ItemKind::Enum(..) => Some("enum"),
ItemKind::Trait(..) => Some("trait"),
ItemKind::Union(..) => Some("union"),
_ => None,
};
if let Some(name) = previous_item_kind_name {
err.help(&format!("{} declarations are not followed by a semicolon", name));
}
}
err.emit();
true
} else {
false
}
}

/// Given a termination token, parse all of the items in a module
fn parse_mod_items(&mut self, term: &token::Token, inner_lo: Span) -> PResult<'a, Mod> {
let mut items = vec![];
while let Some(item) = self.parse_item()? {
items.push(item);
self.maybe_consume_incorrect_semicolon(&items);
}

if !self.eat(term) {
let token_str = self.this_token_descr();
let mut err = self.fatal(&format!("expected item, found {}", token_str));
if self.token == token::Semi {
let msg = "consider removing this semicolon";
err.span_suggestion_short_with_applicability(
self.span, msg, String::new(), Applicability::MachineApplicable
);
if !items.is_empty() { // Issue #51603
let previous_item = &items[items.len()-1];
let previous_item_kind_name = match previous_item.node {
// say "braced struct" because tuple-structs and
// braceless-empty-struct declarations do take a semicolon
ItemKind::Struct(..) => Some("braced struct"),
ItemKind::Enum(..) => Some("enum"),
ItemKind::Trait(..) => Some("trait"),
ItemKind::Union(..) => Some("union"),
_ => None,
};
if let Some(name) = previous_item_kind_name {
err.help(&format!("{} declarations are not followed by a semicolon",
name));
}
}
} else {
if !self.maybe_consume_incorrect_semicolon(&items) {
let mut err = self.fatal(&format!("expected item, found {}", token_str));
err.span_label(self.span, "expected item");
return Err(err);
}
return Err(err);
}

let hi = if self.span.is_dummy() {
Expand Down
3 changes: 2 additions & 1 deletion src/test/ui/issues/issue-46186.rs
@@ -1,5 +1,6 @@
struct Struct {
a: usize,
}; //~ ERROR expected item, found `;`
};
//~^ ERROR expected item, found `;`

fn main() {}
4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-46186.stderr
@@ -1,8 +1,8 @@
error: expected item, found `;`
--> $DIR/issue-46186.rs:3:2
|
LL | }; //~ ERROR expected item, found `;`
| ^ help: consider removing this semicolon
LL | };
| ^ help: remove this semicolon
|
= help: braced struct declarations are not followed by a semicolon

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-49040.rs
@@ -1,2 +1,2 @@
#![allow(unused_variables)]; //~ ERROR expected item, found `;`
fn main() {}
fn foo() {}
9 changes: 7 additions & 2 deletions src/test/ui/issues/issue-49040.stderr
Expand Up @@ -2,7 +2,12 @@ error: expected item, found `;`
--> $DIR/issue-49040.rs:1:28
|
LL | #![allow(unused_variables)]; //~ ERROR expected item, found `;`
| ^ help: consider removing this semicolon
| ^ help: remove this semicolon

error: aborting due to previous error
error[E0601]: `main` function not found in crate `issue_49040`
|
= note: consider adding a `main` function to `$DIR/issue-49040.rs`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0601`.
16 changes: 16 additions & 0 deletions src/test/ui/suggestions/recover-from-semicolon-trailing-item.rs
@@ -0,0 +1,16 @@
// verify that after encountering a semicolon after an item the parser recovers
mod M {};
//~^ ERROR expected item, found `;`
struct S {};
//~^ ERROR expected item, found `;`
fn foo(a: usize) {};
//~^ ERROR expected item, found `;`
fn main() {
struct X {}; // ok
let _: usize = S {};
//~^ ERROR mismatched types
let _: usize = X {};
//~^ ERROR mismatched types
foo("");
//~^ ERROR mismatched types
}
@@ -0,0 +1,50 @@
error: expected item, found `;`
--> $DIR/recover-from-semicolon-trailing-item.rs:2:9
|
LL | mod M {};
| ^ help: remove this semicolon

error: expected item, found `;`
--> $DIR/recover-from-semicolon-trailing-item.rs:4:12
|
LL | struct S {};
| ^ help: remove this semicolon
|
= help: braced struct declarations are not followed by a semicolon

error: expected item, found `;`
--> $DIR/recover-from-semicolon-trailing-item.rs:6:20
|
LL | fn foo(a: usize) {};
| ^ help: remove this semicolon

error[E0308]: mismatched types
--> $DIR/recover-from-semicolon-trailing-item.rs:10:20
|
LL | let _: usize = S {};
| ^^^^ expected usize, found struct `S`
|
= note: expected type `usize`
found type `S`

error[E0308]: mismatched types
--> $DIR/recover-from-semicolon-trailing-item.rs:12:20
|
LL | let _: usize = X {};
| ^^^^ expected usize, found struct `main::X`
|
= note: expected type `usize`
found type `main::X`

error[E0308]: mismatched types
--> $DIR/recover-from-semicolon-trailing-item.rs:14:9
|
LL | foo("");
| ^^ expected usize, found reference
|
= note: expected type `usize`
found type `&'static str`

error: aborting due to 6 previous errors

For more information about this error, try `rustc --explain E0308`.

0 comments on commit 3874c77

Please sign in to comment.