diff --git a/src/lib.rs b/src/lib.rs index ab7656e7..b9d77f7c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -559,10 +559,11 @@ macro_rules! __impl_bitflags { /// representation contains bits that do not correspond to a flag. #[inline] pub const fn from_bits(bits: $T) -> $crate::_core::option::Option { - if (bits & !Self::all().bits()) == 0 { - $crate::_core::option::Option::Some(Self { bits }) + let truncated = Self::from_bits_truncate(bits).bits; + if truncated == bits { + Some(Self{ bits }) } else { - $crate::_core::option::Option::None + None } } @@ -570,7 +571,22 @@ macro_rules! __impl_bitflags { /// that do not correspond to flags. #[inline] pub const fn from_bits_truncate(bits: $T) -> Self { - Self { bits: bits & Self::all().bits } + if bits == 0 { + return Self{ bits } + } + + #[allow(unused_mut)] + let mut truncated = 0; + + $( + #[allow(unused_doc_comments, unused_attributes)] + $(#[$attr $($args)*])* + if bits & Self::$Flag.bits == Self::$Flag.bits { + truncated |= Self::$Flag.bits + } + )* + + Self { bits: truncated } } /// Convert from underlying bit representation, preserving all @@ -1891,6 +1907,37 @@ mod tests { } } + #[test] + fn test_from_bits_edge_cases() { + bitflags! { + struct Flags: u8 { + const A = 0b00000001; + const BC = 0b00000110; + } + } + + + let flags = Flags::from_bits(0b00000100); + assert_eq!(flags, None); + let flags = Flags::from_bits(0b00000101); + assert_eq!(flags, None); + } + + #[test] + fn test_from_bits_truncate_edge_cases() { + bitflags! { + struct Flags: u8 { + const A = 0b00000001; + const BC = 0b00000110; + } + } + + let flags = Flags::from_bits_truncate(0b00000100); + assert_eq!(flags, Flags::empty()); + let flags = Flags::from_bits_truncate(0b00000101); + assert_eq!(flags, Flags::A); + } + #[test] fn test_iter() { bitflags! { @@ -1924,22 +1971,4 @@ mod tests { assert_eq!(iter.next().unwrap(), Flags::THREE); assert_eq!(iter.next(), None); } - - #[test] - fn test_iter_edge_cases() { - bitflags! { - struct Flags: u8 { - const A = 0b00000001; - const BC = 0b00000110; - } - } - - - let flags = Flags::all(); - assert_eq!(flags.iter().count(), 2); - let mut iter = flags.iter(); - assert_eq!(iter.next().unwrap(), Flags::A); - assert_eq!(iter.next().unwrap(), Flags::BC); - assert_eq!(iter.next(), None); - } } diff --git a/tests/compile-fail/non_integer_base/all_defined.stderr.beta b/tests/compile-fail/non_integer_base/all_defined.stderr.beta index 6fada425..0607f234 100644 --- a/tests/compile-fail/non_integer_base/all_defined.stderr.beta +++ b/tests/compile-fail/non_integer_base/all_defined.stderr.beta @@ -49,8 +49,41 @@ error[E0308]: mismatched types = note: this error originates in the macro `__impl_bitflags` (in Nightly builds, run with -Z macro-backtrace for more info) help: try wrapping the expression in `MyInt` | -562 | if (bits & !Self::all().bits()) == MyInt(0) { - | ++++++ + +574 | if bits == MyInt(0) { + | ++++++ + + +error[E0277]: no implementation for `{integer} |= MyInt` + --> $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 | | } + | |_^ no implementation for `{integer} |= MyInt` + | + = help: the trait `BitOrAssign` is not implemented for `{integer}` + = note: this error originates in the macro `__impl_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) +help: try wrapping the expression in `MyInt` + | +589 | Self { bits: MyInt(truncated) } + | ++++++ + error[E0308]: mismatched types --> $DIR/all_defined.rs:115:1