#![doc(test(attr(
deny(warnings),
allow(unused),
deny(unused_unsafe),
)))]
#![cfg_attr(feature = "nightly",
feature(external_doc),
)]
#![cfg_attr(feature = "nightly",
doc(include = "../README.md")
)]
#[cfg(not(all(test, feature = "unit-tests")))]
extern crate proc_macro;
#[cfg(not(all(test, feature = "unit-tests")))]
use ::proc_macro::TokenStream;
#[cfg(not(all(test, feature = "unit-tests")))]
use ::syn::parse;
#[cfg(all(test, feature = "unit-tests"))]
use ::proc_macro2::TokenStream;
#[cfg(all(test, feature = "unit-tests"))]
use ::syn::parse2 as parse;
use ::proc_macro2::{
Span,
};
use ::quote::{
ToTokens,
};
use ::syn::*;
macro_rules! parse_macro_input {
(
$token_stream:ident as $T:ty
) => (
match parse::<$T>($token_stream) {
| Ok(data) => data,
| Err(err) => {
return TokenStream::from(err.to_compile_error());
}
}
);
(
$token_stream:ident
) => (
parse_macro_input!($token_stream as _)
);
}
#[cfg_attr(feature = "nightly",
doc(include = "docs/require_unsafe_in_body.md")
)]
#[proc_macro_attribute] pub
fn require_unsafe_in_body (
attrs: TokenStream,
input: TokenStream,
) -> TokenStream
{
let _: parse::Nothing = parse_macro_input!(attrs);
::func_wrap::parse_and_func_wrap_with(input, edit_function_in_place)
.map_or_else(
|err| err.to_compile_error(),
|item| {
if let Item::Fn(_) = item {
item.into_token_stream()
} else {
Error::new(Span::call_site(), "\
`#[require_unsafe_in_body]` must be applied to \
a function.\
").to_compile_error()
}
},
)
.into()
}
#[cfg_attr(feature = "nightly",
doc(include = "docs/require_unsafe_in_bodies.md")
)]
#[proc_macro_attribute] pub
fn require_unsafe_in_bodies (
attrs: TokenStream,
input: TokenStream,
) -> TokenStream
{
let _: parse::Nothing = parse_macro_input!(attrs);
::func_wrap::parse_and_func_wrap_with(input, edit_function_in_place)
.map_or_else(
|err| err.to_compile_error(),
|item| {
if let Item::Trait(_) | Item::Impl(_) = item {
item.into_token_stream()
} else {
Error::new(Span::call_site(), "\
`#[require_unsafe_in_bodies]` must be applied to \
an `impl` block or a `trait` definition.\
").to_compile_error()
}
},
)
.into()
}
fn edit_function_in_place (
func: &'_ mut ImplItemMethod,
wrapped_func_call: Option<::func_wrap::WrappedFuncCall<'_>>,
) -> Result<()>
{
let mut wrapped_func_call =
if let Some(it) = wrapped_func_call {
it
} else {
return Err(Error::new(Span::call_site(), "\
Missing `#[require_unsafe_in_bodies]` on the enscoping \
`trait` or `impl` block.
"));
}
;
func.block = if func.sig.unsafety.is_none() {
func.sig = wrapped_func_call.sig; wrapped_func_call.block } else {
wrapped_func_call.sig.unsafety = None;
wrapped_func_call.sig.inputs.push(parse_quote!(
_: r#unsafe
));
wrapped_func_call.call_site_args.push(parse_quote!( r#unsafe ));
let fname = &wrapped_func_call.sig.ident;
let renamed = ::quote::format_ident!("__unsafe_{}", fname);
wrapped_func_call.sig.ident = renamed;
parse_quote!({
#[allow(nonstandard_style)]
struct r#unsafe;
#wrapped_func_call
})
};
Ok(())
}
#[cfg(all(test, feature = "unit-tests"))]
mod tests;