Skip to content

Commit 2f3e352

Browse files
authored
Merge pull request rust-lang#18608 from Veykril/push-rrozmpnyknkx
Better parser recovery for paths
2 parents d83f0fe + 1e47f7d commit 2f3e352

File tree

12 files changed

+154
-40
lines changed

12 files changed

+154
-40
lines changed

src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1759,8 +1759,9 @@ fn f() {
17591759
17601760
17611761
1762-
1763-
1762+
1763+
1764+
17641765
17651766
17661767

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
242242
// struct MyStruct(pub ());
243243
if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) {
244244
p.bump(T!['(']);
245-
paths::use_path(p);
245+
paths::vis_path(p);
246246
p.expect(T![')']);
247247
}
248248
}
@@ -252,7 +252,7 @@ fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
252252
T![in] => {
253253
p.bump(T!['(']);
254254
p.bump(T![in]);
255-
paths::use_path(p);
255+
paths::vis_path(p);
256256
p.expect(T![')']);
257257
}
258258
_ => {}

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/generic_args.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,10 @@ pub(super) fn const_arg_expr(p: &mut Parser<'_>) {
168168
expressions::literal(p);
169169
lm.complete(p, PREFIX_EXPR);
170170
}
171-
_ if paths::is_use_path_start(p) => {
171+
_ if paths::is_path_start(p) => {
172172
// This shouldn't be hit by `const_arg`
173173
let lm = p.start();
174-
paths::use_path(p);
174+
paths::expr_path(p);
175175
lm.complete(p, PATH_EXPR);
176176
}
177177
_ => {

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

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

22+
pub(super) fn vis_path(p: &mut Parser<'_>) {
23+
path(p, Mode::Vis);
24+
}
25+
26+
pub(super) fn attr_path(p: &mut Parser<'_>) {
27+
path(p, Mode::Attr);
28+
}
29+
2230
pub(crate) fn type_path(p: &mut Parser<'_>) {
2331
path(p, Mode::Type);
2432
}
@@ -37,15 +45,20 @@ pub(crate) fn type_path_for_qualifier(
3745
#[derive(Clone, Copy, Eq, PartialEq)]
3846
enum Mode {
3947
Use,
48+
Attr,
4049
Type,
4150
Expr,
51+
Vis,
4252
}
4353

44-
fn path(p: &mut Parser<'_>, mode: Mode) {
54+
fn path(p: &mut Parser<'_>, mode: Mode) -> Option<CompletedMarker> {
4555
let path = p.start();
46-
path_segment(p, mode, true);
56+
if path_segment(p, mode, true).is_none() {
57+
path.abandon(p);
58+
return None;
59+
}
4760
let qual = path.complete(p, PATH);
48-
path_for_qualifier(p, mode, qual);
61+
Some(path_for_qualifier(p, mode, qual))
4962
}
5063

5164
fn path_for_qualifier(
@@ -71,7 +84,7 @@ const EXPR_PATH_SEGMENT_RECOVERY_SET: TokenSet =
7184
items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![')'], T![,], T![let]]));
7285
const TYPE_PATH_SEGMENT_RECOVERY_SET: TokenSet = types::TYPE_RECOVERY_SET;
7386

74-
fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
87+
fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) -> Option<CompletedMarker> {
7588
let m = p.start();
7689
// test qual_paths
7790
// type X = <A as B>::Output;
@@ -93,12 +106,7 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
93106
p.error("expected `::`");
94107
}
95108
} else {
96-
let empty = if first {
97-
p.eat(T![::]);
98-
false
99-
} else {
100-
true
101-
};
109+
let mut empty = if first { !p.eat(T![::]) } else { true };
102110
match p.current() {
103111
IDENT => {
104112
name_ref(p);
@@ -114,25 +122,29 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
114122
_ => {
115123
let recover_set = match mode {
116124
Mode::Use => items::ITEM_RECOVERY_SET,
125+
Mode::Attr => {
126+
items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![']'], T![=], T![#]]))
127+
}
128+
Mode::Vis => items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![')']])),
117129
Mode::Type => TYPE_PATH_SEGMENT_RECOVERY_SET,
118130
Mode::Expr => EXPR_PATH_SEGMENT_RECOVERY_SET,
119131
};
120-
p.err_recover("expected identifier", recover_set);
132+
empty &= p.err_recover("expected identifier", recover_set);
121133
if empty {
122134
// test_err empty_segment
123135
// use crate::;
124136
m.abandon(p);
125-
return;
137+
return None;
126138
}
127139
}
128140
};
129141
}
130-
m.complete(p, PATH_SEGMENT);
142+
Some(m.complete(p, PATH_SEGMENT))
131143
}
132144

133145
fn opt_path_type_args(p: &mut Parser<'_>, mode: Mode) {
134146
match mode {
135-
Mode::Use => {}
147+
Mode::Use | Mode::Attr | Mode::Vis => {}
136148
Mode::Type => {
137149
// test typepathfn_with_coloncolon
138150
// type F = Start::(Middle) -> (Middle)::End;

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

Lines changed: 11 additions & 8 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) {
@@ -324,10 +327,10 @@ impl Marker {
324327
self.bomb.defuse();
325328
let idx = self.pos as usize;
326329
if idx == p.events.len() - 1 {
327-
match p.events.pop() {
328-
Some(Event::Start { kind: TOMBSTONE, forward_parent: None }) => (),
329-
_ => unreachable!(),
330-
}
330+
assert!(matches!(
331+
p.events.pop(),
332+
Some(Event::Start { kind: TOMBSTONE, forward_parent: None })
333+
));
331334
}
332335
}
333336
}

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

src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ SOURCE_FILE
33
VISIBILITY
44
PUB_KW "pub"
55
L_PAREN "("
6-
PATH
7-
PATH_SEGMENT
8-
ERROR
9-
R_PAREN ")"
6+
R_PAREN ")"
107
WHITESPACE " "
118
STRUCT_KW "struct"
129
WHITESPACE " "
@@ -15,4 +12,3 @@ SOURCE_FILE
1512
SEMICOLON ";"
1613
WHITESPACE "\n"
1714
error 4: expected identifier
18-
error 5: expected R_PAREN
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
SOURCE_FILE
2+
ATTR
3+
POUND "#"
4+
BANG "!"
5+
L_BRACK "["
6+
META
7+
R_BRACK "]"
8+
WHITESPACE "\n"
9+
ATTR
10+
POUND "#"
11+
BANG "!"
12+
L_BRACK "["
13+
META
14+
PATH
15+
PATH_SEGMENT
16+
NAME_REF
17+
IDENT "p"
18+
WHITESPACE " "
19+
EQ "="
20+
WHITESPACE " "
21+
R_BRACK "]"
22+
WHITESPACE "\n"
23+
ATTR
24+
POUND "#"
25+
BANG "!"
26+
L_BRACK "["
27+
META
28+
PATH
29+
PATH
30+
PATH_SEGMENT
31+
NAME_REF
32+
IDENT "p"
33+
COLON2 "::"
34+
R_BRACK "]"
35+
WHITESPACE "\n"
36+
ATTR
37+
POUND "#"
38+
BANG "!"
39+
L_BRACK "["
40+
META
41+
PATH
42+
PATH
43+
PATH_SEGMENT
44+
NAME_REF
45+
IDENT "p"
46+
COLON2 "::"
47+
WHITESPACE " "
48+
EQ "="
49+
R_BRACK "]"
50+
WHITESPACE "\n"
51+
ATTR
52+
POUND "#"
53+
BANG "!"
54+
L_BRACK "["
55+
META
56+
UNSAFE_KW "unsafe"
57+
R_BRACK "]"
58+
WHITESPACE "\n"
59+
ATTR
60+
POUND "#"
61+
BANG "!"
62+
L_BRACK "["
63+
META
64+
UNSAFE_KW "unsafe"
65+
WHITESPACE " "
66+
EQ "="
67+
R_BRACK "]"
68+
WHITESPACE "\n"
69+
error 3: expected identifier
70+
error 11: expected expression
71+
error 11: expected expression
72+
error 20: expected identifier
73+
error 28: expected identifier
74+
error 30: expected expression
75+
error 30: expected expression
76+
error 41: expected L_PAREN
77+
error 41: expected identifier
78+
error 41: expected R_PAREN
79+
error 52: expected L_PAREN
80+
error 52: expected identifier
81+
error 54: expected expression
82+
error 54: expected expression
83+
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)