Skip to content

Commit

Permalink
Auto merge of #76160 - scileo:format-recovery, r=petrochenkov
Browse files Browse the repository at this point in the history
Improve recovery on malformed format call

The token following a format expression should be a comma. However, when it is replaced with a similar token (such as a dot), then the corresponding error is emitted, but the token is treated as a comma, and the parsing step continues.

r? @petrochenkov
  • Loading branch information
bors committed Sep 2, 2020
2 parents a167485 + 3524c3e commit 80fc9b0
Show file tree
Hide file tree
Showing 13 changed files with 88 additions and 53 deletions.
28 changes: 20 additions & 8 deletions compiler/rustc_builtin_macros/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,26 @@ fn parse_args<'a>(
while p.token != token::Eof {
if !p.eat(&token::Comma) {
if first {
// After `format!(""` we always expect *only* a comma...
let mut err = ecx.struct_span_err(p.token.span, "expected token: `,`");
err.span_label(p.token.span, "expected `,`");
p.maybe_annotate_with_ascription(&mut err, false);
return Err(err);
} else {
// ...after that delegate to `expect` to also include the other expected tokens.
let _ = p.expect(&token::Comma)?;
p.clear_expected_tokens();
}

// `Parser::expect` tries to recover using the
// `Parser::unexpected_try_recover` function. This function is able
// to recover if the expected token is a closing delimiter.
//
// As `,` is not a closing delimiter, it will always return an `Err`
// variant.
let mut err = p.expect(&token::Comma).unwrap_err();

match token::TokenKind::Comma.similar_tokens() {
Some(tks) if tks.contains(&p.token.kind) => {
// If a similar token is found, then it may be a typo. We
// consider it as a comma, and continue parsing.
err.emit();
p.bump();
}
// Otherwise stop the parsing and return the error.
_ => return Err(err),
}
}
first = false;
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_parse/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,10 @@ impl<'a> Parser<'a> {
*t == token::OpenDelim(token::Brace) || *t == token::BinOp(token::Star)
})
}

pub fn clear_expected_tokens(&mut self) {
self.expected_tokens.clear();
}
}

crate fn make_unclosed_delims_error(
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/codemap_tests/bad-format-args.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
fn main() {
format!(); //~ ERROR requires at least a format string argument
format!("" 1); //~ ERROR expected token: `,`
format!("" 1); //~ ERROR expected `,`, found `1`
format!("", 1 1); //~ ERROR expected one of
}
2 changes: 1 addition & 1 deletion src/test/ui/codemap_tests/bad-format-args.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LL | format!();
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: expected token: `,`
error: expected `,`, found `1`
--> $DIR/bad-format-args.rs:3:16
|
LL | format!("" 1);
Expand Down
32 changes: 0 additions & 32 deletions src/test/ui/fmt/incorrect-first-separator.stderr

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,25 @@ use std::iter;

fn main() {
format!("A number: {}". iter::once(42).next().unwrap());
//~^ ERROR expected token: `,`
//~^ ERROR expected `,`, found `.`

// Other kind of types are also checked:

format!("A number: {}" / iter::once(42).next().unwrap());
//~^ ERROR expected token: `,`
//~^ ERROR expected `,`, found `/`

format!("A number: {}"; iter::once(42).next().unwrap());
//~^ ERROR expected token: `,`
//~^ ERROR expected `,`, found `;`

// Note: this character is an COMBINING COMMA BELOW unicode char
format!("A number: {}" ̦ iter::once(42).next().unwrap());
//~^ ERROR expected token: `,`
//~^ ERROR expected `,`, found `iter`
//~^^ ERROR unknown start of token: \u{326}

// Here recovery is tested.
// If the `compile_error!` is emitted, then the parser is able to recover
// from the incorrect first separator.
format!("{}". compile_error!("fail"));
//~^ ERROR expected `,`, found `.`
//~^^ ERROR fail
}
44 changes: 44 additions & 0 deletions src/test/ui/fmt/incorrect-separator.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
error: unknown start of token: \u{326}
--> $DIR/incorrect-separator.rs:19:28
|
LL | format!("A number: {}" ̦ iter::once(42).next().unwrap());
| ^

error: expected `,`, found `.`
--> $DIR/incorrect-separator.rs:7:27
|
LL | format!("A number: {}". iter::once(42).next().unwrap());
| ^ expected `,`

error: expected `,`, found `/`
--> $DIR/incorrect-separator.rs:12:28
|
LL | format!("A number: {}" / iter::once(42).next().unwrap());
| ^ expected `,`

error: expected `,`, found `;`
--> $DIR/incorrect-separator.rs:15:27
|
LL | format!("A number: {}"; iter::once(42).next().unwrap());
| ^ expected `,`

error: expected `,`, found `iter`
--> $DIR/incorrect-separator.rs:19:30
|
LL | format!("A number: {}" ̦ iter::once(42).next().unwrap());
| ^^^^ expected `,`

error: expected `,`, found `.`
--> $DIR/incorrect-separator.rs:26:17
|
LL | format!("{}". compile_error!("fail"));
| ^ expected `,`

error: fail
--> $DIR/incorrect-separator.rs:26:19
|
LL | format!("{}". compile_error!("fail"));
| ^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 7 previous errors

2 changes: 1 addition & 1 deletion src/test/ui/macros/missing-comma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ macro_rules! check {

fn main() {
println!("{}" a);
//~^ ERROR expected token: `,`
//~^ ERROR expected `,`, found `a`
foo!(a b);
//~^ ERROR no rules expected the token `b`
foo!(a, b, c, d e);
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/macros/missing-comma.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: expected token: `,`
error: expected `,`, found `a`
--> $DIR/missing-comma.rs:19:19
|
LL | println!("{}" a);
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/parser/unicode-quote-chars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ fn main() {
//~^^ HELP Unicode characters '“' (Left Double Quotation Mark) and '”' (Right Double Quotation Mark) look like '"' (Quotation Mark), but are not
//~^^^ ERROR unknown start of token: \u{201d}
//~^^^^ HELP Unicode character '”' (Right Double Quotation Mark) looks like '"' (Quotation Mark), but it is not
//~^^^^^ ERROR expected token: `,`
//~^^^^^ ERROR expected `,`, found `world`
}
2 changes: 1 addition & 1 deletion src/test/ui/parser/unicode-quote-chars.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ help: Unicode character '”' (Right Double Quotation Mark) looks like '"' (Quot
LL | println!(“hello world");
| ^

error: expected token: `,`
error: expected `,`, found `world`
--> $DIR/unicode-quote-chars.rs:4:21
|
LL | println!(“hello world”);
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/tests/ui/issue-3145.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
fn main() {
println!("{}" a); //~ERROR expected token: `,`
println!("{}" a); //~ERROR expected `,`, found `a`
}
4 changes: 2 additions & 2 deletions src/tools/clippy/tests/ui/issue-3145.stderr
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
error: expected token: `,`
error: expected `,`, found `a`
--> $DIR/issue-3145.rs:2:19
|
LL | println!("{}" a); //~ERROR expected token: `,`
LL | println!("{}" a); //~ERROR expected `,`, found `a`
| ^ expected `,`

error: aborting due to previous error
Expand Down

0 comments on commit 80fc9b0

Please sign in to comment.