use ast::{self, Name};
use attr::{self, AttrMetaMethods, HasAttrs};
use codemap::{ExpnInfo, MacroAttribute, NameAndSpan};
use ext::base::*;
use ext::build::AstBuilder;
use ext::expand::{MacroExpander, expand_multi_modified};
use parse::token::intern;
use util::small_vector::SmallVector;
pub fn expand_annotatable(
item: Annotatable,
fld: &mut MacroExpander,
) -> SmallVector<Annotatable> {
let mut decorator_items = SmallVector::zero();
let mut new_attrs = Vec::new();
expand_decorators(&item, fld, &mut decorator_items, &mut new_attrs);
let item = item.fold_attrs(new_attrs);
let mut new_items = expand_multi_modified(item, fld);
new_items.push_all(decorator_items);
new_items
}
fn expand_decorators(
item: &Annotatable,
fld: &mut MacroExpander,
decorator_items: &mut SmallVector<Annotatable>,
new_attrs: &mut Vec<ast::Attribute>,
) {
for attr in item.attrs() {
match attr.node.value.node {
ast::MetaItemKind::List(ref word, ref vec)
if word == "cfg_attr" && vec.len() == 2 =>
{
let cond = fld.cx.attribute(
attr.span,
fld.cx.meta_list(
attr.node.value.span,
intern("cfg").as_str(),
vec[..1].to_vec()));
let spec = fld.cx.attribute(
attr.span,
vec[1].clone());
let mut items = SmallVector::zero();
let mut attrs = Vec::new();
expand_decorators_2(item, &spec, fld, &mut items, &mut attrs);
for item in items {
let item = item.map_attrs(|mut attrs| {
attrs.push(cond.clone());
attrs
});
decorator_items.push(item);
}
for new_attr in attrs {
let new_attr = fld.cx.attribute(
attr.span,
fld.cx.meta_list(
attr.node.value.span,
word.clone(),
vec![vec[0].clone(), new_attr.node.value]));
new_attrs.push(new_attr);
}
}
_ => {
expand_decorators_2(&item, attr, fld, decorator_items, new_attrs);
}
}
}
}
fn expand_decorators_2(
item: &Annotatable,
attr: &ast::Attribute,
fld: &mut MacroExpander,
decorator_items: &mut SmallVector<Annotatable>,
new_attrs: &mut Vec<ast::Attribute>,
) {
let mname = intern(&attr.name());
let mitem = &attr.node.value;
if mname.as_str() == "derive" {
let traits = mitem.meta_item_list().unwrap_or(&[]);
if traits.is_empty() {
fld.cx.span_warn(mitem.span, "empty trait list in `derive`");
}
let mut not_handled = Vec::new();
for titem in traits.iter().rev() {
let tname = match titem.node {
ast::MetaItemKind::Word(ref tname) => tname,
_ => {
fld.cx.span_err(titem.span, "malformed `derive` entry");
continue;
}
};
let tname = intern(&format!("derive_{}", tname));
let derive = fld.cx.attribute(
attr.span,
fld.cx.meta_word(titem.span, tname.as_str()));
let handled = expand_decorators_3(item, &derive, fld, decorator_items, tname);
if !handled {
not_handled.push((*titem).clone());
}
}
if !not_handled.is_empty() {
let derive = fld.cx.attribute(
attr.span,
fld.cx.meta_list(mitem.span, mname.as_str(), not_handled));
new_attrs.push(derive);
}
} else {
let handled = expand_decorators_3(item, attr, fld, decorator_items, mname);
if !handled {
new_attrs.push((*attr).clone());
}
}
}
fn expand_decorators_3(
item: &Annotatable,
attr: &ast::Attribute,
fld: &mut MacroExpander,
decorator_items: &mut SmallVector<Annotatable>,
mname: Name,
) -> bool {
match fld.cx.syntax_env.find(mname) {
Some(rc) => match *rc {
MultiDecorator(ref mac) => {
attr::mark_used(&attr);
fld.cx.bt_push(ExpnInfo {
call_site: attr.span,
callee: NameAndSpan {
format: MacroAttribute(mname),
span: Some(attr.span),
allow_internal_unstable: true,
}
});
let mut modified = Vec::new();
mac.expand(fld.cx, attr.span, &attr.node.value, item,
&mut |item| modified.push(item));
fld.cx.bt_pop();
decorator_items.extend(modified.into_iter()
.flat_map(|ann| expand_annotatable(ann, fld).into_iter()));
true
}
_ => false,
},
_ => false,
}
}