internal: Improve parser recovery a bunch

This commit is contained in:
Lukas Wirth 2023-02-11 18:06:07 +01:00
parent 5fdf640fb3
commit 1be24e0899
14 changed files with 249 additions and 134 deletions

1
Cargo.lock generated
View File

@ -1173,6 +1173,7 @@ dependencies = [
"limit",
"rustc-ap-rustc_lexer",
"sourcegen",
"stdx",
]
[[package]]

View File

@ -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"));
}

View File

@ -831,7 +831,7 @@ macro_rules! rgb_color {
/* 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);
}

View File

@ -20,4 +20,5 @@ limit.workspace = true
[dev-dependencies]
expect-test = "1.4.0"
stdx.workspace = true
sourcegen.workspace = true

View File

@ -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![')']);

View File

@ -40,26 +40,28 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet =
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;
}
};

View File

@ -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::;

View File

@ -17,8 +17,9 @@ pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[
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: () }

View File

@ -15,6 +15,7 @@ use crate::{LexedStr, TopEntryPoint};
#[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)

View File

@ -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(

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
fn main() {
foo(bar::);
foo(bar:);
foo(bar+);
}