Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow syntax extensions which modify and decorate #33738

Closed
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Allow syntax extensions which modify and decorate
This adds a new SyntaxExtension type - Renovator.

A renovator is a syntax extension which can modify *and* decorate the
items it is attached to - the union of `MultiModifier` and
`MultiDecorator`.

This allows things such as syntax extensions which modify existing
methods for types, and implement traits all in one.
  • Loading branch information
dylanmckay committed Jun 8, 2016
commit 600417172e3b59d8ace3aeeb3b82a7bc00ab0e30
3 changes: 2 additions & 1 deletion src/librustc_plugin/registry.rs
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ use rustc::session::Session;
use rustc::mir::transform::MirMapPass;

use syntax::ext::base::{SyntaxExtension, NamedSyntaxExtension, NormalTT};
use syntax::ext::base::{IdentTT, MultiModifier, MultiDecorator};
use syntax::ext::base::{IdentTT, MultiModifier, MultiDecorator, Renovator};
use syntax::ext::base::{MacroExpanderFn, MacroRulesTT};
use syntax::codemap::Span;
use syntax::parse::token;
@@ -112,6 +112,7 @@ impl<'a> Registry<'a> {
}
MultiDecorator(ext) => MultiDecorator(ext),
MultiModifier(ext) => MultiModifier(ext),
Renovator(ext) => Renovator(ext),
MacroRulesTT => {
self.sess.err("plugin tried to register a new MacroRulesTT");
return;
33 changes: 33 additions & 0 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
@@ -149,6 +149,33 @@ impl<F> MultiItemModifier for F
}
}

pub trait ItemRenovator {
fn expand(&self,
ecx: &mut ExtCtxt,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable,
push: &mut FnMut(Annotatable))
-> Annotatable;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we can drop the return type and just have implementors push all of the things they're creating/preserving into push?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a good idea, I'm adding this in now.

}

impl<F> ItemRenovator for F
where F : Fn(&mut ExtCtxt,
Span,
&ast::MetaItem,
Annotatable,
&mut FnMut(Annotatable)) -> Annotatable
{
fn expand(&self,
ecx: &mut ExtCtxt,
sp: Span,
meta_item: &ast::MetaItem,
item: Annotatable,
push: &mut FnMut(Annotatable)) -> Annotatable {
(*self)(ecx, sp, meta_item, item, push)
}
}

/// Represents a thing that maps token trees to Macro Results
pub trait TTMacroExpander {
fn expand<'cx>(&self,
@@ -419,6 +446,12 @@ pub enum SyntaxExtension {
/// in-place. More flexible version than Modifier.
MultiModifier(Box<MultiItemModifier + 'static>),

/// A syntax extension that is attached to an item, modifying it in
/// place *and* creating new items based upon it.
///
/// It can be thought of as the union of `MultiDecorator` and `MultiModifier`.
Renovator(Box<ItemRenovator + 'static>),

/// A normal, function-like syntax extension.
///
/// `bytes!` is a `NormalTT`.
70 changes: 43 additions & 27 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
@@ -725,7 +725,7 @@ fn expand_annotatable(a: Annotatable,

let mut decorator_items = SmallVector::zero();
let mut new_attrs = Vec::new();
expand_decorators(a.clone(), fld, &mut decorator_items, &mut new_attrs);
let a = expand_renovators_and_decorators(a, fld, &mut decorator_items, &mut new_attrs);

let mut new_items: SmallVector<Annotatable> = match a {
Annotatable::Item(it) => match it.node {
@@ -816,47 +816,63 @@ macro_rules! partition {
partition!(multi_modifiers, MultiModifier);


fn expand_decorators(a: Annotatable,
fld: &mut MacroExpander,
decorator_items: &mut SmallVector<Annotatable>,
new_attrs: &mut Vec<ast::Attribute>)
fn expand_renovators_and_decorators(mut item: Annotatable,
fld: &mut MacroExpander,
decorator_items: &mut SmallVector<Annotatable>,
new_attrs: &mut Vec<ast::Attribute>) -> Annotatable
{
for attr in a.attrs() {
let attrs: Vec<_> = item.attrs().iter().cloned().collect();

for attr in attrs {
let mname = intern(&attr.name());

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,
}
});

// we'd ideally decorator_items.push_all(expand_annotatable(ann, fld)),
// but that double-mut-borrows fld
let mut items: SmallVector<Annotatable> = SmallVector::zero();

match fld.cx.syntax_env.find(mname) {
Some(rc) => match *rc {
MultiDecorator(ref dec) => {
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,
}
});

// we'd ideally decorator_items.push_all(expand_annotatable(ann, fld)),
// but that double-mut-borrows fld
let mut items: SmallVector<Annotatable> = SmallVector::zero();
dec.expand(fld.cx,
attr.span,
&attr.node.value,
&a,
&item,
&mut |ann| items.push(ann));
decorator_items.extend(items.into_iter()
.flat_map(|ann| expand_annotatable(ann, fld).into_iter()));
},
Renovator(ref ren) => {
attr::mark_used(&attr);

fld.cx.bt_pop();
}
_ => new_attrs.push((*attr).clone()),
item = ren.expand(fld.cx,
attr.span,
&attr.node.value,
item,
&mut |ann| items.push(ann));
},
_ => new_attrs.push(attr),
},
_ => new_attrs.push((*attr).clone()),
_ => new_attrs.push(attr),
}

decorator_items.extend(items.into_iter()
.flat_map(|ann| expand_annotatable(ann, fld).into_iter()));

fld.cx.bt_pop();
}

item
}

fn expand_item_multi_modifier(mut it: Annotatable,
24 changes: 24 additions & 0 deletions src/test/run-pass-fulldeps/auxiliary/macro_crate_test.rs
Original file line number Diff line number Diff line change
@@ -39,6 +39,9 @@ pub fn plugin_registrar(reg: &mut Registry) {
token::intern("duplicate"),
// FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
MultiDecorator(Box::new(expand_duplicate)));
reg.register_syntax_extension(
token::intern("wrap"),
Renovator(Box::new(expand_wrap)));
}

fn expand_make_a_1(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
@@ -138,4 +141,25 @@ fn expand_duplicate(cx: &mut ExtCtxt,
}
}

fn expand_wrap(cx: &mut ExtCtxt,
sp: Span,
meta_item: &MetaItem,
mut item: Annotatable,
push: &mut FnMut(Annotatable)) -> Annotatable
{

match item {
Annotatable::Item(item) => {
push(Annotatable::Item(item.clone()));

Annotatable::Item(P(Item {
attrs: item.attrs.clone(),
..(*quote_item!(cx, enum Foo2 { Bar2, Baz2 }).unwrap()).clone()
}))
},
_ => item,
}
}

pub fn foo() {}
pub fn main() { }
8 changes: 8 additions & 0 deletions src/test/run-pass-fulldeps/macro-crate.rs
Original file line number Diff line number Diff line change
@@ -33,6 +33,14 @@ impl Qux for i32 {

impl Qux for u8 {}

#[wrap]
struct Bar;

impl Bar
{
fn new() -> Self { Bar }
}

pub fn main() {
assert_eq!(1, make_a_1!());
assert_eq!(2, exported_macro!());