#![feature(proc_macro)]
extern crate proc_macro;
use proc_macro::TokenStream;
extern crate syn;
#[macro_use]
extern crate quote;
extern crate regex;
#[macro_use]
extern crate lazy_static;
mod config;
use config::*;
#[proc_macro_attribute]
pub fn fn_mut(attrs: TokenStream, input: TokenStream) -> TokenStream {
let attrs = attrs.to_string();
let config = get_config(&attrs);
let s = input.to_string();
let ast = syn::parse_item(&s).unwrap();
let gen = gen_mut_fn(ast, config);
gen.parse().unwrap()
}
fn into_mut_ty(ty: &mut syn::Ty) {
match ty {
&mut syn::Ty::Ptr(ref mut mut_ty) |
&mut syn::Ty::Rptr(_, ref mut mut_ty) => {
mut_ty.mutability = syn::Mutability::Mutable;
}
&mut syn::Ty::Array(ref mut inner, ..) |
&mut syn::Ty::Slice(ref mut inner) |
&mut syn::Ty::Paren(ref mut inner) => return into_mut_ty(inner),
&mut syn::Ty::Tup(ref mut vec) => {
for ty in vec.iter_mut() {
into_mut_ty(ty);
}
}
&mut syn::Ty::Path(ref mut qualified, ref mut path) => {
if let &mut Some(ref mut qualified) = qualified {
unimplemented!()
}
for seg in path.segments.iter_mut() {
match seg.parameters {
syn::PathParameters::AngleBracketed(ref mut param) => {
for ty in param.types.iter_mut() {
into_mut_ty(ty);
}
},
_ => ()
}
}
}
_ => ()
};
}
fn add_code(ast: &mut syn::Item, is_mut: bool) {
let code = if is_mut {
quote! {
fn code() {
macro_rules! if_mut {
{ $( $code:tt )* } => (
$( $code )*
)
}
macro_rules! if_const {
{ $( $code:tt )* } => ()
}
macro_rules! ptr {
($v:expr) => (
&mut $v
)
}
macro_rules! raw_ptr {
($v:expr) => (
*mut $v
)
}
}
}
} else {
quote! {
fn code() {
macro_rules! if_const {
{ $( $code:tt )* } => (
$( $code )*
)
}
macro_rules! if_mut {
{ $( $code:tt )* } => ()
}
macro_rules! ptr {
($v:expr) => (
&$v
)
}
macro_rules! raw_ptr {
($v:expr) => (
*const $v
)
}
}
}
};
let code = code.into_string();
let syn_code = syn::parse_item(&code).unwrap();
let mut stmts = match syn_code.node {
syn::ItemKind::Fn(_, _, _, _, _, block) => {
block.stmts
}
_ => unreachable!()
};
match ast.node {
syn::ItemKind::Fn(_, _, _, _, _, ref mut block) => {
stmts.extend_from_slice(&block.stmts);
block.stmts = stmts;
},
_ => ()
}
}
fn gen_mut_fn(ast: syn::Item, config: Config) -> quote::Tokens {
let mut const_fn = ast.clone();
add_code(&mut const_fn, false);
let mut mut_fn = ast;
add_code(&mut mut_fn, true);
mut_fn.ident = syn::Ident::from(format!("{}_mut", &mut_fn.ident).as_ref());
match mut_fn.node {
syn::ItemKind::Fn(ref mut header, ..) => {
if header.variadic {
panic!("#[mut_fn] does not support variadic functions");
}
if config.enable_output {
match header.output {
syn::FunctionRetTy::Default => (),
syn::FunctionRetTy::Ty(ref mut t) => {
into_mut_ty(t);
}
}
}
for input in header.inputs.iter_mut() {
match input {
&mut syn::FnArg::SelfRef(ref _lifetime, ref mut mutability) => if config.enable_self {
*mutability = syn::Mutability::Mutable
},
&mut syn::FnArg::SelfValue(ref mut mutability) => if config.enable_self {
*mutability = syn::Mutability::Mutable
},
&mut syn::FnArg::Captured(ref pat, ref mut ty) => {
match pat {
&syn::Pat::Ident(_, ref ident, _) => {
if config.enable_attrs.contains(ident.as_ref()) {
into_mut_ty(ty)
}
},
_ => ()
}
},
_ => ()
}
}
},
_ => panic!("#[mut_fn] used on non-fn type")
}
quote! {
#const_fn
#mut_fn
}
}