internal: Improve parser recovery a bunch
This commit is contained in:
parent
5fdf640fb3
commit
1be24e0899
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1173,6 +1173,7 @@ dependencies = [
|
||||
"limit",
|
||||
"rustc-ap-rustc_lexer",
|
||||
"sourcegen",
|
||||
"stdx",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1476,7 +1476,7 @@ macro_rules! m {
|
||||
/* parse error: expected identifier */
|
||||
/* parse error: expected SEMICOLON */
|
||||
/* parse error: expected SEMICOLON */
|
||||
/* parse error: expected expression */
|
||||
/* parse error: expected expression, item or let statement */
|
||||
fn f() {
|
||||
K::(C("0"));
|
||||
}
|
||||
|
@ -831,7 +831,7 @@ pub fn new() {
|
||||
/* parse error: expected R_ANGLE */
|
||||
/* parse error: expected SEMICOLON */
|
||||
/* parse error: expected SEMICOLON */
|
||||
/* parse error: expected expression */
|
||||
/* parse error: expected expression, item or let statement */
|
||||
pub fn new() {
|
||||
let _ = 0as u32<<(8+8);
|
||||
}
|
||||
|
@ -20,4 +20,5 @@ limit.workspace = true
|
||||
[dev-dependencies]
|
||||
expect-test = "1.4.0"
|
||||
|
||||
stdx.workspace = true
|
||||
sourcegen.workspace = true
|
||||
|
@ -68,6 +68,12 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) {
|
||||
Err(m) => m,
|
||||
};
|
||||
|
||||
if !p.at_ts(EXPR_FIRST) {
|
||||
p.err_and_bump("expected expression, item or let statement");
|
||||
m.abandon(p);
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some((cm, blocklike)) = expr_stmt(p, Some(m)) {
|
||||
if !(p.at(T!['}']) || (semicolon != Semicolon::Required && p.at(EOF))) {
|
||||
// test no_semi_after_block
|
||||
@ -227,6 +233,12 @@ fn expr_bp(
|
||||
attributes::outer_attrs(p);
|
||||
m
|
||||
});
|
||||
|
||||
if !p.at_ts(EXPR_FIRST) {
|
||||
p.err_recover("expected expression", atom::EXPR_RECOVERY_SET);
|
||||
m.abandon(p);
|
||||
return None;
|
||||
}
|
||||
let mut lhs = match lhs(p, r) {
|
||||
Some((lhs, blocklike)) => {
|
||||
let lhs = lhs.extend_to(p, m);
|
||||
@ -551,6 +563,12 @@ fn cast_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker {
|
||||
m.complete(p, CAST_EXPR)
|
||||
}
|
||||
|
||||
// test_err arg_list_recovery
|
||||
// fn main() {
|
||||
// foo(bar::);
|
||||
// foo(bar:);
|
||||
// foo(bar+);
|
||||
// }
|
||||
fn arg_list(p: &mut Parser<'_>) {
|
||||
assert!(p.at(T!['(']));
|
||||
let m = p.start();
|
||||
@ -563,8 +581,15 @@ fn arg_list(p: &mut Parser<'_>) {
|
||||
if !expr(p) {
|
||||
break;
|
||||
}
|
||||
if !p.at(T![')']) && !p.expect(T![,]) {
|
||||
break;
|
||||
if !p.at(T![,]) {
|
||||
if p.at_ts(EXPR_FIRST) {
|
||||
p.error("expected `,`");
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
p.bump(T![,]);
|
||||
}
|
||||
}
|
||||
p.eat(T![')']);
|
||||
|
@ -40,26 +40,28 @@ pub(crate) fn literal(p: &mut Parser<'_>) -> Option<CompletedMarker> {
|
||||
T!['{'],
|
||||
T!['['],
|
||||
T![|],
|
||||
T![move],
|
||||
T![box],
|
||||
T![if],
|
||||
T![while],
|
||||
T![match],
|
||||
T![unsafe],
|
||||
T![return],
|
||||
T![yield],
|
||||
T![do],
|
||||
T![break],
|
||||
T![continue],
|
||||
T![async],
|
||||
T![try],
|
||||
T![box],
|
||||
T![break],
|
||||
T![const],
|
||||
T![loop],
|
||||
T![continue],
|
||||
T![do],
|
||||
T![for],
|
||||
T![if],
|
||||
T![let],
|
||||
T![loop],
|
||||
T![match],
|
||||
T![move],
|
||||
T![return],
|
||||
T![static],
|
||||
T![try],
|
||||
T![unsafe],
|
||||
T![while],
|
||||
T![yield],
|
||||
LIFETIME_IDENT,
|
||||
]));
|
||||
|
||||
const EXPR_RECOVERY_SET: TokenSet = TokenSet::new(&[T![let]]);
|
||||
pub(super) const EXPR_RECOVERY_SET: TokenSet = TokenSet::new(&[T![')'], T![']']]);
|
||||
|
||||
pub(super) fn atom_expr(
|
||||
p: &mut Parser<'_>,
|
||||
@ -157,7 +159,7 @@ pub(super) fn atom_expr(
|
||||
T![for] => for_expr(p, None),
|
||||
|
||||
_ => {
|
||||
p.err_recover("expected expression", EXPR_RECOVERY_SET);
|
||||
p.err_and_bump("expected expression");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
@ -67,6 +67,10 @@ fn path_for_qualifier(
|
||||
}
|
||||
}
|
||||
|
||||
const EXPR_PATH_SEGMENT_RECOVERY_SET: TokenSet =
|
||||
items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![')'], T![,], T![let]]));
|
||||
const TYPE_PATH_SEGMENT_RECOVERY_SET: TokenSet = types::TYPE_RECOVERY_SET;
|
||||
|
||||
fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
|
||||
let m = p.start();
|
||||
// test qual_paths
|
||||
@ -102,7 +106,12 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
|
||||
m.complete(p, NAME_REF);
|
||||
}
|
||||
_ => {
|
||||
p.err_recover("expected identifier", items::ITEM_RECOVERY_SET);
|
||||
let recover_set = match mode {
|
||||
Mode::Use => items::ITEM_RECOVERY_SET,
|
||||
Mode::Type => TYPE_PATH_SEGMENT_RECOVERY_SET,
|
||||
Mode::Expr => EXPR_PATH_SEGMENT_RECOVERY_SET,
|
||||
};
|
||||
p.err_recover("expected identifier", recover_set);
|
||||
if empty {
|
||||
// test_err empty_segment
|
||||
// use crate::;
|
||||
|
@ -17,8 +17,9 @@
|
||||
T![Self],
|
||||
]));
|
||||
|
||||
const TYPE_RECOVERY_SET: TokenSet = TokenSet::new(&[
|
||||
pub(super) const TYPE_RECOVERY_SET: TokenSet = TokenSet::new(&[
|
||||
T![')'],
|
||||
T![>],
|
||||
T![,],
|
||||
// test_err struct_field_recover
|
||||
// struct S { f pub g: () }
|
||||
|
@ -15,6 +15,7 @@
|
||||
#[test]
|
||||
fn lex_ok() {
|
||||
for case in TestCase::list("lexer/ok") {
|
||||
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
|
||||
let actual = lex(&case.text);
|
||||
expect_file![case.rast].assert_eq(&actual)
|
||||
}
|
||||
@ -23,6 +24,7 @@ fn lex_ok() {
|
||||
#[test]
|
||||
fn lex_err() {
|
||||
for case in TestCase::list("lexer/err") {
|
||||
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
|
||||
let actual = lex(&case.text);
|
||||
expect_file![case.rast].assert_eq(&actual)
|
||||
}
|
||||
@ -46,6 +48,7 @@ fn lex(text: &str) -> String {
|
||||
#[test]
|
||||
fn parse_ok() {
|
||||
for case in TestCase::list("parser/ok") {
|
||||
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
|
||||
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
|
||||
assert!(!errors, "errors in an OK file {}:\n{actual}", case.rs.display());
|
||||
expect_file![case.rast].assert_eq(&actual);
|
||||
@ -55,6 +58,7 @@ fn parse_ok() {
|
||||
#[test]
|
||||
fn parse_inline_ok() {
|
||||
for case in TestCase::list("parser/inline/ok") {
|
||||
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
|
||||
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
|
||||
assert!(!errors, "errors in an OK file {}:\n{actual}", case.rs.display());
|
||||
expect_file![case.rast].assert_eq(&actual);
|
||||
@ -64,6 +68,7 @@ fn parse_inline_ok() {
|
||||
#[test]
|
||||
fn parse_err() {
|
||||
for case in TestCase::list("parser/err") {
|
||||
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
|
||||
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
|
||||
assert!(errors, "no errors in an ERR file {}:\n{actual}", case.rs.display());
|
||||
expect_file![case.rast].assert_eq(&actual)
|
||||
@ -73,6 +78,7 @@ fn parse_err() {
|
||||
#[test]
|
||||
fn parse_inline_err() {
|
||||
for case in TestCase::list("parser/inline/err") {
|
||||
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
|
||||
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
|
||||
assert!(errors, "no errors in an ERR file {}:\n{actual}", case.rs.display());
|
||||
expect_file![case.rast].assert_eq(&actual)
|
||||
|
@ -65,7 +65,7 @@ fn macro_stmt() {
|
||||
MACRO_STMTS
|
||||
ERROR
|
||||
SHEBANG "#!/usr/bin/rust"
|
||||
error 0: expected expression
|
||||
error 0: expected expression, item or let statement
|
||||
"##]],
|
||||
);
|
||||
check(
|
||||
|
@ -145,27 +145,27 @@ SOURCE_FILE
|
||||
error 16: expected expression
|
||||
error 17: expected R_BRACK
|
||||
error 17: expected SEMICOLON
|
||||
error 17: expected expression
|
||||
error 17: expected expression, item or let statement
|
||||
error 25: expected a name
|
||||
error 26: expected `;`, `{`, or `(`
|
||||
error 30: expected pattern
|
||||
error 31: expected SEMICOLON
|
||||
error 53: expected expression
|
||||
error 54: expected SEMICOLON
|
||||
error 54: expected expression
|
||||
error 54: expected expression, item or let statement
|
||||
error 60: expected type
|
||||
error 60: expected `{`
|
||||
error 60: expected expression
|
||||
error 60: expected expression, item or let statement
|
||||
error 65: expected pattern
|
||||
error 65: expected SEMICOLON
|
||||
error 65: expected expression
|
||||
error 65: expected expression, item or let statement
|
||||
error 92: expected expression
|
||||
error 93: expected SEMICOLON
|
||||
error 93: expected expression
|
||||
error 95: expected expression
|
||||
error 96: expected expression
|
||||
error 93: expected expression, item or let statement
|
||||
error 95: expected expression, item or let statement
|
||||
error 96: expected expression, item or let statement
|
||||
error 103: expected a name
|
||||
error 104: expected `{`
|
||||
error 108: expected pattern
|
||||
error 108: expected SEMICOLON
|
||||
error 108: expected expression
|
||||
error 108: expected expression, item or let statement
|
||||
|
@ -180,32 +180,32 @@ SOURCE_FILE
|
||||
ERROR
|
||||
PLUS "+"
|
||||
WHITESPACE " "
|
||||
TUPLE_EXPR
|
||||
L_PAREN "("
|
||||
CLOSURE_EXPR
|
||||
FOR_KW "for"
|
||||
GENERIC_PARAM_LIST
|
||||
L_ANGLE "<"
|
||||
LIFETIME_PARAM
|
||||
LIFETIME
|
||||
LIFETIME_IDENT "'a"
|
||||
R_ANGLE ">"
|
||||
WHITESPACE " "
|
||||
EXPR_STMT
|
||||
BIN_EXPR
|
||||
BIN_EXPR
|
||||
BIN_EXPR
|
||||
TUPLE_EXPR
|
||||
L_PAREN "("
|
||||
CLOSURE_EXPR
|
||||
FOR_KW "for"
|
||||
GENERIC_PARAM_LIST
|
||||
L_ANGLE "<"
|
||||
LIFETIME_PARAM
|
||||
LIFETIME
|
||||
LIFETIME_IDENT "'a"
|
||||
R_ANGLE ">"
|
||||
WHITESPACE " "
|
||||
BIN_EXPR
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "Trait"
|
||||
L_ANGLE "<"
|
||||
ERROR
|
||||
LIFETIME_IDENT "'a"
|
||||
R_ANGLE ">"
|
||||
ERROR
|
||||
R_PAREN ")"
|
||||
BIN_EXPR
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "Trait"
|
||||
L_ANGLE "<"
|
||||
ERROR
|
||||
LIFETIME_IDENT "'a"
|
||||
R_ANGLE ">"
|
||||
R_PAREN ")"
|
||||
WHITESPACE " "
|
||||
PLUS "+"
|
||||
WHITESPACE " "
|
||||
@ -220,108 +220,96 @@ SOURCE_FILE
|
||||
R_ANGLE ">"
|
||||
ERROR
|
||||
SEMICOLON ";"
|
||||
WHITESPACE "\n "
|
||||
LET_EXPR
|
||||
LET_KW "let"
|
||||
WHITESPACE " "
|
||||
WILDCARD_PAT
|
||||
UNDERSCORE "_"
|
||||
ERROR
|
||||
COLON ":"
|
||||
WHITESPACE "\n "
|
||||
LET_STMT
|
||||
LET_KW "let"
|
||||
WHITESPACE " "
|
||||
BIN_EXPR
|
||||
BIN_EXPR
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "Box"
|
||||
L_ANGLE "<"
|
||||
TUPLE_EXPR
|
||||
L_PAREN "("
|
||||
CLOSURE_EXPR
|
||||
FOR_KW "for"
|
||||
GENERIC_PARAM_LIST
|
||||
L_ANGLE "<"
|
||||
LIFETIME_PARAM
|
||||
LIFETIME
|
||||
LIFETIME_IDENT "'a"
|
||||
R_ANGLE ">"
|
||||
WHITESPACE " "
|
||||
BIN_EXPR
|
||||
BIN_EXPR
|
||||
BIN_EXPR
|
||||
BIN_EXPR
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "Trait"
|
||||
WILDCARD_PAT
|
||||
UNDERSCORE "_"
|
||||
COLON ":"
|
||||
WHITESPACE " "
|
||||
DYN_TRAIT_TYPE
|
||||
TYPE_BOUND_LIST
|
||||
TYPE_BOUND
|
||||
PATH_TYPE
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "Box"
|
||||
GENERIC_ARG_LIST
|
||||
L_ANGLE "<"
|
||||
ERROR
|
||||
LIFETIME_IDENT "'a"
|
||||
R_ANGLE ">"
|
||||
ERROR
|
||||
R_PAREN ")"
|
||||
WHITESPACE " "
|
||||
PLUS "+"
|
||||
WHITESPACE " "
|
||||
PAREN_EXPR
|
||||
L_PAREN "("
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "Copy"
|
||||
R_PAREN ")"
|
||||
WHITESPACE " "
|
||||
PLUS "+"
|
||||
WHITESPACE " "
|
||||
PAREN_EXPR
|
||||
L_PAREN "("
|
||||
ERROR
|
||||
QUESTION "?"
|
||||
PATH_EXPR
|
||||
TYPE_ARG
|
||||
PAREN_TYPE
|
||||
L_PAREN "("
|
||||
FOR_TYPE
|
||||
FOR_KW "for"
|
||||
GENERIC_PARAM_LIST
|
||||
L_ANGLE "<"
|
||||
LIFETIME_PARAM
|
||||
LIFETIME
|
||||
LIFETIME_IDENT "'a"
|
||||
R_ANGLE ">"
|
||||
WHITESPACE " "
|
||||
PATH_TYPE
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "Trait"
|
||||
GENERIC_ARG_LIST
|
||||
L_ANGLE "<"
|
||||
LIFETIME_ARG
|
||||
LIFETIME
|
||||
LIFETIME_IDENT "'a"
|
||||
R_ANGLE ">"
|
||||
R_PAREN ")"
|
||||
WHITESPACE " "
|
||||
PLUS "+"
|
||||
WHITESPACE " "
|
||||
TYPE_BOUND
|
||||
L_PAREN "("
|
||||
PATH_TYPE
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "Copy"
|
||||
R_PAREN ")"
|
||||
WHITESPACE " "
|
||||
PLUS "+"
|
||||
WHITESPACE " "
|
||||
TYPE_BOUND
|
||||
L_PAREN "("
|
||||
QUESTION "?"
|
||||
PATH_TYPE
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "Sized"
|
||||
R_PAREN ")"
|
||||
R_ANGLE ">"
|
||||
ERROR
|
||||
SEMICOLON ";"
|
||||
ERROR
|
||||
R_ANGLE ">"
|
||||
SEMICOLON ";"
|
||||
WHITESPACE "\n"
|
||||
R_CURLY "}"
|
||||
WHITESPACE "\n"
|
||||
error 88: expected COMMA
|
||||
error 88: expected R_ANGLE
|
||||
error 121: expected SEMICOLON
|
||||
error 121: expected expression
|
||||
error 121: expected expression, item or let statement
|
||||
error 140: expected type
|
||||
error 141: expected R_PAREN
|
||||
error 141: expected COMMA
|
||||
error 141: expected R_ANGLE
|
||||
error 141: expected SEMICOLON
|
||||
error 146: expected SEMICOLON
|
||||
error 146: expected expression
|
||||
error 148: expected expression
|
||||
error 146: expected expression, item or let statement
|
||||
error 148: expected expression, item or let statement
|
||||
error 158: expected `|`
|
||||
error 158: expected COMMA
|
||||
error 165: expected expression
|
||||
error 168: expected expression
|
||||
error 179: expected expression
|
||||
error 180: expected COMMA
|
||||
error 190: expected EQ
|
||||
error 190: expected expression
|
||||
error 191: expected COMMA
|
||||
error 204: expected `|`
|
||||
error 204: expected COMMA
|
||||
error 211: expected expression
|
||||
error 214: expected expression
|
||||
error 228: expected expression
|
||||
error 229: expected R_PAREN
|
||||
error 229: expected COMMA
|
||||
error 236: expected expression
|
||||
error 237: expected COMMA
|
||||
error 237: expected expression
|
||||
error 237: expected R_PAREN
|
||||
error 180: expected SEMICOLON
|
||||
error 215: expected COMMA
|
||||
error 215: expected R_ANGLE
|
||||
error 235: expected SEMICOLON
|
||||
error 235: expected expression, item or let statement
|
||||
|
@ -0,0 +1,77 @@
|
||||
SOURCE_FILE
|
||||
FN
|
||||
FN_KW "fn"
|
||||
WHITESPACE " "
|
||||
NAME
|
||||
IDENT "main"
|
||||
PARAM_LIST
|
||||
L_PAREN "("
|
||||
R_PAREN ")"
|
||||
WHITESPACE " "
|
||||
BLOCK_EXPR
|
||||
STMT_LIST
|
||||
L_CURLY "{"
|
||||
WHITESPACE "\n "
|
||||
EXPR_STMT
|
||||
CALL_EXPR
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "foo"
|
||||
ARG_LIST
|
||||
L_PAREN "("
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "bar"
|
||||
COLON2 "::"
|
||||
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 "bar"
|
||||
ERROR
|
||||
COLON ":"
|
||||
R_PAREN ")"
|
||||
SEMICOLON ";"
|
||||
WHITESPACE "\n "
|
||||
EXPR_STMT
|
||||
CALL_EXPR
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "foo"
|
||||
ARG_LIST
|
||||
L_PAREN "("
|
||||
BIN_EXPR
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "bar"
|
||||
PLUS "+"
|
||||
R_PAREN ")"
|
||||
SEMICOLON ";"
|
||||
WHITESPACE "\n"
|
||||
R_CURLY "}"
|
||||
WHITESPACE "\n"
|
||||
error 25: expected identifier
|
||||
error 39: expected `,`
|
||||
error 39: expected expression
|
||||
error 55: expected expression
|
@ -0,0 +1,5 @@
|
||||
fn main() {
|
||||
foo(bar::);
|
||||
foo(bar:);
|
||||
foo(bar+);
|
||||
}
|
Loading…
Reference in New Issue
Block a user