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::*;
#[plugin_registrar]
pub fn registrar(registry: &mut Registry) {
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) => {
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")));
new_attrs.push(attribute);
}
{
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"))])));
}
{
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) => {
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")));
new_attrs.push(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;
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);
}
}
_ => {}
}
}
}
}
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() {
} else {
context.sess()
.span_err(item.span,
"packet keyword must come with a struct definition");
}
}
_ => {}
}
}
}
}
}