Auto merge of #34253 - jseyfried:improve_multi_modifiers, r=nrc
Allow `MultiItemModifier`s to expand into zero or many items Fixes #34223. r? @nrc
This commit is contained in:
commit
3908913db5
@ -12,6 +12,7 @@ pub use self::SyntaxExtension::*;
|
||||
|
||||
use ast;
|
||||
use ast::{Name, PatKind};
|
||||
use attr::HasAttrs;
|
||||
use codemap;
|
||||
use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION};
|
||||
use errors::DiagnosticBuilder;
|
||||
@ -40,29 +41,31 @@ pub enum Annotatable {
|
||||
ImplItem(P<ast::ImplItem>),
|
||||
}
|
||||
|
||||
impl Annotatable {
|
||||
pub fn attrs(&self) -> &[ast::Attribute] {
|
||||
impl HasAttrs for Annotatable {
|
||||
fn attrs(&self) -> &[ast::Attribute] {
|
||||
match *self {
|
||||
Annotatable::Item(ref i) => &i.attrs,
|
||||
Annotatable::TraitItem(ref ti) => &ti.attrs,
|
||||
Annotatable::ImplItem(ref ii) => &ii.attrs,
|
||||
Annotatable::Item(ref item) => &item.attrs,
|
||||
Annotatable::TraitItem(ref trait_item) => &trait_item.attrs,
|
||||
Annotatable::ImplItem(ref impl_item) => &impl_item.attrs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fold_attrs(self, attrs: Vec<ast::Attribute>) -> Annotatable {
|
||||
fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self {
|
||||
match self {
|
||||
Annotatable::Item(i) => Annotatable::Item(i.map(|i| ast::Item {
|
||||
attrs: attrs,
|
||||
..i
|
||||
})),
|
||||
Annotatable::TraitItem(i) => Annotatable::TraitItem(i.map(|ti| {
|
||||
ast::TraitItem { attrs: attrs, ..ti }
|
||||
})),
|
||||
Annotatable::ImplItem(i) => Annotatable::ImplItem(i.map(|ii| {
|
||||
ast::ImplItem { attrs: attrs, ..ii }
|
||||
})),
|
||||
Annotatable::Item(item) => Annotatable::Item(item.map_attrs(f)),
|
||||
Annotatable::TraitItem(trait_item) => Annotatable::TraitItem(trait_item.map_attrs(f)),
|
||||
Annotatable::ImplItem(impl_item) => Annotatable::ImplItem(impl_item.map_attrs(f)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Annotatable {
|
||||
pub fn attrs(&self) -> &[ast::Attribute] {
|
||||
HasAttrs::attrs(self)
|
||||
}
|
||||
pub fn fold_attrs(self, attrs: Vec<ast::Attribute>) -> Annotatable {
|
||||
self.map_attrs(|_| attrs)
|
||||
}
|
||||
|
||||
pub fn expect_item(self) -> P<ast::Item> {
|
||||
match self {
|
||||
@ -129,9 +132,7 @@ impl<F> MultiItemDecorator for F
|
||||
}
|
||||
}
|
||||
|
||||
// A more flexible ItemKind::Modifier (ItemKind::Modifier should go away, eventually, FIXME).
|
||||
// meta_item is the annotation, item is the item being modified, parent_item
|
||||
// is the impl or trait item is declared in if item is part of such a thing.
|
||||
// `meta_item` is the annotation, and `item` is the item being modified.
|
||||
// FIXME Decorators should follow the same pattern too.
|
||||
pub trait MultiItemModifier {
|
||||
fn expand(&self,
|
||||
@ -139,22 +140,26 @@ pub trait MultiItemModifier {
|
||||
span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
item: Annotatable)
|
||||
-> Annotatable;
|
||||
-> Vec<Annotatable>;
|
||||
}
|
||||
|
||||
impl<F> MultiItemModifier for F
|
||||
where F: Fn(&mut ExtCtxt,
|
||||
Span,
|
||||
&ast::MetaItem,
|
||||
Annotatable) -> Annotatable
|
||||
impl<F, T> MultiItemModifier for F
|
||||
where F: Fn(&mut ExtCtxt, Span, &ast::MetaItem, Annotatable) -> T,
|
||||
T: Into<Vec<Annotatable>>,
|
||||
{
|
||||
fn expand(&self,
|
||||
ecx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
item: Annotatable)
|
||||
-> Annotatable {
|
||||
(*self)(ecx, span, meta_item, item)
|
||||
-> Vec<Annotatable> {
|
||||
(*self)(ecx, span, meta_item, item).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<Annotatable>> for Annotatable {
|
||||
fn into(self) -> Vec<Annotatable> {
|
||||
vec![self]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ use ast::{Local, Ident, Mac_, Name, SpannedIdent};
|
||||
use ast::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind};
|
||||
use ast::TokenTree;
|
||||
use ast;
|
||||
use attr::HasAttrs;
|
||||
use ext::mtwt;
|
||||
use ext::build::AstBuilder;
|
||||
use attr;
|
||||
@ -724,11 +725,7 @@ impl<'a> Folder for PatIdentRenamer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_annotatable(a: Annotatable,
|
||||
fld: &mut MacroExpander)
|
||||
-> SmallVector<Annotatable> {
|
||||
let a = expand_item_multi_modifier(a, fld);
|
||||
|
||||
fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
|
||||
let new_items: SmallVector<Annotatable> = match a {
|
||||
Annotatable::Item(it) => match it.node {
|
||||
ast::ItemKind::Mac(..) => {
|
||||
@ -796,29 +793,6 @@ fn decorate(a: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable>
|
||||
new_items
|
||||
}
|
||||
|
||||
// Partition a set of attributes into one kind of attribute, and other kinds.
|
||||
macro_rules! partition {
|
||||
($fn_name: ident, $variant: ident) => {
|
||||
#[allow(deprecated)] // The `allow` is needed because the `Modifier` variant might be used.
|
||||
fn $fn_name(attrs: &[ast::Attribute],
|
||||
fld: &MacroExpander)
|
||||
-> (Vec<ast::Attribute>, Vec<ast::Attribute>) {
|
||||
attrs.iter().cloned().partition(|attr| {
|
||||
match fld.cx.syntax_env.find(intern(&attr.name())) {
|
||||
Some(rc) => match *rc {
|
||||
$variant(..) => true,
|
||||
_ => false
|
||||
},
|
||||
_ => false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partition!(multi_modifiers, MultiModifier);
|
||||
|
||||
|
||||
fn expand_decorators(a: Annotatable,
|
||||
fld: &mut MacroExpander,
|
||||
decorator_items: &mut SmallVector<Annotatable>,
|
||||
@ -864,46 +838,41 @@ fn expand_decorators(a: Annotatable,
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_item_multi_modifier(mut it: Annotatable,
|
||||
fld: &mut MacroExpander)
|
||||
-> Annotatable {
|
||||
let (modifiers, other_attrs) = multi_modifiers(it.attrs(), fld);
|
||||
|
||||
// Update the attrs, leave everything else alone. Is this mutation really a good idea?
|
||||
it = it.fold_attrs(other_attrs);
|
||||
|
||||
if modifiers.is_empty() {
|
||||
return it
|
||||
}
|
||||
|
||||
for attr in &modifiers {
|
||||
let mname = intern(&attr.name());
|
||||
|
||||
match fld.cx.syntax_env.find(mname) {
|
||||
Some(rc) => match *rc {
|
||||
MultiModifier(ref mac) => {
|
||||
attr::mark_used(attr);
|
||||
fld.cx.bt_push(ExpnInfo {
|
||||
call_site: attr.span,
|
||||
callee: NameAndSpan {
|
||||
format: MacroAttribute(mname),
|
||||
span: Some(attr.span),
|
||||
// attributes can do whatever they like,
|
||||
// for now
|
||||
allow_internal_unstable: true,
|
||||
}
|
||||
});
|
||||
it = mac.expand(fld.cx, attr.span, &attr.node.value, it);
|
||||
fld.cx.bt_pop();
|
||||
fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
|
||||
let mut multi_modifier = None;
|
||||
item = item.map_attrs(|mut attrs| {
|
||||
for i in 0..attrs.len() {
|
||||
if let Some(extension) = fld.cx.syntax_env.find(intern(&attrs[i].name())) {
|
||||
if let MultiModifier(..) = *extension {
|
||||
multi_modifier = Some((attrs.remove(i), extension));
|
||||
break;
|
||||
}
|
||||
_ => unreachable!()
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
attrs
|
||||
});
|
||||
|
||||
match multi_modifier {
|
||||
None => expand_multi_modified(item, fld),
|
||||
Some((attr, extension)) => match *extension {
|
||||
MultiModifier(ref mac) => {
|
||||
attr::mark_used(&attr);
|
||||
fld.cx.bt_push(ExpnInfo {
|
||||
call_site: attr.span,
|
||||
callee: NameAndSpan {
|
||||
format: MacroAttribute(intern(&attr.name())),
|
||||
span: Some(attr.span),
|
||||
// attributes can do whatever they like, for now
|
||||
allow_internal_unstable: true,
|
||||
}
|
||||
});
|
||||
let modified = mac.expand(fld.cx, attr.span, &attr.node.value, item);
|
||||
fld.cx.bt_pop();
|
||||
modified.into_iter().flat_map(|it| expand_annotatable(it, fld)).collect()
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// Expansion may have added new ItemKind::Modifiers.
|
||||
expand_item_multi_modifier(it, fld)
|
||||
}
|
||||
|
||||
fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
|
||||
|
@ -62,15 +62,16 @@ fn expand_identity(cx: &mut ExtCtxt, _span: Span, tts: &[TokenTree])
|
||||
fn expand_into_foo_multi(cx: &mut ExtCtxt,
|
||||
sp: Span,
|
||||
attr: &MetaItem,
|
||||
it: Annotatable) -> Annotatable {
|
||||
it: Annotatable) -> Vec<Annotatable> {
|
||||
match it {
|
||||
Annotatable::Item(it) => {
|
||||
Annotatable::Item(it) => vec![
|
||||
Annotatable::Item(P(Item {
|
||||
attrs: it.attrs.clone(),
|
||||
..(*quote_item!(cx, enum Foo2 { Bar2, Baz2 }).unwrap()).clone()
|
||||
}))
|
||||
}
|
||||
Annotatable::ImplItem(it) => {
|
||||
})),
|
||||
Annotatable::Item(quote_item!(cx, enum Foo3 { Bar }).unwrap()),
|
||||
],
|
||||
Annotatable::ImplItem(it) => vec![
|
||||
quote_item!(cx, impl X { fn foo(&self) -> i32 { 42 } }).unwrap().and_then(|i| {
|
||||
match i.node {
|
||||
ItemKind::Impl(_, _, _, _, _, mut items) => {
|
||||
@ -79,8 +80,8 @@ fn expand_into_foo_multi(cx: &mut ExtCtxt,
|
||||
_ => unreachable!("impl parsed to something other than impl")
|
||||
}
|
||||
})
|
||||
}
|
||||
Annotatable::TraitItem(it) => {
|
||||
],
|
||||
Annotatable::TraitItem(it) => vec![
|
||||
quote_item!(cx, trait X { fn foo(&self) -> i32 { 0 } }).unwrap().and_then(|i| {
|
||||
match i.node {
|
||||
ItemKind::Trait(_, _, _, mut items) => {
|
||||
@ -89,7 +90,7 @@ fn expand_into_foo_multi(cx: &mut ExtCtxt,
|
||||
_ => unreachable!("trait parsed to something other than trait")
|
||||
}
|
||||
})
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,8 @@ pub fn main() {
|
||||
assert_eq!(Foo2::Bar2, Foo2::Bar2);
|
||||
test(None::<Foo2>);
|
||||
|
||||
let _ = Foo3::Bar;
|
||||
|
||||
let x = 10i32;
|
||||
assert_eq!(x.foo(), 42);
|
||||
let x = 10u8;
|
||||
|
Loading…
x
Reference in New Issue
Block a user