Skip to content

Improve recovery on malformed format call #76160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1233,6 +1233,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