//! initial version courtesy of @danielhenrymantilla. //! //! extended to support fns with args/return types, //! visibility, #[attributes], unsafe, const, async, //! extern "abi" ... //! //! left as an exercise for later (cuz it sux): //! - generics? //! - where clauses? //! - probably other shit im missing //! - ... //! //! probably don't do stuff like this, honestly. #[macro_export] macro_rules! with_sections { ( $(#[$m:meta])* $v:vis $(const $(@$const_:tt)?)? $(async $(@$async_:tt)?)? $(unsafe $(@$unsafe_:tt)?)? $(extern $($abi:literal)?)? fn $fname:ident ($($args:tt)*) -> $ret:ty { $($contents:tt)* } ) => (with_sections! { // note: the "param" to `@__inner` is // passed verbatim (as a `:tt`) until // the end @__inner ( @name $fname @vis ($v) @args ($($args)*) @ret ($ret) @attrs ($(#[$m])*) @abi ($(extern $($abi)?)?) @unsafety ($(unsafe $(@$unsafe_)?)?) @constitude ($(const $(@$const_)?)?) @asynchrony ($(async $(@$async_)?)?) ) @out [] @in [{$($contents)*}] }); // `fn blah(...) {}` — no explicit return type, but we // fake it with explicit `-> ()` ( $(#[$m:meta])* $v:vis $(const $(@$const_:tt)?)? $(async $(@$async_:tt)?)? $(unsafe $(@$unsafe_:tt)?)? $(extern $($abi:literal)?)? fn $fname:ident ($($args:tt)*) $contents:tt ) => (with_sections! { $(#[$m])* $v $(const $(@$const_)?)? $(async $(@$async_)?)? $(unsafe $(@$unsafe_)?)? $(extern $($abi)?)? fn $fname($($args)*) -> () $contents }); (@__inner $funcinfo:tt @out $out:tt @in [#[section($section:expr $(,)?)] $body:tt $($rest:tt)*] ) => (with_sections! { @__inner $funcinfo @out $out @in [match Section::new($section) { _ => $body } $($rest)*] }); (@__inner $funcinfo:tt @out $out:tt @in [{ $($inner:tt)* } $($rest:tt)*] ) => (with_sections! { @__inner $funcinfo @out [$out] @in [$($inner)* @end_brace $($rest)*] }); (@__inner $funcinfo:tt @out [[$($out:tt)*] $($acc:tt)*] @in [@end_brace $($rest:tt)*] ) => (with_sections! { @__inner $funcinfo @out [$($out)* { $($acc)* }] @in [$($rest)*] }); (@__inner $funcinfo:tt @out [$($out:tt)*] @in [$current:tt $($rest:tt)*] ) => (with_sections! { @__inner $funcinfo @out [$($out)* $current] @in [$($rest)*] }); ( @__inner ( @name $fname:ident @vis ($v:vis) @args ($($args:tt)*) @ret ($ret:ty) @attrs ($(#[$m:meta])*) @abi ($(extern $($abi:literal)?)?) @unsafety ($(unsafe $(@$unsafe_:tt)?)?) @constitude ($(const $(@$const_:tt)?)?) @asynchrony ($(async $(@$async_:tt)?)?) ) @out [$($out:tt)*] @in [/* nothing */] ) => ( $(#[$m])* $v $(const $(@$const_)?)? $(async $(@$async_)?)? $(unsafe $(@$unsafe_)?)? $(extern $($abi)?)? fn $fname($($args)*) -> $ret $($out)* ); } pub struct Section { name: &'static str, } impl Section { pub fn new(name: &'static str) -> Section { println!("begin {}", name); Self { name } } } impl Drop for Section { fn drop(&mut self) { println!("end {}", self.name); } } // rest of this is various kinds of test code. // (most stuff got tested by modifying these in place) with_sections! { /// ``` /// playground::doit(1); /// ``` pub fn doit(x: i32) { println!("b4 everything {}", x); #[section("foo")] { println!("inside foo before bar"); #[section("bar")] { println!("inside bar") } println!("inside foo after bar"); } #[section("baz")] { println!("inside baz") } println!("after everything"); } } with_sections! { // extern "C" const unsafe fn no() -> i32 { // println!("no"); 4 } } with_sections! { // extern "C" async unsafe fn _ayes() { println!("no"); // 4 } } fn main() { fn type_name_of(_: T) -> &'static str { std::any::type_name::() } doit(1); // let _f: extern "C" fn() -> i32 = no; unsafe { println!("{}", no()) }; }