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:
parent
8ed5cb56b5
commit
3524c3ef43
13 changed files with 88 additions and 53 deletions
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
src/test/ui/fmt/incorrect-separator.stderr
Normal file
44
src/test/ui/fmt/incorrect-separator.stderr
Normal 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
|
||||
|
|
@ -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);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: expected token: `,`
|
||||
error: expected `,`, found `a`
|
||||
--> $DIR/missing-comma.rs:19:19
|
||||
|
|
||||
LL | println!("{}" a);
|
||||
|
|
|
@ -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`
|
||||
}
|
||||
|
|
|
@ -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”);
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
fn main() {
|
||||
println!("{}" a); //~ERROR expected token: `,`
|
||||
println!("{}" a); //~ERROR expected `,`, found `a`
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue