Skip to content

Commit dbd5017

Browse files
committed
Make Renovators push all items they wish to preserve
1 parent 6004171 commit dbd5017

File tree

4 files changed

+194
-62
lines changed

4 files changed

+194
-62
lines changed

src/libsyntax/ext/base.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ impl Annotatable {
6565
}
6666
}
6767

68+
pub fn node_id(&self) -> ast::NodeId {
69+
match *self {
70+
Annotatable::Item(ref i) => i.id,
71+
Annotatable::TraitItem(ref i) => i.id,
72+
Annotatable::ImplItem(ref i) => i.id,
73+
}
74+
}
75+
6876
pub fn expect_item(self) -> P<ast::Item> {
6977
match self {
7078
Annotatable::Item(i) => i,
@@ -155,23 +163,22 @@ pub trait ItemRenovator {
155163
span: Span,
156164
meta_item: &ast::MetaItem,
157165
item: Annotatable,
158-
push: &mut FnMut(Annotatable))
159-
-> Annotatable;
166+
push: &mut FnMut(Annotatable));
160167
}
161168

162169
impl<F> ItemRenovator for F
163170
where F : Fn(&mut ExtCtxt,
164171
Span,
165172
&ast::MetaItem,
166173
Annotatable,
167-
&mut FnMut(Annotatable)) -> Annotatable
174+
&mut FnMut(Annotatable))
168175
{
169176
fn expand(&self,
170177
ecx: &mut ExtCtxt,
171178
sp: Span,
172179
meta_item: &ast::MetaItem,
173180
item: Annotatable,
174-
push: &mut FnMut(Annotatable)) -> Annotatable {
181+
push: &mut FnMut(Annotatable)) {
175182
(*self)(ecx, sp, meta_item, item, push)
176183
}
177184
}
@@ -450,14 +457,20 @@ pub enum SyntaxExtension {
450457
/// place *and* creating new items based upon it.
451458
///
452459
/// It can be thought of as the union of `MultiDecorator` and `MultiModifier`.
460+
///
461+
/// Renovators must push *all* of the items they wish to preserve
462+
/// and create. This includes the original item they are given.
463+
///
464+
/// This allows renovators to remove the original item as well as
465+
/// create new items if it is desired.
453466
Renovator(Box<ItemRenovator + 'static>),
454467

455468
/// A normal, function-like syntax extension.
456469
///
457470
/// `bytes!` is a `NormalTT`.
458471
///
459472
/// The `bool` dictates whether the contents of the macro can
460-
/// directly use `#[unstable]` things (true == yes).
473+
/// directly use `#[unstable]` things (`true` == yes).
461474
NormalTT(Box<TTMacroExpander + 'static>, Option<Span>, bool),
462475

463476
/// A function-like syntax extension that has an extra ident before

src/libsyntax/ext/expand.rs

Lines changed: 122 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attr
302302
None
303303
}
304304

305-
MultiDecorator(..) | MultiModifier(..) => {
305+
Renovator(..) | MultiDecorator(..) | MultiModifier(..) => {
306306
fld.cx.span_err(path.span,
307307
&format!("`{}` can only be used in attributes", extname));
308308
None
@@ -718,14 +718,23 @@ impl<'a> Folder for PatIdentRenamer<'a> {
718718
}
719719
}
720720

721-
fn expand_annotatable(a: Annotatable,
721+
fn expand_annotatable(original_item: Annotatable,
722722
fld: &mut MacroExpander)
723723
-> SmallVector<Annotatable> {
724-
let a = expand_item_multi_modifier(a, fld);
724+
let a = expand_item_multi_modifier(original_item, fld);
725+
726+
let mut renovated_items = expand_renovators(a.clone(), fld);
727+
728+
// Take the original item out (if any).
729+
let a = if let Some(index) = renovated_items.iter().position(|i| is_original_item(i, &a)) {
730+
renovated_items.remove(index)
731+
} else {
732+
return SmallVector::zero(); // The renovator ate the item.
733+
};
725734

726735
let mut decorator_items = SmallVector::zero();
727736
let mut new_attrs = Vec::new();
728-
let a = expand_renovators_and_decorators(a, fld, &mut decorator_items, &mut new_attrs);
737+
expand_decorators(a.clone(), fld, &mut decorator_items, &mut new_attrs);
729738

730739
let mut new_items: SmallVector<Annotatable> = match a {
731740
Annotatable::Item(it) => match it.node {
@@ -789,7 +798,9 @@ fn expand_annotatable(a: Annotatable,
789798
}
790799
};
791800

801+
new_items.extend(renovated_items.into_iter());
792802
new_items.push_all(decorator_items);
803+
793804
new_items
794805
}
795806

@@ -816,63 +827,131 @@ macro_rules! partition {
816827
partition!(multi_modifiers, MultiModifier);
817828

818829

819-
fn expand_renovators_and_decorators(mut item: Annotatable,
820-
fld: &mut MacroExpander,
821-
decorator_items: &mut SmallVector<Annotatable>,
822-
new_attrs: &mut Vec<ast::Attribute>) -> Annotatable
830+
fn expand_decorators(a: Annotatable,
831+
fld: &mut MacroExpander,
832+
decorator_items: &mut SmallVector<Annotatable>,
833+
new_attrs: &mut Vec<ast::Attribute>)
823834
{
824-
let attrs: Vec<_> = item.attrs().iter().cloned().collect();
825-
826-
for attr in attrs {
835+
for attr in a.attrs() {
827836
let mname = intern(&attr.name());
828-
829-
fld.cx.bt_push(ExpnInfo {
830-
call_site: attr.span,
831-
callee: NameAndSpan {
832-
format: MacroAttribute(mname),
833-
span: Some(attr.span),
834-
// attributes can do whatever they like,
835-
// for now.
836-
allow_internal_unstable: true,
837-
}
838-
});
839-
840-
// we'd ideally decorator_items.push_all(expand_annotatable(ann, fld)),
841-
// but that double-mut-borrows fld
842-
let mut items: SmallVector<Annotatable> = SmallVector::zero();
843-
844837
match fld.cx.syntax_env.find(mname) {
845838
Some(rc) => match *rc {
846839
MultiDecorator(ref dec) => {
847840
attr::mark_used(&attr);
848841

842+
fld.cx.bt_push(ExpnInfo {
843+
call_site: attr.span,
844+
callee: NameAndSpan {
845+
format: MacroAttribute(mname),
846+
span: Some(attr.span),
847+
// attributes can do whatever they like,
848+
// for now.
849+
allow_internal_unstable: true,
850+
}
851+
});
852+
853+
// we'd ideally decorator_items.push_all(expand_annotatable(ann, fld)),
854+
// but that double-mut-borrows fld
855+
let mut items: SmallVector<Annotatable> = SmallVector::zero();
849856
dec.expand(fld.cx,
850857
attr.span,
851858
&attr.node.value,
852-
&item,
859+
&a,
853860
&mut |ann| items.push(ann));
854-
},
855-
Renovator(ref ren) => {
856-
attr::mark_used(&attr);
861+
decorator_items.extend(items.into_iter()
862+
.flat_map(|ann| expand_annotatable(ann, fld).into_iter()));
857863

858-
item = ren.expand(fld.cx,
859-
attr.span,
860-
&attr.node.value,
861-
item,
862-
&mut |ann| items.push(ann));
863-
},
864-
_ => new_attrs.push(attr),
864+
fld.cx.bt_pop();
865+
}
866+
_ => new_attrs.push((*attr).clone()),
865867
},
866-
_ => new_attrs.push(attr),
868+
_ => new_attrs.push((*attr).clone()),
867869
}
870+
}
871+
}
872+
873+
fn is_original_item(item: &Annotatable, original: &Annotatable) -> bool {
874+
item.node_id() == original.node_id()
875+
}
876+
877+
fn expand_renovators(original_item: Annotatable,
878+
fld: &mut MacroExpander) -> Vec<Annotatable>
879+
{
880+
let mut items = Vec::new();
881+
items.push(original_item.clone());
882+
883+
let mut processed_attributes: Vec<ast::Attribute> = Vec::new();
868884

869-
decorator_items.extend(items.into_iter()
870-
.flat_map(|ann| expand_annotatable(ann, fld).into_iter()));
885+
'main_loop: loop {
886+
let item_idx = items.iter().position(|i| is_original_item(i, &original_item));
871887

872-
fld.cx.bt_pop();
888+
let item = if let Some(idx) = item_idx {
889+
items.remove(idx)
890+
} else {
891+
break 'main_loop;
892+
};
893+
894+
// Find the first unprocessed attribute.
895+
let attr = if let Some(attr) = item.attrs().iter().cloned().
896+
find(|i| !processed_attributes.iter().any(|pi| i == pi)) {
897+
attr
898+
} else {
899+
items.push(item); // put the item back.
900+
break 'main_loop;
901+
};
902+
903+
processed_attributes.push(attr.clone());
904+
905+
let macro_name = intern(&attr.name());
906+
907+
match fld.cx.syntax_env.find(macro_name) {
908+
Some(rc) => match *rc {
909+
Renovator(ref dec) => {
910+
attr::mark_used(&attr);
911+
912+
fld.cx.bt_push(ExpnInfo {
913+
call_site: attr.span,
914+
callee: NameAndSpan {
915+
format: MacroAttribute(macro_name),
916+
span: Some(attr.span),
917+
// attributes can do whatever they like,
918+
// for now.
919+
allow_internal_unstable: true,
920+
}
921+
});
922+
923+
let mut new_items: SmallVector<Annotatable> = SmallVector::zero();
924+
dec.expand(fld.cx,
925+
attr.span,
926+
&attr.node.value,
927+
item,
928+
&mut |ann| new_items.push(ann));
929+
930+
let new_items = new_items.into_iter().map(|item| {
931+
// Remove all attributes that are already processed.
932+
let attrs: Vec<_> = item.attrs().iter().cloned().
933+
filter(|a| processed_attributes.iter().any(|pa| pa == a)).collect();
934+
935+
item.fold_attrs(attrs)
936+
});
937+
938+
for new_item in new_items {
939+
if is_original_item(&new_item, &original_item) {
940+
items.push(new_item);
941+
} else {
942+
items.extend(expand_annotatable(new_item, fld).into_iter());
943+
}
944+
}
945+
946+
fld.cx.bt_pop();
947+
},
948+
_ => items.push(item.clone()),
949+
},
950+
_ => items.push(item.clone()),
951+
}
873952
}
874953

875-
item
954+
items
876955
}
877956

878957
fn expand_item_multi_modifier(mut it: Annotatable,

src/test/run-pass-fulldeps/auxiliary/macro_crate_test.rs

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,13 @@ pub fn plugin_registrar(reg: &mut Registry) {
4040
// FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
4141
MultiDecorator(Box::new(expand_duplicate)));
4242
reg.register_syntax_extension(
43-
token::intern("wrap"),
44-
Renovator(Box::new(expand_wrap)));
43+
token::intern("remove"),
44+
// FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
45+
Renovator(Box::new(expand_remove)));
46+
reg.register_syntax_extension(
47+
token::intern("spawn"),
48+
// FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
49+
Renovator(Box::new(expand_spawn)));
4550
}
4651

4752
fn expand_make_a_1(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
@@ -141,23 +146,44 @@ fn expand_duplicate(cx: &mut ExtCtxt,
141146
}
142147
}
143148

144-
fn expand_wrap(cx: &mut ExtCtxt,
145-
sp: Span,
146-
meta_item: &MetaItem,
147-
mut item: Annotatable,
148-
push: &mut FnMut(Annotatable)) -> Annotatable
149+
fn expand_remove(cx: &mut ExtCtxt,
150+
sp: Span,
151+
meta_item: &MetaItem,
152+
mut item: Annotatable,
153+
push: &mut FnMut(Annotatable))
154+
{
155+
// eat the item
156+
}
157+
158+
fn expand_spawn(cx: &mut ExtCtxt,
159+
sp: Span,
160+
meta_item: &MetaItem,
161+
mut item: Annotatable,
162+
push: &mut FnMut(Annotatable))
149163
{
164+
// Keep original item around
165+
push(item.clone());
150166

151167
match item {
152168
Annotatable::Item(item) => {
153-
push(Annotatable::Item(item.clone()));
169+
let base_name = item.ident.name.as_str().to_owned();
154170

155-
Annotatable::Item(P(Item {
156-
attrs: item.attrs.clone(),
157-
..(*quote_item!(cx, enum Foo2 { Bar2, Baz2 }).unwrap()).clone()
158-
}))
171+
for i in 0..5 {
172+
let new_name = format!("{}{}", base_name, i);
173+
174+
let new_ident = ast::Ident {
175+
name: token::intern(&new_name),
176+
..item.ident
177+
};
178+
179+
// Duplicate the item but with a number on the end of the name.
180+
push(Annotatable::Item(P(Item {
181+
ident: new_ident,
182+
..(*item).clone()
183+
})));
184+
}
159185
},
160-
_ => item,
186+
_ => unimplemented!(),
161187
}
162188
}
163189

src/test/run-pass-fulldeps/macro-crate.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,18 @@ impl Qux for i32 {
3333

3434
impl Qux for u8 {}
3535

36-
#[wrap]
36+
#[remove]
37+
struct TypeToBeRemoved
38+
{
39+
foo: NonexistentType,
40+
bar: Baz,
41+
}
42+
43+
#[spawn]
44+
struct Baz {
45+
n: u32,
46+
}
47+
3748
struct Bar;
3849

3950
impl Bar
@@ -52,6 +63,9 @@ pub fn main() {
5263
assert_eq!(x.foo(), 42);
5364
let x = 10u8;
5465
assert_eq!(x.foo(), 0);
66+
67+
let a = Baz4 { n: 15 };
68+
assert_eq!(a.n, 15);
5569
}
5670

5771
fn test<T: PartialEq+Clone>(_: Option<T>) {}

0 commit comments

Comments
 (0)