Improve recovery on malformed format call

If a comma in a format call is replaced with a similar token, then we
emit an error and continue parsing, instead of stopping at this point.
This commit is contained in:
Sasha 2020-08-31 11:45:50 +02:00
parent 8ed5cb56b5
commit 3524c3ef43
13 changed files with 88 additions and 53 deletions

View file

@ -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;

View file

@ -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(

View file

@ -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
}

View file

@ -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);

View file

@ -1,32 +0,0 @@
error: unknown start of token: \u{326}
--> $DIR/incorrect-first-separator.rs:19:28
|
LL | format!("A number: {}" ̦ iter::once(42).next().unwrap());
| ^
error: expected token: `,`
--> $DIR/incorrect-first-separator.rs:7:27
|
LL | format!("A number: {}". iter::once(42).next().unwrap());
| ^ expected `,`
error: expected token: `,`
--> $DIR/incorrect-first-separator.rs:12:28
|
LL | format!("A number: {}" / iter::once(42).next().unwrap());
| ^ expected `,`
error: expected token: `,`
--> $DIR/incorrect-first-separator.rs:15:27
|
LL | format!("A number: {}"; iter::once(42).next().unwrap());
| ^ expected `,`
error: expected token: `,`
--> $DIR/incorrect-first-separator.rs:19:30
|
LL | format!("A number: {}" ̦ iter::once(42).next().unwrap());
| ^^^^ expected `,`
error: aborting due to 5 previous errors

View file

@ -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
}

View file

@ -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

View file

@ -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);

View file

@ -1,4 +1,4 @@
error: expected token: `,`
error: expected `,`, found `a`
--> $DIR/missing-comma.rs:19:19
|
LL | println!("{}" a);

View file

@ -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`
}

View file

@ -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”);

View file

@ -1,3 +1,3 @@
fn main() {
println!("{}" a); //~ERROR expected token: `,`
println!("{}" a); //~ERROR expected `,`, found `a`
}

View file

@ -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