1
Fork 0

parser: unify item list parsing.

as a consequence, `trait X { #![attr] }` becomes legal.
This commit is contained in:
Mazdak Farrokhzad 2020-01-31 06:43:33 +01:00
parent 9fed2d587c
commit 7737d0ffde
12 changed files with 80 additions and 71 deletions

View file

@ -1269,6 +1269,7 @@ impl<'a> State<'a> {
self.print_where_clause(&generics.where_clause); self.print_where_clause(&generics.where_clause);
self.s.word(" "); self.s.word(" ");
self.bopen(); self.bopen();
self.print_inner_attributes(&item.attrs);
for trait_item in trait_items { for trait_item in trait_items {
self.print_assoc_item(trait_item); self.print_assoc_item(trait_item);
} }

View file

@ -867,7 +867,7 @@ pub fn parse_ast_fragment<'a>(
AstFragmentKind::ForeignItems => { AstFragmentKind::ForeignItems => {
let mut items = SmallVec::new(); let mut items = SmallVec::new();
while this.token != token::Eof { while this.token != token::Eof {
items.push(this.parse_foreign_item()?); items.push(this.parse_foreign_item(&mut false)?);
} }
AstFragment::ForeignItems(items) AstFragment::ForeignItems(items)
} }

View file

@ -515,7 +515,7 @@ impl<'a> Parser<'a> {
generics.where_clause = self.parse_where_clause()?; generics.where_clause = self.parse_where_clause()?;
let (impl_items, attrs) = self.parse_impl_body()?; let (impl_items, attrs) = self.parse_item_list(|p, at_end| p.parse_impl_item(at_end))?;
let item_kind = match ty_second { let item_kind = match ty_second {
Some(ty_second) => { Some(ty_second) => {
@ -571,15 +571,21 @@ impl<'a> Parser<'a> {
Ok((Ident::invalid(), item_kind, Some(attrs))) Ok((Ident::invalid(), item_kind, Some(attrs)))
} }
fn parse_impl_body(&mut self) -> PResult<'a, (Vec<P<AssocItem>>, Vec<Attribute>)> { fn parse_item_list<T>(
&mut self,
mut parse_item: impl FnMut(&mut Parser<'a>, &mut bool) -> PResult<'a, T>,
) -> PResult<'a, (Vec<T>, Vec<Attribute>)> {
self.expect(&token::OpenDelim(token::Brace))?; self.expect(&token::OpenDelim(token::Brace))?;
let attrs = self.parse_inner_attributes()?; let attrs = self.parse_inner_attributes()?;
let mut impl_items = Vec::new(); let mut items = Vec::new();
while !self.eat(&token::CloseDelim(token::Brace)) { while !self.eat(&token::CloseDelim(token::Brace)) {
if self.recover_doc_comment_before_brace() {
continue;
}
let mut at_end = false; let mut at_end = false;
match self.parse_impl_item(&mut at_end) { match parse_item(self, &mut at_end) {
Ok(impl_item) => impl_items.push(impl_item), Ok(item) => items.push(item),
Err(mut err) => { Err(mut err) => {
err.emit(); err.emit();
if !at_end { if !at_end {
@ -589,7 +595,30 @@ impl<'a> Parser<'a> {
} }
} }
} }
Ok((impl_items, attrs)) Ok((items, attrs))
}
/// Recover on a doc comment before `}`.
fn recover_doc_comment_before_brace(&mut self) -> bool {
if let token::DocComment(_) = self.token.kind {
if self.look_ahead(1, |tok| tok == &token::CloseDelim(token::Brace)) {
struct_span_err!(
self.diagnostic(),
self.token.span,
E0584,
"found a documentation comment that doesn't document anything",
)
.span_label(self.token.span, "this doc comment doesn't document anything")
.help(
"doc comments must come before what they document, maybe a \
comment was intended with `//`?",
)
.emit();
self.bump();
return true;
}
}
false
} }
/// Parses defaultness (i.e., `default` or nothing). /// Parses defaultness (i.e., `default` or nothing).
@ -660,39 +689,8 @@ impl<'a> Parser<'a> {
} else { } else {
// It's a normal trait. // It's a normal trait.
tps.where_clause = self.parse_where_clause()?; tps.where_clause = self.parse_where_clause()?;
self.expect(&token::OpenDelim(token::Brace))?; let (items, attrs) = self.parse_item_list(|p, at_end| p.parse_trait_item(at_end))?;
let mut trait_items = vec![]; Ok((ident, ItemKind::Trait(is_auto, unsafety, tps, bounds, items), Some(attrs)))
while !self.eat(&token::CloseDelim(token::Brace)) {
if let token::DocComment(_) = self.token.kind {
if self.look_ahead(1, |tok| tok == &token::CloseDelim(token::Brace)) {
struct_span_err!(
self.diagnostic(),
self.token.span,
E0584,
"found a documentation comment that doesn't document anything",
)
.help(
"doc comments must come before what they document, maybe a \
comment was intended with `//`?",
)
.emit();
self.bump();
continue;
}
}
let mut at_end = false;
match self.parse_trait_item(&mut at_end) {
Ok(item) => trait_items.push(item),
Err(mut e) => {
e.emit();
if !at_end {
self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
break;
}
}
}
}
Ok((ident, ItemKind::Trait(is_auto, unsafety, tps, bounds, trait_items), None))
} }
} }
@ -942,26 +940,18 @@ impl<'a> Parser<'a> {
&mut self, &mut self,
lo: Span, lo: Span,
abi: Option<StrLit>, abi: Option<StrLit>,
visibility: Visibility, vis: Visibility,
mut attrs: Vec<Attribute>, mut attrs: Vec<Attribute>,
) -> PResult<'a, P<Item>> { ) -> PResult<'a, P<Item>> {
self.expect(&token::OpenDelim(token::Brace))?; let (items, iattrs) = self.parse_item_list(|p, at_end| p.parse_foreign_item(at_end))?;
attrs.extend(iattrs);
attrs.extend(self.parse_inner_attributes()?); let span = lo.to(self.prev_span);
let m = ast::ForeignMod { abi, items };
let mut foreign_items = vec![]; Ok(self.mk_item(span, Ident::invalid(), ItemKind::ForeignMod(m), vis, attrs))
while !self.eat(&token::CloseDelim(token::Brace)) {
foreign_items.push(self.parse_foreign_item()?);
}
let prev_span = self.prev_span;
let m = ast::ForeignMod { abi, items: foreign_items };
let invalid = Ident::invalid();
Ok(self.mk_item(lo.to(prev_span), invalid, ItemKind::ForeignMod(m), visibility, attrs))
} }
/// Parses a foreign item (one in an `extern { ... }` block). /// Parses a foreign item (one in an `extern { ... }` block).
pub fn parse_foreign_item(&mut self) -> PResult<'a, P<ForeignItem>> { pub fn parse_foreign_item(&mut self, at_end: &mut bool) -> PResult<'a, P<ForeignItem>> {
maybe_whole!(self, NtForeignItem, |ni| ni); maybe_whole!(self, NtForeignItem, |ni| ni);
let mut attrs = self.parse_outer_attributes()?; let mut attrs = self.parse_outer_attributes()?;
@ -973,7 +963,7 @@ impl<'a> Parser<'a> {
self.parse_item_foreign_type()? self.parse_item_foreign_type()?
} else if self.check_fn_front_matter() { } else if self.check_fn_front_matter() {
// FOREIGN FUNCTION ITEM // FOREIGN FUNCTION ITEM
let (ident, sig, generics, body) = self.parse_fn(&mut false, &mut attrs, |_| true)?; let (ident, sig, generics, body) = self.parse_fn(at_end, &mut attrs, |_| true)?;
(ident, ForeignItemKind::Fn(sig, generics, body)) (ident, ForeignItemKind::Fn(sig, generics, body))
} else if self.is_static_global() { } else if self.is_static_global() {
// FOREIGN STATIC ITEM // FOREIGN STATIC ITEM
@ -991,7 +981,7 @@ impl<'a> Parser<'a> {
) )
.emit(); .emit();
self.parse_item_foreign_static()? self.parse_item_foreign_static()?
} else if let Some(mac) = self.parse_assoc_macro_invoc("extern", Some(&vis), &mut false)? { } else if let Some(mac) = self.parse_assoc_macro_invoc("extern", Some(&vis), at_end)? {
(Ident::invalid(), ForeignItemKind::Macro(mac)) (Ident::invalid(), ForeignItemKind::Macro(mac))
} else { } else {
if !attrs.is_empty() { if !attrs.is_empty() {

View file

@ -0,0 +1,7 @@
// pp-exact
trait Foo {
#![allow(bar)]
}
fn main() { }

View file

@ -1,13 +1,7 @@
// Constants (static variables) can be used to match in patterns, but mutable // Make sure there's an error when given `extern { ... #[attr] }`.
// statics cannot. This ensures that there's some form of error if this is
// attempted.
extern crate libc; fn main() {}
extern { extern {
static mut rust_dbg_static_mut: libc::c_int;
pub fn rust_dbg_static_mut_check_four();
#[cfg(stage37)] //~ ERROR expected item after attributes #[cfg(stage37)] //~ ERROR expected item after attributes
} }
pub fn main() {}

View file

@ -1,5 +1,5 @@
error: expected item after attributes error: expected item after attributes
--> $DIR/attrs-after-extern-mod.rs:10:19 --> $DIR/attrs-after-extern-mod.rs:6:19
| |
LL | #[cfg(stage37)] LL | #[cfg(stage37)]
| ^ | ^

View file

@ -1,4 +1,6 @@
fn main() {}
extern { extern {
/// hi /// hi
//~^ ERROR expected item after doc comment //~^ ERROR found a documentation comment that doesn't document anything
} }

View file

@ -1,8 +1,11 @@
error: expected item after doc comment error[E0584]: found a documentation comment that doesn't document anything
--> $DIR/doc-before-extern-rbrace.rs:2:5 --> $DIR/doc-before-extern-rbrace.rs:4:5
| |
LL | /// hi LL | /// hi
| ^^^^^^ this doc comment doesn't document anything | ^^^^^^ this doc comment doesn't document anything
|
= help: doc comments must come before what they document, maybe a comment was intended with `//`?
error: aborting due to previous error error: aborting due to previous error
For more information about this error, try `rustc --explain E0584`.

View file

@ -2,7 +2,7 @@ error[E0584]: found a documentation comment that doesn't document anything
--> $DIR/doc-inside-trait-item.rs:3:5 --> $DIR/doc-inside-trait-item.rs:3:5
| |
LL | /// empty doc LL | /// empty doc
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^ this doc comment doesn't document anything
| |
= help: doc comments must come before what they document, maybe a comment was intended with `//`? = help: doc comments must come before what they document, maybe a comment was intended with `//`?

View file

@ -1,4 +1,7 @@
// error-pattern: expected one of `(`, `async`, `const`, `extern`, `fn` // error-pattern: expected one of `(`, `async`, `const`, `extern`, `fn`
fn main() {}
extern { extern {
pub pub fn foo(); pub pub fn foo();
} }

View file

@ -1,5 +1,5 @@
error: expected one of `(`, `async`, `const`, `extern`, `fn`, `static`, `type`, or `unsafe`, found keyword `pub` error: expected one of `(`, `async`, `const`, `extern`, `fn`, `static`, `type`, or `unsafe`, found keyword `pub`
--> $DIR/duplicate-visibility.rs:3:9 --> $DIR/duplicate-visibility.rs:6:9
| |
LL | pub pub fn foo(); LL | pub pub fn foo();
| ^^^ expected one of 8 possible tokens | ^^^ expected one of 8 possible tokens

View file

@ -0,0 +1,9 @@
// check-pass
#![deny(non_camel_case_types)]
fn main() {}
trait foo_bar {
#![allow(non_camel_case_types)]
}