FPS 0.0.3

Flow processing system (under construction).
use rustc::lint::*;
use rustc::middle;
use rustc_plugin::Registry;
use rustc_front::hir;
use syntax::parse::token::intern;
use syntax::parse::token::InternedString;
use syntax::ext;
use syntax::ext::base::MultiModifier;
use syntax::ext::base::ExtCtxt;
use syntax::ext::base::Annotatable;
use syntax::attr;
use syntax::abi;
use syntax::ast;
use syntax::ast::MetaItem;
use syntax::ast::MetaWord;
use syntax::ast::ItemFn;
use syntax::ast::Item;
use syntax::ptr::P;
use syntax::codemap::Span;
use syntax::visit;

use build_macro_def::*;

//fn expand_mapping_mac<'cx>(ctx: &'cx mut ExtCtxt, span: Span, _:&[ast::TokenTree]) -> Box<ext::base::MacResult+'cx> {
//    ext::base::MacEager::expr(ctx.lambda_stmts_1(span, vec![], ast::Ident::new(intern("fn_name"), ast::EMPTY_CTXT)))
//}

#[plugin_registrar]
pub fn registrar(registry: &mut Registry) {
    //registry.register_macro("millefeuille_ffi_map", expand_mapping_mac);
    registry.register_syntax_extension(intern("millefeuille_ffi"),
                                       MultiModifier(Box::new(millefeuille_ffi_expand)));
    registry.register_syntax_extension(intern("millefeuille"),
                                       MultiModifier(Box::new(millefeuille_expand)));
    registry.register_syntax_extension(intern("packet"), MultiModifier(Box::new(packet_expand)));
    
    registry.register_early_lint_pass(Box::new(MillefeuilleEarlyLint {
        packet_def: None,
        millefeuille_spans: vec![],
    }) as EarlyLintPassObject);
    registry.register_late_lint_pass(Box::new(MillefeuilleLateLint) as LateLintPassObject);
}

fn millefeuille_expand(context: &mut ExtCtxt, span: Span, meta_item: &MetaItem, orig_item: Annotatable) -> Annotatable {
    match meta_item.node {
        ast::MetaWord(_) => {}
        _ => {
            context.span_err(meta_item.span,
                             "expected #[millefeuille] without any parameter");
            return orig_item;
        }
    }
    match orig_item.clone() {
        Annotatable::Item(ast_item) => {
            // add millefeuille attribute
            let mut new_attrs = vec![];
            for prev in &ast_item.attrs {
                new_attrs.push(prev.clone());
            }

            {
                let attribute = attr::mk_attr_outer(attr::mk_attr_id(),
                                                    attr::mk_word_item(InternedString::new("millefeuille_function")));
                // attr::mark_used(&attribute);
                new_attrs.push(attribute);
            }
            // add auto inline
            {
                new_attrs.push(attr::mk_attr_outer(attr::mk_attr_id(),
                                                   attr::mk_list_item(InternedString::new("inline"),
                                                                      vec![attr::mk_word_item(InternedString::new("always"))])));
            }
            // return with new attribute
            {
                Annotatable::Item(P(Item {
                    ident: ast_item.ident.clone(),
                    attrs: new_attrs,
                    id: ast_item.id.clone(),
                    node: ast_item.node.clone(),
                    vis: ast_item.vis.clone(),
                    span: ast_item.span.clone(),
                }))
            }
        }
        _ => {
            context.span_err(span, "unexpected annotatable.");
            orig_item
        }
    }
}

struct FfiContext<'a,'b:'a> {
    all_list: Vec<(ast::Ident,ast::Ident)>,
    last_module: ast::Ident,
    expand: &'a mut ExtCtxt<'b>,
}

impl<'a,'b> FfiContext<'a,'b> {
    fn new(name: ast::Ident, expand: &'a mut ExtCtxt<'b>) -> FfiContext<'a,'b> {
        FfiContext {
            all_list: vec![],
            last_module: name,
            expand: expand,
        }
    }
}

impl<'v,'a,'b> visit::Visitor<'v> for FfiContext<'a,'b> {
    fn visit_item(&mut self, item: &'v ast::Item) {
        if let ast::ItemMod(_) = item.node {
            self.last_module = item.ident.clone();
        }
        
        let mut is_millefeuille = false;
        for attr in &item.attrs {
            if let MetaWord(word) = attr.node.value.node.clone() {
                match &*word {
                	"millefeuille" | "millefeuille_function" => {
                		is_millefeuille = true;                		
                	}
                	_ => {
                		
                	}
                }
            }
        }
        
        if is_millefeuille {
            if let ast::ItemFn(_, _, _, _, _, _) = item.node.clone() {
                self.all_list.push((self.last_module, item.ident)); 
            }
        }
        
        let small_vec = ext::expand::expand_item(P(item.clone()), &mut self.expand.expander());
        for sub_item in small_vec.as_slice() {
            visit::walk_item(self, sub_item)
        }
    }
}

fn millefeuille_ffi_expand(context: &mut ExtCtxt, span: Span, meta_item: &MetaItem, orig_item: Annotatable) -> Annotatable {
    match meta_item.node {
        ast::MetaWord(_) => {}
        _ => {
            context.span_err(meta_item.span,
                             "expected #[millefeuille_ffi] without any parameter");
            return orig_item;
        }
    }
    match orig_item.clone() {
        Annotatable::Item(ast_item) => {
            if let ast::ItemMod(prev_module) = ast_item.node.clone() {
            	let mut def_list = vec![];
            	context.insert_macro(build_millefeuille_ffi_map(&def_list));
                
                {
                	let mut local_context = FfiContext::new(ast_item.ident, context);
                	visit::walk_mod(&mut local_context, &prev_module);
                	def_list = local_context.all_list;
                }
                
                context.insert_macro(build_millefeuille_ffi_map(&def_list));
                
                let mut item_vec = vec![];
                for item in &prev_module.items {
                    item_vec.push(item.clone());
                }
                
                Annotatable::Item(P(Item {
                    ident: ast_item.ident.clone(),
                    attrs: ast_item.attrs.clone(),
                    id: ast_item.id.clone(),
                    node: ast::ItemMod( ast::Mod {
                        inner: prev_module.inner.clone(),
                        items: item_vec,
                    }),
                    vis: ast_item.vis.clone(),
                    span: ast_item.span.clone(),
                }))
            } else {
                context.span_err(span, "expected #[millefeuille_ffi] with module definitions");
                orig_item
            }
        }
        _ => {
            context.span_err(span, "unexpected annotatable.");
            orig_item
        }
    }
}

fn packet_expand(context: &mut ExtCtxt, span: Span, meta_item: &MetaItem, orig_item: Annotatable) -> Annotatable {
    match meta_item.node {
        ast::MetaWord(_) => {}
        _ => {
            context.span_err(meta_item.span, "expected #[packet] without any parameter");
            return orig_item;
        }
    }
    match orig_item.clone() {
        Annotatable::Item(ast_item) => {
            // add millefeuille attribute
            let mut new_attrs = vec![];
            for prev in &ast_item.attrs {
                new_attrs.push(prev.clone());
            }

            {
                let attribute = attr::mk_attr_outer(attr::mk_attr_id(),
                                                    attr::mk_word_item(InternedString::new("millefeuille_packet")));
                // attr::mark_used(&attribute);
                new_attrs.push(attribute);
            }
            // return with new attribute
            {
                Annotatable::Item(P(Item {
                    ident: ast_item.ident.clone(),
                    attrs: new_attrs,
                    id: ast_item.id.clone(),
                    node: ast_item.node.clone(),
                    vis: ast_item.vis.clone(),
                    span: ast_item.span.clone(),
                }))
            }
        }
        _ => {
            context.span_err(span, "unexpected annotatable.");
            orig_item
        }
    }
}

declare_lint!(EARLY_LINT, Forbid, "Forbidden rules in millefeuille (early)");
declare_lint!(LATE_LINT, Forbid, "Forbidden rules in millefeuille (late)");

struct MillefeuilleEarlyLint {
    packet_def: Option<Span>,
    millefeuille_spans: Vec<Span>,
}
struct MillefeuilleLateLint;

impl LintPass for MillefeuilleEarlyLint {
    fn get_lints(&self) -> LintArray {
        lint_array!(EARLY_LINT)
    }
}

impl EarlyLintPass for MillefeuilleEarlyLint {
    fn check_item(&mut self, context: &EarlyContext, item: &ast::Item) {
        let mut found: bool = false;
        for attr in &item.attrs {
            if let MetaWord(word) = attr.node.value.node.clone() {
                if found {
                    context.sess.span_err(item.span, "cannot have two or more Packet definitions");
                    break;
                }
                match &*word {
                    "millefeuille_function" => {
                        found = true;
                        // println!("found millefeuille_function");
                        // attr::mark_used(&attr);
                        self.millefeuille_spans.push(item.span.clone());
                    }
                    "millefeuille_packet" => {
                        found = true;
                        if let Some(span) = self.packet_def {
                            context.sess.span_err(item.span, "cannot have two Packet definitions");
                            context.sess.span_note(span, "previous definition was here");
                        } else {
                            self.packet_def = Some(item.span);
                        }
                        // attr::mark_used(&attr);
                    }
                    _ => {}
                }
            }
        }
    }
    fn exit_lint_attrs(&mut self, context: &EarlyContext, _: &[ast::Attribute]) {
        if self.packet_def == None {
            for span in &self.millefeuille_spans {
                context.sess
                       .span_err(span.clone(),
                                 "cannot use millefeuille without the Packet definition");
            }
        }
    }
}

impl LintPass for MillefeuilleLateLint {
    fn get_lints(&self) -> LintArray {
        lint_array!(LATE_LINT)
    }
}

impl LateLintPass for MillefeuilleLateLint {
    fn check_item(&mut self, context: &LateContext, item: &hir::Item) {
        for attr in &item.attrs {
            if let MetaWord(word) = attr.node.value.node.clone() {
                match &*word {
                    "millefeuille_function" => {
                        attr::mark_used(&attr);
                        if let hir::ItemFn(_, unsafety, _, _, _, _) = item.node.clone() {
                            if hir::Unsafety::Unsafe == unsafety {
                                context.sess().span_err(item.span, "unsafe function is not allowed");
                                continue;
                            }
                            if let Some(fn_ty) = context.tcx.node_id_to_type_opt(item.id) {
                                if let middle::ty::TyBareFn(_, fn_ty) = fn_ty.sty {
                                    if hir::Unsafety::Unsafe == fn_ty.unsafety {
                                        context.sess().span_err(item.span, "unsafe function is not allowed");
                                        continue;
                                    }
                                    if abi::Rust != fn_ty.abi {
                                        context.sess().span_err(item.span, "millefeuille must rely on the Rust ABI");
                                        continue;
                                    }
                                    let sig = context.tcx.erase_regions(fn_ty.sig.skip_binder());
                                    if sig.inputs.len() != 1 {
                                        context.sess().span_err(item.span, "must take exactly 1 input parameter");
                                        continue;
                                    }
                                    if let (Some(in_ty), middle::ty::FnConverging(out_ty)) = (sig.inputs.first(), sig.output) {
                                        if let (Some(in_def), Some(out_def)) = (in_ty.ty_to_def_id(), out_ty.ty_to_def_id()) {
                                            if context.tcx.has_attr(in_def, "millefeuille_packet") == false {
                                                context.sess().span_err(item.span, "function input is not a packet");
                                                continue;
                                            }
                                            if context.tcx.has_attr(out_def, "millefeuille_packet") == false {
                                                context.sess().span_err(item.span, "function output is not a packet");
                                                continue;
                                            }
                                        } else {
                                            context.sess().span_err(item.span, "cannot find type definitions");
                                            continue;
                                        }
                                    } else {
                                        context.sess().span_err(item.span, "diverging function is not allowed");
                                        continue;
                                    }

                                } else {
                                    context.sess().span_err(item.span, "not a function");
                                    continue;
                                }
                            } else {
                                context.sess().span_err(item.span, "cannot find type definition");
                                continue;
                            }
                        } else {
                            context.sess()
                                   .span_err(item.span,
                                             "millefeuille keyword must come with function definitions");
                            continue;
                        }
                    }
                    "millefeuille_packet" => {
                        attr::mark_used(&attr);
                        if let hir::ItemStruct(_, _) = item.node.clone() {
                            // struct verification
                        } else {
                            context.sess()
                                   .span_err(item.span,
                                             "packet keyword must come with a struct definition");
                        }
                    }
                    _ => {}
                }
            }
        }
    }
}