use crate::helper::{Entry, Input};
use proc_macro2::TokenStream;
use syn::{Error, Item, Result};
use template_quote::quote;
impl Input {
pub fn newest(&self) -> Option<&Entry> {
self.entries.first()
}
pub fn forward_transformed(&self, entry: Entry) -> Result<TokenStream> {
if self.entries.is_empty() {
return Err(Error::new(
proc_macro2::Span::call_site(),
"announce: a transformer must come after the definition it transforms, \
e.g. `[F!, transformer!, callback!]`",
));
}
let mut next = self.clone();
next.entries[0] = entry;
next.forward().ok_or_else(|| {
Error::new(
proc_macro2::Span::call_site(),
"announce: a transformer cannot be the last element of the continuation list",
)
})
}
}
#[macro_export]
macro_rules! transformer {
($(#[$m:meta])* $vis:vis fn $name:ident ($entry:ident) $body:block) => {
$(#[$m])*
#[proc_macro]
$vis fn $name(__announce_ts: ::proc_macro::TokenStream) -> ::proc_macro::TokenStream {
let __announce_run = |__announce_ts: ::proc_macro::TokenStream|
-> $crate::helper::__private::syn::Result<$crate::helper::__private::proc_macro2::TokenStream> {
let __announce_input: $crate::helper::Input = $crate::helper::__private::syn::parse(__announce_ts)?;
let __announce_newest = __announce_input.newest().cloned().ok_or_else(|| {
$crate::helper::__private::syn::Error::new(
$crate::helper::__private::proc_macro2::Span::call_site(),
"announce: a transformer must come after the definition it transforms",
)
})?;
#[allow(unused_mut)]
let mut $entry: $crate::helper::Entry = __announce_newest;
let __announce_new = (move || -> $crate::helper::__private::syn::Result<$crate::helper::Entry> { $body })()?;
__announce_input.forward_transformed(__announce_new)
};
match __announce_run(__announce_ts) {
::core::result::Result::Ok(ts) => ts.into(),
::core::result::Result::Err(e) => e.to_compile_error().into(),
}
}
};
}
pub fn signature_only(item: &mut Item) {
fn returns_impl(sig: &syn::Signature) -> bool {
fn has_impl(ts: TokenStream) -> bool {
ts.into_iter().any(|tt| match tt {
proc_macro2::TokenTree::Ident(i) => i == "impl",
proc_macro2::TokenTree::Group(g) => has_impl(g.stream()),
_ => false,
})
}
match &sig.output {
syn::ReturnType::Default => false,
syn::ReturnType::Type(_, ty) => has_impl(quote!(#ty)),
}
}
fn placeholder() -> syn::Block {
syn::parse_quote!({ ::core::unimplemented!() })
}
match item {
Item::Trait(t) => {
for ti in &mut t.items {
if let syn::TraitItem::Fn(f) = ti {
if f.default.is_some() && !returns_impl(&f.sig) {
f.default = Some(placeholder());
}
}
}
}
Item::Impl(i) => {
for ii in &mut i.items {
if let syn::ImplItem::Fn(f) = ii {
if !returns_impl(&f.sig) && f.sig.constness.is_none() {
f.block = placeholder();
}
}
}
}
Item::Fn(f) => {
if !returns_impl(&f.sig) && f.sig.constness.is_none() {
*f.block = placeholder();
}
}
Item::Mod(m) => {
if let Some((_, items)) = &mut m.content {
for i in items {
signature_only(i);
}
}
}
_ => {}
}
}