use proc_macro2::TokenStream;
use quote::ToTokens;
use std::mem::take;
use syn::*;
pub fn split_attrs_and_rest(ts: TokenStream) -> Input {
if let Ok(stmt) = parse2::<Stmt>(ts.clone()) {
match stmt {
Stmt::Item(item) => parse_item(item),
Stmt::Expr(expr, _) => parse_expr(expr),
Stmt::Local(mut local) => Input::new(take(&mut local.attrs), local),
Stmt::Macro(mut macro_) => Input::new(take(&mut macro_.attrs), macro_),
}
} else {
Input::new(Vec::new(), ts)
}
}
fn parse_item(mut item: Item) -> Input {
let attrs = match &mut item {
Item::Fn(fun) => take(&mut fun.attrs),
Item::Impl(imp) => take(&mut imp.attrs),
Item::Trait(trait_) => take(&mut trait_.attrs),
_ => Vec::new(),
};
Input::new(attrs, item).set_gen_doc()
}
fn parse_expr(mut expr: Expr) -> Input {
let attrs = match &mut expr {
Expr::Unsafe(unsafe_) => take(&mut unsafe_.attrs),
Expr::Let(let_) => take(&mut let_.attrs),
Expr::Assign(assign) => take(&mut assign.attrs),
Expr::Block(block) => take(&mut block.attrs),
Expr::Call(call) => take(&mut call.attrs),
Expr::MethodCall(call) => take(&mut call.attrs),
Expr::Match(match_) => take(&mut match_.attrs),
Expr::If(if_) => take(&mut if_.attrs),
Expr::While(while_) => take(&mut while_.attrs),
Expr::Loop(loop_) => take(&mut loop_.attrs),
Expr::ForLoop(for_) => take(&mut for_.attrs),
Expr::Macro(macro_) => take(&mut macro_.attrs),
Expr::Struct(struct_) => take(&mut struct_.attrs),
Expr::Array(array) => take(&mut array.attrs),
Expr::Closure(closure) => take(&mut closure.attrs),
Expr::Path(path) => take(&mut path.attrs),
Expr::Async(async_) => take(&mut async_.attrs),
Expr::Await(await_) => take(&mut await_.attrs),
Expr::Const(const_) => take(&mut const_.attrs),
Expr::Break(break_) => take(&mut break_.attrs),
Expr::Continue(continue_) => take(&mut continue_.attrs),
Expr::Repeat(repeat) => take(&mut repeat.attrs),
Expr::Return(return_) => take(&mut return_.attrs),
Expr::Try(try_) => take(&mut try_.attrs),
Expr::TryBlock(block) => take(&mut block.attrs),
Expr::Yield(yield_) => take(&mut yield_.attrs),
_ => Vec::new(),
};
Input::new(attrs, expr)
}
#[derive(Debug)]
pub struct Input {
pub attrs: TokenStream,
pub rest: TokenStream,
pub gen_doc: bool,
}
impl Input {
fn new(attrs: Vec<Attribute>, rest: impl ToTokens) -> Input {
let attrs = attrs.into_iter().map(|attr| attr.into_token_stream()).collect();
Input { attrs, rest: rest.into_token_stream(), gen_doc: false }
}
fn set_gen_doc(mut self) -> Self {
self.gen_doc = true;
self
}
}
#[test]
fn split_attrs_on_item() {
let ts = quote::quote! {
#[a]
unsafe fn f() {}
};
let input = dbg!(split_attrs_and_rest(ts));
assert!(!input.attrs.is_empty());
}
#[test]
fn split_attrs_on_expr() {
let ts = quote::quote! {
#[a]
unsafe { call() };
};
dbg!(split_attrs_and_rest(ts));
}