Better parser recovery for incomplete attributes

This commit is contained in:
Lukas Wirth 2024-12-04 06:27:43 +01:00
parent b65911d5ee
commit b6fc9c14ac
8 changed files with 130 additions and 19 deletions

View file

@ -36,6 +36,14 @@ fn attr(p: &mut Parser<'_>, inner: bool) {
attr.complete(p, ATTR);
}
// test_err meta_recovery
// #![]
// #![p = ]
// #![p::]
// #![p:: =]
// #![unsafe]
// #![unsafe =]
// test metas
// #![simple_ident]
// #![simple::path]
@ -63,7 +71,7 @@ pub(super) fn meta(p: &mut Parser<'_>) {
if is_unsafe {
p.expect(T!['(']);
}
paths::use_path(p);
paths::attr_path(p);
match p.current() {
T![=] => {

View file

@ -19,6 +19,10 @@ pub(super) fn use_path(p: &mut Parser<'_>) {
path(p, Mode::Use);
}
pub(super) fn attr_path(p: &mut Parser<'_>) {
path(p, Mode::Attr);
}
pub(crate) fn type_path(p: &mut Parser<'_>) {
path(p, Mode::Type);
}
@ -37,6 +41,7 @@ pub(crate) fn type_path_for_qualifier(
#[derive(Clone, Copy, Eq, PartialEq)]
enum Mode {
Use,
Attr,
Type,
Expr,
}
@ -93,12 +98,7 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
p.error("expected `::`");
}
} else {
let empty = if first {
p.eat(T![::]);
false
} else {
true
};
let mut empty = if first { !p.eat(T![::]) } else { true };
match p.current() {
IDENT => {
name_ref(p);
@ -114,10 +114,13 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
_ => {
let recover_set = match mode {
Mode::Use => items::ITEM_RECOVERY_SET,
Mode::Attr => {
items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![']'], T![=], T![#]]))
}
Mode::Type => TYPE_PATH_SEGMENT_RECOVERY_SET,
Mode::Expr => EXPR_PATH_SEGMENT_RECOVERY_SET,
};
p.err_recover("expected identifier", recover_set);
empty &= p.err_recover("expected identifier", recover_set);
if empty {
// test_err empty_segment
// use crate::;
@ -132,7 +135,7 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
fn opt_path_type_args(p: &mut Parser<'_>, mode: Mode) {
match mode {
Mode::Use => {}
Mode::Use | Mode::Attr => {}
Mode::Type => {
// test typepathfn_with_coloncolon
// type F = Start::(Middle) -> (Middle)::End;

View file

@ -258,22 +258,25 @@ impl<'t> Parser<'t> {
self.err_recover(message, TokenSet::EMPTY);
}
/// Create an error node and consume the next token.
pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) {
/// Create an error node and consume the next token unless it is in the recovery set.
///
/// Returns true if recovery kicked in.
pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) -> bool {
if matches!(self.current(), T!['{'] | T!['}']) {
self.error(message);
return;
return true;
}
if self.at_ts(recovery) {
self.error(message);
return;
return true;
}
let m = self.start();
self.error(message);
self.bump_any();
m.complete(self, ERROR);
false
}
fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {