Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 05aac8c

Browse files
committed
Better parser recovery for incomplete attributes
1 parent b747197 commit 05aac8c

File tree

8 files changed

+130
-19
lines changed

8 files changed

+130
-19
lines changed

src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ fn attr(p: &mut Parser<'_>, inner: bool) {
3636
attr.complete(p, ATTR);
3737
}
3838

39+
// test_err meta_recovery
40+
// #![]
41+
// #![p = ]
42+
// #![p::]
43+
// #![p:: =]
44+
// #![unsafe]
45+
// #![unsafe =]
46+
3947
// test metas
4048
// #![simple_ident]
4149
// #![simple::path]
@@ -63,7 +71,7 @@ pub(super) fn meta(p: &mut Parser<'_>) {
6371
if is_unsafe {
6472
p.expect(T!['(']);
6573
}
66-
paths::use_path(p);
74+
paths::attr_path(p);
6775

6876
match p.current() {
6977
T![=] => {

src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ pub(super) fn use_path(p: &mut Parser<'_>) {
1919
path(p, Mode::Use);
2020
}
2121

22+
pub(super) fn attr_path(p: &mut Parser<'_>) {
23+
path(p, Mode::Attr);
24+
}
25+
2226
pub(crate) fn type_path(p: &mut Parser<'_>) {
2327
path(p, Mode::Type);
2428
}
@@ -37,6 +41,7 @@ pub(crate) fn type_path_for_qualifier(
3741
#[derive(Clone, Copy, Eq, PartialEq)]
3842
enum Mode {
3943
Use,
44+
Attr,
4045
Type,
4146
Expr,
4247
}
@@ -93,12 +98,7 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
9398
p.error("expected `::`");
9499
}
95100
} else {
96-
let empty = if first {
97-
p.eat(T![::]);
98-
false
99-
} else {
100-
true
101-
};
101+
let mut empty = if first { !p.eat(T![::]) } else { true };
102102
match p.current() {
103103
IDENT => {
104104
name_ref(p);
@@ -114,10 +114,13 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
114114
_ => {
115115
let recover_set = match mode {
116116
Mode::Use => items::ITEM_RECOVERY_SET,
117+
Mode::Attr => {
118+
items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![']'], T![=], T![#]]))
119+
}
117120
Mode::Type => TYPE_PATH_SEGMENT_RECOVERY_SET,
118121
Mode::Expr => EXPR_PATH_SEGMENT_RECOVERY_SET,
119122
};
120-
p.err_recover("expected identifier", recover_set);
123+
empty &= p.err_recover("expected identifier", recover_set);
121124
if empty {
122125
// test_err empty_segment
123126
// use crate::;
@@ -132,7 +135,7 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
132135

133136
fn opt_path_type_args(p: &mut Parser<'_>, mode: Mode) {
134137
match mode {
135-
Mode::Use => {}
138+
Mode::Use | Mode::Attr => {}
136139
Mode::Type => {
137140
// test typepathfn_with_coloncolon
138141
// type F = Start::(Middle) -> (Middle)::End;

src/tools/rust-analyzer/crates/parser/src/parser.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,22 +258,25 @@ impl<'t> Parser<'t> {
258258
self.err_recover(message, TokenSet::EMPTY);
259259
}
260260

261-
/// Create an error node and consume the next token.
262-
pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) {
261+
/// Create an error node and consume the next token unless it is in the recovery set.
262+
///
263+
/// Returns true if recovery kicked in.
264+
pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) -> bool {
263265
if matches!(self.current(), T!['{'] | T!['}']) {
264266
self.error(message);
265-
return;
267+
return true;
266268
}
267269

268270
if self.at_ts(recovery) {
269271
self.error(message);
270-
return;
272+
return true;
271273
}
272274

273275
let m = self.start();
274276
self.error(message);
275277
self.bump_any();
276278
m.complete(self, ERROR);
279+
false
277280
}
278281

279282
fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {

src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,8 @@ mod err {
772772
run_and_expect_errors("test_data/parser/inline/err/match_arms_recovery.rs");
773773
}
774774
#[test]
775+
fn meta_recovery() { run_and_expect_errors("test_data/parser/inline/err/meta_recovery.rs"); }
776+
#[test]
775777
fn method_call_missing_argument_list() {
776778
run_and_expect_errors("test_data/parser/inline/err/method_call_missing_argument_list.rs");
777779
}

src/tools/rust-analyzer/crates/parser/test_data/parser/err/0004_use_path_bad_segment.rast

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ SOURCE_FILE
99
NAME_REF
1010
IDENT "foo"
1111
COLON2 "::"
12-
ERROR
13-
INT_NUMBER "92"
12+
PATH_SEGMENT
13+
ERROR
14+
INT_NUMBER "92"
1415
SEMICOLON ";"
1516
error 9: expected identifier

src/tools/rust-analyzer/crates/parser/test_data/parser/err/0048_double_fish.rast

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ SOURCE_FILE
3939
IDENT "lol"
4040
R_ANGLE ">"
4141
COLON2 "::"
42-
ERROR
43-
L_ANGLE "<"
42+
PATH_SEGMENT
43+
ERROR
44+
L_ANGLE "<"
4445
TYPE_ARG
4546
PATH_TYPE
4647
PATH
@@ -91,8 +92,9 @@ SOURCE_FILE
9192
IDENT "lol"
9293
R_ANGLE ">"
9394
COLON2 "::"
94-
ERROR
95-
L_ANGLE "<"
95+
PATH_SEGMENT
96+
ERROR
97+
L_ANGLE "<"
9698
EXPR_STMT
9799
BIN_EXPR
98100
PATH_EXPR
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
SOURCE_FILE
2+
ATTR
3+
POUND "#"
4+
BANG "!"
5+
L_BRACK "["
6+
META
7+
PATH
8+
R_BRACK "]"
9+
WHITESPACE "\n"
10+
ATTR
11+
POUND "#"
12+
BANG "!"
13+
L_BRACK "["
14+
META
15+
PATH
16+
PATH_SEGMENT
17+
NAME_REF
18+
IDENT "p"
19+
WHITESPACE " "
20+
EQ "="
21+
WHITESPACE " "
22+
R_BRACK "]"
23+
WHITESPACE "\n"
24+
ATTR
25+
POUND "#"
26+
BANG "!"
27+
L_BRACK "["
28+
META
29+
PATH
30+
PATH
31+
PATH_SEGMENT
32+
NAME_REF
33+
IDENT "p"
34+
COLON2 "::"
35+
R_BRACK "]"
36+
WHITESPACE "\n"
37+
ATTR
38+
POUND "#"
39+
BANG "!"
40+
L_BRACK "["
41+
META
42+
PATH
43+
PATH
44+
PATH_SEGMENT
45+
NAME_REF
46+
IDENT "p"
47+
COLON2 "::"
48+
WHITESPACE " "
49+
EQ "="
50+
R_BRACK "]"
51+
WHITESPACE "\n"
52+
ATTR
53+
POUND "#"
54+
BANG "!"
55+
L_BRACK "["
56+
META
57+
UNSAFE_KW "unsafe"
58+
PATH
59+
R_BRACK "]"
60+
WHITESPACE "\n"
61+
ATTR
62+
POUND "#"
63+
BANG "!"
64+
L_BRACK "["
65+
META
66+
UNSAFE_KW "unsafe"
67+
WHITESPACE " "
68+
PATH
69+
EQ "="
70+
R_BRACK "]"
71+
WHITESPACE "\n"
72+
error 3: expected identifier
73+
error 11: expected expression
74+
error 11: expected expression
75+
error 20: expected identifier
76+
error 28: expected identifier
77+
error 30: expected expression
78+
error 30: expected expression
79+
error 41: expected L_PAREN
80+
error 41: expected identifier
81+
error 41: expected R_PAREN
82+
error 52: expected L_PAREN
83+
error 53: expected identifier
84+
error 54: expected expression
85+
error 54: expected expression
86+
error 54: expected R_PAREN
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#![]
2+
#![p = ]
3+
#![p::]
4+
#![p:: =]
5+
#![unsafe]
6+
#![unsafe =]

0 commit comments

Comments
 (0)