parser: introduce parse_item_kind as central ItemInfo logic.

this also extracts macro item parsers.
This commit is contained in:
Mazdak Farrokhzad 2020-01-31 08:37:09 +01:00
parent 511dfdb8b3
commit 73d5970cdc
3 changed files with 172 additions and 201 deletions

View File

@ -11,7 +11,7 @@
use rustc_span::BytePos;
use syntax::ast::{self, AttrKind, AttrStyle, AttrVec, Attribute, Ident, DUMMY_NODE_ID};
use syntax::ast::{AssocItem, AssocItemKind, Item, ItemKind, UseTree, UseTreeKind};
use syntax::ast::{Async, Const, Defaultness, IsAuto, PathSegment, StrLit, Unsafe};
use syntax::ast::{Async, Const, Defaultness, IsAuto, PathSegment, Unsafe};
use syntax::ast::{BindingMode, Block, FnDecl, FnSig, Mac, MacArgs, MacDelimiter, Param, SelfKind};
use syntax::ast::{EnumDef, Generics, StructField, TraitRef, Ty, TyKind, Variant, VariantData};
use syntax::ast::{FnHeader, ForeignItem, ForeignItemKind, Mutability, Visibility, VisibilityKind};
@ -83,45 +83,60 @@ fn parse_item_implementation(
});
let lo = self.token.span;
let vis = self.parse_visibility(FollowedByType::No)?;
if self.eat_keyword(kw::Use) {
if let Some(info) = self.parse_item_kind(&mut attrs, macros_allowed, lo, &vis)? {
return Ok(Some(self.mk_item_with_info(attrs, lo, vis, info)));
}
// FAILURE TO PARSE ITEM
match vis.node {
VisibilityKind::Inherited => {}
_ => {
self.struct_span_err(vis.span, "unmatched visibility `pub`")
.span_label(vis.span, "the unmatched visibility")
.help("you likely meant to define an item, e.g., `pub fn foo() {}`")
.emit();
}
}
if !attributes_allowed && !attrs.is_empty() {
self.expected_item_err(&attrs)?;
}
Ok(None)
}
/// Parses one of the items allowed by the flags.
fn parse_item_kind(
&mut self,
attrs: &mut Vec<Attribute>,
macros_allowed: bool,
lo: Span,
vis: &Visibility,
) -> PResult<'a, Option<ItemInfo>> {
let info = if self.eat_keyword(kw::Use) {
// USE ITEM
let item_ = ItemKind::Use(P(self.parse_use_tree()?));
let tree = self.parse_use_tree()?;
self.expect_semi()?;
let span = lo.to(self.prev_span);
let item = self.mk_item(span, Ident::invalid(), item_, vis, attrs);
return Ok(Some(item));
}
if self.check_fn_front_matter() {
(Ident::invalid(), ItemKind::Use(P(tree)), None)
} else if self.check_fn_front_matter() {
// FUNCTION ITEM
let (ident, sig, generics, body) = self.parse_fn(&mut false, &mut attrs, |_| true)?;
let kind = ItemKind::Fn(sig, generics, body);
return self.mk_item_with_info(attrs, lo, vis, (ident, kind, None));
}
if self.eat_keyword(kw::Extern) {
let (ident, sig, generics, body) = self.parse_fn(&mut false, attrs, |_| true)?;
(ident, ItemKind::Fn(sig, generics, body), None)
} else if self.eat_keyword(kw::Extern) {
if self.eat_keyword(kw::Crate) {
// EXTERN CRATE
return Ok(Some(self.parse_item_extern_crate(lo, vis, attrs)?));
self.parse_item_extern_crate()?
} else {
// EXTERN BLOCK
self.parse_item_foreign_mod()?
}
// EXTERN BLOCK
let abi = self.parse_abi();
return Ok(Some(self.parse_item_foreign_mod(lo, abi, vis, attrs)?));
}
if self.is_static_global() {
} else if self.is_static_global() {
// STATIC ITEM
self.bump();
self.bump(); // `static`
let m = self.parse_mutability();
let info = self.parse_item_const(Some(m))?;
return self.mk_item_with_info(attrs, lo, vis, info);
}
if let Const::Yes(const_span) = self.parse_constness() {
self.parse_item_const(Some(m))?
} else if let Const::Yes(const_span) = self.parse_constness() {
// CONST ITEM
if self.eat_keyword(kw::Mut) {
let prev_span = self.prev_span;
@ -136,18 +151,13 @@ fn parse_item_implementation(
.emit();
}
let info = self.parse_item_const(None)?;
return self.mk_item_with_info(attrs, lo, vis, info);
}
if self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Trait, kw::Auto]) {
self.parse_item_const(None)?
} else if self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Trait, kw::Auto])
{
// UNSAFE TRAIT ITEM
let unsafety = self.parse_unsafety();
let info = self.parse_item_trait(lo, unsafety)?;
return self.mk_item_with_info(attrs, lo, vis, info);
}
if self.check_keyword(kw::Impl)
self.parse_item_trait(lo, unsafety)?
} 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])
{
@ -155,58 +165,48 @@ fn parse_item_implementation(
let defaultness = self.parse_defaultness();
let unsafety = self.parse_unsafety();
self.expect_keyword(kw::Impl)?;
let info = self.parse_item_impl(unsafety, defaultness)?;
return self.mk_item_with_info(attrs, lo, vis, info);
}
if self.eat_keyword(kw::Mod) {
self.parse_item_impl(unsafety, defaultness)?
} else if self.eat_keyword(kw::Mod) {
// MODULE ITEM
let info = self.parse_item_mod(&attrs[..])?;
return self.mk_item_with_info(attrs, lo, vis, info);
}
if self.eat_keyword(kw::Type) {
self.parse_item_mod(&attrs[..])?
} else if self.eat_keyword(kw::Type) {
// TYPE ITEM
let (ident, ty, generics) = self.parse_type_alias()?;
let kind = ItemKind::TyAlias(ty, generics);
return self.mk_item_with_info(attrs, lo, vis, (ident, kind, None));
}
if self.eat_keyword(kw::Enum) {
(ident, ItemKind::TyAlias(ty, generics), None)
} else if self.eat_keyword(kw::Enum) {
// ENUM ITEM
let info = self.parse_item_enum()?;
return self.mk_item_with_info(attrs, lo, vis, info);
}
if self.check_keyword(kw::Trait)
self.parse_item_enum()?
} else if self.check_keyword(kw::Trait)
|| (self.check_keyword(kw::Auto) && self.is_keyword_ahead(1, &[kw::Trait]))
{
// TRAIT ITEM
let info = self.parse_item_trait(lo, Unsafe::No)?;
return self.mk_item_with_info(attrs, lo, vis, info);
}
if self.eat_keyword(kw::Struct) {
self.parse_item_trait(lo, Unsafe::No)?
} else if self.eat_keyword(kw::Struct) {
// STRUCT ITEM
let info = self.parse_item_struct()?;
return self.mk_item_with_info(attrs, lo, vis, info);
}
if self.is_union_item() {
self.parse_item_struct()?
} else if self.is_union_item() {
// UNION ITEM
self.bump();
let info = self.parse_item_union()?;
return self.mk_item_with_info(attrs, lo, vis, info);
}
if let Some(macro_def) = self.eat_macro_def(&attrs, &vis, lo)? {
return Ok(Some(macro_def));
}
if vis.node.is_pub() && self.check_ident() && self.look_ahead(1, |t| *t != token::Not) {
self.bump(); // `union`
self.parse_item_union()?
} else if self.eat_keyword(kw::Macro) {
// MACROS 2.0 ITEM
self.parse_item_decl_macro(lo)?
} else if self.is_macro_rules_item() {
// MACRO_RULES ITEM
self.parse_item_macro_rules(vis)?
} else if vis.node.is_pub()
&& self.check_ident()
&& self.look_ahead(1, |t| *t != token::Not)
{
self.recover_missing_kw_before_item()?;
}
self.parse_macro_use_or_failure(attrs, macros_allowed, attributes_allowed, lo, vis)
return Ok(None);
} else if macros_allowed && self.token.is_path_start() {
// MACRO INVOCATION ITEM
self.parse_item_macro(vis)?
} else {
return Ok(None);
};
Ok(Some(info))
}
/// Recover on encountering a struct or method definition where the user
@ -312,11 +312,11 @@ pub(super) fn mk_item_with_info(
lo: Span,
vis: Visibility,
info: ItemInfo,
) -> PResult<'a, Option<P<Item>>> {
) -> P<Item> {
let (ident, item, extra_attrs) = info;
let span = lo.to(self.prev_span);
let attrs = Self::maybe_append(attrs, extra_attrs);
Ok(Some(self.mk_item(span, ident, item, vis, attrs)))
self.mk_item(span, ident, item, vis, attrs)
}
fn maybe_append<T>(mut lhs: Vec<T>, mut rhs: Option<Vec<T>>) -> Vec<T> {
@ -326,49 +326,20 @@ fn maybe_append<T>(mut lhs: Vec<T>, mut rhs: Option<Vec<T>>) -> Vec<T> {
lhs
}
/// This is the fall-through for parsing items.
fn parse_macro_use_or_failure(
&mut self,
attrs: Vec<Attribute>,
macros_allowed: bool,
attributes_allowed: bool,
lo: Span,
visibility: Visibility,
) -> PResult<'a, Option<P<Item>>> {
if macros_allowed
&& self.token.is_path_start()
&& !(self.is_async_fn() && self.token.span.rust_2015())
{
// MACRO INVOCATION ITEM
/// Parses an item macro, e.g., `item!();`.
fn parse_item_macro(&mut self, vis: &Visibility) -> PResult<'a, ItemInfo> {
self.complain_if_pub_macro(&vis.node, vis.span);
let prev_span = self.prev_span;
self.complain_if_pub_macro(&visibility.node, prev_span);
// Item macro
let path = self.parse_path(PathStyle::Mod)?;
self.expect(&token::Not)?;
let args = self.parse_mac_args()?;
if args.need_semicolon() && !self.eat(&token::Semi) {
self.report_invalid_macro_expansion_item();
}
let hi = self.prev_span;
let mac = Mac { path, args, prior_type_ascription: self.last_type_ascription };
let item =
self.mk_item(lo.to(hi), Ident::invalid(), ItemKind::Mac(mac), visibility, attrs);
return Ok(Some(item));
// Item macro
let path = self.parse_path(PathStyle::Mod)?;
self.expect(&token::Not)?;
let args = self.parse_mac_args()?;
if args.need_semicolon() && !self.eat(&token::Semi) {
self.report_invalid_macro_expansion_item();
}
// FAILURE TO PARSE ITEM
match visibility.node {
VisibilityKind::Inherited => {}
_ => return Err(self.struct_span_err(self.prev_span, "unmatched visibility `pub`")),
}
if !attributes_allowed && !attrs.is_empty() {
self.expected_item_err(&attrs)?;
}
Ok(None)
let mac = Mac { path, args, prior_type_ascription: self.last_type_ascription };
Ok((Ident::invalid(), ItemKind::Mac(mac), None))
}
/// Emits an expected-item-after-attributes error.
@ -874,12 +845,7 @@ fn parse_ident_or_underscore(&mut self) -> PResult<'a, ast::Ident> {
/// extern crate foo;
/// extern crate bar as foo;
/// ```
fn parse_item_extern_crate(
&mut self,
lo: Span,
visibility: Visibility,
attrs: Vec<Attribute>,
) -> PResult<'a, P<Item>> {
fn parse_item_extern_crate(&mut self) -> PResult<'a, ItemInfo> {
// Accept `extern crate name-like-this` for better diagnostics
let orig_name = self.parse_crate_name_with_dashes()?;
let (item_name, orig_name) = if let Some(rename) = self.parse_rename()? {
@ -888,9 +854,7 @@ fn parse_item_extern_crate(
(orig_name, None)
};
self.expect_semi()?;
let span = lo.to(self.prev_span);
Ok(self.mk_item(span, item_name, ItemKind::ExternCrate(orig_name), visibility, attrs))
Ok((item_name, ItemKind::ExternCrate(orig_name), None))
}
fn parse_crate_name_with_dashes(&mut self) -> PResult<'a, ast::Ident> {
@ -933,8 +897,7 @@ fn parse_crate_name_with_dashes(&mut self) -> PResult<'a, ast::Ident> {
/// Parses `extern` for foreign ABIs modules.
///
/// `extern` is expected to have been
/// consumed before calling this method.
/// `extern` is expected to have been consumed before calling this method.
///
/// # Examples
///
@ -942,18 +905,11 @@ fn parse_crate_name_with_dashes(&mut self) -> PResult<'a, ast::Ident> {
/// extern "C" {}
/// extern {}
/// ```
fn parse_item_foreign_mod(
&mut self,
lo: Span,
abi: Option<StrLit>,
vis: Visibility,
mut attrs: Vec<Attribute>,
) -> PResult<'a, P<Item>> {
let (items, iattrs) = self.parse_item_list(|p, at_end| p.parse_foreign_item(at_end))?;
attrs.extend(iattrs);
let span = lo.to(self.prev_span);
let m = ast::ForeignMod { abi, items };
Ok(self.mk_item(span, Ident::invalid(), ItemKind::ForeignMod(m), vis, attrs))
fn parse_item_foreign_mod(&mut self) -> PResult<'a, ItemInfo> {
let abi = self.parse_abi(); // ABI?
let (items, attrs) = self.parse_item_list(|p, at_end| p.parse_foreign_item(at_end))?;
let module = ast::ForeignMod { abi, items };
Ok((Ident::invalid(), ItemKind::ForeignMod(module), Some(attrs)))
}
/// Parses a foreign item (one in an `extern { ... }` block).
@ -1386,64 +1342,72 @@ fn parse_name_and_ty(
})
}
/// Parses a declarative macro 2.0 definition.
/// The `macro` keyword has already been parsed.
fn parse_item_decl_macro(&mut self, lo: Span) -> PResult<'a, ItemInfo> {
let ident = self.parse_ident()?;
let body = if self.check(&token::OpenDelim(token::Brace)) {
self.parse_mac_args()?
} else if self.check(&token::OpenDelim(token::Paren)) {
let params = self.parse_token_tree();
let pspan = params.span();
let body = if self.check(&token::OpenDelim(token::Brace)) {
self.parse_token_tree()
} else {
return self.unexpected();
};
let bspan = body.span();
let tokens = TokenStream::new(vec![
params.into(),
TokenTree::token(token::FatArrow, pspan.between(bspan)).into(),
body.into(),
]);
let dspan = DelimSpan::from_pair(pspan.shrink_to_lo(), bspan.shrink_to_hi());
P(MacArgs::Delimited(dspan, MacDelimiter::Brace, tokens))
} else {
return self.unexpected();
};
self.sess.gated_spans.gate(sym::decl_macro, lo.to(self.prev_span));
Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, legacy: false }), None))
}
/// Is this unambiguously the start of a `macro_rules! foo` item defnition?
fn is_macro_rules_item(&mut self) -> bool {
self.check_keyword(sym::macro_rules)
&& self.look_ahead(1, |t| *t == token::Not)
&& self.look_ahead(2, |t| t.is_ident())
}
/// Parses a legacy `macro_rules! foo { ... }` declarative macro.
fn parse_item_macro_rules(&mut self, vis: &Visibility) -> PResult<'a, ItemInfo> {
self.complain_if_pub_macro(&vis.node, vis.span);
self.expect_keyword(sym::macro_rules)?; // `macro_rules`
self.expect(&token::Not)?; // `!`
let ident = self.parse_ident()?;
let body = self.parse_mac_args()?;
if body.need_semicolon() && !self.eat(&token::Semi) {
self.report_invalid_macro_expansion_item();
}
Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, legacy: true }), None))
}
pub(super) fn eat_macro_def(
&mut self,
attrs: &[Attribute],
vis: &Visibility,
lo: Span,
) -> PResult<'a, Option<P<Item>>> {
let (ident, def) = if self.eat_keyword(kw::Macro) {
let ident = self.parse_ident()?;
let body = if self.check(&token::OpenDelim(token::Brace)) {
self.parse_mac_args()?
} else if self.check(&token::OpenDelim(token::Paren)) {
let params = self.parse_token_tree();
let pspan = params.span();
let body = if self.check(&token::OpenDelim(token::Brace)) {
self.parse_token_tree()
} else {
return self.unexpected();
};
let bspan = body.span();
let tokens = TokenStream::new(vec![
params.into(),
TokenTree::token(token::FatArrow, pspan.between(bspan)).into(),
body.into(),
]);
let dspan = DelimSpan::from_pair(pspan.shrink_to_lo(), bspan.shrink_to_hi());
P(MacArgs::Delimited(dspan, MacDelimiter::Brace, tokens))
} else {
return self.unexpected();
};
(ident, ast::MacroDef { body, legacy: false })
} else if self.check_keyword(sym::macro_rules)
&& self.look_ahead(1, |t| *t == token::Not)
&& self.look_ahead(2, |t| t.is_ident())
{
let prev_span = self.prev_span;
self.complain_if_pub_macro(&vis.node, prev_span);
self.bump();
self.bump();
let ident = self.parse_ident()?;
let body = self.parse_mac_args()?;
if body.need_semicolon() && !self.eat(&token::Semi) {
self.report_invalid_macro_expansion_item();
}
(ident, ast::MacroDef { body, legacy: true })
let info = if self.eat_keyword(kw::Macro) {
self.parse_item_decl_macro(lo)?
} else if self.is_macro_rules_item() {
self.parse_item_macro_rules(vis)?
} else {
return Ok(None);
};
let span = lo.to(self.prev_span);
if !def.legacy {
self.sess.gated_spans.gate(sym::decl_macro, span);
}
Ok(Some(self.mk_item(span, ident, ItemKind::MacroDef(def), vis.clone(), attrs.to_vec())))
Ok(Some(self.mk_item_with_info(attrs.to_vec(), lo, vis.clone(), info)))
}
fn complain_if_pub_macro(&self, vis: &VisibilityKind, sp: Span) {

View File

@ -1,3 +1,2 @@
#![feature(pub_restricted)]
pub(crate) () fn foo() {} //~ unmatched visibility
//~^ ERROR expected item, found `(`

View File

@ -1,8 +1,16 @@
error: unmatched visibility `pub`
--> $DIR/pub-restricted-error-fn.rs:3:10
--> $DIR/pub-restricted-error-fn.rs:1:1
|
LL | pub(crate) () fn foo() {}
| ^
| ^^^^^^^^^^ the unmatched visibility
|
= help: you likely meant to define an item, e.g., `pub fn foo() {}`
error: aborting due to previous error
error: expected item, found `(`
--> $DIR/pub-restricted-error-fn.rs:1:12
|
LL | pub(crate) () fn foo() {}
| ^ expected item
error: aborting due to 2 previous errors