#[cfg_eval]
Expand description
#[::core::prelude::v1::cfg_eval]
in stable Rust.
-
Note: this macro, by default, only works on
struct
,enum
, andunion
definitions (i.e., on#[derive]
input).Enable
features = ["items"]
to get support for arbitary items.
§Example
use ::macro_rules_attribute::apply;
#[macro_use]
extern crate cfg_eval;
fn main()
{
let output_without_cfg_eval = {
#[apply(stringify!)]
enum Foo {
Bar,
#[cfg(FALSE)]
NonExisting,
}
};
// This is usually not great.
assert!(output_without_cfg_eval.contains("NonExisting"));
let output_with_cfg_eval = {
#[cfg_eval]
#[apply(stringify!)]
enum Foo {
Bar,
#[cfg(FALSE)]
NonExisting,
}
};
assert_eq!(output_with_cfg_eval, stringify! {
enum Foo {
Bar,
}
});
}
§How it works
The way this is achieved is by taking advantage of #[derive(SomeDerive)]
having #[cfg_eval]
semantics built in.
-
This means that if you have the luxury of hesitating between offering a derive macro, or an attribute macro, and need
#[cfg_eval]
semantics, youād be better off using a derive than#[cfg_eval]
! -
but the reality is that certain macros do want to modify their input, which rules out implementing it as a
#[derive]
.
§Illustration
With that knowledge, letās see a step-by-step description of what the macro is doing.
Consider, for instance, the following snippet:
#[cfg_eval]
#[other attrs...]
#[cfg_attr(all(), for_instance)]
struct Foo {
x: i32,
#[cfg(any())]
y: u8,
}
- Remember:
all()
iscfg
-speak fortrue
, andany()
, forfalse
.
What #[cfg_eval]
does, then, on this snippet, is emitting the
following:
#[derive(RemoveExterminate)] // š
#[exterminate] // š
#[other attrs...]
#[cfg_attr(all(), for_instance)]
struct Foo {
x: i32,
#[cfg(any())]
y: u8,
}
With the added macros doing what their names indicate. If this is not clear enough, then feel free to read the following more detailed section:
Click to show
The #[derive(RemoveExterminate)]
invocation leads to the following two
snippets of code, as per the rules of #[derive()]
s (i.e., that the
annotated item be emitted independently of what the macro emits):
-
independently of what
RemoveExterminate
does, we have:#[exterminate] // š #[other attrs...] #[cfg_attr(all(), for_instance)] struct Foo { x: i32, #[cfg(any())] y: u8, }
which then, thus, calls
#[exterminate]
.What
#[exterminate]
does, as hinted to the attentive reader by its otherwise totally unconspicuous name, is removing the annotated item:/* nothing here! */
-
independently of the previous bullet,
#[derive(RemoveExterminate)]
gets called, and we get://! Pseudo-code! RemoveExterminate! { // Note: thanks to `#[derive]` Magicā¢, the following tokens have // been `#[cfg_eval]`-cleaned up! // ----+ #[exterminate] // | #[other attrs...] // | #[for_instance] // <---------------------+ struct Foo { // | x: i32, // | /* removed! <----------------------------+ #[cfg(any())] y: u8, */ } }
From here, this
RemoveExterminate
macro knows it has been invoked on an item doomed to die. It can thus, contrary to usual derives, reƫmit the item it receives, modifying it at leisure.What it actually does, then, is reƫmitting it almost as-is, but for that pesky
#[exterminate]
which is no longer useful to us.#[other attrs...] #[for_instance] struct Foo { x: i32, }
Which matches the expected #[cfg_eval]
semantics on that original input:
#[cfg_eval]
#[other attrs...]
#[cfg_attr(all(), for_instance)]
struct Foo {
x: i32,
#[cfg(any())]
y: u8,
}
§Reëxporting this macro: the crate = ::some::path
optional attribute arg
The following section is only meaningful to macro authors wishing to
reƫxport #[cfg_eval]
/ reüse it from further downstream code.
Click to show
Given the above implementation, it should be no surprise that
#[cfg_eval]
needs to refer to sibling helper macros in a
path-robust manner.
By default, it uses ::cfg_eval::*
paths, expecting there to be an external
::cfg_eval
crate with the items of this very crate (like proc-macros are
known to do, given lack of $crate
for non-function-like proc-macros), as
a direct dependency.
This means that if you have your own macro which, internally (or publicly),
needs #[cfg_eval]
, then your downstream dependents, which
are likely not to depend on ::cfg_eval
directly, will be unable to
make it work.
The solution, to this, is an optional crate = ::some::path
attribute arg:
#[doc(hidden)] /** Not part of the public API */
pub mod __internal {
pub use ::cfg_eval;
}
#[macro_export]
macro_rules! example {( $input:item ) => (
#[$crate::__internal::cfg_eval::cfg_eval(
// Add this:
crate = $crate::__internal::cfg_eval // š
)]
$input
)}
// so that downstream users can write:
::your_crate::example! {
struct Foo { /* ⦠*/ }
}