Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Recover from missing argument in call expressions #16124

Merged
merged 2 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions crates/parser/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,11 +393,26 @@ fn delimited(
bra: SyntaxKind,
ket: SyntaxKind,
delim: SyntaxKind,
unexpected_delim_message: impl Fn() -> String,
first_set: TokenSet,
mut parser: impl FnMut(&mut Parser<'_>) -> bool,
) {
p.bump(bra);
while !p.at(ket) && !p.at(EOF) {
if p.at(delim) {
// Recover if an argument is missing and only got a delimiter,
// e.g. `(a, , b)`.

// Wrap the erroneous delimiter in an error node so that fixup logic gets rid of it.
// FIXME: Ideally this should be handled in fixup in a structured way, but our list
// nodes currently have no concept of a missing node between two delimiters.
// So doing it this way is easier.
let m = p.start();
p.error(unexpected_delim_message());
p.bump(delim);
m.complete(p, ERROR);
continue;
}
if !parser(p) {
break;
}
Expand Down
4 changes: 3 additions & 1 deletion crates/parser/src/grammar/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!['(']));
Expand All @@ -624,8 +625,9 @@ fn arg_list(p: &mut Parser<'_>) {
T!['('],
T![')'],
T![,],
|| "expected expression".into(),
EXPR_FIRST.union(ATTRIBUTE_FIRST),
|p: &mut Parser<'_>| expr(p).is_some(),
|p| expr(p).is_some(),
);
m.complete(p, ARG_LIST);
}
Expand Down
12 changes: 11 additions & 1 deletion crates/parser/src/grammar/generic_args.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use super::*;

// test_err generic_arg_list_recover
// type T = T<0, ,T>;
pub(super) fn opt_generic_arg_list(p: &mut Parser<'_>, colon_colon_required: bool) {
let m;
if p.at(T![::]) && p.nth(2) == T![<] {
Expand All @@ -11,7 +13,15 @@ pub(super) fn opt_generic_arg_list(p: &mut Parser<'_>, colon_colon_required: boo
return;
}

delimited(p, T![<], T![>], T![,], GENERIC_ARG_FIRST, generic_arg);
delimited(
p,
T![<],
T![>],
T![,],
|| "expected generic argument".into(),
GENERIC_ARG_FIRST,
generic_arg,
);
m.complete(p, GENERIC_ARG_LIST);
}

Expand Down
25 changes: 18 additions & 7 deletions crates/parser/src/grammar/generic_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,27 @@ pub(super) fn opt_generic_param_list(p: &mut Parser<'_>) {

// test generic_param_list
// fn f<T: Clone>() {}

// test_err generic_param_list_recover
// fn f<T: Clone,, U:, V>() {}
fn generic_param_list(p: &mut Parser<'_>) {
assert!(p.at(T![<]));
let m = p.start();
delimited(p, T![<], T![>], T![,], GENERIC_PARAM_FIRST.union(ATTRIBUTE_FIRST), |p| {
// test generic_param_attribute
// fn foo<#[lt_attr] 'a, #[t_attr] T>() {}
let m = p.start();
attributes::outer_attrs(p);
generic_param(p, m)
});
delimited(
p,
T![<],
T![>],
T![,],
|| "expected generic parameter".into(),
GENERIC_PARAM_FIRST.union(ATTRIBUTE_FIRST),
|p| {
// test generic_param_attribute
// fn foo<#[lt_attr] 'a, #[t_attr] T>() {}
let m = p.start();
attributes::outer_attrs(p);
generic_param(p, m)
},
);

m.complete(p, GENERIC_PARAM_LIST);
}
Expand Down
47 changes: 29 additions & 18 deletions crates/parser/src/grammar/items/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,28 +146,39 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) {
const TUPLE_FIELD_FIRST: TokenSet =
types::TYPE_FIRST.union(ATTRIBUTE_FIRST).union(VISIBILITY_FIRST);

// test_err tuple_field_list_recovery
// struct S(struct S;
// struct S(A,,B);
fn tuple_field_list(p: &mut Parser<'_>) {
assert!(p.at(T!['(']));
let m = p.start();
delimited(p, T!['('], T![')'], T![,], TUPLE_FIELD_FIRST, |p| {
let m = p.start();
// test tuple_field_attrs
// struct S (#[attr] f32);
attributes::outer_attrs(p);
let has_vis = opt_visibility(p, true);
if !p.at_ts(types::TYPE_FIRST) {
p.error("expected a type");
if has_vis {
m.complete(p, ERROR);
} else {
m.abandon(p);
delimited(
p,
T!['('],
T![')'],
T![,],
|| "expected tuple field".into(),
TUPLE_FIELD_FIRST,
|p| {
let m = p.start();
// test tuple_field_attrs
// struct S (#[attr] f32);
attributes::outer_attrs(p);
let has_vis = opt_visibility(p, true);
if !p.at_ts(types::TYPE_FIRST) {
p.error("expected a type");
if has_vis {
m.complete(p, ERROR);
} else {
m.abandon(p);
}
return false;
}
return false;
}
types::type_(p);
m.complete(p, TUPLE_FIELD);
true
});
types::type_(p);
m.complete(p, TUPLE_FIELD);
true
},
);

m.complete(p, TUPLE_FIELD_LIST);
}
13 changes: 10 additions & 3 deletions crates/parser/src/grammar/items/use_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,16 @@ pub(crate) fn use_tree_list(p: &mut Parser<'_>) {
// use b;
// struct T;
// fn test() {}
delimited(p, T!['{'], T!['}'], T![,], USE_TREE_LIST_FIRST_SET, |p: &mut Parser<'_>| {
use_tree(p, false) || p.at_ts(USE_TREE_LIST_RECOVERY_SET)
});
// use {a ,, b};
delimited(
p,
T!['{'],
T!['}'],
T![,],
|| "expected use tree".into(),
USE_TREE_LIST_FIRST_SET,
|p: &mut Parser<'_>| use_tree(p, false) || p.at_ts(USE_TREE_LIST_RECOVERY_SET),
);

m.complete(p, USE_TREE_LIST);
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,38 @@ 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 " "
ERROR
COMMA ","
WHITESPACE " "
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "b"
R_PAREN ")"
SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
error 25: expected identifier
error 39: expected COMMA
error 39: expected expression
error 55: expected expression
error 69: expected expression
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ fn main() {
foo(bar::);
foo(bar:);
foo(bar+);
foo(a, , b);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,29 @@ SOURCE_FILE
L_CURLY "{"
R_CURLY "}"
WHITESPACE "\n"
USE
USE_KW "use"
WHITESPACE " "
USE_TREE
USE_TREE_LIST
L_CURLY "{"
USE_TREE
PATH
PATH_SEGMENT
NAME_REF
IDENT "a"
WHITESPACE " "
COMMA ","
ERROR
COMMA ","
WHITESPACE " "
USE_TREE
PATH
PATH_SEGMENT
NAME_REF
IDENT "b"
R_CURLY "}"
SEMICOLON ";"
WHITESPACE "\n"
error 6: expected R_CURLY
error 46: expected use tree
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ use {a;
use b;
struct T;
fn test() {}
use {a ,, b};
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
SOURCE_FILE
STRUCT
STRUCT_KW "struct"
WHITESPACE " "
NAME
IDENT "S"
TUPLE_FIELD_LIST
L_PAREN "("
STRUCT
STRUCT_KW "struct"
WHITESPACE " "
NAME
IDENT "S"
SEMICOLON ";"
WHITESPACE "\n"
STRUCT
STRUCT_KW "struct"
WHITESPACE " "
NAME
IDENT "S"
TUPLE_FIELD_LIST
L_PAREN "("
TUPLE_FIELD
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "A"
COMMA ","
ERROR
COMMA ","
TUPLE_FIELD
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "B"
R_PAREN ")"
SEMICOLON ";"
WHITESPACE "\n"
error 9: expected a type
error 9: expected R_PAREN
error 9: expected SEMICOLON
error 30: expected tuple field
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
struct S(struct S;
struct S(A,,B);
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
SOURCE_FILE
TYPE_ALIAS
TYPE_KW "type"
WHITESPACE " "
NAME
IDENT "T"
WHITESPACE " "
EQ "="
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "T"
GENERIC_ARG_LIST
L_ANGLE "<"
CONST_ARG
LITERAL
INT_NUMBER "0"
COMMA ","
WHITESPACE " "
ERROR
COMMA ","
TYPE_ARG
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "T"
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
error 14: expected generic argument
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
type T = T<0, ,T>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
SOURCE_FILE
FN
FN_KW "fn"
WHITESPACE " "
NAME
IDENT "f"
GENERIC_PARAM_LIST
L_ANGLE "<"
TYPE_PARAM
NAME
IDENT "T"
COLON ":"
WHITESPACE " "
TYPE_BOUND_LIST
TYPE_BOUND
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "Clone"
COMMA ","
ERROR
COMMA ","
WHITESPACE " "
TYPE_PARAM
NAME
IDENT "U"
COLON ":"
TYPE_BOUND_LIST
COMMA ","
WHITESPACE " "
TYPE_PARAM
NAME
IDENT "V"
R_ANGLE ">"
PARAM_LIST
L_PAREN "("
R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
R_CURLY "}"
WHITESPACE "\n"
error 14: expected generic parameter
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn f<T: Clone,, U:, V>() {}
Loading