Skip to content

Commit 80fc9b0

Browse files
committed
Auto merge of #76160 - scileo:format-recovery, r=petrochenkov
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
2 parents a167485 + 3524c3e commit 80fc9b0

13 files changed

+88
-53
lines changed

compiler/rustc_builtin_macros/src/format.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,26 @@ fn parse_args<'a>(
161161
while p.token != token::Eof {
162162
if !p.eat(&token::Comma) {
163163
if first {
164-
// After `format!(""` we always expect *only* a comma...
165-
let mut err = ecx.struct_span_err(p.token.span, "expected token: `,`");
166-
err.span_label(p.token.span, "expected `,`");
167-
p.maybe_annotate_with_ascription(&mut err, false);
168-
return Err(err);
169-
} else {
170-
// ...after that delegate to `expect` to also include the other expected tokens.
171-
let _ = p.expect(&token::Comma)?;
164+
p.clear_expected_tokens();
165+
}
166+
167+
// `Parser::expect` tries to recover using the
168+
// `Parser::unexpected_try_recover` function. This function is able
169+
// to recover if the expected token is a closing delimiter.
170+
//
171+
// As `,` is not a closing delimiter, it will always return an `Err`
172+
// variant.
173+
let mut err = p.expect(&token::Comma).unwrap_err();
174+
175+
match token::TokenKind::Comma.similar_tokens() {
176+
Some(tks) if tks.contains(&p.token.kind) => {
177+
// If a similar token is found, then it may be a typo. We
178+
// consider it as a comma, and continue parsing.
179+
err.emit();
180+
p.bump();
181+
}
182+
// Otherwise stop the parsing and return the error.
183+
_ => return Err(err),
172184
}
173185
}
174186
first = false;

compiler/rustc_parse/src/parser/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1234,6 +1234,10 @@ impl<'a> Parser<'a> {
12341234
*t == token::OpenDelim(token::Brace) || *t == token::BinOp(token::Star)
12351235
})
12361236
}
1237+
1238+
pub fn clear_expected_tokens(&mut self) {
1239+
self.expected_tokens.clear();
1240+
}
12371241
}
12381242

12391243
crate fn make_unclosed_delims_error(
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
fn main() {
22
format!(); //~ ERROR requires at least a format string argument
3-
format!("" 1); //~ ERROR expected token: `,`
3+
format!("" 1); //~ ERROR expected `,`, found `1`
44
format!("", 1 1); //~ ERROR expected one of
55
}

src/test/ui/codemap_tests/bad-format-args.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | format!();
66
|
77
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
88

9-
error: expected token: `,`
9+
error: expected `,`, found `1`
1010
--> $DIR/bad-format-args.rs:3:16
1111
|
1212
LL | format!("" 1);

src/test/ui/fmt/incorrect-first-separator.stderr

-32
This file was deleted.

src/test/ui/fmt/incorrect-first-separator.rs src/test/ui/fmt/incorrect-separator.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,25 @@ use std::iter;
55

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

1010
// Other kind of types are also checked:
1111

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

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

1818
// Note: this character is an COMBINING COMMA BELOW unicode char
1919
format!("A number: {}" ̦ iter::once(42).next().unwrap());
20-
//~^ ERROR expected token: `,`
20+
//~^ ERROR expected `,`, found `iter`
2121
//~^^ ERROR unknown start of token: \u{326}
22+
23+
// Here recovery is tested.
24+
// If the `compile_error!` is emitted, then the parser is able to recover
25+
// from the incorrect first separator.
26+
format!("{}". compile_error!("fail"));
27+
//~^ ERROR expected `,`, found `.`
28+
//~^^ ERROR fail
2229
}
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
error: unknown start of token: \u{326}
2+
--> $DIR/incorrect-separator.rs:19:28
3+
|
4+
LL | format!("A number: {}" ̦ iter::once(42).next().unwrap());
5+
| ^
6+
7+
error: expected `,`, found `.`
8+
--> $DIR/incorrect-separator.rs:7:27
9+
|
10+
LL | format!("A number: {}". iter::once(42).next().unwrap());
11+
| ^ expected `,`
12+
13+
error: expected `,`, found `/`
14+
--> $DIR/incorrect-separator.rs:12:28
15+
|
16+
LL | format!("A number: {}" / iter::once(42).next().unwrap());
17+
| ^ expected `,`
18+
19+
error: expected `,`, found `;`
20+
--> $DIR/incorrect-separator.rs:15:27
21+
|
22+
LL | format!("A number: {}"; iter::once(42).next().unwrap());
23+
| ^ expected `,`
24+
25+
error: expected `,`, found `iter`
26+
--> $DIR/incorrect-separator.rs:19:30
27+
|
28+
LL | format!("A number: {}" ̦ iter::once(42).next().unwrap());
29+
| ^^^^ expected `,`
30+
31+
error: expected `,`, found `.`
32+
--> $DIR/incorrect-separator.rs:26:17
33+
|
34+
LL | format!("{}". compile_error!("fail"));
35+
| ^ expected `,`
36+
37+
error: fail
38+
--> $DIR/incorrect-separator.rs:26:19
39+
|
40+
LL | format!("{}". compile_error!("fail"));
41+
| ^^^^^^^^^^^^^^^^^^^^^^
42+
43+
error: aborting due to 7 previous errors
44+

src/test/ui/macros/missing-comma.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ macro_rules! check {
1717

1818
fn main() {
1919
println!("{}" a);
20-
//~^ ERROR expected token: `,`
20+
//~^ ERROR expected `,`, found `a`
2121
foo!(a b);
2222
//~^ ERROR no rules expected the token `b`
2323
foo!(a, b, c, d e);

src/test/ui/macros/missing-comma.stderr

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

src/test/ui/parser/unicode-quote-chars.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ fn main() {
66
//~^^ HELP Unicode characters '“' (Left Double Quotation Mark) and '”' (Right Double Quotation Mark) look like '"' (Quotation Mark), but are not
77
//~^^^ ERROR unknown start of token: \u{201d}
88
//~^^^^ HELP Unicode character '”' (Right Double Quotation Mark) looks like '"' (Quotation Mark), but it is not
9-
//~^^^^^ ERROR expected token: `,`
9+
//~^^^^^ ERROR expected `,`, found `world`
1010
}

src/test/ui/parser/unicode-quote-chars.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ help: Unicode character '”' (Right Double Quotation Mark) looks like '"' (Quot
2020
LL | println!(“hello world");
2121
| ^
2222

23-
error: expected token: `,`
23+
error: expected `,`, found `world`
2424
--> $DIR/unicode-quote-chars.rs:4:21
2525
|
2626
LL | println!(“hello world”);
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
fn main() {
2-
println!("{}" a); //~ERROR expected token: `,`
2+
println!("{}" a); //~ERROR expected `,`, found `a`
33
}

src/tools/clippy/tests/ui/issue-3145.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
error: expected token: `,`
1+
error: expected `,`, found `a`
22
--> $DIR/issue-3145.rs:2:19
33
|
4-
LL | println!("{}" a); //~ERROR expected token: `,`
4+
LL | println!("{}" a); //~ERROR expected `,`, found `a`
55
| ^ expected `,`
66

77
error: aborting due to previous error

0 commit comments

Comments
 (0)