cargo / bon / audit
cargo : bon @ 3.9.1
PE Patrick Elsen signed 2026-05-27 published 2026-05-27

src/__/cfg_eval.rs

138 lines · rust

/// This is all a big embarrassing workaround, please don't oversee 😳😳😳.////// Anyway, if you are curious what the hell is going on here, then here is/// an explanation 😸. So... where to start 🤔. Ah! The problem!////// ## The problem////// Proc macro attributes (like `#[builder]`) see all the `#[cfg(...)]` and `#[cfg_attr(...)]`/// attributes unexpanded. For example, if you write smth like this:////// ```/// #[bon::builder]/// fn func(///     #[cfg(windows)]///     windows_only_param: u32,/// ) {}////// ```////// then the `#[builder]` macro will see the full `#[cfg(...)]` attribute with/// the `windows_only_param` it is attached to verbatim. The `#[cfg(...)]` isn't/// removed by the time the `#[builder]`'s macro expansion is invoked.////// It is a problem because the `#[builder]` macro needs to know the exact list/// of members it has to generate setters for. It doesn't know whether/// the `windows` predicate evaluates to `true` or `false`, especially if this was/// a more complex predicate. So it can't decide whether to generate a setter for/// the `windows_only_param` or not.////// ## The solution////// This macro allows us to evaluate the `cfg` predicates by using a variation of/// [the trick] shared by @recatek.////// When the `#[builder]` macro finds any usage of `#[cfg(...)]` or `#[cfg_attr(...)]`/// it generates a call to this macro with all `cfg` predicates collected from the/// item it was placed on. The `#[builder]` macro deduplicates and sorts the `cfg`/// predicates and passes them as `$pred` to this macro.////// This macro then dispatches to `__eval_cfg_callback_true` or `__eval_cfg_callback_false`/// by defining a conditional `use ...` statement for each predicate and collects the/// results of the evaluation in the `$results` list.////// For the last call to this macro (when no more `$pred` are left) the macro calls back/// to the proc macro attribute that called it with the results of the evaluation and/// the original parameters and the item which are passed through via the `$rest` macro variable.////// [the trick]: https://users.rust-lang.org/t/supporting-or-evaluating-cfg-in-proc-macro-parameters/93240/2#[macro_export]#[doc(hidden)]macro_rules! __eval_cfg_callback {    (        { $($results:tt)* }        ( $pred_id:ident: $($pred:tt)* )        $($rest:tt)*    ) => {        // The `pred_id` is required to be a unique identifier for the current        // predicate evaluation so that we can use it in a `use` statement to define        // a new unique name for the macro to call.        #[cfg($($pred)*)]        #[doc(hidden)]        #[allow(deprecated)]        use $crate::__eval_cfg_callback_true as $pred_id;        #[cfg(not($($pred)*))]        #[doc(hidden)]        #[allow(deprecated)]        use $crate::__eval_cfg_callback_false as $pred_id;        // The trick here is that `$pred_id` now resolves either to        // `__eval_cfg_callback_true` or `__eval_cfg_callback_false`        // depending on the evaluation of the cfg predicate, so by        // invoking it as a macro, that macro internally pushes either        // `true` or `false` to the `$results` list.        $pred_id! {            { $($results)* }            $($rest)*        }    };    // The terminal case for the recursion when there are no more predicates left.    // We have collected all the results of the cfg evaluations and now we can    // delegate them to the proc macro attribute that called this macro.    (        // The results of the cfg evaluation        { $($results:tt)* }        // The proc macro attribute to invoke with the results        $final_macro:path,        // The number of times this macro was called recursively from the proc macro        $recursion_counter:literal,        // Parameters to pass to the proc macro attribute after the cfg results        ( $($macro_params:tt)* )        // The item to attach the proc macro attribute to        $($item:tt)*    ) => {        // The special `__cfgs(...)` prefix is parsed by the proc macro attribute        // to get the results of the cfg evaluations.        #[$final_macro(__cfgs($recursion_counter, $($results)*) $($macro_params)*)]        $($item)*    };}/// The `cfg` predicate evaluated to `true`, now push that information into/// the `$results` list.#[macro_export]#[doc(hidden)]macro_rules! __eval_cfg_callback_true {    (        { $($results:tt)* }        $($tt:tt)*    ) => {        $crate::__eval_cfg_callback! {            { $($results)* true, }            $($tt)*        }    };}/// The `cfg` predicate evaluated to `false`, now push that information into/// the `$results` list.#[macro_export]#[doc(hidden)]macro_rules! __eval_cfg_callback_false {    (        { $($results:tt)* }        $($tt:tt)*    ) => {        $crate::__eval_cfg_callback! {            { $($results)* false, }            $($tt)*        }    };}