Skip to content

Commit

Permalink
Add support for non #[derive]-input items (#1)
Browse files Browse the repository at this point in the history
The trick being to use the discriminant specifier of an `enum` as a
channel through which to smuggle an arbitrary block, within which
arbitrary items may occur.
  • Loading branch information
danielhenrymantilla authored Jun 25, 2023
2 parents 66b30d4 + 8c6cdab commit 548aa65
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 8 deletions.
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "cfg_eval"
authors = [
"Daniel Henry-Mantilla <[email protected]>",
]
version = "0.1.0"
version = "0.1.2"
edition = "2021"
rust-version = "1.61.0"

Expand All @@ -31,10 +31,13 @@ docs-rs = [
"better-docs",
]

# Support for any kind of item, not only derive inputs.
items = []

[dependencies]
proc-macro2.version = "1.0.0"
quote.version = "1.0.0"
syn.version = "=2.0.0"
syn.version = "2.0.0"
syn.default-features = false
syn.features = [
"parsing",
Expand All @@ -44,6 +47,8 @@ syn.features = [

[dev-dependencies]
macro_rules_attribute.version = "0.2.0"
cfg_eval.path = "."
cfg_eval.features = ["items"]

[workspace]

Expand Down
12 changes: 12 additions & 0 deletions examples/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,16 @@ fn main()
#[cfg(any())]
NonExistingVariant,
}

#[cfg_eval]
#[apply(debugger)]
const _: () = {
enum _WithoutCfgEval {
#[cfg(any())]
NonExistingVariant,
}
#[cfg(all())] #[cfg_attr(all(), foo)] fn foo() {}
#[cfg(all())] { 42 }
#[cfg(any())] { 27 }
};
}
54 changes: 49 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ use ::syn::{*,
/// https://doc.rust-lang.org/1.70.0/core/prelude/v1/attr.cfg_eval.html)
/// in stable Rust.
///
/// - Note: this macro only works on `struct`, `enum`, and `union`
/// definitions (_i.e._, on `#[derive]` input).
/// - Note: this macro, by default, only works on `struct`, `enum`, and
/// `union` definitions (_i.e._, on `#[derive]` input).
///
/// Enable `features = ["items"]` to get support for arbitary items.
///
/// ## Example
///
Expand Down Expand Up @@ -333,11 +335,22 @@ fn cfg_eval_impl(
),
));

Ok(quote_spanned!(span=>
let attrs_to_add = quote_spanned!(span=>
#[::core::prelude::v1::derive(
#krate::ඞRemoveExterminate
)]
#[#krate::ඞdalek_exterminate]
);

if cfg!(feature = "items") {
return Ok(quote_spanned!(Span::mixed_site()=>
#attrs_to_add
enum{ ඞ = { #input } }
));
}

Ok(quote_spanned!(Span::mixed_site()=>
#attrs_to_add
#input
))
}
Expand All @@ -347,8 +360,39 @@ fn cfg_eval_impl(
fn __(input: TokenStream)
-> TokenStream
{
// The only thing left for us to do is removing the `#` and `[ …exterminate ]`
input.into_iter().skip(2).collect()
if cfg!(not(feature = "items")) {
// The only thing left for us to do is removing the
// `#` and `[ …exterminate ]`
return input.into_iter().skip(2).collect();
}

// From:
// ```rs
// #[…exterminate]
// enum ඞ {
// ඞ = { #input }
// }
// ```
// to:
// ```rs
// #input
// ```
let mut tts = input.into_iter();

// Remove `#[…] enum ඞ`
tts.by_ref().take(4).for_each(drop);
// Remove the `{}` in `{ ඞ = … }`
if let Some(::proc_macro::TokenTree::Group(g)) = tts.next() {
tts = g.stream().into_iter();
}
// Remove `ඞ =`
tts.by_ref().take(2).for_each(drop);
// Remove the `{}` in `{ #input }`
if let Some(::proc_macro::TokenTree::Group(g)) = tts.next() {
tts = g.stream().into_iter();
}

tts.collect()
}

#[doc(hidden)] /** Not part of the public API */
Expand Down
57 changes: 57 additions & 0 deletions tests/item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
macro_rules! stringify_rhs {(
const _: () = $value:tt ;
) => (
stringify! $value
)}

macro_rules! cfg_eval_stringify {(
$($body:tt)*
) => ({
#[::cfg_eval::cfg_eval]
#[::macro_rules_attribute::apply(stringify_rhs!)]
const _: () = { $($body)* };
})}

#[test]
fn main()
{
assert_eq!(
cfg_eval_stringify! {
enum _WithoutCfgEval {
#[cfg(any())]
NonExistingVariant,
}

#[cfg(all())]
#[cfg_attr(all(), foo)]
fn foo()
{}

cfg!(any());

#[cfg(all())] {
42
}

#[cfg(any())] {
27
}
},
stringify! {
enum _WithoutCfgEval {
}

#[cfg(all())] // <- Redundantly kept.
#[foo]
fn foo()
{}

// Not expanded.
cfg!(any());

#[cfg(all())] /* <- Redundantly kept. */ {
42
}
},
);
}

0 comments on commit 548aa65

Please sign in to comment.