Skip to content

Commit 1be24e0

Browse files
committed
internal: Improve parser recovery a bunch
1 parent 5fdf640 commit 1be24e0

File tree

14 files changed

+247
-132
lines changed

14 files changed

+247
-132
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hir-def/src/macro_expansion_tests/mbe.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1476,7 +1476,7 @@ macro_rules! m {
14761476
/* parse error: expected identifier */
14771477
/* parse error: expected SEMICOLON */
14781478
/* parse error: expected SEMICOLON */
1479-
/* parse error: expected expression */
1479+
/* parse error: expected expression, item or let statement */
14801480
fn f() {
14811481
K::(C("0"));
14821482
}

crates/hir-def/src/macro_expansion_tests/mbe/regression.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,7 @@ macro_rules! rgb_color {
831831
/* parse error: expected R_ANGLE */
832832
/* parse error: expected SEMICOLON */
833833
/* parse error: expected SEMICOLON */
834-
/* parse error: expected expression */
834+
/* parse error: expected expression, item or let statement */
835835
pub fn new() {
836836
let _ = 0as u32<<(8+8);
837837
}

crates/parser/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ limit.workspace = true
2020
[dev-dependencies]
2121
expect-test = "1.4.0"
2222

23+
stdx.workspace = true
2324
sourcegen.workspace = true

crates/parser/src/grammar/expressions.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) {
6868
Err(m) => m,
6969
};
7070

71+
if !p.at_ts(EXPR_FIRST) {
72+
p.err_and_bump("expected expression, item or let statement");
73+
m.abandon(p);
74+
return;
75+
}
76+
7177
if let Some((cm, blocklike)) = expr_stmt(p, Some(m)) {
7278
if !(p.at(T!['}']) || (semicolon != Semicolon::Required && p.at(EOF))) {
7379
// test no_semi_after_block
@@ -227,6 +233,12 @@ fn expr_bp(
227233
attributes::outer_attrs(p);
228234
m
229235
});
236+
237+
if !p.at_ts(EXPR_FIRST) {
238+
p.err_recover("expected expression", atom::EXPR_RECOVERY_SET);
239+
m.abandon(p);
240+
return None;
241+
}
230242
let mut lhs = match lhs(p, r) {
231243
Some((lhs, blocklike)) => {
232244
let lhs = lhs.extend_to(p, m);
@@ -551,6 +563,12 @@ fn cast_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker {
551563
m.complete(p, CAST_EXPR)
552564
}
553565

566+
// test_err arg_list_recovery
567+
// fn main() {
568+
// foo(bar::);
569+
// foo(bar:);
570+
// foo(bar+);
571+
// }
554572
fn arg_list(p: &mut Parser<'_>) {
555573
assert!(p.at(T!['(']));
556574
let m = p.start();
@@ -563,8 +581,15 @@ fn arg_list(p: &mut Parser<'_>) {
563581
if !expr(p) {
564582
break;
565583
}
566-
if !p.at(T![')']) && !p.expect(T![,]) {
567-
break;
584+
if !p.at(T![,]) {
585+
if p.at_ts(EXPR_FIRST) {
586+
p.error("expected `,`");
587+
continue;
588+
} else {
589+
break;
590+
}
591+
} else {
592+
p.bump(T![,]);
568593
}
569594
}
570595
p.eat(T![')']);

crates/parser/src/grammar/expressions/atom.rs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,26 +40,28 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet =
4040
T!['{'],
4141
T!['['],
4242
T![|],
43-
T![move],
43+
T![async],
4444
T![box],
45+
T![break],
46+
T![const],
47+
T![continue],
48+
T![do],
49+
T![for],
4550
T![if],
46-
T![while],
51+
T![let],
52+
T![loop],
4753
T![match],
48-
T![unsafe],
54+
T![move],
4955
T![return],
50-
T![yield],
51-
T![do],
52-
T![break],
53-
T![continue],
54-
T![async],
56+
T![static],
5557
T![try],
56-
T![const],
57-
T![loop],
58-
T![for],
58+
T![unsafe],
59+
T![while],
60+
T![yield],
5961
LIFETIME_IDENT,
6062
]));
6163

62-
const EXPR_RECOVERY_SET: TokenSet = TokenSet::new(&[T![let]]);
64+
pub(super) const EXPR_RECOVERY_SET: TokenSet = TokenSet::new(&[T![')'], T![']']]);
6365

6466
pub(super) fn atom_expr(
6567
p: &mut Parser<'_>,
@@ -157,7 +159,7 @@ pub(super) fn atom_expr(
157159
T![for] => for_expr(p, None),
158160

159161
_ => {
160-
p.err_recover("expected expression", EXPR_RECOVERY_SET);
162+
p.err_and_bump("expected expression");
161163
return None;
162164
}
163165
};

crates/parser/src/grammar/paths.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ fn path_for_qualifier(
6767
}
6868
}
6969

70+
const EXPR_PATH_SEGMENT_RECOVERY_SET: TokenSet =
71+
items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![')'], T![,], T![let]]));
72+
const TYPE_PATH_SEGMENT_RECOVERY_SET: TokenSet = types::TYPE_RECOVERY_SET;
73+
7074
fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
7175
let m = p.start();
7276
// test qual_paths
@@ -102,7 +106,12 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
102106
m.complete(p, NAME_REF);
103107
}
104108
_ => {
105-
p.err_recover("expected identifier", items::ITEM_RECOVERY_SET);
109+
let recover_set = match mode {
110+
Mode::Use => items::ITEM_RECOVERY_SET,
111+
Mode::Type => TYPE_PATH_SEGMENT_RECOVERY_SET,
112+
Mode::Expr => EXPR_PATH_SEGMENT_RECOVERY_SET,
113+
};
114+
p.err_recover("expected identifier", recover_set);
106115
if empty {
107116
// test_err empty_segment
108117
// use crate::;

crates/parser/src/grammar/types.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[
1717
T![Self],
1818
]));
1919

20-
const TYPE_RECOVERY_SET: TokenSet = TokenSet::new(&[
20+
pub(super) const TYPE_RECOVERY_SET: TokenSet = TokenSet::new(&[
2121
T![')'],
22+
T![>],
2223
T![,],
2324
// test_err struct_field_recover
2425
// struct S { f pub g: () }

crates/parser/src/tests.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::{LexedStr, TopEntryPoint};
1515
#[test]
1616
fn lex_ok() {
1717
for case in TestCase::list("lexer/ok") {
18+
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
1819
let actual = lex(&case.text);
1920
expect_file![case.rast].assert_eq(&actual)
2021
}
@@ -23,6 +24,7 @@ fn lex_ok() {
2324
#[test]
2425
fn lex_err() {
2526
for case in TestCase::list("lexer/err") {
27+
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
2628
let actual = lex(&case.text);
2729
expect_file![case.rast].assert_eq(&actual)
2830
}
@@ -46,6 +48,7 @@ fn lex(text: &str) -> String {
4648
#[test]
4749
fn parse_ok() {
4850
for case in TestCase::list("parser/ok") {
51+
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
4952
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
5053
assert!(!errors, "errors in an OK file {}:\n{actual}", case.rs.display());
5154
expect_file![case.rast].assert_eq(&actual);
@@ -55,6 +58,7 @@ fn parse_ok() {
5558
#[test]
5659
fn parse_inline_ok() {
5760
for case in TestCase::list("parser/inline/ok") {
61+
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
5862
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
5963
assert!(!errors, "errors in an OK file {}:\n{actual}", case.rs.display());
6064
expect_file![case.rast].assert_eq(&actual);
@@ -64,6 +68,7 @@ fn parse_inline_ok() {
6468
#[test]
6569
fn parse_err() {
6670
for case in TestCase::list("parser/err") {
71+
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
6772
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
6873
assert!(errors, "no errors in an ERR file {}:\n{actual}", case.rs.display());
6974
expect_file![case.rast].assert_eq(&actual)
@@ -73,6 +78,7 @@ fn parse_err() {
7378
#[test]
7479
fn parse_inline_err() {
7580
for case in TestCase::list("parser/inline/err") {
81+
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
7682
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
7783
assert!(errors, "no errors in an ERR file {}:\n{actual}", case.rs.display());
7884
expect_file![case.rast].assert_eq(&actual)

crates/parser/src/tests/top_entries.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ fn macro_stmt() {
6565
MACRO_STMTS
6666
ERROR
6767
SHEBANG "#!/usr/bin/rust"
68-
error 0: expected expression
68+
error 0: expected expression, item or let statement
6969
"##]],
7070
);
7171
check(

0 commit comments

Comments
 (0)