diff options
author | Joel Galenson <jgalenson@google.com> | 2021-10-08 21:09:14 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2021-10-08 21:09:14 +0000 |
commit | 2c02339005579fc4513097496f9ef74e60f5a3a9 (patch) | |
tree | b2594e1fc7ab61aee60e80ead080e1eef8e6e147 | |
parent | 78a7961e8a870a1fc9f29bd383333ffb01138d45 (diff) | |
parent | f9c300de26bddd15abcbf12dc0d30385017a2611 (diff) | |
download | bitflags-android-s-v2-preview-1.tar.gz |
Merge "Upgrade rust/crates/bitflags to 1.3.2"android-s-v2-preview-2android-s-v2-preview-1android-s-v2-beta-2android-s-v2-preview-1
39 files changed, 1392 insertions, 368 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 2499ccd..8e82c01 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,5 @@ { "git": { - "sha1": "30668016aca6bd3b02c766e8347e0b4080d4c296" + "sha1": "ed185cfb1c447c1b4bd6ac021c9ec3bb02c9e2f2" } } diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..ffe0eda --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,56 @@ +name: Rust + +on: [push, pull_request] + +env: + CARGO_TERM_COLOR: always + +jobs: + check: + name: Test + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + rust: + - stable + - beta + - nightly + - 1.46.0 + 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: --features example_generated + + embedded: + name: Build (embedded) + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + target: thumbv6m-none-eabi + override: true + + - name: Default features + uses: actions-rs/cargo@v1 + with: + command: build + args: -Z avoid-dev-deps --features example_generated --target thumbv6m-none-eabi @@ -1,3 +1,4 @@ +wip target Cargo.lock @@ -38,20 +38,30 @@ license { } rust_defaults { - name: "bitflags_defaults", + name: "bitflags_test_defaults", crate_name: "bitflags", // has rustc warnings srcs: ["src/lib.rs"], + cargo_env_compat: true, + cargo_pkg_version: "1.3.2", test_suites: ["general-tests"], auto_gen_config: true, - edition: "2015", + edition: "2018", features: ["default"], - cfgs: ["bitflags_const_fn"], + rustlibs: [ + "libserde", + "libserde_json", + "libwalkdir", + ], + proc_macros: [ + "librustversion", + "libserde_derive", + ], } rust_test_host { name: "bitflags_host_test_src_lib", - defaults: ["bitflags_defaults"], + defaults: ["bitflags_test_defaults"], test_options: { unit_test: true, }, @@ -59,7 +69,43 @@ rust_test_host { rust_test { name: "bitflags_device_test_src_lib", - defaults: ["bitflags_defaults"], + defaults: ["bitflags_test_defaults"], +} + +rust_defaults { + name: "bitflags_test_defaults_bitflags", + crate_name: "bitflags", + // has rustc warnings + srcs: ["tests/basic.rs"], + cargo_env_compat: true, + cargo_pkg_version: "1.3.2", + test_suites: ["general-tests"], + auto_gen_config: true, + edition: "2018", + features: ["default"], + rustlibs: [ + "libbitflags", + "libserde", + "libserde_json", + "libwalkdir", + ], + proc_macros: [ + "librustversion", + "libserde_derive", + ], +} + +rust_test_host { + name: "bitflags_host_test_tests_basic", + defaults: ["bitflags_test_defaults_bitflags"], + test_options: { + unit_test: true, + }, +} + +rust_test { + name: "bitflags_device_test_tests_basic", + defaults: ["bitflags_test_defaults_bitflags"], } rust_library { @@ -67,10 +113,11 @@ rust_library { // has rustc warnings host_supported: true, crate_name: "bitflags", + cargo_env_compat: true, + cargo_pkg_version: "1.3.2", srcs: ["src/lib.rs"], - edition: "2015", + edition: "2018", features: ["default"], - cfgs: ["bitflags_const_fn"], apex_available: [ "//apex_available:platform", "com.android.compos", diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d49101..12fea16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,60 @@ +# 1.3.2 + +- Allow `non_snake_case` in generated flags types ([#256]) + +[#252]: 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) + +- 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]) @@ -11,11 +11,11 @@ # will likely look very different (and much more reasonable) [package] +edition = "2018" name = "bitflags" -version = "1.2.1" +version = "1.3.2" authors = ["The Rust Project Developers"] -build = "build.rs" -exclude = [".travis.yml", "appveyor.yml", "bors.toml"] +exclude = ["bors.toml"] description = "A macro to generate structures which behave like bitflags.\n" homepage = "https://github.com/bitflags/bitflags" documentation = "https://docs.rs/bitflags" @@ -26,9 +26,33 @@ license = "MIT/Apache-2.0" repository = "https://github.com/bitflags/bitflags" [package.metadata.docs.rs] features = ["example_generated"] +[dependencies.compiler_builtins] +version = "0.1.2" +optional = true + +[dependencies.core] +version = "1.0.0" +optional = true +package = "rustc-std-workspace-core" +[dev-dependencies.rustversion] +version = "1.0" + +[dev-dependencies.serde] +version = "1.0" + +[dev-dependencies.serde_derive] +version = "1.0" + +[dev-dependencies.serde_json] +version = "1.0" + +[dev-dependencies.trybuild] +version = "1.0" + +[dev-dependencies.walkdir] +version = "2.3" [features] default = [] example_generated = [] -[badges.travis-ci] -repository = "bitflags/bitflags" +rustc-dep-of-std = ["core", "compiler_builtins"] diff --git a/Cargo.toml.orig b/Cargo.toml.orig index afbe066..be9e05a 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -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,22 +16,24 @@ categories = ["no-std"] description = """ A macro to generate structures which behave like bitflags. """ -exclude = [ - ".travis.yml", - "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 } -[badges] -travis-ci = { repository = "bitflags/bitflags" } +[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"] @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/bitflags/bitflags-1.2.1.crate" + value: "https://static.crates.io/crates/bitflags/bitflags-1.3.2.crate" } - version: "1.2.1" + version: "1.3.2" license_type: NOTICE last_upgrade_date { - year: 2019 - month: 12 - day: 12 + year: 2021 + month: 9 + day: 22 } } @@ -1,11 +1,10 @@ bitflags ======== -[![Build Status](https://travis-ci.com/bitflags/bitflags.svg?branch=master)](https://travis-ci.com/bitflags/bitflags) +[![Rust](https://github.com/bitflags/bitflags/workflows/Rust/badge.svg)](https://github.com/bitflags/bitflags/actions) [![Join the chat at https://gitter.im/bitflags/Lobby](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bitflags/Lobby?utm_source=badge&utm_medium=badge&utm_content=badge) [![Latest version](https://img.shields.io/crates/v/bitflags.svg)](https://crates.io/crates/bitflags) [![Documentation](https://docs.rs/bitflags/badge.svg)](https://docs.rs/bitflags) -![Minimum rustc version](https://img.shields.io/badge/rustc-1.20+-yellow.svg) ![License](https://img.shields.io/crates/l/bitflags.svg) A Rust macro to generate structures which behave like a set of bitflags @@ -19,16 +18,15 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -bitflags = "1.0" +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 985757a..0000000 --- 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<u32> { - 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() -}
\ No newline at end of file diff --git a/cargo2android.json b/cargo2android.json index a4a2e17..ac356cf 100644 --- a/cargo2android.json +++ b/cargo2android.json @@ -4,8 +4,13 @@ "com.android.compos", "com.android.virt" ], - "dependencies": true, + "dependency-blocklist": [ + "trybuild" + ], "device": true, "run": true, - "tests": true + "tests": true, + "test-blocklist": [ + "tests/compile.rs" + ] }
\ No newline at end of file diff --git a/patches/std.diff b/patches/std.diff new file mode 100644 index 0000000..c2ccaee --- /dev/null +++ b/patches/std.diff @@ -0,0 +1,15 @@ +diff --git a/src/lib.rs b/src/lib.rs +index 935e432..36c0699 100644 +--- a/src/lib.rs ++++ b/src/lib.rs +@@ -278,6 +278,10 @@ + #![cfg_attr(not(test), no_std)] + #![doc(html_root_url = "https://docs.rs/bitflags/1.3.2")] + ++// ANDROID: Unconditionally use std to allow building as a dylib ++#[macro_use] ++extern crate std; ++ + #[doc(hidden)] + pub extern crate core as _core; + @@ -11,15 +11,14 @@ //! A typesafe bitmask flag generator useful for sets of C-style bitmask flags. //! It can be used for creating typesafe wrappers around C APIs. //! -//! The `bitflags!` macro generates a `struct` that manages a set of flags. The +//! The `bitflags!` macro generates `struct`s that manage a set of flags. The //! flags should only be defined for integer types, otherwise unexpected type //! errors may occur at compile time. //! //! # 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; @@ -84,21 +82,19 @@ //! //! # Visibility //! -//! The generated struct and its associated flag constants are not exported +//! The generated structs and their associated flag constants are not exported //! out of the current module by default. A definition can be exported out of -//! the current module by adding `pub` before `flags`: +//! 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; //! } -//! } -//! bitflags! { +//! //! # pub //! struct Flags2: u32 { //! const B = 0b00000010; @@ -114,26 +110,44 @@ //! //! # Attributes //! -//! Attributes can be attached to the generated `struct` by placing them -//! before the `flags` keyword. +//! 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` -//! traits automatically derived for the `struct` using the `derive` attribute. +//! traits are automatically derived for the `struct`s using the `derive` attribute. //! Additional traits can be derived by providing an explicit `derive` -//! attribute on `flags`. +//! attribute on `struct`. //! -//! The `Extend` and `FromIterator` traits are implemented for the `struct`, +//! The `Extend` and `FromIterator` traits are implemented for the `struct`s, //! too: `Extend` adds the union of the instances of the `struct` iterated over, //! while `FromIterator` calculates the union. //! -//! The `Binary`, `Debug`, `LowerHex`, `Octal` and `UpperHex` trait is also +//! The `Binary`, `Debug`, `LowerHex`, `Octal` and `UpperHex` traits are also //! implemented by displaying the bits value of the internal struct. //! //! ## Operators //! -//! The following operator traits are implemented for the generated `struct`: +//! The following operator traits are implemented for the generated `struct`s: //! //! - `BitOr` and `BitOrAssign`: union //! - `BitAnd` and `BitAndAssign`: intersection @@ -143,7 +157,7 @@ //! //! # Methods //! -//! The following methods are defined for the generated `struct`: +//! The following methods are defined for the generated `struct`s: //! //! - `empty`: an empty set of flags //! - `all`: the set of all defined flags @@ -159,23 +173,34 @@ //! - `is_empty`: `true` if no flags are currently stored //! - `is_all`: `true` if currently set flags exactly equal all defined flags //! - `intersects`: `true` if there are flags common to both `self` and `other` -//! - `contains`: `true` all of the flags in `other` are contained within `self` +//! - `contains`: `true` if all of the flags in `other` are contained within `self` //! - `insert`: inserts the specified flags in-place //! - `remove`: removes the specified flags in-place //! - `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 //! -//! The `Default` trait is not automatically implemented for the generated struct. +//! The `Default` trait is not automatically implemented for the generated structs. //! //! If your default value is equal to `0` (which is the same value as calling `empty()` //! 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 @@ -196,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 { @@ -225,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 { @@ -249,28 +272,27 @@ //! 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_attr(not(test), no_std)] +#![doc(html_root_url = "https://docs.rs/bitflags/1.3.2")] // ANDROID: Unconditionally use std to allow building as a dylib #[macro_use] extern crate std; -// 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; -/// The macro used to generate the flag structure. +/// The macro used to generate the flag structures. /// /// See the [crate level docs](../bitflags/index.html) for complete documentation. /// /// # Example /// /// ``` -/// #[macro_use] -/// extern crate bitflags; +/// use bitflags::bitflags; /// /// bitflags! { /// struct Flags: u32 { @@ -295,11 +317,10 @@ pub extern crate core as _core; /// implementations: /// /// ``` -/// #[macro_use] -/// extern crate bitflags; -/// /// use std::fmt; /// +/// use bitflags::bitflags; +/// /// bitflags! { /// struct Flags: u32 { /// const A = 0b00000001; @@ -333,78 +354,18 @@ pub extern crate core as _core; macro_rules! bitflags { ( $(#[$outer:meta])* - pub struct $BitFlags:ident: $T:ty { + $vis:vis struct $BitFlags:ident: $T:ty { $( $(#[$inner:ident $($args:tt)*])* const $Flag:ident = $value:expr; - )+ - } - ) => { - __bitflags! { - $(#[$outer])* - (pub) $BitFlags: $T { - $( - $(#[$inner $($args)*])* - $Flag = $value; - )+ - } + )* } - }; - ( - $(#[$outer:meta])* - struct $BitFlags:ident: $T:ty { - $( - $(#[$inner:ident $($args:tt)*])* - const $Flag:ident = $value:expr; - )+ - } - ) => { - __bitflags! { - $(#[$outer])* - () $BitFlags: $T { - $( - $(#[$inner $($args)*])* - $Flag = $value; - )+ - } - } - }; - ( - $(#[$outer:meta])* - pub ($($vis:tt)+) struct $BitFlags:ident: $T:ty { - $( - $(#[$inner:ident $($args:tt)*])* - const $Flag:ident = $value:expr; - )+ - } - ) => { - __bitflags! { - $(#[$outer])* - (pub ($($vis)+)) $BitFlags: $T { - $( - $(#[$inner $($args)*])* - $Flag = $value; - )+ - } - } - }; -} -#[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; - )+ - } + $($t:tt)* ) => { $(#[$outer])* #[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] - $($vis)* struct $BitFlags { + $vis struct $BitFlags { bits: $T, } @@ -413,63 +374,52 @@ macro_rules! __bitflags { $( $(#[$inner $($args)*])* $Flag = $value; - )+ + )* } } - }; -} -#[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)* + bitflags! { + $($t)* + } }; + () => {}; } +// A helper macro to implement the `all` function. #[macro_export(local_inner_macros)] #[doc(hidden)] -#[cfg(not(bitflags_const_fn))] -macro_rules! __fn_bitflags { +macro_rules! __impl_all_bitflags { ( - $(# $attr_args:tt)* - const fn $($item:tt)* - ) => { - $(# $attr_args)* - fn $($item)* - }; - ( - $(# $attr_args:tt)* - pub const fn $($item:tt)* + $BitFlags:ident: $T:ty { + $( + $(#[$attr:ident $($args:tt)*])* + $Flag:ident = $value:expr; + )+ + } ) => { - $(# $attr_args)* - pub fn $($item)* + // 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; + } + )+ + } + Self { bits: $(<Self as __BitFlags>::$Flag)|+ } }; ( - $(# $attr_args:tt)* - pub const unsafe fn $($item:tt)* + $BitFlags:ident: $T:ty { } ) => { - $(# $attr_args)* - pub unsafe fn $($item)* + Self { bits: 0 } }; } @@ -481,7 +431,7 @@ macro_rules! __impl_bitflags { $( $(#[$attr:ident $($args:tt)*])* $Flag:ident = $value:expr; - )+ + )* } ) => { impl $crate::_core::fmt::Debug for $BitFlags { @@ -499,11 +449,12 @@ macro_rules! __impl_bitflags { $( #[inline] fn $Flag(&self) -> bool { false } - )+ + )* } // Conditionally override the check for just those flags that // are not #[cfg]ed away. + #[allow(non_snake_case)] impl __BitFlags for $BitFlags { $( __impl_bitflags! { @@ -518,20 +469,20 @@ macro_rules! __impl_bitflags { } } } - )+ + )* } let mut first = true; $( - if <$BitFlags as __BitFlags>::$Flag(self) { + if <Self as __BitFlags>::$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(" | ")?; @@ -571,227 +522,295 @@ 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 } } - __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 { + /// Returns the set containing all flags. + #[inline] + pub const fn all() -> Self { + __impl_all_bitflags! { + $BitFlags: $T { $( - const $Flag: $T = 0; - )+ + $(#[$attr $($args)*])* + $Flag = $value; + )* } - impl __BitFlags for $BitFlags { - $( - __impl_bitflags! { - #[allow(deprecated)] - $(? #[$attr $($args)*])* - const $Flag: $T = Self::$Flag.bits; - } - )+ - } - $BitFlags { bits: $(<$BitFlags as __BitFlags>::$Flag)|+ } } } - __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 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<Self> { + 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). - #[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 { - self.bits == $BitFlags::all().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<T: $crate::_core::iter::IntoIterator<Item=$BitFlags>>(&mut self, iterator: T) { + fn extend<T: $crate::_core::iter::IntoIterator<Item=Self>>(&mut self, iterator: T) { for item in iterator { self.insert(item) } @@ -799,7 +818,7 @@ macro_rules! __impl_bitflags { } impl $crate::_core::iter::FromIterator<$BitFlags> for $BitFlags { - fn from_iter<T: $crate::_core::iter::IntoIterator<Item=$BitFlags>>(iterator: T) -> $BitFlags { + fn from_iter<T: $crate::_core::iter::IntoIterator<Item=Self>>(iterator: T) -> Self { let mut result = Self::empty(); result.extend(iterator); result @@ -817,7 +836,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 { /* ... */ } // @@ -872,7 +891,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 { /* ... */ } // @@ -916,16 +935,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; @@ -939,6 +948,7 @@ mod tests { #[doc = "> you are the easiest person to fool."] #[doc = "> "] #[doc = "> - Richard Feynman"] + #[derive(Default)] struct Flags: u32 { const A = 0b00000001; #[doc = "<pcwalton> macros are way better at generating code than trans is"] @@ -949,9 +959,7 @@ mod tests { #[doc = "<strcat> wait what?"] const ABC = Self::A.bits | Self::B.bits | Self::C.bits; } - } - bitflags! { struct _CfgFlags: u32 { #[cfg(unix)] const _CFG_A = 0b01; @@ -960,20 +968,21 @@ mod tests { #[cfg(unix)] const _CFG_C = Self::_CFG_A.bits | 0b10; } - } - bitflags! { struct AnotherSetOfFlags: i8 { const ANOTHER_FLAG = -1_i8; } - } - bitflags! { struct LongFlags: u32 { const LONG_A = 0b1111111111111111; } } + bitflags! { + struct EmptyFlags: u32 { + } + } + #[test] fn test_bits() { assert_eq!(Flags::empty().bits(), 0b00000000); @@ -982,6 +991,8 @@ mod tests { assert_eq!(AnotherSetOfFlags::empty().bits(), 0b00); assert_eq!(AnotherSetOfFlags::ANOTHER_FLAG.bits(), !0_i8); + + assert_eq!(EmptyFlags::empty().bits(), 0b00000000); } #[test] @@ -996,6 +1007,9 @@ mod tests { AnotherSetOfFlags::from_bits(!0_i8), Some(AnotherSetOfFlags::ANOTHER_FLAG) ); + + assert_eq!(EmptyFlags::from_bits(0), Some(EmptyFlags::empty())); + assert_eq!(EmptyFlags::from_bits(0b1), None); } #[test] @@ -1011,6 +1025,9 @@ mod tests { AnotherSetOfFlags::from_bits_truncate(0_i8), AnotherSetOfFlags::empty() ); + + assert_eq!(EmptyFlags::from_bits_truncate(0), EmptyFlags::empty()); + assert_eq!(EmptyFlags::from_bits_truncate(0b1), EmptyFlags::empty()); } #[test] @@ -1019,9 +1036,25 @@ mod tests { assert_eq!(unsafe { Flags::from_bits_unchecked(0) }, Flags::empty()); assert_eq!(unsafe { Flags::from_bits_unchecked(0b1) }, Flags::A); assert_eq!(unsafe { Flags::from_bits_unchecked(0b10) }, Flags::B); - assert_eq!(unsafe { Flags::from_bits_unchecked(0b11) }, (Flags::A | Flags::B)); - assert_eq!(unsafe { Flags::from_bits_unchecked(0b1000) }, (extra | Flags::empty())); - assert_eq!(unsafe { Flags::from_bits_unchecked(0b1001) }, (extra | Flags::A)); + + assert_eq!( + unsafe { Flags::from_bits_unchecked(0b11) }, + (Flags::A | Flags::B) + ); + assert_eq!( + unsafe { Flags::from_bits_unchecked(0b1000) }, + (extra | Flags::empty()) + ); + assert_eq!( + unsafe { Flags::from_bits_unchecked(0b1001) }, + (extra | Flags::A) + ); + + let extra = unsafe { EmptyFlags::from_bits_unchecked(0b1000) }; + assert_eq!( + unsafe { EmptyFlags::from_bits_unchecked(0b1000) }, + (extra | EmptyFlags::empty()) + ); } #[test] @@ -1031,6 +1064,9 @@ mod tests { assert!(!Flags::ABC.is_empty()); assert!(!AnotherSetOfFlags::ANOTHER_FLAG.is_empty()); + + assert!(EmptyFlags::empty().is_empty()); + assert!(EmptyFlags::all().is_empty()); } #[test] @@ -1039,7 +1075,15 @@ mod tests { assert!(!Flags::A.is_all()); assert!(Flags::ABC.is_all()); + let extra = unsafe { Flags::from_bits_unchecked(0b1000) }; + assert!(!extra.is_all()); + assert!(!(Flags::A | extra).is_all()); + assert!((Flags::ABC | extra).is_all()); + assert!(AnotherSetOfFlags::ANOTHER_FLAG.is_all()); + + assert!(EmptyFlags::all().is_all()); + assert!(EmptyFlags::empty().is_all()); } #[test] @@ -1081,6 +1125,8 @@ mod tests { assert!(Flags::ABC.contains(e2)); assert!(AnotherSetOfFlags::ANOTHER_FLAG.contains(AnotherSetOfFlags::ANOTHER_FLAG)); + + assert!(EmptyFlags::empty().contains(EmptyFlags::empty())); } #[test] @@ -1142,6 +1188,188 @@ mod tests { } #[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; e1.set(Flags::B, true); @@ -1168,8 +1396,6 @@ mod tests { assert_eq!(m1, e1); } - - #[cfg(bitflags_const_fn)] #[test] fn test_const_fn() { const _M1: Flags = Flags::empty(); @@ -1260,6 +1486,11 @@ mod tests { } #[test] + fn test_default() { + assert_eq!(Flags::empty(), Flags::default()); + } + + #[test] fn test_debug() { assert_eq!(format!("{:?}", Flags::A | Flags::B), "A | B"); assert_eq!(format!("{:?}", Flags::empty()), "(empty)"); @@ -1267,7 +1498,13 @@ mod tests { let extra = unsafe { Flags::from_bits_unchecked(0xb8) }; assert_eq!(format!("{:?}", extra), "0xb8"); assert_eq!(format!("{:?}", Flags::A | extra), "A | 0xb8"); - assert_eq!(format!("{:?}", Flags::ABC | extra), "A | B | C | ABC | 0xb8"); + + assert_eq!( + format!("{:?}", Flags::ABC | extra), + "A | B | C | ABC | 0xb8" + ); + + assert_eq!(format!("{:?}", EmptyFlags::empty()), "(empty)"); } #[test] @@ -1311,8 +1548,7 @@ mod tests { pub struct PublicFlags: i8 { const X = 0; } - } - bitflags! { + struct PrivateFlags: i8 { const Y = 0; } @@ -1427,4 +1663,71 @@ mod tests { assert_eq!(format!("{:?}", Flags::empty()), "NONE"); assert_eq!(format!("{:?}", Flags::SOME), "SOME"); } + + #[test] + 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/tests/basic.rs b/tests/basic.rs new file mode 100644 index 0000000..73a52be --- /dev/null +++ b/tests/basic.rs @@ -0,0 +1,20 @@ +#![no_std] + +use bitflags::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 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 0000000..4dd9abc --- /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 0000000..38f4822 --- /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 0000000..0c13aa5 --- /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 0000000..4abbd63 --- /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 0000000..8a1a3b4 --- /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 0000000..c2856b1 --- /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 0000000..1f0fb5c --- /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 0000000..fff6b2c --- /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 0000000..ee95f83 --- /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 0000000..a6a3912 --- /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 0000000..58a0466 --- /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/tests/compile-fail/visibility/private_flags.rs b/tests/compile-fail/visibility/private_flags.rs new file mode 100644 index 0000000..85a5b18 --- /dev/null +++ b/tests/compile-fail/visibility/private_flags.rs @@ -0,0 +1,18 @@ +mod example { + use bitflags::bitflags; + + bitflags! { + pub struct Flags1: u32 { + const FLAG_A = 0b00000001; + } + + struct Flags2: u32 { + const FLAG_B = 0b00000010; + } + } +} + +fn main() { + let flag1 = example::Flags1::FLAG_A; + let flag2 = example::Flags2::FLAG_B; +} 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 0000000..d23f832 --- /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 0000000..b90f0ce --- /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 0000000..b01122c --- /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 0000000..1f02982 --- /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<u32> 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 0000000..a97b653 --- /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 0000000..3052c46 --- /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 0000000..4754921 --- /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 0000000..b04f2f6 --- /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 0000000..6feba36 --- /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 0000000..e38db4d --- /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 0000000..33a7967 --- /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 0000000..c11050e --- /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 0000000..ed02d01 --- /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<Path>) -> 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<Path>, to: impl AsRef<Path>) -> io::Result<()> { + fs::copy(from, to)?; + + Ok(()) +} + +#[rustversion::not(beta)] +fn rename_beta_stderr(_: impl AsRef<Path>, _: impl AsRef<Path>) -> io::Result<()> { + Ok(()) +} |