Unnamed repository; edit this file 'description' to name the repository.
fix: Recover from missing argument in call expressions
Previously, when parsing an argument list with a missing argument (e.g., `(a, , b)` in `foo(a, , b)`), the parser would stop upon an unexpected token (at the second comma in the example), resulting in an incorrect parse tree. This commit improves error handling in such cases, ensuring a more accurate parse tree is built.
Yutaro Ohno 2024-02-08
parent bcc8a09 · commit e865d45
-rw-r--r--crates/hir-expand/src/fixup.rs14
-rw-r--r--crates/parser/src/grammar/expressions.rs33
-rw-r--r--crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rast27
-rw-r--r--crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rs1
4 files changed, 53 insertions, 22 deletions
diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs
index a3b2c700ff..ed29411a6b 100644
--- a/crates/hir-expand/src/fixup.rs
+++ b/crates/hir-expand/src/fixup.rs
@@ -632,20 +632,6 @@ fn foo () {a . b ; bar () ;}
}
#[test]
- fn extraneous_comma() {
- check(
- r#"
-fn foo() {
- bar(,);
-}
-"#,
- expect![[r#"
-fn foo () {__ra_fixup ;}
-"#]],
- )
- }
-
- #[test]
fn fixup_if_1() {
check(
r#"
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index e99c111d39..52a08c4fb1 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -611,6 +611,7 @@ fn cast_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker {
// foo(bar::);
// foo(bar:);
// foo(bar+);
+// foo(a, , b);
// }
fn arg_list(p: &mut Parser<'_>) {
assert!(p.at(T!['(']));
@@ -619,14 +620,30 @@ fn arg_list(p: &mut Parser<'_>) {
// fn main() {
// foo(#[attr] 92)
// }
- delimited(
- p,
- T!['('],
- T![')'],
- T![,],
- EXPR_FIRST.union(ATTRIBUTE_FIRST),
- |p: &mut Parser<'_>| expr(p).is_some(),
- );
+ p.bump(T!['(']);
+ while !p.at(T![')']) && !p.at(EOF) {
+ if p.at(T![,]) {
+ // Recover if an argument is missing and only got a delimiter,
+ // e.g. `(a, , b)`.
+ p.error("expected expression");
+ p.bump(T![,]);
+ continue;
+ }
+
+ if expr(p).is_none() {
+ break;
+ }
+ if !p.at(T![,]) {
+ if p.at_ts(EXPR_FIRST.union(ATTRIBUTE_FIRST)) {
+ p.error(format!("expected {:?}", T![,]));
+ } else {
+ break;
+ }
+ } else {
+ p.bump(T![,]);
+ }
+ }
+ p.expect(T![')']);
m.complete(p, ARG_LIST);
}
diff --git a/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rast b/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rast
index 5d0fe859c2..85def9e148 100644
--- a/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rast
+++ b/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rast
@@ -68,6 +68,32 @@ SOURCE_FILE
PLUS "+"
R_PAREN ")"
SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "foo"
+ ARG_LIST
+ L_PAREN "("
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ COMMA ","
+ WHITESPACE " "
+ COMMA ","
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ R_PAREN ")"
+ SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
@@ -75,3 +101,4 @@ error 25: expected identifier
error 39: expected COMMA
error 39: expected expression
error 55: expected expression
+error 68: expected expression
diff --git a/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rs b/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rs
index 0e7ac9cc30..175a31f8b5 100644
--- a/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rs
+++ b/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rs
@@ -2,4 +2,5 @@ fn main() {
foo(bar::);
foo(bar:);
foo(bar+);
+ foo(a, , b);
}