diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ffe0edad..3242d3ed 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -32,7 +32,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --features example_generated + args: --features example_generated,arbitrary embedded: name: Build (embedded) diff --git a/Cargo.toml b/Cargo.toml index be9e05ad..fed83f2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ exclude = ["bors.toml"] [dependencies] core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' } compiler_builtins = { version = '0.1.2', optional = true } +arbitrary = { version = "1", optional = true } [dev-dependencies] trybuild = "1.0" @@ -36,4 +37,4 @@ example_generated = [] rustc-dep-of-std = ["core", "compiler_builtins"] [package.metadata.docs.rs] -features = ["example_generated"] +features = ["example_generated", "arbitrary"] diff --git a/README.md b/README.md index 0da0f853..e7f780c0 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,11 @@ and this to your source code: use bitflags::bitflags; ``` +Enable the `arbitrary` feature to enable implementations of the [`Arbitrary`] +trait for the generated structures. + +[`Arbitrary`]: https://docs.rs/arbitrary/1.0.1/arbitrary/trait.Arbitrary.html + ## Rust Version Support The minimum supported Rust version is 1.46 due to use of associated constants and const functions. diff --git a/src/lib.rs b/src/lib.rs index 935e432f..eb33bb5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -820,6 +820,8 @@ macro_rules! __impl_bitflags { result } } + + __impl_arbitrary_for_bitflags!($BitFlags); }; // Every attribute that the user writes on a const is applied to the @@ -931,6 +933,36 @@ macro_rules! __impl_bitflags { }; } +// When "arbitrary" is enabled, emit code to implement the `Arbitrary` trait. +#[cfg(feature = "arbitrary")] +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_arbitrary_for_bitflags { + ($BitFlags:ident) => { + impl<'a> $crate::_arbitrary::Arbitrary<'a> for $BitFlags { + fn arbitrary( + u: &mut $crate::_arbitrary::Unstructured<'a> + ) -> $crate::_arbitrary::Result { + Self::from_bits(u.arbitrary()?).ok_or_else(|| $crate::_arbitrary::Error::IncorrectFormat) + } + } + }; +} + +// When "arbitrary" is not enabled, don't emit any code for it. +#[cfg(not(feature = "arbitrary"))] +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_arbitrary_for_bitflags { + ($BitFlags:ident) => {}; +} + +// Re-export `arbitrary` so that our macro expansions can refer to it by a +// known name. +#[cfg(feature = "arbitrary")] +#[doc(hidden)] +pub extern crate arbitrary as _arbitrary; + #[cfg(feature = "example_generated")] pub mod example_generated; diff --git a/tests/arbitrary-redefinition.rs b/tests/arbitrary-redefinition.rs new file mode 100644 index 00000000..0e961eeb --- /dev/null +++ b/tests/arbitrary-redefinition.rs @@ -0,0 +1,23 @@ +#![cfg(feature = "arbitrary")] + +extern crate arbitrary as a; + +use a::Arbitrary; +use bitflags::bitflags; + +// Checks for possible errors caused by overriding names used by `bitflags!` internally. + +mod arbitrary {} +mod _arbitrary {} + +bitflags! { + struct Test: u8 { + const A = 1; + } +} + +#[test] +fn test_arbitrary_redefinition() { + let mut unstructured = a::Unstructured::new(&[0_u8; 256]); + let _test = Test::arbitrary(&mut unstructured); +} diff --git a/tests/arbitrary.rs b/tests/arbitrary.rs new file mode 100644 index 00000000..a6b25e97 --- /dev/null +++ b/tests/arbitrary.rs @@ -0,0 +1,17 @@ +#![cfg(feature = "arbitrary")] + +use arbitrary::Arbitrary; + +bitflags::bitflags! { + struct Color: u32 { + const RED = 0x1; + const GREEN = 0x2; + const BLUE = 0x4; + } +} + +#[test] +fn test_arbitrary() { + let mut unstructured = arbitrary::Unstructured::new(&[0_u8; 256]); + let _color = Color::arbitrary(&mut unstructured); +}