Skip to content

Commit fedafa2

Browse files
committed
parse: backtrack to avoid stealing e.g. x.f() at mod level.
1 parent ccc47fd commit fedafa2

20 files changed

+191
-85
lines changed

src/librustc_parse/parser/item.rs

+41-10
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ impl<'a> Parser<'a> {
3030

3131
fn parse_item_(&mut self, req_name: ReqName) -> PResult<'a, Option<Item>> {
3232
let attrs = self.parse_outer_attributes()?;
33-
self.parse_item_common(attrs, true, false, req_name)
33+
self.parse_item_common(attrs, true, false, req_name, true)
3434
}
3535

3636
pub(super) fn parse_item_common(
@@ -39,6 +39,7 @@ impl<'a> Parser<'a> {
3939
mac_allowed: bool,
4040
attrs_allowed: bool,
4141
req_name: ReqName,
42+
mod_stmt: bool,
4243
) -> PResult<'a, Option<Item>> {
4344
maybe_whole!(self, NtItem, |item| {
4445
let mut item = item;
@@ -49,9 +50,9 @@ impl<'a> Parser<'a> {
4950

5051
let mut unclosed_delims = vec![];
5152
let (mut item, tokens) = self.collect_tokens(|this| {
52-
let item = this.parse_item_common_(attrs, mac_allowed, attrs_allowed, req_name);
53+
let i = this.parse_item_common_(attrs, mac_allowed, attrs_allowed, req_name, mod_stmt);
5354
unclosed_delims.append(&mut this.unclosed_delims);
54-
item
55+
i
5556
})?;
5657
self.unclosed_delims.append(&mut unclosed_delims);
5758

@@ -83,11 +84,13 @@ impl<'a> Parser<'a> {
8384
mac_allowed: bool,
8485
attrs_allowed: bool,
8586
req_name: ReqName,
87+
mod_stmt: bool,
8688
) -> PResult<'a, Option<Item>> {
8789
let lo = self.token.span;
8890
let vis = self.parse_visibility(FollowedByType::No)?;
8991
let mut def = self.parse_defaultness();
90-
let kind = self.parse_item_kind(&mut attrs, mac_allowed, lo, &vis, &mut def, req_name)?;
92+
let kind =
93+
self.parse_item_kind(&mut attrs, mac_allowed, lo, &vis, &mut def, req_name, mod_stmt)?;
9194
if let Some((ident, kind)) = kind {
9295
self.error_on_unconsumed_default(def, &kind);
9396
let span = lo.to(self.prev_span);
@@ -148,6 +151,7 @@ impl<'a> Parser<'a> {
148151
vis: &Visibility,
149152
def: &mut Defaultness,
150153
req_name: ReqName,
154+
mod_stmt: bool,
151155
) -> PResult<'a, Option<ItemInfo>> {
152156
let mut def = || mem::replace(def, Defaultness::Final);
153157

@@ -212,9 +216,13 @@ impl<'a> Parser<'a> {
212216
} else if vis.node.is_pub() && self.isnt_macro_invocation() {
213217
self.recover_missing_kw_before_item()?;
214218
return Ok(None);
215-
} else if macros_allowed && self.token.is_path_start() {
219+
} else if let Some(kind) = if macros_allowed && self.token.is_path_start() {
220+
self.parse_item_macro(vis, mod_stmt)?
221+
} else {
222+
None
223+
} {
216224
// MACRO INVOCATION ITEM
217-
(Ident::invalid(), ItemKind::Mac(self.parse_item_macro(vis)?))
225+
(Ident::invalid(), ItemKind::Mac(kind))
218226
} else {
219227
return Ok(None);
220228
};
@@ -333,13 +341,36 @@ impl<'a> Parser<'a> {
333341
}
334342

335343
/// Parses an item macro, e.g., `item!();`.
336-
fn parse_item_macro(&mut self, vis: &Visibility) -> PResult<'a, Mac> {
337-
let path = self.parse_path(PathStyle::Mod)?; // `foo::bar`
338-
self.expect(&token::Not)?; // `!`
344+
fn parse_item_macro(&mut self, vis: &Visibility, mod_stmt: bool) -> PResult<'a, Option<Mac>> {
345+
let parse_prefix = |p: &mut Self| -> PResult<'a, ast::Path> {
346+
let path = p.parse_path(PathStyle::Mod)?; // `foo::bar`
347+
p.expect(&token::Not)?; // `!`
348+
Ok(path)
349+
};
350+
let path = if mod_stmt {
351+
// We're in statement-as-module-item recovery mode.
352+
// To avoid "stealing" syntax from e.g. `x.f()` as a module-level statement,
353+
// we backtrack if we failed to parse `$path!`; after we have, we commit firmly.
354+
// This is only done when `mod_stmt` holds to avoid backtracking inside functions.
355+
let snapshot = self.clone();
356+
match parse_prefix(self) {
357+
Ok(path) => path,
358+
Err(mut err) => {
359+
// Assert that this is only for diagnostics!
360+
// This is a safeguard against breaking LL(k) accidentally in the spec,
361+
// assuming no one has gated the syntax with something like `#[cfg(FALSE)]`.
362+
err.delay_as_bug();
363+
*self = snapshot;
364+
return Ok(None);
365+
}
366+
}
367+
} else {
368+
parse_prefix(self)?
369+
};
339370
let args = self.parse_mac_args()?; // `( .. )` or `[ .. ]` (followed by `;`), or `{ .. }`.
340371
self.eat_semi_for_macro_if_needed(&args);
341372
self.complain_if_pub_macro(vis, false);
342-
Ok(Mac { path, args, prior_type_ascription: self.last_type_ascription })
373+
Ok(Some(Mac { path, args, prior_type_ascription: self.last_type_ascription }))
343374
}
344375

345376
/// Recover if we parsed attributes and expected an item but there was none.

src/librustc_parse/parser/module.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,9 @@ impl<'a> Parser<'a> {
195195

196196
// Construct the return type; either default, `-> _`, or `-> Result<_, _>`.
197197
let output = match (has_ret_unit, has_ret_expr, has_try_expr) {
198+
// `-> ()`; We either had `return;`, so return type is unit, or nothing was returned.
198199
(true, _, _) | (false, false, false) => FnRetTy::Default(span),
200+
// `-> Result<_, _>`; We had `?` somewhere so `-> Result<_, _>` is a good bet.
199201
(_, _, true) => {
200202
let arg = GenericArg::Type(self.mk_ty(span, TyKind::Infer));
201203
let args = [arg.clone(), arg].to_vec();
@@ -204,10 +206,11 @@ impl<'a> Parser<'a> {
204206
path.segments[0].args = Some(P(GenericArgs::AngleBracketed(args)));
205207
FnRetTy::Ty(self.mk_ty(span, TyKind::Path(None, path)))
206208
}
209+
// `-> _`; We had `return $expr;` so it's probably not `()` as return type.
207210
(_, true, _) => FnRetTy::Ty(self.mk_ty(span, TyKind::Infer)),
208211
};
209212

210-
// Finalize the AST for the function item.
213+
// Finalize the AST for the function item: `fn $ident() $output { $stmts }`.
211214
let sig = FnSig { header: FnHeader::default(), decl: P(FnDecl { inputs: vec![], output }) };
212215
let body = self.mk_block(stmts, BlockCheckMode::Default, span);
213216
let kind = ItemKind::Fn(Defaultness::Final, sig, Generics::default(), Some(body));

src/librustc_parse/parser/stmt.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ impl<'a> Parser<'a> {
9292

9393
fn parse_stmt_item(&mut self, attrs: Vec<Attribute>) -> PResult<'a, Option<ast::Item>> {
9494
let old = mem::replace(&mut self.directory.ownership, DirectoryOwnership::UnownedViaBlock);
95-
let item = self.parse_item_common(attrs.clone(), false, true, |_| true)?;
95+
let item = self.parse_item_common(attrs.clone(), false, true, |_| true, false)?;
9696
self.directory.ownership = old;
9797
Ok(item)
9898
}

src/test/ui/did_you_mean/issue-40006.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
11
impl dyn A {
2-
Y
3-
} //~ ERROR expected one of `!` or `::`, found `}`
2+
Y //~ ERROR non-item in item list
3+
}
44

55
struct S;
66

77
trait X {
8-
X() {} //~ ERROR expected one of `!` or `::`, found `(`
8+
X() {} //~ ERROR non-item in item list
99
fn xxx() { ### }
1010
L = M;
1111
Z = { 2 + 3 };
1212
::Y ();
1313
}
1414

1515
trait A {
16-
X() {} //~ ERROR expected one of `!` or `::`, found `(`
16+
X() {} //~ ERROR non-item in item list
1717
}
1818
trait B {
1919
fn xxx() { ### } //~ ERROR expected
2020
}
2121
trait C {
22-
L = M; //~ ERROR expected one of `!` or `::`, found `=`
22+
L = M; //~ ERROR non-item in item list
2323
}
2424
trait D {
25-
Z = { 2 + 3 }; //~ ERROR expected one of `!` or `::`, found `=`
25+
Z = { 2 + 3 }; //~ ERROR non-item in item list
2626
}
2727
trait E {
28-
::Y (); //~ ERROR expected one of
28+
::Y (); //~ ERROR non-item in item list
2929
}
3030

3131
impl S {

src/test/ui/did_you_mean/issue-40006.stderr

+30-33
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,69 @@
1-
error: expected one of `!` or `::`, found `}`
2-
--> $DIR/issue-40006.rs:3:1
1+
error: non-item in item list
2+
--> $DIR/issue-40006.rs:2:5
33
|
44
LL | impl dyn A {
5-
| - while parsing this item list starting here
5+
| - item list starts here
66
LL | Y
7-
| - expected one of `!` or `::`
7+
| ^ non-item starts here
88
LL | }
9-
| ^
10-
| |
11-
| unexpected token
12-
| the item list ends here
9+
| - item list ends here
1310

14-
error: expected one of `!` or `::`, found `(`
15-
--> $DIR/issue-40006.rs:8:6
11+
error: non-item in item list
12+
--> $DIR/issue-40006.rs:8:5
1613
|
1714
LL | trait X {
18-
| - while parsing this item list starting here
15+
| - item list starts here
1916
LL | X() {}
20-
| ^ expected one of `!` or `::`
17+
| ^ non-item starts here
2118
...
2219
LL | }
23-
| - the item list ends here
20+
| - item list ends here
2421

25-
error: expected one of `!` or `::`, found `(`
26-
--> $DIR/issue-40006.rs:16:6
22+
error: non-item in item list
23+
--> $DIR/issue-40006.rs:16:5
2724
|
2825
LL | trait A {
29-
| - while parsing this item list starting here
26+
| - item list starts here
3027
LL | X() {}
31-
| ^ expected one of `!` or `::`
28+
| ^ non-item starts here
3229
LL | }
33-
| - the item list ends here
30+
| - item list ends here
3431

3532
error: expected `[`, found `#`
3633
--> $DIR/issue-40006.rs:19:17
3734
|
3835
LL | fn xxx() { ### }
3936
| ^ expected `[`
4037

41-
error: expected one of `!` or `::`, found `=`
42-
--> $DIR/issue-40006.rs:22:7
38+
error: non-item in item list
39+
--> $DIR/issue-40006.rs:22:5
4340
|
4441
LL | trait C {
45-
| - while parsing this item list starting here
42+
| - item list starts here
4643
LL | L = M;
47-
| ^ expected one of `!` or `::`
44+
| ^ non-item starts here
4845
LL | }
49-
| - the item list ends here
46+
| - item list ends here
5047

51-
error: expected one of `!` or `::`, found `=`
52-
--> $DIR/issue-40006.rs:25:7
48+
error: non-item in item list
49+
--> $DIR/issue-40006.rs:25:5
5350
|
5451
LL | trait D {
55-
| - while parsing this item list starting here
52+
| - item list starts here
5653
LL | Z = { 2 + 3 };
57-
| ^ expected one of `!` or `::`
54+
| ^ non-item starts here
5855
LL | }
59-
| - the item list ends here
56+
| - item list ends here
6057

61-
error: expected one of `!` or `::`, found `(`
62-
--> $DIR/issue-40006.rs:28:9
58+
error: non-item in item list
59+
--> $DIR/issue-40006.rs:28:5
6360
|
6461
LL | trait E {
65-
| - while parsing this item list starting here
62+
| - item list starts here
6663
LL | ::Y ();
67-
| ^ expected one of `!` or `::`
64+
| ^^ non-item starts here
6865
LL | }
69-
| - the item list ends here
66+
| - item list ends here
7067

7168
error: missing `fn` for method definition
7269
--> $DIR/issue-40006.rs:32:8
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
can-only-test-this-in-run-make-fulldeps //~ ERROR expected one of `!` or `::`, found `-`
1+
can-only-test-this-in-run-make-fulldeps //~ ERROR expected item, found `can`
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected one of `!` or `::`, found `-`
2-
--> $DIR/feature-gate-extern_prelude.rs:1:4
1+
error: expected item, found `can`
2+
--> $DIR/feature-gate-extern_prelude.rs:1:1
33
|
44
LL | can-only-test-this-in-run-make-fulldeps
5-
| ^ expected one of `!` or `::`
5+
| ^^^ expected item
66

77
error: aborting due to previous error
88

src/test/ui/issues/issue-21146.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
// error-pattern: expected one of `!` or `::`, found `<eof>`
1+
// error-pattern: expected item, found `parse_error`
22
include!("auxiliary/issue-21146-inc.rs");
33
fn main() {}

src/test/ui/issues/issue-21146.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected one of `!` or `::`, found `<eof>`
1+
error: expected item, found `parse_error`
22
--> $DIR/auxiliary/issue-21146-inc.rs:3:1
33
|
44
LL | parse_error
5-
| ^^^^^^^^^^^ expected one of `!` or `::`
5+
| ^^^^^^^^^^^
66

77
error: aborting due to previous error
88

Original file line numberDiff line numberDiff line change
@@ -1,8 +1,36 @@
1-
error: expected one of `!` or `::`, found `cat`
2-
--> $DIR/class-implements-bad-trait.rs:2:7
1+
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
2+
--> $DIR/class-implements-bad-trait.rs:4:21
33
|
4-
LL | class cat : nonexistent {
5-
| ^^^ expected one of `!` or `::`
4+
LL | new(in_x : usize) { self.meows = in_x; }
5+
| ^ expected one of `.`, `;`, `?`, `}`, or an operator
66

7-
error: aborting due to previous error
7+
error: statements cannot reside in modules
8+
--> $DIR/class-implements-bad-trait.rs:2:1
9+
|
10+
LL | class cat : nonexistent {
11+
| _^^^^^___________________^
12+
LL | | let meows: usize;
13+
LL | | new(in_x : usize) { self.meows = in_x; }
14+
LL | | }
15+
| |_^
16+
|
17+
= note: the program entry point starts in `fn main() { ... }`, defined in `main.rs`
18+
= note: for more on functions and how to structure your program, see https://doc.rust-lang.org/book/ch03-03-how-functions-work.html
19+
help: consider moving the statements into a function
20+
|
21+
LL | fn my_function() -> _ {
22+
LL | class;
23+
LL | cat: nonexistent;
24+
LL | { let meows: usize; (/*ERROR*/) }
25+
LL | }
26+
|
27+
28+
error[E0425]: cannot find function `cat` in this scope
29+
--> $DIR/class-implements-bad-trait.rs:8:14
30+
|
31+
LL | let nyan = cat(0);
32+
| ^^^ not found in this scope
33+
34+
error: aborting due to 3 previous errors
835

36+
For more information about this error, try `rustc --explain E0425`.

src/test/ui/parser/extern-no-fn.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
extern {
2-
f(); //~ ERROR expected one of `!` or `::`, found `(`
2+
f(); //~ ERROR non-item in item list
33
}
44

55
fn main() {
+5-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
error: expected one of `!` or `::`, found `(`
2-
--> $DIR/extern-no-fn.rs:2:6
1+
error: non-item in item list
2+
--> $DIR/extern-no-fn.rs:2:5
33
|
44
LL | extern {
5-
| - while parsing this item list starting here
5+
| - item list starts here
66
LL | f();
7-
| ^ expected one of `!` or `::`
7+
| ^ non-item starts here
88
LL | }
9-
| - the item list ends here
9+
| - item list ends here
1010

1111
error: aborting due to previous error
1212

src/test/ui/parser/issue-21153.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
trait MyTrait<T>: Iterator {
22
Item = T;
3-
//~^ ERROR expected one of `!` or `::`, found `=`
3+
//~^ ERROR non-item in item list
44
}
55

66
fn main() {}

0 commit comments

Comments
 (0)