diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 37360a56395..d2875a5f275 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -796,6 +796,10 @@ fn tokens_to_string(tokens: &[TokenType]) -> String { .chain(inedible.iter().map(|x| TokenType::Token(x.clone()))) .chain(self.expected_tokens.iter().cloned()) .collect::>(); + let expects_semi = expected.iter().any(|t| match t { + TokenType::Token(token::Semi) => true, + _ => false, + }); expected.sort_by_cached_key(|x| x.to_string()); expected.dedup(); let expect = tokens_to_string(&expected[..]); @@ -835,6 +839,17 @@ fn tokens_to_string(tokens: &[TokenType]) -> String { Applicability::MaybeIncorrect, ); } + let is_semi_suggestable = expects_semi && ( + self.token.is_keyword(keywords::Break) || + self.token.is_keyword(keywords::Continue) || + self.token.is_keyword(keywords::For) || + self.token.is_keyword(keywords::If) || + self.token.is_keyword(keywords::Let) || + self.token.is_keyword(keywords::Loop) || + self.token.is_keyword(keywords::Match) || + self.token.is_keyword(keywords::Return) || + self.token.is_keyword(keywords::While) + ); let sp = if self.token == token::Token::Eof { // This is EOF, don't want to point at the following char, but rather the last token self.prev_span @@ -853,6 +868,18 @@ fn tokens_to_string(tokens: &[TokenType]) -> String { let cm = self.sess.source_map(); match (cm.lookup_line(self.span.lo()), cm.lookup_line(sp.lo())) { + (Ok(ref a), Ok(ref b)) if a.line != b.line && is_semi_suggestable => { + // The spans are in different lines, expected `;` and found `let` or `return`. + // High likelihood that it is only a missing `;`. + err.span_suggestion_short( + label_sp, + "missing semicolon here", + ";".to_string(), + Applicability::MaybeIncorrect, + ); + err.emit(); + return Ok(true); + } (Ok(ref a), Ok(ref b)) if a.line == b.line => { // When the spans are in the same line, it means that the only content between // them is whitespace, point at the found token in that case: diff --git a/src/test/ui/parser/recover-missing-semi.rs b/src/test/ui/parser/recover-missing-semi.rs new file mode 100644 index 00000000000..1893dc716be --- /dev/null +++ b/src/test/ui/parser/recover-missing-semi.rs @@ -0,0 +1,13 @@ +fn main() { + let _: usize = () + //~^ ERROR mismatched types + let _ = 3; + //~^ ERROR expected one of +} + +fn foo() -> usize { + let _: usize = () + //~^ ERROR mismatched types + return 3; + //~^ ERROR expected one of +} diff --git a/src/test/ui/parser/recover-missing-semi.stderr b/src/test/ui/parser/recover-missing-semi.stderr new file mode 100644 index 00000000000..25ce408d8b2 --- /dev/null +++ b/src/test/ui/parser/recover-missing-semi.stderr @@ -0,0 +1,39 @@ +error: expected one of `.`, `;`, `?`, or an operator, found `let` + --> $DIR/recover-missing-semi.rs:4:5 + | +LL | let _: usize = () + | - help: missing semicolon here +LL | +LL | let _ = 3; + | ^^^ + +error: expected one of `.`, `;`, `?`, or an operator, found `return` + --> $DIR/recover-missing-semi.rs:11:5 + | +LL | let _: usize = () + | - help: missing semicolon here +LL | +LL | return 3; + | ^^^^^^ + +error[E0308]: mismatched types + --> $DIR/recover-missing-semi.rs:2:20 + | +LL | let _: usize = () + | ^^ expected usize, found () + | + = note: expected type `usize` + found type `()` + +error[E0308]: mismatched types + --> $DIR/recover-missing-semi.rs:9:20 + | +LL | let _: usize = () + | ^^ expected usize, found () + | + = note: expected type `usize` + found type `()` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`.