parse: recover `default` on free items.

This commit is contained in:
Mazdak Farrokhzad 2020-02-22 04:53:02 +01:00
parent 9ed4c09983
commit a920a05603
7 changed files with 226 additions and 18 deletions

View File

@ -81,17 +81,30 @@ impl<'a> Parser<'a> {
Some(item)
});
let item = self.parse_item_common(attrs, macros_allowed, attributes_allowed)?;
if let Some(ref item) = item {
self.error_on_illegal_default(item.defaultness);
}
Ok(item.map(P))
}
fn parse_item_common(
&mut self,
mut attrs: Vec<Attribute>,
macros_allowed: bool,
attributes_allowed: bool,
) -> PResult<'a, Option<Item>> {
let lo = self.token.span;
let vis = self.parse_visibility(FollowedByType::No)?;
if let Some((ident, kind)) = self.parse_item_kind(&mut attrs, macros_allowed, lo, &vis)? {
return Ok(Some(P(self.mk_item(lo, ident, kind, vis, Defaultness::Final, attrs))));
let mut def = self.parse_defaultness();
let kind = self.parse_item_kind(&mut attrs, macros_allowed, lo, &vis, &mut def)?;
if let Some((ident, kind)) = kind {
return Ok(Some(self.mk_item(lo, ident, kind, vis, def, attrs)));
}
// At this point, we have failed to parse an item.
self.error_on_unmatched_vis(&vis);
self.error_on_unmatched_defaultness(def);
if !attributes_allowed {
self.recover_attrs_no_item(&attrs)?;
}
@ -111,6 +124,25 @@ impl<'a> Parser<'a> {
.emit();
}
/// Error in-case a `default` was parsed but no item followed.
fn error_on_unmatched_defaultness(&self, def: Defaultness) {
if let Defaultness::Default(span) = def {
self.struct_span_err(span, "unmatched `default`")
.span_label(span, "the unmatched `default`")
.emit();
}
}
/// Error in-case `default` was parsed in an in-appropriate context.
fn error_on_illegal_default(&self, def: Defaultness) {
if let Defaultness::Default(span) = def {
self.struct_span_err(span, "item cannot be `default`")
.span_label(span, "`default` because of this")
.note("only associated `fn`, `const`, and `type` items can be `default`")
.emit();
}
}
/// Parses one of the items allowed by the flags.
fn parse_item_kind(
&mut self,
@ -118,6 +150,7 @@ impl<'a> Parser<'a> {
macros_allowed: bool,
lo: Span,
vis: &Visibility,
def: &mut Defaultness,
) -> PResult<'a, Option<ItemInfo>> {
let info = if self.eat_keyword(kw::Use) {
// USE ITEM
@ -150,10 +183,9 @@ impl<'a> Parser<'a> {
self.parse_item_trait(attrs, lo)?
} else if self.check_keyword(kw::Impl)
|| self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Impl])
|| self.check_keyword(kw::Default) && self.is_keyword_ahead(1, &[kw::Impl, kw::Unsafe])
{
// IMPL ITEM
self.parse_item_impl(attrs)?
self.parse_item_impl(attrs, mem::replace(def, Defaultness::Final))?
} else if self.eat_keyword(kw::Mod) {
// MODULE ITEM
self.parse_item_mod(attrs)?
@ -366,8 +398,11 @@ impl<'a> Parser<'a> {
/// "impl" GENERICS "const"? "!"? TYPE "for"? (TYPE | "..") ("where" PREDICATES)? "{" BODY "}"
/// "impl" GENERICS "const"? "!"? TYPE ("where" PREDICATES)? "{" BODY "}"
/// ```
fn parse_item_impl(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
let defaultness = self.parse_defaultness();
fn parse_item_impl(
&mut self,
attrs: &mut Vec<Attribute>,
defaultness: Defaultness,
) -> PResult<'a, ItemInfo> {
let unsafety = self.parse_unsafety();
self.expect_keyword(kw::Impl)?;
@ -531,13 +566,11 @@ impl<'a> Parser<'a> {
/// Parses defaultness (i.e., `default` or nothing).
fn parse_defaultness(&mut self) -> Defaultness {
// We are interested in `default` followed by another keyword.
// We are interested in `default` followed by another identifier.
// However, we must avoid keywords that occur as binary operators.
// Currently, the only applicable keyword is `as` (`default as Ty`).
if self.check_keyword(kw::Default)
&& self.look_ahead(1, |t| {
t.is_non_raw_ident_where(|i| i.is_reserved() && i.name != kw::As)
})
&& self.look_ahead(1, |t| t.is_non_raw_ident_where(|i| i.name != kw::As))
{
self.bump(); // `default`
Defaultness::Default(self.prev_span)

View File

@ -0,0 +1,26 @@
// Test parsing for `default` where it doesn't belong.
// Specifically, we are interested in kinds of items or items in certain contexts.
fn main() {}
#[cfg(FALSE)]
mod free_items {
default extern crate foo; //~ ERROR item cannot be `default`
default use foo; //~ ERROR item cannot be `default`
default static foo: u8; //~ ERROR item cannot be `default`
default const foo: u8; //~ ERROR item cannot be `default`
default fn foo(); //~ ERROR item cannot be `default`
default mod foo {} //~ ERROR item cannot be `default`
default extern "C" {} //~ ERROR item cannot be `default`
default type foo = u8; //~ ERROR item cannot be `default`
default enum foo {} //~ ERROR item cannot be `default`
default struct foo {} //~ ERROR item cannot be `default`
default union foo {} //~ ERROR item cannot be `default`
default trait foo {} //~ ERROR item cannot be `default`
default trait foo = Ord; //~ ERROR item cannot be `default`
default impl foo {}
default!();
default::foo::bar!();
default macro foo {} //~ ERROR item cannot be `default`
default macro_rules! foo {} //~ ERROR item cannot be `default`
}

View File

@ -0,0 +1,122 @@
error: item cannot be `default`
--> $DIR/default-on-wrong-item-kind.rs:8:5
|
LL | default extern crate foo;
| ^^^^^^^ `default` because of this
|
= note: only associated `fn`, `const`, and `type` items can be `default`
error: item cannot be `default`
--> $DIR/default-on-wrong-item-kind.rs:9:5
|
LL | default use foo;
| ^^^^^^^ `default` because of this
|
= note: only associated `fn`, `const`, and `type` items can be `default`
error: item cannot be `default`
--> $DIR/default-on-wrong-item-kind.rs:10:5
|
LL | default static foo: u8;
| ^^^^^^^ `default` because of this
|
= note: only associated `fn`, `const`, and `type` items can be `default`
error: item cannot be `default`
--> $DIR/default-on-wrong-item-kind.rs:11:5
|
LL | default const foo: u8;
| ^^^^^^^ `default` because of this
|
= note: only associated `fn`, `const`, and `type` items can be `default`
error: item cannot be `default`
--> $DIR/default-on-wrong-item-kind.rs:12:5
|
LL | default fn foo();
| ^^^^^^^ `default` because of this
|
= note: only associated `fn`, `const`, and `type` items can be `default`
error: item cannot be `default`
--> $DIR/default-on-wrong-item-kind.rs:13:5
|
LL | default mod foo {}
| ^^^^^^^ `default` because of this
|
= note: only associated `fn`, `const`, and `type` items can be `default`
error: item cannot be `default`
--> $DIR/default-on-wrong-item-kind.rs:14:5
|
LL | default extern "C" {}
| ^^^^^^^ `default` because of this
|
= note: only associated `fn`, `const`, and `type` items can be `default`
error: item cannot be `default`
--> $DIR/default-on-wrong-item-kind.rs:15:5
|
LL | default type foo = u8;
| ^^^^^^^ `default` because of this
|
= note: only associated `fn`, `const`, and `type` items can be `default`
error: item cannot be `default`
--> $DIR/default-on-wrong-item-kind.rs:16:5
|
LL | default enum foo {}
| ^^^^^^^ `default` because of this
|
= note: only associated `fn`, `const`, and `type` items can be `default`
error: item cannot be `default`
--> $DIR/default-on-wrong-item-kind.rs:17:5
|
LL | default struct foo {}
| ^^^^^^^ `default` because of this
|
= note: only associated `fn`, `const`, and `type` items can be `default`
error: item cannot be `default`
--> $DIR/default-on-wrong-item-kind.rs:18:5
|
LL | default union foo {}
| ^^^^^^^ `default` because of this
|
= note: only associated `fn`, `const`, and `type` items can be `default`
error: item cannot be `default`
--> $DIR/default-on-wrong-item-kind.rs:19:5
|
LL | default trait foo {}
| ^^^^^^^ `default` because of this
|
= note: only associated `fn`, `const`, and `type` items can be `default`
error: item cannot be `default`
--> $DIR/default-on-wrong-item-kind.rs:20:5
|
LL | default trait foo = Ord;
| ^^^^^^^ `default` because of this
|
= note: only associated `fn`, `const`, and `type` items can be `default`
error: item cannot be `default`
--> $DIR/default-on-wrong-item-kind.rs:24:5
|
LL | default macro foo {}
| ^^^^^^^ `default` because of this
|
= note: only associated `fn`, `const`, and `type` items can be `default`
error: item cannot be `default`
--> $DIR/default-on-wrong-item-kind.rs:25:5
|
LL | default macro_rules! foo {}
| ^^^^^^^ `default` because of this
|
= note: only associated `fn`, `const`, and `type` items can be `default`
error: aborting due to 15 previous errors

View File

@ -0,0 +1,6 @@
mod foo {
default!(); // OK.
default do
//~^ ERROR unmatched `default`
//~| ERROR expected item, found reserved keyword `do`
}

View File

@ -0,0 +1,14 @@
error: unmatched `default`
--> $DIR/default-unmatched.rs:3:5
|
LL | default do
| ^^^^^^^ the unmatched `default`
error: expected item, found reserved keyword `do`
--> $DIR/default-unmatched.rs:3:13
|
LL | default do
| ^^ expected item
error: aborting due to 2 previous errors

View File

@ -6,4 +6,5 @@ impl Trait .. {} //~ ERROR missing `for` in a trait impl
impl ?Sized for Type {} //~ ERROR expected a trait, found type
impl ?Sized for .. {} //~ ERROR expected a trait, found type
default unsafe FAIL //~ ERROR expected `impl`, found `FAIL`
default unsafe FAIL //~ ERROR expected item, found keyword `unsafe`
//~^ ERROR unmatched `default`

View File

@ -22,11 +22,17 @@ error: expected a trait, found type
LL | impl ?Sized for .. {}
| ^^^^^^
error: expected `impl`, found `FAIL`
--> $DIR/impl-parsing.rs:9:16
error: unmatched `default`
--> $DIR/impl-parsing.rs:9:1
|
LL | default unsafe FAIL
| ^^^^ expected `impl`
| ^^^^^^^ the unmatched `default`
error: aborting due to 5 previous errors
error: expected item, found keyword `unsafe`
--> $DIR/impl-parsing.rs:9:9
|
LL | default unsafe FAIL
| ^^^^^^ expected item
error: aborting due to 6 previous errors