Skip to content

Commit 7737d0f

Browse files
committed
parser: unify item list parsing.
as a consequence, `trait X { #![attr] }` becomes legal.
1 parent 9fed2d5 commit 7737d0f

12 files changed

+80
-71
lines changed

src/librustc_ast_pretty/pprust.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,6 +1269,7 @@ impl<'a> State<'a> {
12691269
self.print_where_clause(&generics.where_clause);
12701270
self.s.word(" ");
12711271
self.bopen();
1272+
self.print_inner_attributes(&item.attrs);
12721273
for trait_item in trait_items {
12731274
self.print_assoc_item(trait_item);
12741275
}

src/librustc_expand/expand.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -867,7 +867,7 @@ pub fn parse_ast_fragment<'a>(
867867
AstFragmentKind::ForeignItems => {
868868
let mut items = SmallVec::new();
869869
while this.token != token::Eof {
870-
items.push(this.parse_foreign_item()?);
870+
items.push(this.parse_foreign_item(&mut false)?);
871871
}
872872
AstFragment::ForeignItems(items)
873873
}

src/librustc_parse/parser/item.rs

Lines changed: 46 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ impl<'a> Parser<'a> {
515515

516516
generics.where_clause = self.parse_where_clause()?;
517517

518-
let (impl_items, attrs) = self.parse_impl_body()?;
518+
let (impl_items, attrs) = self.parse_item_list(|p, at_end| p.parse_impl_item(at_end))?;
519519

520520
let item_kind = match ty_second {
521521
Some(ty_second) => {
@@ -571,15 +571,21 @@ impl<'a> Parser<'a> {
571571
Ok((Ident::invalid(), item_kind, Some(attrs)))
572572
}
573573

574-
fn parse_impl_body(&mut self) -> PResult<'a, (Vec<P<AssocItem>>, Vec<Attribute>)> {
574+
fn parse_item_list<T>(
575+
&mut self,
576+
mut parse_item: impl FnMut(&mut Parser<'a>, &mut bool) -> PResult<'a, T>,
577+
) -> PResult<'a, (Vec<T>, Vec<Attribute>)> {
575578
self.expect(&token::OpenDelim(token::Brace))?;
576579
let attrs = self.parse_inner_attributes()?;
577580

578-
let mut impl_items = Vec::new();
581+
let mut items = Vec::new();
579582
while !self.eat(&token::CloseDelim(token::Brace)) {
583+
if self.recover_doc_comment_before_brace() {
584+
continue;
585+
}
580586
let mut at_end = false;
581-
match self.parse_impl_item(&mut at_end) {
582-
Ok(impl_item) => impl_items.push(impl_item),
587+
match parse_item(self, &mut at_end) {
588+
Ok(item) => items.push(item),
583589
Err(mut err) => {
584590
err.emit();
585591
if !at_end {
@@ -589,7 +595,30 @@ impl<'a> Parser<'a> {
589595
}
590596
}
591597
}
592-
Ok((impl_items, attrs))
598+
Ok((items, attrs))
599+
}
600+
601+
/// Recover on a doc comment before `}`.
602+
fn recover_doc_comment_before_brace(&mut self) -> bool {
603+
if let token::DocComment(_) = self.token.kind {
604+
if self.look_ahead(1, |tok| tok == &token::CloseDelim(token::Brace)) {
605+
struct_span_err!(
606+
self.diagnostic(),
607+
self.token.span,
608+
E0584,
609+
"found a documentation comment that doesn't document anything",
610+
)
611+
.span_label(self.token.span, "this doc comment doesn't document anything")
612+
.help(
613+
"doc comments must come before what they document, maybe a \
614+
comment was intended with `//`?",
615+
)
616+
.emit();
617+
self.bump();
618+
return true;
619+
}
620+
}
621+
false
593622
}
594623

595624
/// Parses defaultness (i.e., `default` or nothing).
@@ -660,39 +689,8 @@ impl<'a> Parser<'a> {
660689
} else {
661690
// It's a normal trait.
662691
tps.where_clause = self.parse_where_clause()?;
663-
self.expect(&token::OpenDelim(token::Brace))?;
664-
let mut trait_items = vec![];
665-
while !self.eat(&token::CloseDelim(token::Brace)) {
666-
if let token::DocComment(_) = self.token.kind {
667-
if self.look_ahead(1, |tok| tok == &token::CloseDelim(token::Brace)) {
668-
struct_span_err!(
669-
self.diagnostic(),
670-
self.token.span,
671-
E0584,
672-
"found a documentation comment that doesn't document anything",
673-
)
674-
.help(
675-
"doc comments must come before what they document, maybe a \
676-
comment was intended with `//`?",
677-
)
678-
.emit();
679-
self.bump();
680-
continue;
681-
}
682-
}
683-
let mut at_end = false;
684-
match self.parse_trait_item(&mut at_end) {
685-
Ok(item) => trait_items.push(item),
686-
Err(mut e) => {
687-
e.emit();
688-
if !at_end {
689-
self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
690-
break;
691-
}
692-
}
693-
}
694-
}
695-
Ok((ident, ItemKind::Trait(is_auto, unsafety, tps, bounds, trait_items), None))
692+
let (items, attrs) = self.parse_item_list(|p, at_end| p.parse_trait_item(at_end))?;
693+
Ok((ident, ItemKind::Trait(is_auto, unsafety, tps, bounds, items), Some(attrs)))
696694
}
697695
}
698696

@@ -942,26 +940,18 @@ impl<'a> Parser<'a> {
942940
&mut self,
943941
lo: Span,
944942
abi: Option<StrLit>,
945-
visibility: Visibility,
943+
vis: Visibility,
946944
mut attrs: Vec<Attribute>,
947945
) -> PResult<'a, P<Item>> {
948-
self.expect(&token::OpenDelim(token::Brace))?;
949-
950-
attrs.extend(self.parse_inner_attributes()?);
951-
952-
let mut foreign_items = vec![];
953-
while !self.eat(&token::CloseDelim(token::Brace)) {
954-
foreign_items.push(self.parse_foreign_item()?);
955-
}
956-
957-
let prev_span = self.prev_span;
958-
let m = ast::ForeignMod { abi, items: foreign_items };
959-
let invalid = Ident::invalid();
960-
Ok(self.mk_item(lo.to(prev_span), invalid, ItemKind::ForeignMod(m), visibility, attrs))
946+
let (items, iattrs) = self.parse_item_list(|p, at_end| p.parse_foreign_item(at_end))?;
947+
attrs.extend(iattrs);
948+
let span = lo.to(self.prev_span);
949+
let m = ast::ForeignMod { abi, items };
950+
Ok(self.mk_item(span, Ident::invalid(), ItemKind::ForeignMod(m), vis, attrs))
961951
}
962952

963953
/// Parses a foreign item (one in an `extern { ... }` block).
964-
pub fn parse_foreign_item(&mut self) -> PResult<'a, P<ForeignItem>> {
954+
pub fn parse_foreign_item(&mut self, at_end: &mut bool) -> PResult<'a, P<ForeignItem>> {
965955
maybe_whole!(self, NtForeignItem, |ni| ni);
966956

967957
let mut attrs = self.parse_outer_attributes()?;
@@ -973,7 +963,7 @@ impl<'a> Parser<'a> {
973963
self.parse_item_foreign_type()?
974964
} else if self.check_fn_front_matter() {
975965
// FOREIGN FUNCTION ITEM
976-
let (ident, sig, generics, body) = self.parse_fn(&mut false, &mut attrs, |_| true)?;
966+
let (ident, sig, generics, body) = self.parse_fn(at_end, &mut attrs, |_| true)?;
977967
(ident, ForeignItemKind::Fn(sig, generics, body))
978968
} else if self.is_static_global() {
979969
// FOREIGN STATIC ITEM
@@ -991,7 +981,7 @@ impl<'a> Parser<'a> {
991981
)
992982
.emit();
993983
self.parse_item_foreign_static()?
994-
} else if let Some(mac) = self.parse_assoc_macro_invoc("extern", Some(&vis), &mut false)? {
984+
} else if let Some(mac) = self.parse_assoc_macro_invoc("extern", Some(&vis), at_end)? {
995985
(Ident::invalid(), ForeignItemKind::Macro(mac))
996986
} else {
997987
if !attrs.is_empty() {

src/test/pretty/trait-inner-attr.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// pp-exact
2+
3+
trait Foo {
4+
#![allow(bar)]
5+
}
6+
7+
fn main() { }
Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
1-
// Constants (static variables) can be used to match in patterns, but mutable
2-
// statics cannot. This ensures that there's some form of error if this is
3-
// attempted.
1+
// Make sure there's an error when given `extern { ... #[attr] }`.
42

5-
extern crate libc;
3+
fn main() {}
64

75
extern {
8-
static mut rust_dbg_static_mut: libc::c_int;
9-
pub fn rust_dbg_static_mut_check_four();
106
#[cfg(stage37)] //~ ERROR expected item after attributes
117
}
12-
13-
pub fn main() {}

src/test/ui/parser/attrs-after-extern-mod.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: expected item after attributes
2-
--> $DIR/attrs-after-extern-mod.rs:10:19
2+
--> $DIR/attrs-after-extern-mod.rs:6:19
33
|
44
LL | #[cfg(stage37)]
55
| ^
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
fn main() {}
2+
13
extern {
24
/// hi
3-
//~^ ERROR expected item after doc comment
5+
//~^ ERROR found a documentation comment that doesn't document anything
46
}
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
error: expected item after doc comment
2-
--> $DIR/doc-before-extern-rbrace.rs:2:5
1+
error[E0584]: found a documentation comment that doesn't document anything
2+
--> $DIR/doc-before-extern-rbrace.rs:4:5
33
|
44
LL | /// hi
55
| ^^^^^^ this doc comment doesn't document anything
6+
|
7+
= help: doc comments must come before what they document, maybe a comment was intended with `//`?
68

79
error: aborting due to previous error
810

11+
For more information about this error, try `rustc --explain E0584`.

src/test/ui/parser/doc-inside-trait-item.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0584]: found a documentation comment that doesn't document anything
22
--> $DIR/doc-inside-trait-item.rs:3:5
33
|
44
LL | /// empty doc
5-
| ^^^^^^^^^^^^^
5+
| ^^^^^^^^^^^^^ this doc comment doesn't document anything
66
|
77
= help: doc comments must come before what they document, maybe a comment was intended with `//`?
88

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
// error-pattern: expected one of `(`, `async`, `const`, `extern`, `fn`
2+
3+
fn main() {}
4+
25
extern {
36
pub pub fn foo();
47
}

0 commit comments

Comments
 (0)