Skip to content

Commit

Permalink
parse: backtrack to avoid stealing e.g. x.f() at mod level.
Browse files Browse the repository at this point in the history
  • Loading branch information
Centril committed Feb 26, 2020
1 parent ccc47fd commit fedafa2
Show file tree
Hide file tree
Showing 20 changed files with 191 additions and 85 deletions.
51 changes: 41 additions & 10 deletions src/librustc_parse/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl<'a> Parser<'a> {

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

pub(super) fn parse_item_common(
Expand All @@ -39,6 +39,7 @@ impl<'a> Parser<'a> {
mac_allowed: bool,
attrs_allowed: bool,
req_name: ReqName,
mod_stmt: bool,
) -> PResult<'a, Option<Item>> {
maybe_whole!(self, NtItem, |item| {
let mut item = item;
Expand All @@ -49,9 +50,9 @@ impl<'a> Parser<'a> {

let mut unclosed_delims = vec![];
let (mut item, tokens) = self.collect_tokens(|this| {
let item = this.parse_item_common_(attrs, mac_allowed, attrs_allowed, req_name);
let i = this.parse_item_common_(attrs, mac_allowed, attrs_allowed, req_name, mod_stmt);
unclosed_delims.append(&mut this.unclosed_delims);
item
i
})?;
self.unclosed_delims.append(&mut unclosed_delims);

Expand Down Expand Up @@ -83,11 +84,13 @@ impl<'a> Parser<'a> {
mac_allowed: bool,
attrs_allowed: bool,
req_name: ReqName,
mod_stmt: bool,
) -> PResult<'a, Option<Item>> {
let lo = self.token.span;
let vis = self.parse_visibility(FollowedByType::No)?;
let mut def = self.parse_defaultness();
let kind = self.parse_item_kind(&mut attrs, mac_allowed, lo, &vis, &mut def, req_name)?;
let kind =
self.parse_item_kind(&mut attrs, mac_allowed, lo, &vis, &mut def, req_name, mod_stmt)?;
if let Some((ident, kind)) = kind {
self.error_on_unconsumed_default(def, &kind);
let span = lo.to(self.prev_span);
Expand Down Expand Up @@ -148,6 +151,7 @@ impl<'a> Parser<'a> {
vis: &Visibility,
def: &mut Defaultness,
req_name: ReqName,
mod_stmt: bool,
) -> PResult<'a, Option<ItemInfo>> {
let mut def = || mem::replace(def, Defaultness::Final);

Expand Down Expand Up @@ -212,9 +216,13 @@ impl<'a> Parser<'a> {
} else if vis.node.is_pub() && self.isnt_macro_invocation() {
self.recover_missing_kw_before_item()?;
return Ok(None);
} else if macros_allowed && self.token.is_path_start() {
} else if let Some(kind) = if macros_allowed && self.token.is_path_start() {
self.parse_item_macro(vis, mod_stmt)?
} else {
None
} {
// MACRO INVOCATION ITEM
(Ident::invalid(), ItemKind::Mac(self.parse_item_macro(vis)?))
(Ident::invalid(), ItemKind::Mac(kind))
} else {
return Ok(None);
};
Expand Down Expand Up @@ -333,13 +341,36 @@ impl<'a> Parser<'a> {
}

/// Parses an item macro, e.g., `item!();`.
fn parse_item_macro(&mut self, vis: &Visibility) -> PResult<'a, Mac> {
let path = self.parse_path(PathStyle::Mod)?; // `foo::bar`
self.expect(&token::Not)?; // `!`
fn parse_item_macro(&mut self, vis: &Visibility, mod_stmt: bool) -> PResult<'a, Option<Mac>> {
let parse_prefix = |p: &mut Self| -> PResult<'a, ast::Path> {
let path = p.parse_path(PathStyle::Mod)?; // `foo::bar`
p.expect(&token::Not)?; // `!`
Ok(path)
};
let path = if mod_stmt {
// We're in statement-as-module-item recovery mode.
// To avoid "stealing" syntax from e.g. `x.f()` as a module-level statement,
// we backtrack if we failed to parse `$path!`; after we have, we commit firmly.
// This is only done when `mod_stmt` holds to avoid backtracking inside functions.
let snapshot = self.clone();
match parse_prefix(self) {
Ok(path) => path,
Err(mut err) => {
// Assert that this is only for diagnostics!
// This is a safeguard against breaking LL(k) accidentally in the spec,
// assuming no one has gated the syntax with something like `#[cfg(FALSE)]`.
err.delay_as_bug();
*self = snapshot;
return Ok(None);
}
}
} else {
parse_prefix(self)?
};
let args = self.parse_mac_args()?; // `( .. )` or `[ .. ]` (followed by `;`), or `{ .. }`.
self.eat_semi_for_macro_if_needed(&args);
self.complain_if_pub_macro(vis, false);
Ok(Mac { path, args, prior_type_ascription: self.last_type_ascription })
Ok(Some(Mac { path, args, prior_type_ascription: self.last_type_ascription }))
}

/// Recover if we parsed attributes and expected an item but there was none.
Expand Down
5 changes: 4 additions & 1 deletion src/librustc_parse/parser/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,9 @@ impl<'a> Parser<'a> {

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

// Finalize the AST for the function item.
// Finalize the AST for the function item: `fn $ident() $output { $stmts }`.
let sig = FnSig { header: FnHeader::default(), decl: P(FnDecl { inputs: vec![], output }) };
let body = self.mk_block(stmts, BlockCheckMode::Default, span);
let kind = ItemKind::Fn(Defaultness::Final, sig, Generics::default(), Some(body));
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_parse/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl<'a> Parser<'a> {

fn parse_stmt_item(&mut self, attrs: Vec<Attribute>) -> PResult<'a, Option<ast::Item>> {
let old = mem::replace(&mut self.directory.ownership, DirectoryOwnership::UnownedViaBlock);
let item = self.parse_item_common(attrs.clone(), false, true, |_| true)?;
let item = self.parse_item_common(attrs.clone(), false, true, |_| true, false)?;
self.directory.ownership = old;
Ok(item)
}
Expand Down
14 changes: 7 additions & 7 deletions src/test/ui/did_you_mean/issue-40006.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
impl dyn A {
Y
} //~ ERROR expected one of `!` or `::`, found `}`
Y //~ ERROR non-item in item list
}

struct S;

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

trait A {
X() {} //~ ERROR expected one of `!` or `::`, found `(`
X() {} //~ ERROR non-item in item list
}
trait B {
fn xxx() { ### } //~ ERROR expected
}
trait C {
L = M; //~ ERROR expected one of `!` or `::`, found `=`
L = M; //~ ERROR non-item in item list
}
trait D {
Z = { 2 + 3 }; //~ ERROR expected one of `!` or `::`, found `=`
Z = { 2 + 3 }; //~ ERROR non-item in item list
}
trait E {
::Y (); //~ ERROR expected one of
::Y (); //~ ERROR non-item in item list
}

impl S {
Expand Down
63 changes: 30 additions & 33 deletions src/test/ui/did_you_mean/issue-40006.stderr
Original file line number Diff line number Diff line change
@@ -1,72 +1,69 @@
error: expected one of `!` or `::`, found `}`
--> $DIR/issue-40006.rs:3:1
error: non-item in item list
--> $DIR/issue-40006.rs:2:5
|
LL | impl dyn A {
| - while parsing this item list starting here
| - item list starts here
LL | Y
| - expected one of `!` or `::`
| ^ non-item starts here
LL | }
| ^
| |
| unexpected token
| the item list ends here
| - item list ends here

error: expected one of `!` or `::`, found `(`
--> $DIR/issue-40006.rs:8:6
error: non-item in item list
--> $DIR/issue-40006.rs:8:5
|
LL | trait X {
| - while parsing this item list starting here
| - item list starts here
LL | X() {}
| ^ expected one of `!` or `::`
| ^ non-item starts here
...
LL | }
| - the item list ends here
| - item list ends here

error: expected one of `!` or `::`, found `(`
--> $DIR/issue-40006.rs:16:6
error: non-item in item list
--> $DIR/issue-40006.rs:16:5
|
LL | trait A {
| - while parsing this item list starting here
| - item list starts here
LL | X() {}
| ^ expected one of `!` or `::`
| ^ non-item starts here
LL | }
| - the item list ends here
| - item list ends here

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

error: expected one of `!` or `::`, found `=`
--> $DIR/issue-40006.rs:22:7
error: non-item in item list
--> $DIR/issue-40006.rs:22:5
|
LL | trait C {
| - while parsing this item list starting here
| - item list starts here
LL | L = M;
| ^ expected one of `!` or `::`
| ^ non-item starts here
LL | }
| - the item list ends here
| - item list ends here

error: expected one of `!` or `::`, found `=`
--> $DIR/issue-40006.rs:25:7
error: non-item in item list
--> $DIR/issue-40006.rs:25:5
|
LL | trait D {
| - while parsing this item list starting here
| - item list starts here
LL | Z = { 2 + 3 };
| ^ expected one of `!` or `::`
| ^ non-item starts here
LL | }
| - the item list ends here
| - item list ends here

error: expected one of `!` or `::`, found `(`
--> $DIR/issue-40006.rs:28:9
error: non-item in item list
--> $DIR/issue-40006.rs:28:5
|
LL | trait E {
| - while parsing this item list starting here
| - item list starts here
LL | ::Y ();
| ^ expected one of `!` or `::`
| ^^ non-item starts here
LL | }
| - the item list ends here
| - item list ends here

error: missing `fn` for method definition
--> $DIR/issue-40006.rs:32:8
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/feature-gates/feature-gate-extern_prelude.rs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
can-only-test-this-in-run-make-fulldeps //~ ERROR expected one of `!` or `::`, found `-`
can-only-test-this-in-run-make-fulldeps //~ ERROR expected item, found `can`
6 changes: 3 additions & 3 deletions src/test/ui/feature-gates/feature-gate-extern_prelude.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error: expected one of `!` or `::`, found `-`
--> $DIR/feature-gate-extern_prelude.rs:1:4
error: expected item, found `can`
--> $DIR/feature-gate-extern_prelude.rs:1:1
|
LL | can-only-test-this-in-run-make-fulldeps
| ^ expected one of `!` or `::`
| ^^^ expected item

error: aborting due to previous error

2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-21146.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// error-pattern: expected one of `!` or `::`, found `<eof>`
// error-pattern: expected item, found `parse_error`
include!("auxiliary/issue-21146-inc.rs");
fn main() {}
4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-21146.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error: expected one of `!` or `::`, found `<eof>`
error: expected item, found `parse_error`
--> $DIR/auxiliary/issue-21146-inc.rs:3:1
|
LL | parse_error
| ^^^^^^^^^^^ expected one of `!` or `::`
| ^^^^^^^^^^^

error: aborting due to previous error

38 changes: 33 additions & 5 deletions src/test/ui/parser/class-implements-bad-trait.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,36 @@
error: expected one of `!` or `::`, found `cat`
--> $DIR/class-implements-bad-trait.rs:2:7
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
--> $DIR/class-implements-bad-trait.rs:4:21
|
LL | class cat : nonexistent {
| ^^^ expected one of `!` or `::`
LL | new(in_x : usize) { self.meows = in_x; }
| ^ expected one of `.`, `;`, `?`, `}`, or an operator

error: aborting due to previous error
error: statements cannot reside in modules
--> $DIR/class-implements-bad-trait.rs:2:1
|
LL | class cat : nonexistent {
| _^^^^^___________________^
LL | | let meows: usize;
LL | | new(in_x : usize) { self.meows = in_x; }
LL | | }
| |_^
|
= note: the program entry point starts in `fn main() { ... }`, defined in `main.rs`
= note: for more on functions and how to structure your program, see https://doc.rust-lang.org/book/ch03-03-how-functions-work.html
help: consider moving the statements into a function
|
LL | fn my_function() -> _ {
LL | class;
LL | cat: nonexistent;
LL | { let meows: usize; (/*ERROR*/) }
LL | }
|

error[E0425]: cannot find function `cat` in this scope
--> $DIR/class-implements-bad-trait.rs:8:14
|
LL | let nyan = cat(0);
| ^^^ not found in this scope

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0425`.
2 changes: 1 addition & 1 deletion src/test/ui/parser/extern-no-fn.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
extern {
f(); //~ ERROR expected one of `!` or `::`, found `(`
f(); //~ ERROR non-item in item list
}

fn main() {
Expand Down
10 changes: 5 additions & 5 deletions src/test/ui/parser/extern-no-fn.stderr
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
error: expected one of `!` or `::`, found `(`
--> $DIR/extern-no-fn.rs:2:6
error: non-item in item list
--> $DIR/extern-no-fn.rs:2:5
|
LL | extern {
| - while parsing this item list starting here
| - item list starts here
LL | f();
| ^ expected one of `!` or `::`
| ^ non-item starts here
LL | }
| - the item list ends here
| - item list ends here

error: aborting due to previous error

2 changes: 1 addition & 1 deletion src/test/ui/parser/issue-21153.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
trait MyTrait<T>: Iterator {
Item = T;
//~^ ERROR expected one of `!` or `::`, found `=`
//~^ ERROR non-item in item list
}

fn main() {}
Loading

0 comments on commit fedafa2

Please sign in to comment.