mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-16 15:14:02 +00:00
Auto merge of #16124 - ohno418:call-expr-missing-arg, r=Veykril
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. --- Fixes https://github.com/rust-lang/rust-analyzer/issues/15683.
This commit is contained in:
commit
5c80ad05ae
16 changed files with 267 additions and 30 deletions
|
@ -393,11 +393,26 @@ fn delimited(
|
||||||
bra: SyntaxKind,
|
bra: SyntaxKind,
|
||||||
ket: SyntaxKind,
|
ket: SyntaxKind,
|
||||||
delim: SyntaxKind,
|
delim: SyntaxKind,
|
||||||
|
unexpected_delim_message: impl Fn() -> String,
|
||||||
first_set: TokenSet,
|
first_set: TokenSet,
|
||||||
mut parser: impl FnMut(&mut Parser<'_>) -> bool,
|
mut parser: impl FnMut(&mut Parser<'_>) -> bool,
|
||||||
) {
|
) {
|
||||||
p.bump(bra);
|
p.bump(bra);
|
||||||
while !p.at(ket) && !p.at(EOF) {
|
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) {
|
if !parser(p) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -611,6 +611,7 @@ fn cast_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker {
|
||||||
// foo(bar::);
|
// foo(bar::);
|
||||||
// foo(bar:);
|
// foo(bar:);
|
||||||
// foo(bar+);
|
// foo(bar+);
|
||||||
|
// foo(a, , b);
|
||||||
// }
|
// }
|
||||||
fn arg_list(p: &mut Parser<'_>) {
|
fn arg_list(p: &mut Parser<'_>) {
|
||||||
assert!(p.at(T!['(']));
|
assert!(p.at(T!['(']));
|
||||||
|
@ -624,8 +625,9 @@ fn arg_list(p: &mut Parser<'_>) {
|
||||||
T!['('],
|
T!['('],
|
||||||
T![')'],
|
T![')'],
|
||||||
T![,],
|
T![,],
|
||||||
|
|| "expected expression".into(),
|
||||||
EXPR_FIRST.union(ATTRIBUTE_FIRST),
|
EXPR_FIRST.union(ATTRIBUTE_FIRST),
|
||||||
|p: &mut Parser<'_>| expr(p).is_some(),
|
|p| expr(p).is_some(),
|
||||||
);
|
);
|
||||||
m.complete(p, ARG_LIST);
|
m.complete(p, ARG_LIST);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use super::*;
|
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) {
|
pub(super) fn opt_generic_arg_list(p: &mut Parser<'_>, colon_colon_required: bool) {
|
||||||
let m;
|
let m;
|
||||||
if p.at(T![::]) && p.nth(2) == T![<] {
|
if p.at(T![::]) && p.nth(2) == T![<] {
|
||||||
|
@ -11,7 +13,15 @@ pub(super) fn opt_generic_arg_list(p: &mut Parser<'_>, colon_colon_required: boo
|
||||||
return;
|
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);
|
m.complete(p, GENERIC_ARG_LIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,16 +10,27 @@ pub(super) fn opt_generic_param_list(p: &mut Parser<'_>) {
|
||||||
|
|
||||||
// test generic_param_list
|
// test generic_param_list
|
||||||
// fn f<T: Clone>() {}
|
// fn f<T: Clone>() {}
|
||||||
|
|
||||||
|
// test_err generic_param_list_recover
|
||||||
|
// fn f<T: Clone,, U:, V>() {}
|
||||||
fn generic_param_list(p: &mut Parser<'_>) {
|
fn generic_param_list(p: &mut Parser<'_>) {
|
||||||
assert!(p.at(T![<]));
|
assert!(p.at(T![<]));
|
||||||
let m = p.start();
|
let m = p.start();
|
||||||
delimited(p, T![<], T![>], T![,], GENERIC_PARAM_FIRST.union(ATTRIBUTE_FIRST), |p| {
|
delimited(
|
||||||
// test generic_param_attribute
|
p,
|
||||||
// fn foo<#[lt_attr] 'a, #[t_attr] T>() {}
|
T![<],
|
||||||
let m = p.start();
|
T![>],
|
||||||
attributes::outer_attrs(p);
|
T![,],
|
||||||
generic_param(p, m)
|
|| "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);
|
m.complete(p, GENERIC_PARAM_LIST);
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,28 +146,39 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) {
|
||||||
const TUPLE_FIELD_FIRST: TokenSet =
|
const TUPLE_FIELD_FIRST: TokenSet =
|
||||||
types::TYPE_FIRST.union(ATTRIBUTE_FIRST).union(VISIBILITY_FIRST);
|
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<'_>) {
|
fn tuple_field_list(p: &mut Parser<'_>) {
|
||||||
assert!(p.at(T!['(']));
|
assert!(p.at(T!['(']));
|
||||||
let m = p.start();
|
let m = p.start();
|
||||||
delimited(p, T!['('], T![')'], T![,], TUPLE_FIELD_FIRST, |p| {
|
delimited(
|
||||||
let m = p.start();
|
p,
|
||||||
// test tuple_field_attrs
|
T!['('],
|
||||||
// struct S (#[attr] f32);
|
T![')'],
|
||||||
attributes::outer_attrs(p);
|
T![,],
|
||||||
let has_vis = opt_visibility(p, true);
|
|| "expected tuple field".into(),
|
||||||
if !p.at_ts(types::TYPE_FIRST) {
|
TUPLE_FIELD_FIRST,
|
||||||
p.error("expected a type");
|
|p| {
|
||||||
if has_vis {
|
let m = p.start();
|
||||||
m.complete(p, ERROR);
|
// test tuple_field_attrs
|
||||||
} else {
|
// struct S (#[attr] f32);
|
||||||
m.abandon(p);
|
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);
|
||||||
types::type_(p);
|
true
|
||||||
m.complete(p, TUPLE_FIELD);
|
},
|
||||||
true
|
);
|
||||||
});
|
|
||||||
|
|
||||||
m.complete(p, TUPLE_FIELD_LIST);
|
m.complete(p, TUPLE_FIELD_LIST);
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,9 +93,16 @@ pub(crate) fn use_tree_list(p: &mut Parser<'_>) {
|
||||||
// use b;
|
// use b;
|
||||||
// struct T;
|
// struct T;
|
||||||
// fn test() {}
|
// fn test() {}
|
||||||
delimited(p, T!['{'], T!['}'], T![,], USE_TREE_LIST_FIRST_SET, |p: &mut Parser<'_>| {
|
// use {a ,, b};
|
||||||
use_tree(p, false) || p.at_ts(USE_TREE_LIST_RECOVERY_SET)
|
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);
|
m.complete(p, USE_TREE_LIST);
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,33 @@ SOURCE_FILE
|
||||||
PLUS "+"
|
PLUS "+"
|
||||||
R_PAREN ")"
|
R_PAREN ")"
|
||||||
SEMICOLON ";"
|
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"
|
WHITESPACE "\n"
|
||||||
R_CURLY "}"
|
R_CURLY "}"
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
|
@ -75,3 +102,4 @@ error 25: expected identifier
|
||||||
error 39: expected COMMA
|
error 39: expected COMMA
|
||||||
error 39: expected expression
|
error 39: expected expression
|
||||||
error 55: expected expression
|
error 55: expected expression
|
||||||
|
error 69: expected expression
|
||||||
|
|
|
@ -2,4 +2,5 @@ fn main() {
|
||||||
foo(bar::);
|
foo(bar::);
|
||||||
foo(bar:);
|
foo(bar:);
|
||||||
foo(bar+);
|
foo(bar+);
|
||||||
|
foo(a, , b);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,4 +43,29 @@ SOURCE_FILE
|
||||||
L_CURLY "{"
|
L_CURLY "{"
|
||||||
R_CURLY "}"
|
R_CURLY "}"
|
||||||
WHITESPACE "\n"
|
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 6: expected R_CURLY
|
||||||
|
error 46: expected use tree
|
||||||
|
|
|
@ -2,3 +2,4 @@ use {a;
|
||||||
use b;
|
use b;
|
||||||
struct T;
|
struct T;
|
||||||
fn test() {}
|
fn test() {}
|
||||||
|
use {a ,, b};
|
||||||
|
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
||||||
|
struct S(struct S;
|
||||||
|
struct S(A,,B);
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
type T = T<0, ,T>;
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
fn f<T: Clone,, U:, V>() {}
|
Loading…
Reference in a new issue