extern crate proc_macro;
use proc_macro::Delimiter;
use proc_macro::Group;
use proc_macro::Spacing;
use proc_macro::TokenStream;
use proc_macro::TokenTree;
use std::iter::FromIterator;
macro_rules! group {
($delim:expr, $ts:expr) => {{
let _tt: TokenTree = ::proc_macro::Group::new($delim, $ts).into();
_tt
}};
($delim:expr) => {
group!($delim, ::proc_macro::TokenStream::new())
};
}
macro_rules! ident {
($name:expr, $span:expr) => {{
let _tt: TokenTree = ::proc_macro::Ident::new($name, $span).into();
_tt
}};
($name:expr) => {
ident!($name, ::proc_macro::Span::call_site())
};
}
macro_rules! punct {
($ch:expr, $spacing:expr) => {{
let _tt: TokenTree = ::proc_macro::Punct::new($ch, $spacing).into();
_tt
}};
}
macro_rules! token_stream {
($($tt:expr,)*) => {
{
let _v: Vec<::proc_macro::TokenTree> = vec![$($tt),*];
let _ts: TokenStream = ::proc_macro::TokenStream::from_iter(_v.into_iter());
_ts
}
};
($($tt:expr),*) => {
{
let _v: Vec<::proc_macro::TokenTree> = vec![$($tt),*];
let _ts: TokenStream = ::proc_macro::TokenStream::from_iter(_v.into_iter());
_ts
}
};
}
#[rustfmt::skip]
fn protected_body(fn_body: Group) -> TokenTree {
group!(Delimiter::Brace, token_stream!(
group!(Delimiter::Brace, token_stream!(
punct!(':', Spacing::Joint),
punct!(':', Spacing::Alone),
ident!("qadapt"),
punct!(':', Spacing::Joint),
punct!(':', Spacing::Alone),
ident!("enter_protected"),
group!(Delimiter::Parenthesis)
)),
ident!("let"),
ident!("__ret__"),
punct!('=', Spacing::Alone),
fn_body.into(),
punct!(';', Spacing::Alone),
punct!('#', Spacing::Alone),
group!(Delimiter::Bracket, token_stream!(
ident!("allow"),
group!(Delimiter::Parenthesis, token_stream!(
ident!("unreachable_code")
))
)),
group!(Delimiter::Brace, token_stream!(
group!(Delimiter::Brace, token_stream!(
punct!(':', Spacing::Joint),
punct!(':', Spacing::Alone),
ident!("qadapt"),
punct!(':', Spacing::Joint),
punct!(':', Spacing::Alone),
ident!("exit_protected"),
group!(Delimiter::Parenthesis)
)),
ident!("__ret__")
))
))
}
fn escape_return(ts: TokenStream) -> TokenStream {
let mut protected: Vec<TokenTree> = Vec::new();
let mut in_closure: bool = false;
let mut tt_iter = ts.into_iter();
while let Some(tt) = tt_iter.next() {
let tokens = match tt {
TokenTree::Group(ref g) if g.delimiter() == Delimiter::Brace && !in_closure => {
vec![group!(Delimiter::Brace, escape_return(g.stream()))]
}
TokenTree::Ident(ref i) if i.to_string() == "return" && !in_closure => vec![
group!(
Delimiter::Brace,
token_stream!(
punct!(':', Spacing::Joint),
punct!(':', Spacing::Alone),
ident!("qadapt"),
punct!(':', Spacing::Joint),
punct!(':', Spacing::Alone),
ident!("exit_protected"),
group!(Delimiter::Parenthesis)
)
),
tt.clone(),
],
TokenTree::Punct(ref p) if p.as_char() == '|' => {
in_closure = true;
vec![tt.clone()]
}
TokenTree::Punct(ref p) if p.as_char() == ';' => {
in_closure = false;
vec![tt.clone()]
}
t => vec![t],
};
protected.extend(tokens.into_iter());
}
TokenStream::from_iter(protected.into_iter())
}
#[proc_macro_attribute]
#[deprecated(
since = "1.0.3",
note = "Please use the `alloc_counter` crate instead."
)]
pub fn no_alloc(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut protected_fn: Vec<TokenTree> = Vec::new();
let mut item_iter = item.into_iter();
let mut fn_body = None;
while let Some(tt) = item_iter.next() {
match tt {
TokenTree::Group(ref g) if g.delimiter() == Delimiter::Brace => {
fn_body = Some(Group::new(Delimiter::Brace, escape_return(g.stream())));
break;
}
tt => {
protected_fn.push(tt.clone());
}
}
}
protected_fn.push(protected_body(fn_body.as_ref().unwrap().clone()));
while let Some(tt) = item_iter.next() {
protected_fn.push(tt)
}
TokenStream::from_iter(protected_fn.into_iter())
}