1
Fork 0

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 { while p.token != token::Eof {
if !p.eat(&token::Comma) { if !p.eat(&token::Comma) {
if first { if first {
// After `format!(""` we always expect *only* a comma... p.clear_expected_tokens();
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); // `Parser::expect` tries to recover using the
return Err(err); // `Parser::unexpected_try_recover` function. This function is able
} else { // to recover if the expected token is a closing delimiter.
// ...after that delegate to `expect` to also include the other expected tokens. //
let _ = p.expect(&token::Comma)?; // 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; first = false;

View file

@ -1233,6 +1233,10 @@ impl<'a> Parser<'a> {
*t == token::OpenDelim(token::Brace) || *t == token::BinOp(token::Star) *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( crate fn make_unclosed_delims_error(

View file

@ -1,5 +1,5 @@
fn main() { fn main() {
format!(); //~ ERROR requires at least a format string argument 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 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) = 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 --> $DIR/bad-format-args.rs:3:16
| |
LL | format!("" 1); 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() { fn main() {
format!("A number: {}". iter::once(42).next().unwrap()); format!("A number: {}". iter::once(42).next().unwrap());
//~^ ERROR expected token: `,` //~^ ERROR expected `,`, found `.`
// Other kind of types are also checked: // Other kind of types are also checked:
format!("A number: {}" / iter::once(42).next().unwrap()); format!("A number: {}" / iter::once(42).next().unwrap());
//~^ ERROR expected token: `,` //~^ ERROR expected `,`, found `/`
format!("A number: {}"; iter::once(42).next().unwrap()); format!("A number: {}"; iter::once(42).next().unwrap());
//~^ ERROR expected token: `,` //~^ ERROR expected `,`, found `;`
// Note: this character is an COMBINING COMMA BELOW unicode char // Note: this character is an COMBINING COMMA BELOW unicode char
format!("A number: {}" ̦ iter::once(42).next().unwrap()); format!("A number: {}" ̦ iter::once(42).next().unwrap());
//~^ ERROR expected token: `,` //~^ ERROR expected `,`, found `iter`
//~^^ ERROR unknown start of token: \u{326} //~^^ 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() { fn main() {
println!("{}" a); println!("{}" a);
//~^ ERROR expected token: `,` //~^ ERROR expected `,`, found `a`
foo!(a b); foo!(a b);
//~^ ERROR no rules expected the token `b` //~^ ERROR no rules expected the token `b`
foo!(a, b, c, d e); 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 --> $DIR/missing-comma.rs:19:19
| |
LL | println!("{}" a); 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 //~^^ 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} //~^^^ ERROR unknown start of token: \u{201d}
//~^^^^ HELP Unicode character '”' (Right Double Quotation Mark) looks like '"' (Quotation Mark), but it is not //~^^^^ 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"); LL | println!(“hello world");
| ^ | ^
error: expected token: `,` error: expected `,`, found `world`
--> $DIR/unicode-quote-chars.rs:4:21 --> $DIR/unicode-quote-chars.rs:4:21
| |
LL | println!(“hello world”); LL | println!(“hello world”);

View file

@ -1,3 +1,3 @@
fn main() { 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 --> $DIR/issue-3145.rs:2:19
| |
LL | println!("{}" a); //~ERROR expected token: `,` LL | println!("{}" a); //~ERROR expected `,`, found `a`
| ^ expected `,` | ^ expected `,`
error: aborting due to previous error error: aborting due to previous error