1
Fork 0

expand: Turn ast::Crate into a first class expansion target

And stop creating a fake `mod` item for the crate root when expanding a crate.
This commit is contained in:
Vadim Petrochenkov 2021-10-17 19:32:34 +03:00
parent 4919988fe1
commit 141c6cc78e
20 changed files with 203 additions and 159 deletions

View file

@ -48,6 +48,7 @@ pub enum Annotatable {
Param(ast::Param),
FieldDef(ast::FieldDef),
Variant(ast::Variant),
Crate(ast::Crate),
}
impl Annotatable {
@ -66,6 +67,7 @@ impl Annotatable {
Annotatable::Param(ref p) => p.span,
Annotatable::FieldDef(ref sf) => sf.span,
Annotatable::Variant(ref v) => v.span,
Annotatable::Crate(ref c) => c.span,
}
}
@ -84,6 +86,7 @@ impl Annotatable {
Annotatable::Param(p) => p.visit_attrs(f),
Annotatable::FieldDef(sf) => sf.visit_attrs(f),
Annotatable::Variant(v) => v.visit_attrs(f),
Annotatable::Crate(c) => c.visit_attrs(f),
}
}
@ -102,6 +105,7 @@ impl Annotatable {
Annotatable::Param(p) => visitor.visit_param(p),
Annotatable::FieldDef(sf) => visitor.visit_field_def(sf),
Annotatable::Variant(v) => visitor.visit_variant(v),
Annotatable::Crate(c) => visitor.visit_crate(c),
}
}
@ -122,7 +126,8 @@ impl Annotatable {
| Annotatable::GenericParam(..)
| Annotatable::Param(..)
| Annotatable::FieldDef(..)
| Annotatable::Variant(..) => panic!("unexpected annotatable"),
| Annotatable::Variant(..)
| Annotatable::Crate(..) => panic!("unexpected annotatable"),
}
}
@ -220,6 +225,13 @@ impl Annotatable {
_ => panic!("expected variant"),
}
}
pub fn expect_crate(self) -> ast::Crate {
match self {
Annotatable::Crate(krate) => krate,
_ => panic!("expected krate"),
}
}
}
/// Result of an expansion that may need to be retried.
@ -419,6 +431,11 @@ pub trait MacResult {
fn make_variants(self: Box<Self>) -> Option<SmallVec<[ast::Variant; 1]>> {
None
}
fn make_crate(self: Box<Self>) -> Option<ast::Crate> {
// Fn-like macros cannot produce a crate.
unreachable!()
}
}
macro_rules! make_MacEager {

View file

@ -14,13 +14,13 @@ use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{self, AssocCtxt, Visitor};
use rustc_ast::{AstLike, Block, Inline, ItemKind, MacArgs, MacCall};
use rustc_ast::{MacCallStmt, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem};
use rustc_ast::{NodeId, PatKind, Path, StmtKind, Unsafe};
use rustc_ast::{NodeId, PatKind, Path, StmtKind};
use rustc_ast_pretty::pprust;
use rustc_attr::is_builtin_attr;
use rustc_data_structures::map_in_place::MapInPlace;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, FatalError, PResult};
use rustc_errors::{Applicability, PResult};
use rustc_feature::Features;
use rustc_parse::parser::{
AttemptLocalParseRecovery, ForceCollect, Parser, RecoverColon, RecoverComma,
@ -33,7 +33,7 @@ use rustc_session::Limit;
use rustc_span::symbol::{sym, Ident};
use rustc_span::{FileName, LocalExpnId, Span};
use smallvec::{smallvec, SmallVec};
use smallvec::SmallVec;
use std::ops::DerefMut;
use std::path::PathBuf;
use std::rc::Rc;
@ -205,6 +205,7 @@ ast_fragments! {
Variants(SmallVec<[ast::Variant; 1]>) {
"variant"; many fn flat_map_variant; fn visit_variant(); fn make_variants;
}
Crate(ast::Crate) { "crate"; one fn visit_crate; fn visit_crate; fn make_crate; }
}
pub enum SupportsMacroExpansion {
@ -227,9 +228,8 @@ impl AstFragmentKind {
AstFragmentKind::Items
| AstFragmentKind::TraitItems
| AstFragmentKind::ImplItems
| AstFragmentKind::ForeignItems => {
SupportsMacroExpansion::Yes { supports_inner_attrs: true }
}
| AstFragmentKind::ForeignItems
| AstFragmentKind::Crate => SupportsMacroExpansion::Yes { supports_inner_attrs: true },
AstFragmentKind::Arms
| AstFragmentKind::Fields
| AstFragmentKind::FieldPats
@ -288,6 +288,9 @@ impl AstFragmentKind {
AstFragmentKind::OptExpr => {
AstFragment::OptExpr(items.next().map(Annotatable::expect_expr))
}
AstFragmentKind::Crate => {
AstFragment::Crate(items.next().expect("expected exactly one crate").expect_crate())
}
AstFragmentKind::Pat | AstFragmentKind::Ty => {
panic!("patterns and types aren't annotatable")
}
@ -359,9 +362,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
MacroExpander { cx, monotonic }
}
// FIXME: Avoid visiting the crate as a `Mod` item,
// make crate a first class expansion target instead.
pub fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate {
pub fn expand_crate(&mut self, krate: ast::Crate) -> ast::Crate {
let file_path = match self.cx.source_map().span_to_filename(krate.span) {
FileName::Real(name) => name
.into_local_path()
@ -375,52 +376,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
file_path_stack: vec![file_path],
dir_path,
});
let krate_item = AstFragment::Items(smallvec![P(ast::Item {
attrs: krate.attrs,
span: krate.span,
kind: ast::ItemKind::Mod(
Unsafe::No,
ModKind::Loaded(krate.items, Inline::Yes, krate.span)
),
ident: Ident::empty(),
id: ast::DUMMY_NODE_ID,
vis: ast::Visibility {
span: krate.span.shrink_to_lo(),
kind: ast::VisibilityKind::Public,
tokens: None,
},
tokens: None,
})]);
match self.fully_expand_fragment(krate_item).make_items().pop().map(P::into_inner) {
Some(ast::Item {
attrs,
kind: ast::ItemKind::Mod(_, ModKind::Loaded(items, ..)),
..
}) => {
krate.attrs = attrs;
krate.items = items;
}
None => {
// Resolution failed so we return an empty expansion
krate.attrs = vec![];
krate.items = vec![];
}
Some(ast::Item { span, kind, .. }) => {
krate.attrs = vec![];
krate.items = vec![];
self.cx.span_err(
span,
&format!(
"expected crate top-level item to be a module after macro expansion, found {} {}",
kind.article(), kind.descr()
),
);
// FIXME: this workaround issue #84569
FatalError.raise();
}
};
let krate = self.fully_expand_fragment(AstFragment::Crate(krate)).make_crate();
self.cx.trace_macros_diag();
krate
}
@ -708,26 +664,32 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
SyntaxExtensionKind::Attr(expander) => {
self.gate_proc_macro_input(&item);
self.gate_proc_macro_attr_item(span, &item);
let mut fake_tokens = false;
if let Annotatable::Item(item_inner) = &item {
if let ItemKind::Mod(_, mod_kind) = &item_inner.kind {
// FIXME: Collect tokens and use them instead of generating
// fake ones. These are unstable, so it needs to be
// fixed prior to stabilization
// Fake tokens when we are invoking an inner attribute, and:
fake_tokens = matches!(attr.style, ast::AttrStyle::Inner) &&
// We are invoking an attribute on the crate root, or an outline
// module
(item_inner.ident.name.is_empty() || !matches!(mod_kind, ast::ModKind::Loaded(_, Inline::Yes, _)));
}
}
let tokens = if fake_tokens {
rustc_parse::fake_token_stream(
let tokens = match &item {
// FIXME: Collect tokens and use them instead of generating
// fake ones. These are unstable, so it needs to be
// fixed prior to stabilization
// Fake tokens when we are invoking an inner attribute, and
// we are invoking it on an out-of-line module or crate.
Annotatable::Crate(krate) => rustc_parse::fake_token_stream_for_crate(
&self.cx.sess.parse_sess,
&item.into_nonterminal(),
)
} else {
item.into_tokens(&self.cx.sess.parse_sess)
krate,
),
Annotatable::Item(item_inner)
if matches!(attr.style, ast::AttrStyle::Inner)
&& matches!(
item_inner.kind,
ItemKind::Mod(
_,
ModKind::Unloaded | ModKind::Loaded(_, Inline::No, _),
)
) =>
{
rustc_parse::fake_token_stream(
&self.cx.sess.parse_sess,
&item.into_nonterminal(),
)
}
_ => item.into_tokens(&self.cx.sess.parse_sess),
};
let attr_item = attr.unwrap_normal_item();
if let MacArgs::Eq(..) = attr_item.args {
@ -804,7 +766,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
Annotatable::Item(_)
| Annotatable::TraitItem(_)
| Annotatable::ImplItem(_)
| Annotatable::ForeignItem(_) => return,
| Annotatable::ForeignItem(_)
| Annotatable::Crate(..) => return,
Annotatable::Stmt(stmt) => {
// Attributes are stable on item statements,
// but unstable on all other kinds of statements
@ -949,6 +912,7 @@ pub fn parse_ast_fragment<'a>(
RecoverComma::No,
RecoverColon::Yes,
)?),
AstFragmentKind::Crate => AstFragment::Crate(this.parse_crate_mod()?),
AstFragmentKind::Arms
| AstFragmentKind::Fields
| AstFragmentKind::FieldPats
@ -1195,6 +1159,30 @@ macro_rules! assign_id {
}
impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
fn visit_crate(&mut self, krate: &mut ast::Crate) {
let span = krate.span;
let empty_crate =
|| ast::Crate { attrs: Vec::new(), items: Vec::new(), span, is_placeholder: None };
let mut fold_crate = |krate: ast::Crate| {
let mut krate = match self.configure(krate) {
Some(krate) => krate,
None => return empty_crate(),
};
if let Some(attr) = self.take_first_attr(&mut krate) {
return self
.collect_attr(attr, Annotatable::Crate(krate), AstFragmentKind::Crate)
.make_crate();
}
noop_visit_crate(&mut krate, self);
krate
};
// Cannot use `visit_clobber` here, see the FIXME on it.
*krate = fold_crate(mem::replace(krate, empty_crate()));
}
fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
self.cfg.configure_expr(expr);
visit_clobber(expr.deref_mut(), |mut expr| {

View file

@ -46,6 +46,12 @@ pub fn placeholder(
|| P(ast::Pat { id, kind: ast::PatKind::MacCall(mac_placeholder()), span, tokens: None });
match kind {
AstFragmentKind::Crate => AstFragment::Crate(ast::Crate {
attrs: Default::default(),
items: Default::default(),
span,
is_placeholder: Some(id),
}),
AstFragmentKind::Expr => AstFragment::Expr(expr_placeholder()),
AstFragmentKind::OptExpr => AstFragment::OptExpr(Some(expr_placeholder())),
AstFragmentKind::Items => AstFragment::Items(smallvec![P(ast::Item {
@ -354,4 +360,12 @@ impl MutVisitor for PlaceholderExpander {
_ => noop_visit_ty(ty, self),
}
}
fn visit_crate(&mut self, krate: &mut ast::Crate) {
if let Some(id) = krate.is_placeholder {
*krate = self.remove(id).make_crate();
} else {
noop_visit_crate(krate, self)
}
}
}