#![crate_name = "apply_pub"]
#![crate_type = "dylib"]
#![license = "MIT"]
#![feature(plugin_registrar)]
#![feature(phase)]
extern crate syntax;
extern crate rustc;
use syntax::ast;
use syntax::ptr::P;
use syntax::ast::ViewItem;
use syntax::ast::{MetaItem, Item, StructField, ViewItemUse, ForeignItem};
use syntax::ast::{Method, MethDecl, ItemMac, UnnamedField, NamedField};
use syntax::ast::{Public, ItemFn, ItemImpl, ItemForeignMod, ViewItemExternCrate};
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, SyntaxExtension, ItemModifier};
use syntax::fold::Folder;
use syntax::fold;
use syntax::parse::token;
use syntax::util::small_vector::SmallVector;
use rustc::plugin::Registry;
use self::ItemVariant::{
IsFn,
IsTraitImpl,
IsTypeImpl,
IsExternBlock,
IsOther,
};
const NAME: &'static str = "apply_pub";
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_syntax_extension(token::intern(NAME), SyntaxExtension::Modifier(box Expand));
}
struct ApplyPubFolder {
parent_item_variant: ItemVariant
}
struct Expand;
impl ItemModifier for Expand {
fn expand(&self,
ecx: &mut ExtCtxt,
span: Span,
_meta_item: &ast::MetaItem,
item: P<ast::Item>)
-> P<ast::Item> {
match item.node {
ItemMac(..) => {
ecx.span_err(span,
format!("Can not apply `#[{}]` to a macro invocation", NAME).as_slice());
item
}
_ => {
let expanded = ecx.expander().fold_item(item).into_iter().nth(0).unwrap();
let mut folder = ApplyPubFolder { parent_item_variant: IsOther };
folder.fold_item(expanded).into_iter().nth(0).unwrap()
}
}
}
}
enum ItemVariant {
IsFn,
IsTraitImpl,
IsTypeImpl,
IsExternBlock,
IsOther,
}
impl ItemVariant {
fn is_fn(self) -> bool { match self { IsFn => true, _ => false } }
fn is_trait_impl(self) -> bool { match self { IsTraitImpl => true, _ => false } }
fn is_type_impl(self) -> bool { match self { IsTypeImpl => true, _ => false } }
fn is_extern_block(self) -> bool { match self { IsExternBlock => true, _ => false } }
fn can_be_made_pub(self) -> bool {
!self.is_trait_impl() && !self.is_type_impl() && !self.is_extern_block()
}
}
impl Folder for ApplyPubFolder {
fn fold_view_item(&mut self, vi: ViewItem) -> ViewItem {
let item = fold::noop_fold_view_item(vi, self);
match item.node {
ViewItemExternCrate(..) => item,
ViewItemUse(..) => ViewItem { vis: Public, ..item },
}
}
fn fold_item(&mut self, i: P<Item>) -> SmallVector<P<Item>> {
let parent_item_variant = self.parent_item_variant;
let item_variant = match i.node {
ItemFn(..) => IsFn,
ItemImpl(_, None, _, _) => IsTypeImpl,
ItemImpl(_, _, _, _) => IsTraitImpl,
ItemForeignMod(_) => IsExternBlock,
_ => IsOther,
};
self.parent_item_variant = item_variant;
let items = fold::noop_fold_item(i, self);
self.parent_item_variant = parent_item_variant;
items.into_iter().map(|item| {
item.map(|mut item| {
if !parent_item_variant.is_fn() && item_variant.can_be_made_pub() {
item.vis = Public;
}
item
})
}).collect()
}
fn fold_struct_field(&mut self, sf: StructField) -> StructField {
let mut field = fold::noop_fold_struct_field(sf, self);
match field.node.kind {
NamedField(_, ref mut vis) => *vis = Public,
UnnamedField(ref mut vis) => *vis = Public,
}
field
}
fn fold_method(&mut self, m: P<Method>) -> SmallVector<P<Method>> {
let methods = fold::noop_fold_method(m, self);
if !self.parent_item_variant.is_trait_impl() {
methods.into_iter().map(|method| {
method.map(|mut method| {
match method.node {
MethDecl(_, _, _, _, _, _, _, ref mut vis) => *vis = Public,
_ => (),
}
method
})
}).collect()
} else {
methods
}
}
fn fold_foreign_item(&mut self, ni: P<ForeignItem>) -> P<ForeignItem> {
let foreign_item = fold::noop_fold_foreign_item(ni, self);
foreign_item.map(|mut foreign_item| {
foreign_item.vis = Public;
foreign_item
})
}
}