mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 07:04:49 +00:00
Merge pull request #18608 from Veykril/push-rrozmpnyknkx
Better parser recovery for paths
This commit is contained in:
commit
ab652f7795
12 changed files with 154 additions and 40 deletions
|
@ -1759,8 +1759,9 @@ fn f() {
|
||||||
// NAME_REF@6..7
|
// NAME_REF@6..7
|
||||||
// IDENT@6..7 "K"
|
// IDENT@6..7 "K"
|
||||||
// COLON2@7..9 "::"
|
// COLON2@7..9 "::"
|
||||||
// ERROR@9..10
|
// PATH_SEGMENT@9..10
|
||||||
// L_PAREN@9..10 "("
|
// ERROR@9..10
|
||||||
|
// L_PAREN@9..10 "("
|
||||||
// EXPR_STMT@10..16
|
// EXPR_STMT@10..16
|
||||||
// CALL_EXPR@10..16
|
// CALL_EXPR@10..16
|
||||||
// PATH_EXPR@10..11
|
// PATH_EXPR@10..11
|
||||||
|
|
|
@ -242,7 +242,7 @@ fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
|
||||||
// struct MyStruct(pub ());
|
// struct MyStruct(pub ());
|
||||||
if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) {
|
if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) {
|
||||||
p.bump(T!['(']);
|
p.bump(T!['(']);
|
||||||
paths::use_path(p);
|
paths::vis_path(p);
|
||||||
p.expect(T![')']);
|
p.expect(T![')']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,7 +252,7 @@ fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
|
||||||
T![in] => {
|
T![in] => {
|
||||||
p.bump(T!['(']);
|
p.bump(T!['(']);
|
||||||
p.bump(T![in]);
|
p.bump(T![in]);
|
||||||
paths::use_path(p);
|
paths::vis_path(p);
|
||||||
p.expect(T![')']);
|
p.expect(T![')']);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
@ -36,6 +36,14 @@ fn attr(p: &mut Parser<'_>, inner: bool) {
|
||||||
attr.complete(p, ATTR);
|
attr.complete(p, ATTR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test_err meta_recovery
|
||||||
|
// #![]
|
||||||
|
// #![p = ]
|
||||||
|
// #![p::]
|
||||||
|
// #![p:: =]
|
||||||
|
// #![unsafe]
|
||||||
|
// #![unsafe =]
|
||||||
|
|
||||||
// test metas
|
// test metas
|
||||||
// #![simple_ident]
|
// #![simple_ident]
|
||||||
// #![simple::path]
|
// #![simple::path]
|
||||||
|
@ -63,7 +71,7 @@ pub(super) fn meta(p: &mut Parser<'_>) {
|
||||||
if is_unsafe {
|
if is_unsafe {
|
||||||
p.expect(T!['(']);
|
p.expect(T!['(']);
|
||||||
}
|
}
|
||||||
paths::use_path(p);
|
paths::attr_path(p);
|
||||||
|
|
||||||
match p.current() {
|
match p.current() {
|
||||||
T![=] => {
|
T![=] => {
|
||||||
|
|
|
@ -168,10 +168,10 @@ pub(super) fn const_arg_expr(p: &mut Parser<'_>) {
|
||||||
expressions::literal(p);
|
expressions::literal(p);
|
||||||
lm.complete(p, PREFIX_EXPR);
|
lm.complete(p, PREFIX_EXPR);
|
||||||
}
|
}
|
||||||
_ if paths::is_use_path_start(p) => {
|
_ if paths::is_path_start(p) => {
|
||||||
// This shouldn't be hit by `const_arg`
|
// This shouldn't be hit by `const_arg`
|
||||||
let lm = p.start();
|
let lm = p.start();
|
||||||
paths::use_path(p);
|
paths::expr_path(p);
|
||||||
lm.complete(p, PATH_EXPR);
|
lm.complete(p, PATH_EXPR);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -19,6 +19,14 @@ pub(super) fn use_path(p: &mut Parser<'_>) {
|
||||||
path(p, Mode::Use);
|
path(p, Mode::Use);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn vis_path(p: &mut Parser<'_>) {
|
||||||
|
path(p, Mode::Vis);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn attr_path(p: &mut Parser<'_>) {
|
||||||
|
path(p, Mode::Attr);
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn type_path(p: &mut Parser<'_>) {
|
pub(crate) fn type_path(p: &mut Parser<'_>) {
|
||||||
path(p, Mode::Type);
|
path(p, Mode::Type);
|
||||||
}
|
}
|
||||||
|
@ -37,15 +45,20 @@ pub(crate) fn type_path_for_qualifier(
|
||||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
enum Mode {
|
enum Mode {
|
||||||
Use,
|
Use,
|
||||||
|
Attr,
|
||||||
Type,
|
Type,
|
||||||
Expr,
|
Expr,
|
||||||
|
Vis,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path(p: &mut Parser<'_>, mode: Mode) {
|
fn path(p: &mut Parser<'_>, mode: Mode) -> Option<CompletedMarker> {
|
||||||
let path = p.start();
|
let path = p.start();
|
||||||
path_segment(p, mode, true);
|
if path_segment(p, mode, true).is_none() {
|
||||||
|
path.abandon(p);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
let qual = path.complete(p, PATH);
|
let qual = path.complete(p, PATH);
|
||||||
path_for_qualifier(p, mode, qual);
|
Some(path_for_qualifier(p, mode, qual))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_for_qualifier(
|
fn path_for_qualifier(
|
||||||
|
@ -71,7 +84,7 @@ const EXPR_PATH_SEGMENT_RECOVERY_SET: TokenSet =
|
||||||
items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![')'], T![,], T![let]]));
|
items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![')'], T![,], T![let]]));
|
||||||
const TYPE_PATH_SEGMENT_RECOVERY_SET: TokenSet = types::TYPE_RECOVERY_SET;
|
const TYPE_PATH_SEGMENT_RECOVERY_SET: TokenSet = types::TYPE_RECOVERY_SET;
|
||||||
|
|
||||||
fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
|
fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) -> Option<CompletedMarker> {
|
||||||
let m = p.start();
|
let m = p.start();
|
||||||
// test qual_paths
|
// test qual_paths
|
||||||
// type X = <A as B>::Output;
|
// type X = <A as B>::Output;
|
||||||
|
@ -93,12 +106,7 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
|
||||||
p.error("expected `::`");
|
p.error("expected `::`");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let empty = if first {
|
let mut empty = if first { !p.eat(T![::]) } else { true };
|
||||||
p.eat(T![::]);
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
|
||||||
match p.current() {
|
match p.current() {
|
||||||
IDENT => {
|
IDENT => {
|
||||||
name_ref(p);
|
name_ref(p);
|
||||||
|
@ -114,25 +122,29 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
|
||||||
_ => {
|
_ => {
|
||||||
let recover_set = match mode {
|
let recover_set = match mode {
|
||||||
Mode::Use => items::ITEM_RECOVERY_SET,
|
Mode::Use => items::ITEM_RECOVERY_SET,
|
||||||
|
Mode::Attr => {
|
||||||
|
items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![']'], T![=], T![#]]))
|
||||||
|
}
|
||||||
|
Mode::Vis => items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![')']])),
|
||||||
Mode::Type => TYPE_PATH_SEGMENT_RECOVERY_SET,
|
Mode::Type => TYPE_PATH_SEGMENT_RECOVERY_SET,
|
||||||
Mode::Expr => EXPR_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 {
|
if empty {
|
||||||
// test_err empty_segment
|
// test_err empty_segment
|
||||||
// use crate::;
|
// use crate::;
|
||||||
m.abandon(p);
|
m.abandon(p);
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
m.complete(p, PATH_SEGMENT);
|
Some(m.complete(p, PATH_SEGMENT))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opt_path_type_args(p: &mut Parser<'_>, mode: Mode) {
|
fn opt_path_type_args(p: &mut Parser<'_>, mode: Mode) {
|
||||||
match mode {
|
match mode {
|
||||||
Mode::Use => {}
|
Mode::Use | Mode::Attr | Mode::Vis => {}
|
||||||
Mode::Type => {
|
Mode::Type => {
|
||||||
// test typepathfn_with_coloncolon
|
// test typepathfn_with_coloncolon
|
||||||
// type F = Start::(Middle) -> (Middle)::End;
|
// type F = Start::(Middle) -> (Middle)::End;
|
||||||
|
|
|
@ -258,22 +258,25 @@ impl<'t> Parser<'t> {
|
||||||
self.err_recover(message, TokenSet::EMPTY);
|
self.err_recover(message, TokenSet::EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an error node and consume the next token.
|
/// Create an error node and consume the next token unless it is in the recovery set.
|
||||||
pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) {
|
///
|
||||||
|
/// Returns true if recovery kicked in.
|
||||||
|
pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) -> bool {
|
||||||
if matches!(self.current(), T!['{'] | T!['}']) {
|
if matches!(self.current(), T!['{'] | T!['}']) {
|
||||||
self.error(message);
|
self.error(message);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.at_ts(recovery) {
|
if self.at_ts(recovery) {
|
||||||
self.error(message);
|
self.error(message);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let m = self.start();
|
let m = self.start();
|
||||||
self.error(message);
|
self.error(message);
|
||||||
self.bump_any();
|
self.bump_any();
|
||||||
m.complete(self, ERROR);
|
m.complete(self, ERROR);
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
|
fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
|
||||||
|
@ -324,10 +327,10 @@ impl Marker {
|
||||||
self.bomb.defuse();
|
self.bomb.defuse();
|
||||||
let idx = self.pos as usize;
|
let idx = self.pos as usize;
|
||||||
if idx == p.events.len() - 1 {
|
if idx == p.events.len() - 1 {
|
||||||
match p.events.pop() {
|
assert!(matches!(
|
||||||
Some(Event::Start { kind: TOMBSTONE, forward_parent: None }) => (),
|
p.events.pop(),
|
||||||
_ => unreachable!(),
|
Some(Event::Start { kind: TOMBSTONE, forward_parent: None })
|
||||||
}
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -772,6 +772,8 @@ mod err {
|
||||||
run_and_expect_errors("test_data/parser/inline/err/match_arms_recovery.rs");
|
run_and_expect_errors("test_data/parser/inline/err/match_arms_recovery.rs");
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
|
fn meta_recovery() { run_and_expect_errors("test_data/parser/inline/err/meta_recovery.rs"); }
|
||||||
|
#[test]
|
||||||
fn method_call_missing_argument_list() {
|
fn method_call_missing_argument_list() {
|
||||||
run_and_expect_errors("test_data/parser/inline/err/method_call_missing_argument_list.rs");
|
run_and_expect_errors("test_data/parser/inline/err/method_call_missing_argument_list.rs");
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@ SOURCE_FILE
|
||||||
NAME_REF
|
NAME_REF
|
||||||
IDENT "foo"
|
IDENT "foo"
|
||||||
COLON2 "::"
|
COLON2 "::"
|
||||||
ERROR
|
PATH_SEGMENT
|
||||||
INT_NUMBER "92"
|
ERROR
|
||||||
|
INT_NUMBER "92"
|
||||||
SEMICOLON ";"
|
SEMICOLON ";"
|
||||||
error 9: expected identifier
|
error 9: expected identifier
|
||||||
|
|
|
@ -39,8 +39,9 @@ SOURCE_FILE
|
||||||
IDENT "lol"
|
IDENT "lol"
|
||||||
R_ANGLE ">"
|
R_ANGLE ">"
|
||||||
COLON2 "::"
|
COLON2 "::"
|
||||||
ERROR
|
PATH_SEGMENT
|
||||||
L_ANGLE "<"
|
ERROR
|
||||||
|
L_ANGLE "<"
|
||||||
TYPE_ARG
|
TYPE_ARG
|
||||||
PATH_TYPE
|
PATH_TYPE
|
||||||
PATH
|
PATH
|
||||||
|
@ -91,8 +92,9 @@ SOURCE_FILE
|
||||||
IDENT "lol"
|
IDENT "lol"
|
||||||
R_ANGLE ">"
|
R_ANGLE ">"
|
||||||
COLON2 "::"
|
COLON2 "::"
|
||||||
ERROR
|
PATH_SEGMENT
|
||||||
L_ANGLE "<"
|
ERROR
|
||||||
|
L_ANGLE "<"
|
||||||
EXPR_STMT
|
EXPR_STMT
|
||||||
BIN_EXPR
|
BIN_EXPR
|
||||||
PATH_EXPR
|
PATH_EXPR
|
||||||
|
|
|
@ -3,10 +3,7 @@ SOURCE_FILE
|
||||||
VISIBILITY
|
VISIBILITY
|
||||||
PUB_KW "pub"
|
PUB_KW "pub"
|
||||||
L_PAREN "("
|
L_PAREN "("
|
||||||
PATH
|
R_PAREN ")"
|
||||||
PATH_SEGMENT
|
|
||||||
ERROR
|
|
||||||
R_PAREN ")"
|
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
STRUCT_KW "struct"
|
STRUCT_KW "struct"
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
|
@ -15,4 +12,3 @@ SOURCE_FILE
|
||||||
SEMICOLON ";"
|
SEMICOLON ";"
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
error 4: expected identifier
|
error 4: expected identifier
|
||||||
error 5: expected R_PAREN
|
|
||||||
|
|
83
crates/parser/test_data/parser/inline/err/meta_recovery.rast
Normal file
83
crates/parser/test_data/parser/inline/err/meta_recovery.rast
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
SOURCE_FILE
|
||||||
|
ATTR
|
||||||
|
POUND "#"
|
||||||
|
BANG "!"
|
||||||
|
L_BRACK "["
|
||||||
|
META
|
||||||
|
R_BRACK "]"
|
||||||
|
WHITESPACE "\n"
|
||||||
|
ATTR
|
||||||
|
POUND "#"
|
||||||
|
BANG "!"
|
||||||
|
L_BRACK "["
|
||||||
|
META
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "p"
|
||||||
|
WHITESPACE " "
|
||||||
|
EQ "="
|
||||||
|
WHITESPACE " "
|
||||||
|
R_BRACK "]"
|
||||||
|
WHITESPACE "\n"
|
||||||
|
ATTR
|
||||||
|
POUND "#"
|
||||||
|
BANG "!"
|
||||||
|
L_BRACK "["
|
||||||
|
META
|
||||||
|
PATH
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "p"
|
||||||
|
COLON2 "::"
|
||||||
|
R_BRACK "]"
|
||||||
|
WHITESPACE "\n"
|
||||||
|
ATTR
|
||||||
|
POUND "#"
|
||||||
|
BANG "!"
|
||||||
|
L_BRACK "["
|
||||||
|
META
|
||||||
|
PATH
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "p"
|
||||||
|
COLON2 "::"
|
||||||
|
WHITESPACE " "
|
||||||
|
EQ "="
|
||||||
|
R_BRACK "]"
|
||||||
|
WHITESPACE "\n"
|
||||||
|
ATTR
|
||||||
|
POUND "#"
|
||||||
|
BANG "!"
|
||||||
|
L_BRACK "["
|
||||||
|
META
|
||||||
|
UNSAFE_KW "unsafe"
|
||||||
|
R_BRACK "]"
|
||||||
|
WHITESPACE "\n"
|
||||||
|
ATTR
|
||||||
|
POUND "#"
|
||||||
|
BANG "!"
|
||||||
|
L_BRACK "["
|
||||||
|
META
|
||||||
|
UNSAFE_KW "unsafe"
|
||||||
|
WHITESPACE " "
|
||||||
|
EQ "="
|
||||||
|
R_BRACK "]"
|
||||||
|
WHITESPACE "\n"
|
||||||
|
error 3: expected identifier
|
||||||
|
error 11: expected expression
|
||||||
|
error 11: expected expression
|
||||||
|
error 20: expected identifier
|
||||||
|
error 28: expected identifier
|
||||||
|
error 30: expected expression
|
||||||
|
error 30: expected expression
|
||||||
|
error 41: expected L_PAREN
|
||||||
|
error 41: expected identifier
|
||||||
|
error 41: expected R_PAREN
|
||||||
|
error 52: expected L_PAREN
|
||||||
|
error 52: expected identifier
|
||||||
|
error 54: expected expression
|
||||||
|
error 54: expected expression
|
||||||
|
error 54: expected R_PAREN
|
|
@ -0,0 +1,6 @@
|
||||||
|
#![]
|
||||||
|
#![p = ]
|
||||||
|
#![p::]
|
||||||
|
#![p:: =]
|
||||||
|
#![unsafe]
|
||||||
|
#![unsafe =]
|
Loading…
Add table
Add a link
Reference in a new issue