Skip to content

Commit

Permalink
Merge pull request #345 from KodrAus/fix/recursion
Browse files Browse the repository at this point in the history
Refactor attribute filtering to apply per-flag
  • Loading branch information
KodrAus committed Apr 24, 2023
2 parents cbcafa7 + f3f3b6a commit 638707f
Show file tree
Hide file tree
Showing 4 changed files with 476 additions and 331 deletions.
9 changes: 9 additions & 0 deletions src/example_generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,17 @@ __impl_public_bitflags! {

__impl_public_bitflags_consts! {
Flags {
/// Field `A`.
///
/// This flag has the value `0b00000001`.
A = 0b00000001;
/// Field `B`.
///
/// This flag has the value `0b00000010`.
B = 0b00000010;
/// Field `C`.
///
/// This flag has the value `0b00000100`.
C = 0b00000100;
ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
}
Expand Down
165 changes: 147 additions & 18 deletions src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,10 +224,14 @@ macro_rules! __impl_internal_bitflags {
let mut truncated = <$T as $crate::__private::Bits>::EMPTY;

$(
$(#[$attr $($args)*])*
if bits & $BitFlags::$Flag.bits() == $BitFlags::$Flag.bits() {
truncated |= $BitFlags::$Flag.bits()
}
__expr_safe_flags!(
$(#[$attr $($args)*])*
{
if bits & $BitFlags::$Flag.bits() == $BitFlags::$Flag.bits() {
truncated |= $BitFlags::$Flag.bits()
}
}
);
)*

Self { bits: truncated }
Expand All @@ -240,13 +244,19 @@ macro_rules! __impl_internal_bitflags {

#[inline]
pub fn from_name(name: &str) -> $crate::__private::core::option::Option<Self> {
match name {
$(
$(
__expr_safe_flags!(
$(#[$attr $($args)*])*
$crate::__private::core::stringify!($Flag) => $crate::__private::core::option::Option::Some(Self { bits: $BitFlags::$Flag.bits() }),
)*
_ => $crate::__private::core::option::Option::None,
}
{
if name == $crate::__private::core::stringify!($Flag) {
return $crate::__private::core::option::Option::Some(Self { bits: $BitFlags::$Flag.bits() });
}
}
);
)*

let _ = name;
$crate::__private::core::option::Option::None
}

#[inline]
Expand Down Expand Up @@ -384,26 +394,36 @@ macro_rules! __impl_internal_bitflags {
let mut num_flags = 0;

$(
$(#[$attr $($args)*])*
{
num_flags += 1;
}
__expr_safe_flags!(
$(#[$attr $($args)*])*
{
{ num_flags += 1; }
}
);
)*

num_flags
};

const OPTIONS: [$T; NUM_FLAGS] = [
$(
$(#[$attr $($args)*])*
$BitFlags::$Flag.bits(),
__expr_safe_flags!(
$(#[$attr $($args)*])*
{
$BitFlags::$Flag.bits()
}
),
)*
];

const OPTIONS_NAMES: [&'static str; NUM_FLAGS] = [
$(
$(#[$attr $($args)*])*
$crate::__private::core::stringify!($Flag),
__expr_safe_flags!(
$(#[$attr $($args)*])*
{
$crate::__private::core::stringify!($Flag)
}
),
)*
];

Expand Down Expand Up @@ -439,3 +459,112 @@ macro_rules! __impl_internal_bitflags {
}
};
}

/// A macro that processed the input to `bitflags!` and shuffles attributes around
/// based on whether or not they're "expression-safe".
///
/// This macro is a token-tree muncher that works on 2 levels:
///
/// For each attribute, we explicitly match on its identifier, like `cfg` to determine
/// whether or not it should be considered expression-safe.
///
/// If you find yourself with an attribute that should be considered expression-safe
/// and isn't, it can be added here.
#[macro_export(local_inner_macros)]
#[doc(hidden)]
macro_rules! __expr_safe_flags {
// Entrypoint: Move all flags and all attributes into `unprocessed` lists
// where they'll be munched one-at-a-time
(
$(#[$inner:ident $($args:tt)*])*
{ $e:expr }
) => {
__expr_safe_flags! {
expr: { $e },
attrs: {
// All attributes start here
unprocessed: [$(#[$inner $($args)*])*],
processed: {
// Attributes that are safe on expressions go here
expr: [],
},
},
}
};
// Process the next attribute on the current flag
// `cfg`: The next flag should be propagated to expressions
// NOTE: You can copy this rules block and replace `cfg` with
// your attribute name that should be considered expression-safe
(
expr: { $e:expr },
attrs: {
unprocessed: [
// cfg matched here
#[cfg $($args:tt)*]
$($attrs_rest:tt)*
],
processed: {
expr: [$($expr:tt)*],
},
},
) => {
__expr_safe_flags! {
expr: { $e },
attrs: {
unprocessed: [
$($attrs_rest)*
],
processed: {
expr: [
$($expr)*
// cfg added here
#[cfg $($args)*]
],
},
},
}
};
// Process the next attribute on the current flag
// `$other`: The next flag should not be propagated to expressions
(
expr: { $e:expr },
attrs: {
unprocessed: [
// $other matched here
#[$other:ident $($args:tt)*]
$($attrs_rest:tt)*
],
processed: {
expr: [$($expr:tt)*],
},
},
) => {
__expr_safe_flags! {
expr: { $e },
attrs: {
unprocessed: [
$($attrs_rest)*
],
processed: {
expr: [
// $other not added here
$($expr)*
],
},
},
}
};
// Once all attributes on all flags are processed, generate the actual code
(
expr: { $e:expr },
attrs: {
unprocessed: [],
processed: {
expr: [$(#[$expr:ident $($exprargs:tt)*])*],
},
},
) => {
$(#[$expr $($exprargs)*])*
{ $e }
}
}

0 comments on commit 638707f

Please sign in to comment.