use ast::{self, Name};
use attr::{self, HasAttrs};
use codemap::{ExpnInfo, MacroAttribute, NameAndSpan, respan};
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(
mut item: Annotatable,
fld: &mut MacroExpander,
) -> SmallVector<Annotatable> {
let mut out_items = SmallVector::zero();
let mut new_attrs = Vec::new();
item = expand_1(item, fld, &mut out_items, &mut new_attrs);
item = item.map_attrs(|_| new_attrs);
let expanded = expand_multi_modified(item, fld);
out_items.push_all(expanded);
out_items
}
fn expand_1(
mut item: Annotatable,
fld: &mut MacroExpander,
out_items: &mut SmallVector<Annotatable>,
new_attrs: &mut Vec<ast::Attribute>,
) -> Annotatable {
while !item.attrs().is_empty() {
let mut attr = None;
item = item.map_attrs(|mut attrs| {
attr = Some(attrs.remove(0));
attrs
});
let attr = attr.unwrap();
if let ast::MetaItemKind::List(ref word, ref vec) = attr.node.value.node {
if word == "cfg_attr" && vec.len() == 2 {
if let ast::NestedMetaItemKind::MetaItem(ref spec) = vec[1].node {
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,
spec.clone());
let mut items = SmallVector::zero();
let mut attrs = Vec::new();
item = expand_2(item, &spec, fld, &mut items, &mut attrs);
for new_item in items {
let new_item = new_item.map_attrs(|mut attrs| {
attrs.push(cond.clone());
attrs
});
out_items.push(new_item);
}
for new_attr in attrs {
let new_spec = respan(attr.span,
ast::NestedMetaItemKind::MetaItem(new_attr.node.value));
let new_attr = fld.cx.attribute(
attr.span,
fld.cx.meta_list(
attr.node.value.span,
word.clone(),
vec![vec[0].clone(), new_spec]));
new_attrs.push(new_attr);
}
continue;
}
}
}
item = expand_2(item, &attr, fld, out_items, new_attrs);
}
item
}
fn expand_2(
mut item: Annotatable,
attr: &ast::Attribute,
fld: &mut MacroExpander,
out_items: &mut SmallVector<Annotatable>,
new_attrs: &mut Vec<ast::Attribute>,
) -> Annotatable {
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::NestedMetaItemKind::MetaItem(ref inner) => {
match inner.node {
ast::MetaItemKind::Word(ref tname) => tname,
_ => {
fld.cx.span_err(titem.span, "malformed `derive` entry");
continue;
}
}
}
_ => {
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()));
item = match expand_3(item, &derive, fld, out_items, tname) {
Expansion::Handled(item) => item,
Expansion::NotHandled(item) => {
not_handled.push((*titem).clone());
item
}
};
}
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);
}
item
} else {
match expand_3(item, attr, fld, out_items, mname) {
Expansion::Handled(item) => item,
Expansion::NotHandled(item) => {
new_attrs.push((*attr).clone());
item
}
}
}
}
enum Expansion {
Handled(Annotatable),
NotHandled(Annotatable),
}
fn expand_3(
item: Annotatable,
attr: &ast::Attribute,
fld: &mut MacroExpander,
out_items: &mut SmallVector<Annotatable>,
mname: Name,
) -> Expansion {
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();
out_items.extend(modified.into_iter()
.flat_map(|ann| expand_annotatable(ann, fld).into_iter()));
Expansion::Handled(item)
}
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),
allow_internal_unstable: true,
}
});
let mut modified = mac.expand(fld.cx,
attr.span,
&attr.node.value,
item);
if modified.len() != 1 {
panic!("syntex limitation: expected 1 output from `#[{}]` but got {}",
mname, modified.len());
}
let modified = modified.pop().unwrap();
fld.cx.bt_pop();
let mut expanded = expand_annotatable(modified, fld);
if expanded.is_empty() {
panic!("syntex limitation: output of `#[{}]` must not expand further",
mname);
}
let last = expanded.pop().unwrap();
out_items.extend(expanded);
Expansion::Handled(last)
}
_ => Expansion::NotHandled(item),
},
_ => Expansion::NotHandled(item),
}
}