diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a7042f5a..ffe0edad 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,7 +16,7 @@ jobs: - stable - beta - nightly - - 1.32.0 + - 1.46.0 steps: - name: Checkout sources uses: actions/checkout@v2 @@ -54,28 +54,3 @@ jobs: with: command: build args: -Z avoid-dev-deps --features example_generated --target thumbv6m-none-eabi - - suite: - name: Test suite - runs-on: ubuntu-latest - strategy: - fail-fast: true - matrix: - rust: - - nightly - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: ${{ matrix.rust }} - override: true - - - name: Default features - uses: actions-rs/cargo@v1 - with: - command: test - args: -p test_suite \ No newline at end of file diff --git a/.gitignore b/.gitignore index fbd9642b..61c33314 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +wip target Cargo.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d491015..df1aac79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,62 @@ +# 1.3.2 + +- Allow `non_snake_case` in generated flags types ([#256]) + +[#256]: https://github.com/bitflags/bitflags/pull/256 + +# 1.3.1 + +- Revert unconditional `#[repr(transparent)]` ([#252]) + +[#252]: https://github.com/bitflags/bitflags/pull/252 + +# 1.3.0 (yanked) + +**This release bumps the Minimum Supported Rust Version to `1.46.0`** + +- Add `#[repr(transparent)]` ([#187]) + +- End `empty` doc comment with full stop ([#202]) + +- Fix typo in crate root docs ([#206]) + +- Document from_bits_unchecked unsafety ([#207]) + +- Let `is_all` ignore extra bits ([#211]) + +- Allows empty flag definition ([#225]) + +- Making crate accessible from std ([#227]) + +- Make `from_bits` a const fn ([#229]) + +- Allow multiple bitflags structs in one macro invocation ([#235]) + +- Add named functions to perform set operations ([#244]) + +- Fix typos in method docs ([#245]) + +- Modernization of the `bitflags` macro to take advantage of newer features and 2018 idioms ([#246]) + +- Fix regression (in an unreleased feature) and simplify tests ([#247]) + +- Use `Self` and fix bug when overriding `stringify!` ([#249]) + +[#187]: https://github.com/bitflags/bitflags/pull/187 +[#202]: https://github.com/bitflags/bitflags/pull/202 +[#206]: https://github.com/bitflags/bitflags/pull/206 +[#207]: https://github.com/bitflags/bitflags/pull/207 +[#211]: https://github.com/bitflags/bitflags/pull/211 +[#225]: https://github.com/bitflags/bitflags/pull/225 +[#227]: https://github.com/bitflags/bitflags/pull/227 +[#229]: https://github.com/bitflags/bitflags/pull/229 +[#235]: https://github.com/bitflags/bitflags/pull/235 +[#244]: https://github.com/bitflags/bitflags/pull/244 +[#245]: https://github.com/bitflags/bitflags/pull/245 +[#246]: https://github.com/bitflags/bitflags/pull/246 +[#247]: https://github.com/bitflags/bitflags/pull/247 +[#249]: https://github.com/bitflags/bitflags/pull/249 + # 1.2.1 - Remove extraneous `#[inline]` attributes ([#194]) diff --git a/Cargo.toml b/Cargo.toml index c3fdf183..be9e05ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [package] - name = "bitflags" # NB: When modifying, also modify: # 1. html_root_url in lib.rs # 2. number in readme (for breaking changes) -version = "1.2.1" +version = "1.3.2" +edition = "2018" authors = ["The Rust Project Developers"] license = "MIT/Apache-2.0" keywords = ["bit", "bitmask", "bitflags", "flags"] @@ -16,23 +16,24 @@ categories = ["no-std"] description = """ A macro to generate structures which behave like bitflags. """ -exclude = [ - "appveyor.yml", - "bors.toml" -] -build = "build.rs" +exclude = ["bors.toml"] [dependencies] core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' } compiler_builtins = { version = '0.1.2', optional = true } +[dev-dependencies] +trybuild = "1.0" +rustversion = "1.0" +walkdir = "2.3" +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" + [features] default = [] example_generated = [] rustc-dep-of-std = ["core", "compiler_builtins"] [package.metadata.docs.rs] -features = [ "example_generated" ] - -[workspace] -members = ["test_suite"] +features = ["example_generated"] diff --git a/README.md b/README.md index f447afbf..0da0f853 100644 --- a/README.md +++ b/README.md @@ -18,16 +18,15 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -bitflags = "1.2" +bitflags = "1.3" ``` -and this to your crate root: +and this to your source code: ```rust -#[macro_use] -extern crate bitflags; +use bitflags::bitflags; ``` ## Rust Version Support -The minimum supported Rust version is 1.20 due to use of associated constants. +The minimum supported Rust version is 1.46 due to use of associated constants and const functions. diff --git a/build.rs b/build.rs deleted file mode 100644 index 67abc09d..00000000 --- a/build.rs +++ /dev/null @@ -1,44 +0,0 @@ -use std::env; -use std::process::Command; -use std::str::{self, FromStr}; - -fn main() { - let minor = match rustc_minor_version() { - Some(minor) => minor, - None => return, - }; - - // const fn stabilized in Rust 1.31: - if minor >= 31 { - println!("cargo:rustc-cfg=bitflags_const_fn"); - } -} - -fn rustc_minor_version() -> Option { - let rustc = match env::var_os("RUSTC") { - Some(rustc) => rustc, - None => return None, - }; - - let output = match Command::new(rustc).arg("--version").output() { - Ok(output) => output, - Err(_) => return None, - }; - - let version = match str::from_utf8(&output.stdout) { - Ok(version) => version, - Err(_) => return None, - }; - - let mut pieces = version.split('.'); - if pieces.next() != Some("rustc 1") { - return None; - } - - let next = match pieces.next() { - Some(next) => next, - None => return None, - }; - - u32::from_str(next).ok() -} diff --git a/src/lib.rs b/src/lib.rs index 705ddf35..75108972 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,8 +18,7 @@ //! # Example //! //! ``` -//! #[macro_use] -//! extern crate bitflags; +//! use bitflags::bitflags; //! //! bitflags! { //! struct Flags: u32 { @@ -47,11 +46,10 @@ //! implementations: //! //! ``` -//! #[macro_use] -//! extern crate bitflags; -//! //! use std::fmt; //! +//! use bitflags::bitflags; +//! //! bitflags! { //! struct Flags: u32 { //! const A = 0b00000001; @@ -89,10 +87,9 @@ //! the current module by adding `pub` before `struct`: //! //! ``` -//! #[macro_use] -//! extern crate bitflags; -//! //! mod example { +//! use bitflags::bitflags; +//! //! bitflags! { //! pub struct Flags1: u32 { //! const A = 0b00000001; @@ -116,6 +113,24 @@ //! Attributes can be attached to the generated `struct`s by placing them //! before the `struct` keyword. //! +//! ## Representations +//! +//! It's valid to add a `#[repr(C)]` or `#[repr(transparent)]` attribute to a type +//! generated by `bitflags!`. In these cases, the type is guaranteed to be a newtype. +//! +//! ``` +//! use bitflags::bitflags; +//! +//! bitflags! { +//! #[repr(transparent)] +//! struct Flags: u32 { +//! const A = 0b00000001; +//! const B = 0b00000010; +//! const C = 0b00000100; +//! } +//! } +//! ``` +//! //! # Trait implementations //! //! The `Copy`, `Clone`, `PartialEq`, `Eq`, `PartialOrd`, `Ord` and `Hash` @@ -164,6 +179,18 @@ //! - `toggle`: the specified flags will be inserted if not present, and removed //! if they are. //! - `set`: inserts or removes the specified flags depending on the passed value +//! - `intersection`: returns a new set of flags, containing only the flags present +//! in both `self` and `other` (the argument to the function). +//! - `union`: returns a new set of flags, containing any flags present in +//! either `self` or `other` (the argument to the function). +//! - `difference`: returns a new set of flags, containing all flags present in +//! `self` without any of the flags present in `other` (the +//! argument to the function). +//! - `symmetric_difference`: returns a new set of flags, containing all flags +//! present in either `self` or `other` (the argument +//! to the function), but not both. +//! - `complement`: returns a new set of flags, containing all flags which are +//! not set in `self`, but which are allowed for this type. //! //! ## Default //! @@ -173,8 +200,7 @@ //! on the generated struct), you can simply derive `Default`: //! //! ``` -//! #[macro_use] -//! extern crate bitflags; +//! use bitflags::bitflags; //! //! bitflags! { //! // Results in default value with bits: 0 @@ -195,8 +221,7 @@ //! If your default value is not equal to `0` you need to implement `Default` yourself: //! //! ``` -//! #[macro_use] -//! extern crate bitflags; +//! use bitflags::bitflags; //! //! bitflags! { //! struct Flags: u32 { @@ -224,8 +249,7 @@ //! Flags with a value equal to zero will have some strange behavior that one should be aware of. //! //! ``` -//! #[macro_use] -//! extern crate bitflags; +//! use bitflags::bitflags; //! //! bitflags! { //! struct Flags: u32 { @@ -248,16 +272,12 @@ //! assert!(none.is_empty()); //! } //! ``` +//! +//! Users should generally avoid defining a flag with a value of zero. -#![no_std] -#![doc(html_root_url = "https://docs.rs/bitflags/1.2.1")] - -#[cfg(test)] -#[macro_use] -extern crate std; +#![cfg_attr(not(test), no_std)] +#![doc(html_root_url = "https://docs.rs/bitflags/1.3.2")] -// Re-export libcore using an alias so that the macros can work without -// requiring `extern crate core` downstream. #[doc(hidden)] pub extern crate core as _core; @@ -273,8 +293,7 @@ mod bitflags_trait; /// # Example /// /// ``` -/// #[macro_use] -/// extern crate bitflags; +/// use bitflags::bitflags; /// /// bitflags! { /// struct Flags: u32 { @@ -299,11 +318,10 @@ mod bitflags_trait; /// implementations: /// /// ``` -/// #[macro_use] -/// extern crate bitflags; -/// /// use std::fmt; /// +/// use bitflags::bitflags; +/// /// bitflags! { /// struct Flags: u32 { /// const A = 0b00000001; @@ -337,94 +355,18 @@ mod bitflags_trait; macro_rules! bitflags { ( $(#[$outer:meta])* - pub struct $BitFlags:ident: $T:ty { - $( - $(#[$inner:ident $($args:tt)*])* - const $Flag:ident = $value:expr; - )+ - } - $($t:tt)* - ) => { - __bitflags! { - $(#[$outer])* - (pub) $BitFlags: $T { - $( - $(#[$inner $($args)*])* - $Flag = $value; - )+ - } - } - - bitflags! { - $($t)* - } - }; - ( - $(#[$outer:meta])* - struct $BitFlags:ident: $T:ty { + $vis:vis struct $BitFlags:ident: $T:ty { $( $(#[$inner:ident $($args:tt)*])* const $Flag:ident = $value:expr; )* } - $($t:tt)* - ) => { - __bitflags! { - $(#[$outer])* - () $BitFlags: $T { - $( - $(#[$inner $($args)*])* - $Flag = $value; - )* - } - } - bitflags! { - $($t)* - } - }; - ( - $(#[$outer:meta])* - pub ($($vis:tt)+) struct $BitFlags:ident: $T:ty { - $( - $(#[$inner:ident $($args:tt)*])* - const $Flag:ident = $value:expr; - )+ - } $($t:tt)* - ) => { - __bitflags! { - $(#[$outer])* - (pub ($($vis)+)) $BitFlags: $T { - $( - $(#[$inner $($args)*])* - $Flag = $value; - )+ - } - } - - bitflags! { - $($t)* - } - }; - () => {}; -} - -#[macro_export(local_inner_macros)] -#[doc(hidden)] -macro_rules! __bitflags { - ( - $(#[$outer:meta])* - ($($vis:tt)*) $BitFlags:ident: $T:ty { - $( - $(#[$inner:ident $($args:tt)*])* - $Flag:ident = $value:expr; - )* - } ) => { $(#[$outer])* #[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] - $($vis)* struct $BitFlags { + $vis struct $BitFlags { bits: $T, } @@ -436,66 +378,18 @@ macro_rules! __bitflags { )* } } - }; -} - -#[macro_export(local_inner_macros)] -#[doc(hidden)] -#[cfg(bitflags_const_fn)] -macro_rules! __fn_bitflags { - ( - $(# $attr_args:tt)* - const fn $($item:tt)* - ) => { - $(# $attr_args)* - const fn $($item)* - }; - ( - $(# $attr_args:tt)* - pub const fn $($item:tt)* - ) => { - $(# $attr_args)* - pub const fn $($item)* - }; - ( - $(# $attr_args:tt)* - pub const unsafe fn $($item:tt)* - ) => { - $(# $attr_args)* - pub const unsafe fn $($item)* - }; -} -#[macro_export(local_inner_macros)] -#[doc(hidden)] -#[cfg(not(bitflags_const_fn))] -macro_rules! __fn_bitflags { - ( - $(# $attr_args:tt)* - const fn $($item:tt)* - ) => { - $(# $attr_args)* - fn $($item)* - }; - ( - $(# $attr_args:tt)* - pub const fn $($item:tt)* - ) => { - $(# $attr_args)* - pub fn $($item)* - }; - ( - $(# $attr_args:tt)* - pub const unsafe fn $($item:tt)* - ) => { - $(# $attr_args)* - pub unsafe fn $($item)* + bitflags! { + $($t)* + } }; + () => {}; } +// A helper macro to implement the `all` function. #[macro_export(local_inner_macros)] #[doc(hidden)] -macro_rules! __all_bitflags { +macro_rules! __impl_all_bitflags { ( $BitFlags:ident: $T:ty { $( @@ -504,41 +398,29 @@ macro_rules! __all_bitflags { )+ } ) => { - __fn_bitflags! { - /// Returns the set containing all flags. - #[inline] - pub const fn all() -> $BitFlags { - // See `Debug::fmt` for why this approach is taken. - #[allow(non_snake_case)] - trait __BitFlags { - $( - const $Flag: $T = 0; - )+ - } - impl __BitFlags for $BitFlags { - $( - __impl_bitflags! { - #[allow(deprecated)] - $(? #[$attr $($args)*])* - const $Flag: $T = Self::$Flag.bits; - } - )+ + // See `Debug::fmt` for why this approach is taken. + #[allow(non_snake_case)] + trait __BitFlags { + $( + const $Flag: $T = 0; + )+ + } + #[allow(non_snake_case)] + impl __BitFlags for $BitFlags { + $( + __impl_bitflags! { + #[allow(deprecated)] + $(? #[$attr $($args)*])* + const $Flag: $T = Self::$Flag.bits; } - $BitFlags { bits: $(<$BitFlags as __BitFlags>::$Flag)|+ } - } + )+ } + Self { bits: $(::$Flag)|+ } }; ( - $BitFlags:ident: $T:ty { - } + $BitFlags:ident: $T:ty { } ) => { - __fn_bitflags! { - /// Returns the set containing all flags. - #[inline] - pub const fn all() -> $BitFlags { - $BitFlags { bits: 0 } - } - } + Self { bits: 0 } }; } @@ -573,6 +455,7 @@ macro_rules! __impl_bitflags { // Conditionally override the check for just those flags that // are not #[cfg]ed away. + #[allow(non_snake_case)] impl __BitFlags for $BitFlags { $( __impl_bitflags! { @@ -592,15 +475,15 @@ macro_rules! __impl_bitflags { let mut first = true; $( - if <$BitFlags as __BitFlags>::$Flag(self) { + if ::$Flag(self) { if !first { f.write_str(" | ")?; } first = false; - f.write_str(__bitflags_stringify!($Flag))?; + f.write_str($crate::_core::stringify!($Flag))?; } )* - let extra_bits = self.bits & !$BitFlags::all().bits(); + let extra_bits = self.bits & !Self::all().bits(); if extra_bits != 0 { if !first { f.write_str(" | ")?; @@ -640,18 +523,19 @@ macro_rules! __impl_bitflags { impl $BitFlags { $( $(#[$attr $($args)*])* - pub const $Flag: $BitFlags = $BitFlags { bits: $value }; + pub const $Flag: Self = Self { bits: $value }; )* - __fn_bitflags! { - /// Returns an empty set of flags. - #[inline] - pub const fn empty() -> $BitFlags { - $BitFlags { bits: 0 } - } + /// Returns an empty set of flags. + #[inline] + pub const fn empty() -> Self { + Self { bits: 0 } } - __all_bitflags! { + /// Returns the set containing all flags. + #[inline] + pub const fn all() -> Self { + __impl_all_bitflags! { $BitFlags: $T { $( $(#[$attr $($args)*])* @@ -659,202 +543,275 @@ macro_rules! __impl_bitflags { )* } } + } - __fn_bitflags! { - /// Returns the raw value of the flags currently stored. - #[inline] - pub const fn bits(&self) -> $T { - self.bits - } + /// Returns the raw value of the flags currently stored. + #[inline] + pub const fn bits(&self) -> $T { + self.bits } /// Convert from underlying bit representation, unless that /// representation contains bits that do not correspond to a flag. #[inline] - pub const fn from_bits(bits: $T) -> $crate::_core::option::Option<$BitFlags> { - if (bits & !$BitFlags::all().bits()) == 0 { - $crate::_core::option::Option::Some($BitFlags { bits }) + pub const fn from_bits(bits: $T) -> $crate::_core::option::Option { + if (bits & !Self::all().bits()) == 0 { + $crate::_core::option::Option::Some(Self { bits }) } else { $crate::_core::option::Option::None } } - __fn_bitflags! { - /// Convert from underlying bit representation, dropping any bits - /// that do not correspond to flags. - #[inline] - pub const fn from_bits_truncate(bits: $T) -> $BitFlags { - $BitFlags { bits: bits & $BitFlags::all().bits } - } + /// Convert from underlying bit representation, dropping any bits + /// that do not correspond to flags. + #[inline] + pub const fn from_bits_truncate(bits: $T) -> Self { + Self { bits: bits & Self::all().bits } } - __fn_bitflags! { - /// Convert from underlying bit representation, preserving all - /// bits (even those not corresponding to a defined flag). - /// - /// # Safety - /// - /// The caller of the `bitflags!` macro can chose to allow or - /// disallow extra bits for their bitflags type. - /// - /// The caller of `from_bits_unchecked()` has to ensure that - /// all bits correspond to a defined flag or that extra bits - /// are valid for this bitflags type. - #[inline] - pub const unsafe fn from_bits_unchecked(bits: $T) -> $BitFlags { - $BitFlags { bits } - } + /// Convert from underlying bit representation, preserving all + /// bits (even those not corresponding to a defined flag). + /// + /// # Safety + /// + /// The caller of the `bitflags!` macro can chose to allow or + /// disallow extra bits for their bitflags type. + /// + /// The caller of `from_bits_unchecked()` has to ensure that + /// all bits correspond to a defined flag or that extra bits + /// are valid for this bitflags type. + #[inline] + pub const unsafe fn from_bits_unchecked(bits: $T) -> Self { + Self { bits } } - __fn_bitflags! { - /// Returns `true` if no flags are currently stored. - #[inline] - pub const fn is_empty(&self) -> bool { - self.bits() == $BitFlags::empty().bits() - } + /// Returns `true` if no flags are currently stored. + #[inline] + pub const fn is_empty(&self) -> bool { + self.bits() == Self::empty().bits() } - __fn_bitflags! { - /// Returns `true` if all flags are currently set. - #[inline] - pub const fn is_all(&self) -> bool { - $BitFlags::all().bits | self.bits == self.bits - } + /// Returns `true` if all flags are currently set. + #[inline] + pub const fn is_all(&self) -> bool { + Self::all().bits | self.bits == self.bits } - __fn_bitflags! { - /// Returns `true` if there are flags common to both `self` and `other`. - #[inline] - pub const fn intersects(&self, other: $BitFlags) -> bool { - !$BitFlags{ bits: self.bits & other.bits}.is_empty() - } + /// Returns `true` if there are flags common to both `self` and `other`. + #[inline] + pub const fn intersects(&self, other: Self) -> bool { + !(Self { bits: self.bits & other.bits}).is_empty() } - __fn_bitflags! { - /// Returns `true` all of the flags in `other` are contained within `self`. - #[inline] - pub const fn contains(&self, other: $BitFlags) -> bool { - (self.bits & other.bits) == other.bits - } + /// Returns `true` if all of the flags in `other` are contained within `self`. + #[inline] + pub const fn contains(&self, other: Self) -> bool { + (self.bits & other.bits) == other.bits } /// Inserts the specified flags in-place. #[inline] - pub fn insert(&mut self, other: $BitFlags) { + pub fn insert(&mut self, other: Self) { self.bits |= other.bits; } /// Removes the specified flags in-place. #[inline] - pub fn remove(&mut self, other: $BitFlags) { + pub fn remove(&mut self, other: Self) { self.bits &= !other.bits; } /// Toggles the specified flags in-place. #[inline] - pub fn toggle(&mut self, other: $BitFlags) { + pub fn toggle(&mut self, other: Self) { self.bits ^= other.bits; } /// Inserts or removes the specified flags depending on the passed value. #[inline] - pub fn set(&mut self, other: $BitFlags, value: bool) { + pub fn set(&mut self, other: Self, value: bool) { if value { self.insert(other); } else { self.remove(other); } } + + /// Returns the intersection between the flags in `self` and + /// `other`. + /// + /// Specifically, the returned set contains only the flags which are + /// present in *both* `self` *and* `other`. + /// + /// This is equivalent to using the `&` operator (e.g. + /// [`ops::BitAnd`]), as in `flags & other`. + /// + /// [`ops::BitAnd`]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html + #[inline] + #[must_use] + pub const fn intersection(self, other: Self) -> Self { + Self { bits: self.bits & other.bits } + } + + /// Returns the union of between the flags in `self` and `other`. + /// + /// Specifically, the returned set contains all flags which are + /// present in *either* `self` *or* `other`, including any which are + /// present in both (see [`Self::symmetric_difference`] if that + /// is undesirable). + /// + /// This is equivalent to using the `|` operator (e.g. + /// [`ops::BitOr`]), as in `flags | other`. + /// + /// [`ops::BitOr`]: https://doc.rust-lang.org/std/ops/trait.BitOr.html + #[inline] + #[must_use] + pub const fn union(self, other: Self) -> Self { + Self { bits: self.bits | other.bits } + } + + /// Returns the difference between the flags in `self` and `other`. + /// + /// Specifically, the returned set contains all flags present in + /// `self`, except for the ones present in `other`. + /// + /// It is also conceptually equivalent to the "bit-clear" operation: + /// `flags & !other` (and this syntax is also supported). + /// + /// This is equivalent to using the `-` operator (e.g. + /// [`ops::Sub`]), as in `flags - other`. + /// + /// [`ops::Sub`]: https://doc.rust-lang.org/std/ops/trait.Sub.html + #[inline] + #[must_use] + pub const fn difference(self, other: Self) -> Self { + Self { bits: self.bits & !other.bits } + } + + /// Returns the [symmetric difference][sym-diff] between the flags + /// in `self` and `other`. + /// + /// Specifically, the returned set contains the flags present which + /// are present in `self` or `other`, but that are not present in + /// both. Equivalently, it contains the flags present in *exactly + /// one* of the sets `self` and `other`. + /// + /// This is equivalent to using the `^` operator (e.g. + /// [`ops::BitXor`]), as in `flags ^ other`. + /// + /// [sym-diff]: https://en.wikipedia.org/wiki/Symmetric_difference + /// [`ops::BitXor`]: https://doc.rust-lang.org/std/ops/trait.BitXor.html + #[inline] + #[must_use] + pub const fn symmetric_difference(self, other: Self) -> Self { + Self { bits: self.bits ^ other.bits } + } + + /// Returns the complement of this set of flags. + /// + /// Specifically, the returned set contains all the flags which are + /// not set in `self`, but which are allowed for this type. + /// + /// Alternatively, it can be thought of as the set difference + /// between [`Self::all()`] and `self` (e.g. `Self::all() - self`) + /// + /// This is equivalent to using the `!` operator (e.g. + /// [`ops::Not`]), as in `!flags`. + /// + /// [`Self::all()`]: Self::all + /// [`ops::Not`]: https://doc.rust-lang.org/std/ops/trait.Not.html + #[inline] + #[must_use] + pub const fn complement(self) -> Self { + Self::from_bits_truncate(!self.bits) + } + } impl $crate::_core::ops::BitOr for $BitFlags { - type Output = $BitFlags; + type Output = Self; /// Returns the union of the two sets of flags. #[inline] - fn bitor(self, other: $BitFlags) -> $BitFlags { - $BitFlags { bits: self.bits | other.bits } + fn bitor(self, other: $BitFlags) -> Self { + Self { bits: self.bits | other.bits } } } impl $crate::_core::ops::BitOrAssign for $BitFlags { - /// Adds the set of flags. #[inline] - fn bitor_assign(&mut self, other: $BitFlags) { + fn bitor_assign(&mut self, other: Self) { self.bits |= other.bits; } } impl $crate::_core::ops::BitXor for $BitFlags { - type Output = $BitFlags; + type Output = Self; /// Returns the left flags, but with all the right flags toggled. #[inline] - fn bitxor(self, other: $BitFlags) -> $BitFlags { - $BitFlags { bits: self.bits ^ other.bits } + fn bitxor(self, other: Self) -> Self { + Self { bits: self.bits ^ other.bits } } } impl $crate::_core::ops::BitXorAssign for $BitFlags { - /// Toggles the set of flags. #[inline] - fn bitxor_assign(&mut self, other: $BitFlags) { + fn bitxor_assign(&mut self, other: Self) { self.bits ^= other.bits; } } impl $crate::_core::ops::BitAnd for $BitFlags { - type Output = $BitFlags; + type Output = Self; /// Returns the intersection between the two sets of flags. #[inline] - fn bitand(self, other: $BitFlags) -> $BitFlags { - $BitFlags { bits: self.bits & other.bits } + fn bitand(self, other: Self) -> Self { + Self { bits: self.bits & other.bits } } } impl $crate::_core::ops::BitAndAssign for $BitFlags { - /// Disables all flags disabled in the set. #[inline] - fn bitand_assign(&mut self, other: $BitFlags) { + fn bitand_assign(&mut self, other: Self) { self.bits &= other.bits; } } impl $crate::_core::ops::Sub for $BitFlags { - type Output = $BitFlags; + type Output = Self; /// Returns the set difference of the two sets of flags. #[inline] - fn sub(self, other: $BitFlags) -> $BitFlags { - $BitFlags { bits: self.bits & !other.bits } + fn sub(self, other: Self) -> Self { + Self { bits: self.bits & !other.bits } } } impl $crate::_core::ops::SubAssign for $BitFlags { - /// Disables all flags enabled in the set. #[inline] - fn sub_assign(&mut self, other: $BitFlags) { + fn sub_assign(&mut self, other: Self) { self.bits &= !other.bits; } } impl $crate::_core::ops::Not for $BitFlags { - type Output = $BitFlags; + type Output = Self; /// Returns the complement of this set of flags. #[inline] - fn not(self) -> $BitFlags { - $BitFlags { bits: !self.bits } & $BitFlags::all() + fn not(self) -> Self { + Self { bits: !self.bits } & Self::all() } } impl $crate::_core::iter::Extend<$BitFlags> for $BitFlags { - fn extend>(&mut self, iterator: T) { + fn extend>(&mut self, iterator: T) { for item in iterator { self.insert(item) } @@ -862,7 +819,7 @@ macro_rules! __impl_bitflags { } impl $crate::_core::iter::FromIterator<$BitFlags> for $BitFlags { - fn from_iter>(iterator: T) -> $BitFlags { + fn from_iter>(iterator: T) -> Self { let mut result = Self::empty(); result.extend(iterator); result @@ -940,7 +897,7 @@ macro_rules! __impl_bitflags { // Input: // // ? #[cfg(feature = "advanced")] - // ? #[deprecated(note = "Use somthing else.")] + // ? #[deprecated(note = "Use something else.")] // ? #[doc = r"High quality documentation."] // fn f() -> i32 { /* ... */ } // @@ -995,7 +952,7 @@ macro_rules! __impl_bitflags { // Input: // // ? #[cfg(feature = "advanced")] - // ? #[deprecated(note = "Use somthing else.")] + // ? #[deprecated(note = "Use something else.")] // ? #[doc = r"High quality documentation."] // const f: i32 { /* ... */ } // @@ -1039,16 +996,6 @@ macro_rules! __impl_bitflags { }; } -// Same as std::stringify but callable from __impl_bitflags, which needs to use -// local_inner_macros so can only directly call macros from this crate. -#[macro_export] -#[doc(hidden)] -macro_rules! __bitflags_stringify { - ($s:ident) => { - stringify!($s) - }; -} - #[cfg(feature = "example_generated")] pub mod example_generated; @@ -1062,6 +1009,7 @@ mod tests { #[doc = "> you are the easiest person to fool."] #[doc = "> "] #[doc = "> - Richard Feynman"] + #[derive(Default)] struct Flags: u32 { const A = 0b00000001; #[doc = " macros are way better at generating code than trans is"] @@ -1165,9 +1113,9 @@ mod tests { let extra = unsafe { EmptyFlags::from_bits_unchecked(0b1000) }; assert_eq!( - unsafe { EmptyFlags::from_bits_unchecked(0b1000) }, - (extra | EmptyFlags::empty()) - ); + unsafe { EmptyFlags::from_bits_unchecked(0b1000) }, + (extra | EmptyFlags::empty()) + ); } #[test] @@ -1300,6 +1248,188 @@ mod tests { assert_eq!(e3, Flags::A | Flags::B | extra); } + #[test] + fn test_set_ops_basic() { + let ab = Flags::A.union(Flags::B); + let ac = Flags::A.union(Flags::C); + let bc = Flags::B.union(Flags::C); + assert_eq!(ab.bits, 0b011); + assert_eq!(bc.bits, 0b110); + assert_eq!(ac.bits, 0b101); + + assert_eq!(ab, Flags::B.union(Flags::A)); + assert_eq!(ac, Flags::C.union(Flags::A)); + assert_eq!(bc, Flags::C.union(Flags::B)); + + assert_eq!(ac, Flags::A | Flags::C); + assert_eq!(bc, Flags::B | Flags::C); + assert_eq!(ab.union(bc), Flags::ABC); + + assert_eq!(ac, Flags::A | Flags::C); + assert_eq!(bc, Flags::B | Flags::C); + + assert_eq!(ac.union(bc), ac | bc); + assert_eq!(ac.union(bc), Flags::ABC); + assert_eq!(bc.union(ac), Flags::ABC); + + assert_eq!(ac.intersection(bc), ac & bc); + assert_eq!(ac.intersection(bc), Flags::C); + assert_eq!(bc.intersection(ac), Flags::C); + + assert_eq!(ac.difference(bc), ac - bc); + assert_eq!(bc.difference(ac), bc - ac); + assert_eq!(ac.difference(bc), Flags::A); + assert_eq!(bc.difference(ac), Flags::B); + + assert_eq!(bc.complement(), !bc); + assert_eq!(bc.complement(), Flags::A); + assert_eq!(ac.symmetric_difference(bc), Flags::A.union(Flags::B)); + assert_eq!(bc.symmetric_difference(ac), Flags::A.union(Flags::B)); + } + + #[test] + fn test_set_ops_const() { + // These just test that these compile and don't cause use-site panics + // (would be possible if we had some sort of UB) + const INTERSECT: Flags = Flags::all().intersection(Flags::C); + const UNION: Flags = Flags::A.union(Flags::C); + const DIFFERENCE: Flags = Flags::all().difference(Flags::A); + const COMPLEMENT: Flags = Flags::C.complement(); + const SYM_DIFFERENCE: Flags = UNION.symmetric_difference(DIFFERENCE); + assert_eq!(INTERSECT, Flags::C); + assert_eq!(UNION, Flags::A | Flags::C); + assert_eq!(DIFFERENCE, Flags::all() - Flags::A); + assert_eq!(COMPLEMENT, !Flags::C); + assert_eq!(SYM_DIFFERENCE, (Flags::A | Flags::C) ^ (Flags::all() - Flags::A)); + } + + #[test] + fn test_set_ops_unchecked() { + let extra = unsafe { Flags::from_bits_unchecked(0b1000) }; + let e1 = Flags::A.union(Flags::C).union(extra); + let e2 = Flags::B.union(Flags::C); + assert_eq!(e1.bits, 0b1101); + assert_eq!(e1.union(e2), (Flags::ABC | extra)); + assert_eq!(e1.intersection(e2), Flags::C); + assert_eq!(e1.difference(e2), Flags::A | extra); + assert_eq!(e2.difference(e1), Flags::B); + assert_eq!(e2.complement(), Flags::A); + assert_eq!(e1.complement(), Flags::B); + assert_eq!(e1.symmetric_difference(e2), Flags::A | Flags::B | extra); // toggle + } + + #[test] + fn test_set_ops_exhaustive() { + // Define a flag that contains gaps to help exercise edge-cases, + // especially around "unknown" flags (e.g. ones outside of `all()` + // `from_bits_unchecked`). + // - when lhs and rhs both have different sets of unknown flags. + // - unknown flags at both ends, and in the middle + // - cases with "gaps". + bitflags! { + struct Test: u16 { + // Intentionally no `A` + const B = 0b000000010; + // Intentionally no `C` + const D = 0b000001000; + const E = 0b000010000; + const F = 0b000100000; + const G = 0b001000000; + // Intentionally no `H` + const I = 0b100000000; + } + } + let iter_test_flags = + || (0..=0b111_1111_1111).map(|bits| unsafe { Test::from_bits_unchecked(bits) }); + + for a in iter_test_flags() { + assert_eq!( + a.complement(), + Test::from_bits_truncate(!a.bits), + "wrong result: !({:?})", + a, + ); + assert_eq!(a.complement(), !a, "named != op: !({:?})", a); + for b in iter_test_flags() { + // Check that the named operations produce the expected bitwise + // values. + assert_eq!( + a.union(b).bits, + a.bits | b.bits, + "wrong result: `{:?}` | `{:?}`", + a, + b, + ); + assert_eq!( + a.intersection(b).bits, + a.bits & b.bits, + "wrong result: `{:?}` & `{:?}`", + a, + b, + ); + assert_eq!( + a.symmetric_difference(b).bits, + a.bits ^ b.bits, + "wrong result: `{:?}` ^ `{:?}`", + a, + b, + ); + assert_eq!( + a.difference(b).bits, + a.bits & !b.bits, + "wrong result: `{:?}` - `{:?}`", + a, + b, + ); + // Note: Difference is checked as both `a - b` and `b - a` + assert_eq!( + b.difference(a).bits, + b.bits & !a.bits, + "wrong result: `{:?}` - `{:?}`", + b, + a, + ); + // Check that the named set operations are equivalent to the + // bitwise equivalents + assert_eq!(a.union(b), a | b, "named != op: `{:?}` | `{:?}`", a, b,); + assert_eq!( + a.intersection(b), + a & b, + "named != op: `{:?}` & `{:?}`", + a, + b, + ); + assert_eq!( + a.symmetric_difference(b), + a ^ b, + "named != op: `{:?}` ^ `{:?}`", + a, + b, + ); + assert_eq!(a.difference(b), a - b, "named != op: `{:?}` - `{:?}`", a, b,); + // Note: Difference is checked as both `a - b` and `b - a` + assert_eq!(b.difference(a), b - a, "named != op: `{:?}` - `{:?}`", b, a,); + // Verify that the operations which should be symmetric are + // actually symmetric. + assert_eq!(a.union(b), b.union(a), "asymmetry: `{:?}` | `{:?}`", a, b,); + assert_eq!( + a.intersection(b), + b.intersection(a), + "asymmetry: `{:?}` & `{:?}`", + a, + b, + ); + assert_eq!( + a.symmetric_difference(b), + b.symmetric_difference(a), + "asymmetry: `{:?}` ^ `{:?}`", + a, + b, + ); + } + } + } + #[test] fn test_set() { let mut e1 = Flags::A | Flags::C; @@ -1327,7 +1457,6 @@ mod tests { assert_eq!(m1, e1); } - #[cfg(bitflags_const_fn)] #[test] fn test_const_fn() { const _M1: Flags = Flags::empty(); @@ -1417,6 +1546,11 @@ mod tests { assert_eq!(hash(&x), hash(&y)); } + #[test] + fn test_default() { + assert_eq!(Flags::empty(), Flags::default()); + } + #[test] fn test_debug() { assert_eq!(format!("{:?}", Flags::A | Flags::B), "A | B"); @@ -1431,10 +1565,7 @@ mod tests { "A | B | C | ABC | 0xb8" ); - assert_eq!( - format!("{:?}", EmptyFlags::empty()), - "(empty)" - ); + assert_eq!(format!("{:?}", EmptyFlags::empty()), "(empty)"); } #[test] @@ -1598,4 +1729,66 @@ mod tests { fn test_empty_bitflags() { bitflags! {} } + + #[test] + fn test_u128_bitflags() { + bitflags! { + struct Flags128: u128 { + const A = 0x0000_0000_0000_0000_0000_0000_0000_0001; + const B = 0x0000_0000_0000_1000_0000_0000_0000_0000; + const C = 0x8000_0000_0000_0000_0000_0000_0000_0000; + const ABC = Self::A.bits | Self::B.bits | Self::C.bits; + } + } + + assert_eq!(Flags128::ABC, Flags128::A | Flags128::B | Flags128::C); + assert_eq!(Flags128::A.bits, 0x0000_0000_0000_0000_0000_0000_0000_0001); + assert_eq!(Flags128::B.bits, 0x0000_0000_0000_1000_0000_0000_0000_0000); + assert_eq!(Flags128::C.bits, 0x8000_0000_0000_0000_0000_0000_0000_0000); + assert_eq!( + Flags128::ABC.bits, + 0x8000_0000_0000_1000_0000_0000_0000_0001 + ); + assert_eq!(format!("{:?}", Flags128::A), "A"); + assert_eq!(format!("{:?}", Flags128::B), "B"); + assert_eq!(format!("{:?}", Flags128::C), "C"); + assert_eq!(format!("{:?}", Flags128::ABC), "A | B | C | ABC"); + } + + #[test] + fn test_serde_bitflags_serialize() { + let flags = SerdeFlags::A | SerdeFlags::B; + + let serialized = serde_json::to_string(&flags).unwrap(); + + assert_eq!(serialized, r#"{"bits":3}"#); + } + + #[test] + fn test_serde_bitflags_deserialize() { + let deserialized: SerdeFlags = serde_json::from_str(r#"{"bits":12}"#).unwrap(); + + let expected = SerdeFlags::C | SerdeFlags::D; + + assert_eq!(deserialized.bits, expected.bits); + } + + #[test] + fn test_serde_bitflags_roundtrip() { + let flags = SerdeFlags::A | SerdeFlags::B; + + let deserialized: SerdeFlags = serde_json::from_str(&serde_json::to_string(&flags).unwrap()).unwrap(); + + assert_eq!(deserialized.bits, flags.bits); + } + + bitflags! { + #[derive(serde::Serialize, serde::Deserialize)] + struct SerdeFlags: u32 { + const A = 1; + const B = 2; + const C = 4; + const D = 8; + } + } } diff --git a/test_suite/Cargo.toml b/test_suite/Cargo.toml deleted file mode 100644 index 057a022c..00000000 --- a/test_suite/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[project] -name = "test_suite" -version = "0.0.0" - -[dependencies] -bitflags = { path = "../" } -trybuild = "1.0" -serde = "1.0" -serde_derive = "1.0" -serde_json = "1.0" diff --git a/test_suite/tests/compile-fail/private_flags.stderr b/test_suite/tests/compile-fail/private_flags.stderr deleted file mode 100644 index 2bdf1acb..00000000 --- a/test_suite/tests/compile-fail/private_flags.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0603]: struct `Flags2` is private - --> $DIR/private_flags.rs:18:26 - | -18 | let flag2 = example::Flags2::FLAG_B; - | ^^^^^^ private struct - | -note: the struct `Flags2` is defined here - --> $DIR/private_flags.rs:5:5 - | -5 | / bitflags! { -6 | | pub struct Flags1: u32 { -7 | | const FLAG_A = 0b00000001; -8 | | } -... | -12 | | } -13 | | } - | |_____^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/test_suite/tests/compiletest.rs b/test_suite/tests/compiletest.rs deleted file mode 100644 index db50dfda..00000000 --- a/test_suite/tests/compiletest.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[test] -fn compile_fail() { - let t = trybuild::TestCases::new(); - t.compile_fail("tests/compile-fail/*.rs"); -} diff --git a/test_suite/tests/conflicting_trait_impls.rs b/test_suite/tests/conflicting_trait_impls.rs deleted file mode 100644 index eb7a3251..00000000 --- a/test_suite/tests/conflicting_trait_impls.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![no_std] - -#[macro_use] -extern crate bitflags; - -#[allow(unused_imports)] -use core::fmt::Display; - -bitflags! { - /// baz - struct Flags: u32 { - const A = 0b00000001; - } -} - -#[test] -fn main() {} diff --git a/test_suite/tests/external.rs b/test_suite/tests/external.rs deleted file mode 100644 index 4c88387f..00000000 --- a/test_suite/tests/external.rs +++ /dev/null @@ -1,19 +0,0 @@ -#[macro_use] -extern crate bitflags; - -bitflags! { - /// baz - struct Flags: u32 { - const A = 0b00000001; - #[doc = "bar"] - const B = 0b00000010; - const C = 0b00000100; - #[doc = "foo"] - const ABC = Flags::A.bits | Flags::B.bits | Flags::C.bits; - } -} - -#[test] -fn smoke() { - assert_eq!(Flags::ABC, Flags::A | Flags::B | Flags::C); -} diff --git a/test_suite/tests/serde.rs b/test_suite/tests/serde.rs deleted file mode 100644 index 0424af5f..00000000 --- a/test_suite/tests/serde.rs +++ /dev/null @@ -1,35 +0,0 @@ -#[macro_use] -extern crate bitflags; - -#[macro_use] -extern crate serde_derive; -extern crate serde; -extern crate serde_json; - -bitflags! { - #[derive(Serialize, Deserialize)] - struct Flags: u32 { - const A = 1; - const B = 2; - const C = 4; - const D = 8; - } -} - -#[test] -fn serialize() { - let flags = Flags::A | Flags::B; - - let serialized = serde_json::to_string(&flags).unwrap(); - - assert_eq!(serialized, r#"{"bits":3}"#); -} - -#[test] -fn deserialize() { - let deserialized: Flags = serde_json::from_str(r#"{"bits":12}"#).unwrap(); - - let expected = Flags::C | Flags::D; - - assert_eq!(deserialized.bits, expected.bits); -} diff --git a/test_suite/tests/u128_bitflags.rs b/test_suite/tests/u128_bitflags.rs deleted file mode 100644 index 7ebad3d2..00000000 --- a/test_suite/tests/u128_bitflags.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![cfg(feature = "unstable")] - -#[macro_use] -extern crate bitflags; - -bitflags! { - /// baz - struct Flags128: u128 { - const A = 0x0000_0000_0000_0000_0000_0000_0000_0001; - const B = 0x0000_0000_0000_1000_0000_0000_0000_0000; - const C = 0x8000_0000_0000_0000_0000_0000_0000_0000; - const ABC = Self::A.bits | Self::B.bits | Self::C.bits; - } -} - -#[test] -fn test_u128_bitflags() { - assert_eq!(Flags128::ABC, Flags128::A | Flags128::B | Flags128::C); - assert_eq!(Flags128::A.bits, 0x0000_0000_0000_0000_0000_0000_0000_0001); - assert_eq!(Flags128::B.bits, 0x0000_0000_0000_1000_0000_0000_0000_0000); - assert_eq!(Flags128::C.bits, 0x8000_0000_0000_0000_0000_0000_0000_0000); - assert_eq!( - Flags128::ABC.bits, - 0x8000_0000_0000_1000_0000_0000_0000_0001 - ); - assert_eq!(format!("{:?}", Flags128::A), "A"); - assert_eq!(format!("{:?}", Flags128::B), "B"); - assert_eq!(format!("{:?}", Flags128::C), "C"); - assert_eq!(format!("{:?}", Flags128::ABC), "A | B | C | ABC"); -} diff --git a/test_suite/tests/external_no_std.rs b/tests/basic.rs similarity index 87% rename from test_suite/tests/external_no_std.rs rename to tests/basic.rs index 31f87e42..73a52bec 100644 --- a/test_suite/tests/external_no_std.rs +++ b/tests/basic.rs @@ -1,7 +1,6 @@ #![no_std] -#[macro_use] -extern crate bitflags; +use bitflags::bitflags; bitflags! { /// baz @@ -16,6 +15,6 @@ bitflags! { } #[test] -fn smoke() { +fn basic() { assert_eq!(Flags::ABC, Flags::A | Flags::B | Flags::C); } diff --git a/tests/compile-fail/.gitignore b/tests/compile-fail/.gitignore new file mode 100644 index 00000000..4dd9abc8 --- /dev/null +++ b/tests/compile-fail/.gitignore @@ -0,0 +1 @@ +*.stderr diff --git a/tests/compile-fail/impls/copy.rs b/tests/compile-fail/impls/copy.rs new file mode 100644 index 00000000..38f4822f --- /dev/null +++ b/tests/compile-fail/impls/copy.rs @@ -0,0 +1,10 @@ +use bitflags::bitflags; + +bitflags! { + #[derive(Clone, Copy)] + struct Flags: u32 { + const A = 0b00000001; + } +} + +fn main() {} diff --git a/tests/compile-fail/impls/copy.stderr.beta b/tests/compile-fail/impls/copy.stderr.beta new file mode 100644 index 00000000..0c13aa50 --- /dev/null +++ b/tests/compile-fail/impls/copy.stderr.beta @@ -0,0 +1,27 @@ +error[E0119]: conflicting implementations of trait `std::clone::Clone` for type `Flags` + --> $DIR/copy.rs:3:1 + | +3 | / bitflags! { +4 | | #[derive(Clone, Copy)] + | | ----- first implementation here +5 | | struct Flags: u32 { +6 | | const A = 0b00000001; +7 | | } +8 | | } + | |_^ conflicting implementation for `Flags` + | + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `Flags` + --> $DIR/copy.rs:3:1 + | +3 | / bitflags! { +4 | | #[derive(Clone, Copy)] + | | ---- first implementation here +5 | | struct Flags: u32 { +6 | | const A = 0b00000001; +7 | | } +8 | | } + | |_^ conflicting implementation for `Flags` + | + = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/compile-fail/impls/eq.rs b/tests/compile-fail/impls/eq.rs new file mode 100644 index 00000000..4abbd630 --- /dev/null +++ b/tests/compile-fail/impls/eq.rs @@ -0,0 +1,10 @@ +use bitflags::bitflags; + +bitflags! { + #[derive(PartialEq, Eq)] + struct Flags: u32 { + const A = 0b00000001; + } +} + +fn main() {} diff --git a/tests/compile-fail/impls/eq.stderr.beta b/tests/compile-fail/impls/eq.stderr.beta new file mode 100644 index 00000000..8a1a3b41 --- /dev/null +++ b/tests/compile-fail/impls/eq.stderr.beta @@ -0,0 +1,55 @@ +error[E0119]: conflicting implementations of trait `std::cmp::PartialEq` for type `Flags` + --> $DIR/eq.rs:3:1 + | +3 | / bitflags! { +4 | | #[derive(PartialEq, Eq)] + | | --------- first implementation here +5 | | struct Flags: u32 { +6 | | const A = 0b00000001; +7 | | } +8 | | } + | |_^ conflicting implementation for `Flags` + | + = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `std::cmp::Eq` for type `Flags` + --> $DIR/eq.rs:3:1 + | +3 | / bitflags! { +4 | | #[derive(PartialEq, Eq)] + | | -- first implementation here +5 | | struct Flags: u32 { +6 | | const A = 0b00000001; +7 | | } +8 | | } + | |_^ conflicting implementation for `Flags` + | + = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `std::marker::StructuralPartialEq` for type `Flags` + --> $DIR/eq.rs:3:1 + | +3 | / bitflags! { +4 | | #[derive(PartialEq, Eq)] + | | --------- first implementation here +5 | | struct Flags: u32 { +6 | | const A = 0b00000001; +7 | | } +8 | | } + | |_^ conflicting implementation for `Flags` + | + = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `std::marker::StructuralEq` for type `Flags` + --> $DIR/eq.rs:3:1 + | +3 | / bitflags! { +4 | | #[derive(PartialEq, Eq)] + | | -- first implementation here +5 | | struct Flags: u32 { +6 | | const A = 0b00000001; +7 | | } +8 | | } + | |_^ conflicting implementation for `Flags` + | + = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/compile-fail/non_integer_base/all_defined.rs b/tests/compile-fail/non_integer_base/all_defined.rs new file mode 100644 index 00000000..c2856b10 --- /dev/null +++ b/tests/compile-fail/non_integer_base/all_defined.rs @@ -0,0 +1,123 @@ +use std::{ + fmt::{ + self, + Debug, + Display, + LowerHex, + UpperHex, + Octal, + Binary, + }, + ops::{ + BitAnd, + BitOr, + BitXor, + BitAndAssign, + BitOrAssign, + BitXorAssign, + Not, + }, +}; + +use bitflags::bitflags; + +// Ideally we'd actually want this to work, but currently need something like `num`'s `Zero` +// With some design work it could be made possible +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct MyInt(u8); + +impl BitAnd for MyInt { + type Output = Self; + + fn bitand(self, other: Self) -> Self { + MyInt(self.0 & other.0) + } +} + +impl BitOr for MyInt { + type Output = Self; + + fn bitor(self, other: Self) -> Self { + MyInt(self.0 | other.0) + } +} + +impl BitXor for MyInt { + type Output = Self; + + fn bitxor(self, other: Self) -> Self { + MyInt(self.0 ^ other.0) + } +} + +impl BitAndAssign for MyInt { + fn bitand_assign(&mut self, other: Self) { + self.0 &= other.0 + } +} + +impl BitOrAssign for MyInt { + fn bitor_assign(&mut self, other: Self) { + self.0 |= other.0 + } +} + +impl BitXorAssign for MyInt { + fn bitxor_assign(&mut self, other: Self) { + self.0 ^= other.0 + } +} + +impl Debug for MyInt { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.0, f) + } +} + +impl Display for MyInt { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl LowerHex for MyInt { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + LowerHex::fmt(&self.0, f) + } +} + +impl UpperHex for MyInt { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + UpperHex::fmt(&self.0, f) + } +} + +impl Octal for MyInt { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Octal::fmt(&self.0, f) + } +} + +impl Binary for MyInt { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Binary::fmt(&self.0, f) + } +} + +impl Not for MyInt { + type Output = MyInt; + + fn not(self) -> Self { + MyInt(!self.0) + } +} + +bitflags! { + struct Flags128: MyInt { + const A = MyInt(0b0000_0001u8); + const B = MyInt(0b0000_0010u8); + const C = MyInt(0b0000_0100u8); + } +} + +fn main() {} diff --git a/tests/compile-fail/non_integer_base/all_defined.stderr.beta b/tests/compile-fail/non_integer_base/all_defined.stderr.beta new file mode 100644 index 00000000..1f0fb5cf --- /dev/null +++ b/tests/compile-fail/non_integer_base/all_defined.stderr.beta @@ -0,0 +1,27 @@ +error[E0308]: mismatched types + --> $DIR/all_defined.rs:115:1 + | +115 | / bitflags! { +116 | | struct Flags128: MyInt { +117 | | const A = MyInt(0b0000_0001u8); +118 | | const B = MyInt(0b0000_0010u8); +119 | | const C = MyInt(0b0000_0100u8); +120 | | } +121 | | } + | |_^ expected struct `MyInt`, found integer + | + = note: this error originates in the macro `__impl_all_bitflags` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/all_defined.rs:115:1 + | +115 | / bitflags! { +116 | | struct Flags128: MyInt { +117 | | const A = MyInt(0b0000_0001u8); +118 | | const B = MyInt(0b0000_0010u8); +119 | | const C = MyInt(0b0000_0100u8); +120 | | } +121 | | } + | |_^ expected struct `MyInt`, found integer + | + = note: this error originates in the macro `__impl_bitflags` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/compile-fail/non_integer_base/all_missing.rs b/tests/compile-fail/non_integer_base/all_missing.rs new file mode 100644 index 00000000..fff6b2cc --- /dev/null +++ b/tests/compile-fail/non_integer_base/all_missing.rs @@ -0,0 +1,13 @@ +use bitflags::bitflags; + +struct MyInt(u8); + +bitflags! { + struct Flags128: MyInt { + const A = MyInt(0b0000_0001); + const B = MyInt(0b0000_0010); + const C = MyInt(0b0000_0100); + } +} + +fn main() {} diff --git a/tests/compile-fail/non_integer_base/all_missing.stderr.beta b/tests/compile-fail/non_integer_base/all_missing.stderr.beta new file mode 100644 index 00000000..ee95f836 --- /dev/null +++ b/tests/compile-fail/non_integer_base/all_missing.stderr.beta @@ -0,0 +1,13 @@ +error[E0204]: the trait `Copy` may not be implemented for this type + --> $DIR/all_missing.rs:5:1 + | +5 | / bitflags! { +6 | | struct Flags128: MyInt { +7 | | const A = MyInt(0b0000_0001); +8 | | const B = MyInt(0b0000_0010); +9 | | const C = MyInt(0b0000_0100); +10 | | } +11 | | } + | |_^ this field does not implement `Copy` + | + = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/compile-fail/visibility/private_field.rs b/tests/compile-fail/visibility/private_field.rs new file mode 100644 index 00000000..a6a3912a --- /dev/null +++ b/tests/compile-fail/visibility/private_field.rs @@ -0,0 +1,13 @@ +mod example { + use bitflags::bitflags; + + bitflags! { + pub struct Flags1: u32 { + const FLAG_A = 0b00000001; + } + } +} + +fn main() { + let flag1 = example::Flags1::FLAG_A.bits; +} diff --git a/tests/compile-fail/visibility/private_field.stderr.beta b/tests/compile-fail/visibility/private_field.stderr.beta new file mode 100644 index 00000000..58a04660 --- /dev/null +++ b/tests/compile-fail/visibility/private_field.stderr.beta @@ -0,0 +1,10 @@ +error[E0616]: field `bits` of struct `Flags1` is private + --> $DIR/private_field.rs:12:41 + | +12 | let flag1 = example::Flags1::FLAG_A.bits; + | ^^^^ private field + | +help: a method `bits` also exists, call it with parentheses + | +12 | let flag1 = example::Flags1::FLAG_A.bits(); + | ^^ diff --git a/test_suite/tests/compile-fail/private_flags.rs b/tests/compile-fail/visibility/private_flags.rs similarity index 89% rename from test_suite/tests/compile-fail/private_flags.rs rename to tests/compile-fail/visibility/private_flags.rs index 0532246a..85a5b186 100644 --- a/test_suite/tests/compile-fail/private_flags.rs +++ b/tests/compile-fail/visibility/private_flags.rs @@ -1,7 +1,6 @@ -#[macro_use] -extern crate bitflags; - mod example { + use bitflags::bitflags; + bitflags! { pub struct Flags1: u32 { const FLAG_A = 0b00000001; diff --git a/tests/compile-fail/visibility/private_flags.stderr.beta b/tests/compile-fail/visibility/private_flags.stderr.beta new file mode 100644 index 00000000..d23f8320 --- /dev/null +++ b/tests/compile-fail/visibility/private_flags.stderr.beta @@ -0,0 +1,18 @@ +error[E0603]: struct `Flags2` is private + --> $DIR/private_flags.rs:17:26 + | +17 | let flag2 = example::Flags2::FLAG_B; + | ^^^^^^ private struct + | +note: the struct `Flags2` is defined here + --> $DIR/private_flags.rs:4:5 + | +4 | / bitflags! { +5 | | pub struct Flags1: u32 { +6 | | const FLAG_A = 0b00000001; +7 | | } +... | +11 | | } +12 | | } + | |_____^ + = note: this error originates in the macro `bitflags` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/compile-fail/visibility/pub_const.rs b/tests/compile-fail/visibility/pub_const.rs new file mode 100644 index 00000000..b90f0ce9 --- /dev/null +++ b/tests/compile-fail/visibility/pub_const.rs @@ -0,0 +1,9 @@ +use bitflags::bitflags; + +bitflags! { + pub struct Flags1: u32 { + pub const FLAG_A = 0b00000001; + } +} + +fn main() {} diff --git a/tests/compile-fail/visibility/pub_const.stderr.beta b/tests/compile-fail/visibility/pub_const.stderr.beta new file mode 100644 index 00000000..b01122c7 --- /dev/null +++ b/tests/compile-fail/visibility/pub_const.stderr.beta @@ -0,0 +1,5 @@ +error: no rules expected the token `pub` + --> $DIR/pub_const.rs:5:9 + | +5 | pub const FLAG_A = 0b00000001; + | ^^^ no rules expected this token in macro call diff --git a/tests/compile-pass/impls/convert.rs b/tests/compile-pass/impls/convert.rs new file mode 100644 index 00000000..1f02982a --- /dev/null +++ b/tests/compile-pass/impls/convert.rs @@ -0,0 +1,17 @@ +use bitflags::bitflags; + +bitflags! { + struct Flags: u32 { + const A = 0b00000001; + } +} + +impl From for Flags { + fn from(v: u32) -> Flags { + Flags::from_bits_truncate(v) + } +} + +fn main() { + +} diff --git a/tests/compile-pass/impls/default.rs b/tests/compile-pass/impls/default.rs new file mode 100644 index 00000000..a97b6536 --- /dev/null +++ b/tests/compile-pass/impls/default.rs @@ -0,0 +1,10 @@ +use bitflags::bitflags; + +bitflags! { + #[derive(Default)] + struct Flags: u32 { + const A = 0b00000001; + } +} + +fn main() {} diff --git a/tests/compile-pass/impls/inherent_methods.rs b/tests/compile-pass/impls/inherent_methods.rs new file mode 100644 index 00000000..3052c460 --- /dev/null +++ b/tests/compile-pass/impls/inherent_methods.rs @@ -0,0 +1,15 @@ +use bitflags::bitflags; + +bitflags! { + struct Flags: u32 { + const A = 0b00000001; + } +} + +impl Flags { + pub fn new() -> Flags { + Flags::A + } +} + +fn main() {} diff --git a/tests/compile-pass/redefinition/core.rs b/tests/compile-pass/redefinition/core.rs new file mode 100644 index 00000000..47549215 --- /dev/null +++ b/tests/compile-pass/redefinition/core.rs @@ -0,0 +1,14 @@ +use bitflags::bitflags; + +// Checks for possible errors caused by overriding names used by `bitflags!` internally. + +mod core {} +mod _core {} + +bitflags! { + struct Test: u8 { + const A = 1; + } +} + +fn main() {} diff --git a/tests/compile-pass/redefinition/stringify.rs b/tests/compile-pass/redefinition/stringify.rs new file mode 100644 index 00000000..b04f2f6a --- /dev/null +++ b/tests/compile-pass/redefinition/stringify.rs @@ -0,0 +1,19 @@ +use bitflags::bitflags; + +// Checks for possible errors caused by overriding names used by `bitflags!` internally. + +#[allow(unused_macros)] +macro_rules! stringify { + ($($t:tt)*) => { "..." }; +} + +bitflags! { + struct Test: u8 { + const A = 1; + } +} + +fn main() { + // Just make sure we don't call the redefined `stringify` macro + assert_eq!(format!("{:?}", Test::A), "A"); +} diff --git a/tests/compile-pass/repr/c.rs b/tests/compile-pass/repr/c.rs new file mode 100644 index 00000000..6feba36e --- /dev/null +++ b/tests/compile-pass/repr/c.rs @@ -0,0 +1,10 @@ +use bitflags::bitflags; + +bitflags! { + #[repr(C)] + struct Flags: u32 { + const A = 0b00000001; + } +} + +fn main() {} diff --git a/tests/compile-pass/repr/transparent.rs b/tests/compile-pass/repr/transparent.rs new file mode 100644 index 00000000..e38db4dd --- /dev/null +++ b/tests/compile-pass/repr/transparent.rs @@ -0,0 +1,10 @@ +use bitflags::bitflags; + +bitflags! { + #[repr(transparent)] + struct Flags: u32 { + const A = 0b00000001; + } +} + +fn main() {} diff --git a/tests/compile-pass/visibility/bits_field.rs b/tests/compile-pass/visibility/bits_field.rs new file mode 100644 index 00000000..33a7967e --- /dev/null +++ b/tests/compile-pass/visibility/bits_field.rs @@ -0,0 +1,11 @@ +use bitflags::bitflags; + +bitflags! { + pub struct Flags1: u32 { + const FLAG_A = 0b00000001; + } +} + +fn main() { + assert_eq!(0b00000001, Flags1::FLAG_A.bits); +} diff --git a/tests/compile-pass/visibility/pub_in.rs b/tests/compile-pass/visibility/pub_in.rs new file mode 100644 index 00000000..c11050e3 --- /dev/null +++ b/tests/compile-pass/visibility/pub_in.rs @@ -0,0 +1,19 @@ +mod a { + mod b { + use bitflags::bitflags; + + bitflags! { + pub(in crate::a) struct Flags: u32 { + const FLAG_A = 0b00000001; + } + } + } + + pub fn flags() -> u32 { + b::Flags::FLAG_A.bits() + } +} + +fn main() { + assert_eq!(0b00000001, a::flags()); +} diff --git a/tests/compile.rs b/tests/compile.rs new file mode 100644 index 00000000..ed02d01e --- /dev/null +++ b/tests/compile.rs @@ -0,0 +1,63 @@ +use std::{ + fs, + ffi::OsStr, + io, + path::Path, +}; + +use walkdir::WalkDir; + +#[test] +fn fail() { + prepare_stderr_files("tests/compile-fail").unwrap(); + + let t = trybuild::TestCases::new(); + t.compile_fail("tests/compile-fail/**/*.rs"); +} + +#[test] +fn pass() { + let t = trybuild::TestCases::new(); + t.pass("tests/compile-pass/**/*.rs"); +} + +// Compiler messages may change between versions +// We don't want to have to track these too closely for `bitflags`, but +// having some message to check makes sure user-facing errors are sensical. +// +// The approach we use is to run the test on all compilers, but only check stderr +// output on beta (which is the next stable release). We do this by default ignoring +// any `.stderr` files in the `compile-fail` directory, and copying `.stderr.beta` files +// when we happen to be running on a beta compiler. +fn prepare_stderr_files(path: impl AsRef) -> io::Result<()> { + for entry in WalkDir::new(path) { + let entry = entry?; + + if entry.path().extension().and_then(OsStr::to_str) == Some("beta") { + let renamed = entry.path().with_extension(""); + + // Unconditionally remove a corresponding `.stderr` file for a `.stderr.beta` + // file if it exists. On `beta` compilers, we'll recreate it. On other compilers, + // we don't want to end up checking it anyways. + if renamed.exists() { + fs::remove_file(&renamed)?; + } + + rename_beta_stderr(entry.path(), renamed)?; + } + } + + Ok(()) +} + +#[rustversion::beta] +fn rename_beta_stderr(from: impl AsRef, to: impl AsRef) -> io::Result<()> { + fs::copy(from, to)?; + + Ok(()) +} + +#[rustversion::not(beta)] +fn rename_beta_stderr(_: impl AsRef, _: impl AsRef) -> io::Result<()> { + Ok(()) +}