Repository: bitflags/bitflags
Pull request #371 (2023-07-03 22:44:49)

Support unnamed flags
This PR introduces the concept of _unnamed flags_. You can add an unnamed flag to your flags types like:

```rust
bitflags! {
    pub struct Flags: u8 {
        // regular, named flag
        const A = 1;

        // unnamed flag
        const _ = !0;
    }
}
```

Adding an unnamed flag means its bits will be part of the mask in `from_bits_truncate`, but it won't generate a constant, and it won't be given a name when formatting or parsing.


Patch:
diff --git a/src/example_generated.rs b/src/example_generated.rs
index 3586e3f..abb1118 100644
--- a/src/example_generated.rs
+++ b/src/example_generated.rs
@@ -21,16 +21,16 @@ __impl_internal_bitflags! {
         // Field `A`.
         ///
         /// This flag has the value `0b00000001`.
-        A = 0b00000001;
+        const A = 0b00000001;
         /// Field `B`.
         ///
         /// This flag has the value `0b00000010`.
-        B = 0b00000010;
+        const B = 0b00000010;
         /// Field `C`.
         ///
         /// This flag has the value `0b00000100`.
-        C = 0b00000100;
-        ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
+        const C = 0b00000100;
+        const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
     }
 }
 
@@ -51,15 +51,15 @@ __impl_public_bitflags_consts! {
         /// Field `A`.
         ///
         /// This flag has the value `0b00000001`.
-        A = 0b00000001;
+        const A = 0b00000001;
         /// Field `B`.
         ///
         /// This flag has the value `0b00000010`.
-        B = 0b00000010;
+        const B = 0b00000010;
         /// Field `C`.
         ///
         /// This flag has the value `0b00000100`.
-        C = 0b00000100;
-        ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
+        const C = 0b00000100;
+        const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
     }
 }
diff --git a/src/external.rs b/src/external.rs
index 94bd200..efeaa82 100644
--- a/src/external.rs
+++ b/src/external.rs
@@ -21,8 +21,8 @@ macro_rules! __impl_external_bitflags_my_library {
     (
         $InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
             $(
-                $(#[$attr:ident $($args:tt)*])*
-                $Flag:ident;
+                $(#[$inner:ident $($args:tt)*])*
+                const $Flag:tt;
             )*
         }
     ) => {
@@ -37,8 +37,8 @@ macro_rules! __impl_external_bitflags_my_library {
     (
         $InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
             $(
-                $(#[$attr:ident $($args:tt)*])*
-                $Flag:ident;
+                $(#[$inner:ident $($args:tt)*])*
+                const $Flag:tt;
             )*
         }
     ) => {};
@@ -57,8 +57,8 @@ Now, we add our macro call to the `__impl_external_bitflags` macro body:
 __impl_external_bitflags_my_library! {
     $InternalBitFlags: $T, $PublicBitFlags {
         $(
-            $(#[$attr $($args)*])*
-            $Flag;
+            $(#[$inner $($args)*])*
+            const $Flag;
         )*
     }
 }
@@ -83,8 +83,8 @@ macro_rules! __impl_external_bitflags {
     (
         $InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
             $(
-                $(#[$attr:ident $($args:tt)*])*
-                $Flag:ident;
+                $(#[$inner:ident $($args:tt)*])*
+                const $Flag:tt;
             )*
         }
     ) => {
@@ -95,8 +95,8 @@ macro_rules! __impl_external_bitflags {
         __impl_external_bitflags_serde! {
             $InternalBitFlags: $T, $PublicBitFlags {
                 $(
-                    $(#[$attr $($args)*])*
-                    $Flag;
+                    $(#[$inner $($args)*])*
+                    const $Flag;
                 )*
             }
         }
@@ -104,8 +104,8 @@ macro_rules! __impl_external_bitflags {
         __impl_external_bitflags_arbitrary! {
             $InternalBitFlags: $T, $PublicBitFlags {
                 $(
-                    $(#[$attr $($args)*])*
-                    $Flag;
+                    $(#[$inner $($args)*])*
+                    const $Flag;
                 )*
             }
         }
@@ -113,8 +113,8 @@ macro_rules! __impl_external_bitflags {
         __impl_external_bitflags_bytemuck! {
             $InternalBitFlags: $T, $PublicBitFlags {
                 $(
-                    $(#[$attr $($args)*])*
-                    $Flag;
+                    $(#[$inner $($args)*])*
+                    const $Flag;
                 )*
             }
         }
@@ -132,8 +132,8 @@ macro_rules! __impl_external_bitflags_serde {
     (
         $InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
             $(
-                $(#[$attr:ident $($args:tt)*])*
-                $Flag:ident;
+                $(#[$inner:ident $($args:tt)*])*
+                const $Flag:tt;
             )*
         }
     ) => {
@@ -168,8 +168,8 @@ macro_rules! __impl_external_bitflags_serde {
     (
         $InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
             $(
-                $(#[$attr:ident $($args:tt)*])*
-                $Flag:ident;
+                $(#[$inner:ident $($args:tt)*])*
+                const $Flag:tt;
             )*
         }
     ) => {};
@@ -189,8 +189,8 @@ macro_rules! __impl_external_bitflags_arbitrary {
     (
             $InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
                 $(
-                    $(#[$attr:ident $($args:tt)*])*
-                    $Flag:ident;
+                    $(#[$inner:ident $($args:tt)*])*
+                    const $Flag:tt;
                 )*
             }
     ) => {
@@ -211,8 +211,8 @@ macro_rules! __impl_external_bitflags_arbitrary {
     (
         $InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
             $(
-                $(#[$attr:ident $($args:tt)*])*
-                $Flag:ident;
+                $(#[$inner:ident $($args:tt)*])*
+                const $Flag:tt;
             )*
         }
     ) => {};
@@ -226,8 +226,8 @@ macro_rules! __impl_external_bitflags_bytemuck {
     (
         $InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
             $(
-                $(#[$attr:ident $($args:tt)*])*
-                $Flag:ident;
+                $(#[$inner:ident $($args:tt)*])*
+                const $Flag:tt;
             )*
         }
     ) => {
@@ -254,8 +254,8 @@ macro_rules! __impl_external_bitflags_bytemuck {
     (
         $InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
             $(
-                $(#[$attr:ident $($args:tt)*])*
-                $Flag:ident;
+                $(#[$inner:ident $($args:tt)*])*
+                const $Flag:tt;
             )*
         }
     ) => {};
diff --git a/src/internal.rs b/src/internal.rs
index 824cdde..aca1ac4 100644
--- a/src/internal.rs
+++ b/src/internal.rs
@@ -31,8 +31,8 @@ macro_rules! __impl_internal_bitflags {
     (
         $InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
             $(
-                $(#[$attr:ident $($args:tt)*])*
-                $Flag:ident = $value:expr;
+                $(#[$inner:ident $($args:tt)*])*
+                const $Flag:tt = $value:expr;
             )*
         }
     ) => {
@@ -100,8 +100,8 @@ macro_rules! __impl_internal_bitflags {
         __impl_public_bitflags! {
             $InternalBitFlags: $T, $PublicBitFlags {
                 $(
-                    $(#[$attr $($args)*])*
-                    $Flag;
+                    $(#[$inner $($args)*])*
+                    const $Flag = $value;
                 )*
             }
         }
diff --git a/src/iter.rs b/src/iter.rs
index 761124f..cad7d15 100644
--- a/src/iter.rs
+++ b/src/iter.rs
@@ -109,6 +109,11 @@ impl<B: Flags> Iterator for IterNames<B> {
 
             self.idx += 1;
 
+            // Skip unnamed flags
+            if flag.name().is_empty() {
+                continue;
+            }
+
             let bits = flag.value().bits();
 
             // If the flag is set in the original source _and_ it has bits that haven't
diff --git a/src/lib.rs b/src/lib.rs
index 81785f3..ffeef79 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -352,10 +352,9 @@
 //!
 //! ## Multi-bit Flags
 //!
-//! It is allowed to define a flag with multiple bits set, however such
-//! flags are _not_ treated as a set where any of those bits is a valid
-//! flag. Instead, each flag is treated as a unit when converting from
-//! bits with [`from_bits`] or [`from_bits_truncate`].
+//! It is allowed to define a flag with multiple bits set. When using multi-bit flags
+//! with [`from_bits`] or [`from_bits_truncate`], if only a subset of the bits in that flag
+//! are set, the result will still be non-empty:
 //!
 //! ```
 //! use bitflags::bitflags;
@@ -369,8 +368,8 @@
 //!
 //! fn main() {
 //!     // This bit pattern does not set all the bits in `F3`, so it is rejected.
-//!     assert!(Flags::from_bits(0b00000001).is_none());
-//!     assert!(Flags::from_bits_truncate(0b00000001).is_empty());
+//!     assert!(Flags::from_bits(0b00000001).is_some());
+//!     assert!(!Flags::from_bits_truncate(0b00000001).is_empty());
 //! }
 //! ```
 //!
@@ -560,7 +559,7 @@ macro_rules! bitflags {
         $vis:vis struct $BitFlags:ident: $T:ty {
             $(
                 $(#[$inner:ident $($args:tt)*])*
-                const $Flag:ident = $value:expr;
+                const $Flag:tt = $value:expr;
             )*
         }
 
@@ -578,7 +577,7 @@ macro_rules! bitflags {
             $BitFlags: $T {
                 $(
                     $(#[$inner $($args)*])*
-                    $Flag = $value;
+                    const $Flag = $value;
                 )*
             }
         }
@@ -604,7 +603,7 @@ macro_rules! bitflags {
                 InternalBitFlags: $T, $BitFlags {
                     $(
                         $(#[$inner $($args)*])*
-                        $Flag = $value;
+                        const $Flag = $value;
                     )*
                 }
             }
@@ -614,7 +613,7 @@ macro_rules! bitflags {
                 InternalBitFlags: $T, $BitFlags {
                     $(
                         $(#[$inner $($args)*])*
-                        $Flag;
+                        const $Flag;
                     )*
                 }
             }
@@ -640,7 +639,7 @@ macro_rules! bitflags {
         impl $BitFlags:ident: $T:ty {
             $(
                 $(#[$inner:ident $($args:tt)*])*
-                const $Flag:ident = $value:expr;
+                const $Flag:tt = $value:expr;
             )*
         }
 
@@ -650,7 +649,7 @@ macro_rules! bitflags {
             $BitFlags: $T {
                 $(
                     $(#[$inner $($args)*])*
-                    $Flag = $value;
+                    const $Flag = $value;
                 )*
             }
         }
@@ -670,7 +669,7 @@ macro_rules! bitflags {
                 $BitFlags: $T, $BitFlags {
                     $(
                         $(#[$inner $($args)*])*
-                        $Flag;
+                        const $Flag = $value;
                     )*
                 }
             }
@@ -1012,6 +1011,30 @@ macro_rules! __bitflags_expr_safe_attrs {
     }
 }
 
+/// Implement a flag, which may be a wildcard `_`.
+#[macro_export(local_inner_macros)]
+#[doc(hidden)]
+macro_rules! __bitflags_flag {
+    (
+        {
+            name: _,
+            named: { $($named:tt)* },
+            unnamed: { $($unnamed:tt)* },
+        }
+    ) => {
+        $($unnamed)*
+    };
+    (
+        {
+            name: $Flag:ident,
+            named: { $($named:tt)* },
+            unnamed: { $($unnamed:tt)* },
+        }
+    ) => {
+        $($named)*
+    };
+}
+
 #[macro_use]
 mod public;
 #[macro_use]
diff --git a/src/public.rs b/src/public.rs
index eb8e4c5..1952dce 100644
--- a/src/public.rs
+++ b/src/public.rs
@@ -130,8 +130,8 @@ macro_rules! __impl_public_bitflags {
     (
         $BitFlags:ident: $T:ty, $PublicBitFlags:ident {
             $(
-                $(#[$attr:ident $($args:tt)*])*
-                $Flag:ident;
+                $(#[$inner:ident $($args:tt)*])*
+                const $Flag:tt = $value:expr;
             )*
         }
     ) => {
@@ -142,7 +142,23 @@ macro_rules! __impl_public_bitflags {
                 }
 
                 fn all() {
-                    Self::from_bits_truncate(<$T as $crate::Bits>::ALL)
+                    let mut truncated = <$T as $crate::Bits>::EMPTY;
+                    let mut i = 0;
+
+                    $(
+                        __bitflags_expr_safe_attrs!(
+                            $(#[$inner $($args)*])*
+                            {{
+                                let flag = <$PublicBitFlags as $crate::Flags>::FLAGS[i].value().bits();
+
+                                truncated = truncated | flag;
+                                i += 1;
+                            }}
+                        );
+                    )*
+
+                    let _ = i;
+                    Self::from_bits_retain(truncated)
                 }
 
                 fn bits(f) {
@@ -160,24 +176,7 @@ macro_rules! __impl_public_bitflags {
                 }
 
                 fn from_bits_truncate(bits) {
-                    if bits == <$T as $crate::Bits>::EMPTY {
-                        return Self(bits)
-                    }
-
-                    let mut truncated = <$T as $crate::Bits>::EMPTY;
-
-                    $(
-                        __bitflags_expr_safe_attrs!(
-                            $(#[$attr $($args)*])*
-                            {
-                                if bits & $PublicBitFlags::$Flag.bits() == $PublicBitFlags::$Flag.bits() {
-                                    truncated = truncated | $PublicBitFlags::$Flag.bits()
-                                }
-                            }
-                        );
-                    )*
-
-                    Self(truncated)
+                    Self(bits & Self::all().bits())
                 }
 
                 fn from_bits_retain(bits) {
@@ -186,14 +185,20 @@ macro_rules! __impl_public_bitflags {
 
                 fn from_name(name) {
                     $(
-                        __bitflags_expr_safe_attrs!(
-                            $(#[$attr $($args)*])*
-                            {
-                                if name == $crate::__private::core::stringify!($Flag) {
-                                    return $crate::__private::core::option::Option::Some(Self($PublicBitFlags::$Flag.bits()));
-                                }
-                            }
-                        );
+                        __bitflags_flag!({
+                            name: $Flag,
+                            named: {
+                                __bitflags_expr_safe_attrs!(
+                                    $(#[$inner $($args)*])*
+                                    {
+                                        if name == $crate::__private::core::stringify!($Flag) {
+                                            return $crate::__private::core::option::Option::Some(Self($PublicBitFlags::$Flag.bits()));
+                                        }
+                                    }
+                                );
+                            },
+                            unnamed: {},
+                        });
                     )*
 
                     let _ = name;
@@ -219,15 +224,15 @@ macro_rules! __impl_public_bitflags {
                 }
 
                 fn insert(f, other) {
-                    *f = Self::from_bits_retain(f.bits() | other.bits());
+                    *f = Self::from_bits_retain(f.bits()).union(other);
                 }
 
                 fn remove(f, other) {
-                    *f = Self::from_bits_retain(f.bits() & !other.bits());
+                    *f = Self::from_bits_retain(f.bits()).difference(other);
                 }
 
                 fn toggle(f, other) {
-                    *f = Self::from_bits_retain(f.bits() ^ other.bits());
+                    *f = Self::from_bits_retain(f.bits()).symmetric_difference(other);
                 }
 
                 fn set(f, other, value) {
@@ -355,7 +360,7 @@ macro_rules! __impl_public_bitflags_ops {
             /// Adds the set of flags.
             #[inline]
             fn bitor_assign(&mut self, other: Self) {
-                *self = Self::from_bits_retain(self.bits()).union(other);
+                self.insert(other);
             }
         }
 
@@ -373,7 +378,7 @@ macro_rules! __impl_public_bitflags_ops {
             /// Toggles the set of flags.
             #[inline]
             fn bitxor_assign(&mut self, other: Self) {
-                *self = Self::from_bits_retain(self.bits()).symmetric_difference(other);
+                self.toggle(other);
             }
         }
 
@@ -409,7 +414,7 @@ macro_rules! __impl_public_bitflags_ops {
             /// Disables all flags enabled in the set.
             #[inline]
             fn sub_assign(&mut self, other: Self) {
-                *self = Self::from_bits_retain(self.bits()).difference(other);
+                self.remove(other);
             }
         }
 
@@ -455,35 +460,58 @@ macro_rules! __impl_public_bitflags_consts {
     (
         $PublicBitFlags:ident: $T:ty {
             $(
-                $(#[$attr:ident $($args:tt)*])*
-                $Flag:ident = $value:expr;
+                $(#[$inner:ident $($args:tt)*])*
+                const $Flag:tt = $value:expr;
             )*
         }
     ) => {
         impl $PublicBitFlags {
             $(
-                $(#[$attr $($args)*])*
-                #[allow(
-                    deprecated,
-                    non_upper_case_globals,
-                )]
-                pub const $Flag: Self = Self::from_bits_retain($value);
+                __bitflags_flag!({
+                    name: $Flag,
+                    named: {
+                        $(#[$inner $($args)*])*
+                        #[allow(
+                            deprecated,
+                            non_upper_case_globals,
+                        )]
+                        pub const $Flag: Self = Self::from_bits_retain($value);
+                    },
+                    unnamed: {},
+                });
             )*
         }
 
         impl $crate::Flags for $PublicBitFlags {
             const FLAGS: &'static [$crate::Flag<$PublicBitFlags>] = &[
                 $(
-                    __bitflags_expr_safe_attrs!(
-                        $(#[$attr $($args)*])*
-                        {
-                            #[allow(
-                                deprecated,
-                                non_upper_case_globals,
-                            )]
-                            $crate::Flag::new($crate::__private::core::stringify!($Flag), $PublicBitFlags::$Flag)
-                        }
-                    ),
+                    __bitflags_flag!({
+                        name: $Flag,
+                        named: {
+                            __bitflags_expr_safe_attrs!(
+                                $(#[$inner $($args)*])*
+                                {
+                                    #[allow(
+                                        deprecated,
+                                        non_upper_case_globals,
+                                    )]
+                                    $crate::Flag::new($crate::__private::core::stringify!($Flag), $PublicBitFlags::$Flag)
+                                }
+                            )
+                        },
+                        unnamed: {
+                            __bitflags_expr_safe_attrs!(
+                                $(#[$inner $($args)*])*
+                                {
+                                    #[allow(
+                                        deprecated,
+                                        non_upper_case_globals,
+                                    )]
+                                    $crate::Flag::new("", $PublicBitFlags::from_bits_retain($value))
+                                }
+                            )
+                        },
+                    }),
                 )*
             ];
 
diff --git a/src/traits.rs b/src/traits.rs
index 1e44645..e21a179 100644
--- a/src/traits.rs
+++ b/src/traits.rs
@@ -49,7 +49,13 @@ pub trait Flags: Sized + 'static {
 
     /// Returns the set containing all flags.
     fn all() -> Self {
-        Self::from_bits_truncate(Self::Bits::ALL)
+        let mut truncated = Self::Bits::EMPTY;
+
+        for flag in Self::FLAGS.iter() {
+            truncated = truncated | flag.value().bits();
+        }
+
+        Self::from_bits_retain(truncated)
     }
 
     /// Returns the raw value of the flags currently stored.
@@ -57,10 +63,6 @@ pub trait Flags: Sized + 'static {
 
     /// Convert from underlying bit representation, unless that
     /// representation contains bits that do not correspond to a flag.
-    ///
-    /// Note that each [multi-bit flag] is treated as a unit for this comparison.
-    ///
-    /// [multi-bit flag]: index.html#multi-bit-flags
     fn from_bits(bits: Self::Bits) -> Option<Self> {
         let truncated = Self::from_bits_truncate(bits);
 
@@ -73,26 +75,8 @@ pub trait Flags: Sized + 'static {
 
     /// Convert from underlying bit representation, dropping any bits
     /// that do not correspond to flags.
-    ///
-    /// Note that each [multi-bit flag] is treated as a unit for this comparison.
-    ///
-    /// [multi-bit flag]: index.html#multi-bit-flags
     fn from_bits_truncate(bits: Self::Bits) -> Self {
-        if bits == Self::Bits::EMPTY {
-            return Self::empty();
-        }
-
-        let mut truncated = Self::Bits::EMPTY;
-
-        for flag in Self::FLAGS.iter() {
-            let flag = flag.value();
-
-            if bits & flag.bits() == flag.bits() {
-                truncated = truncated | flag.bits();
-            }
-        }
-
-        Self::from_bits_retain(truncated)
+        Self::from_bits_retain(bits & Self::all().bits())
     }
 
     /// Convert from underlying bit representation, preserving all
@@ -101,6 +85,11 @@ pub trait Flags: Sized + 'static {
 
     /// Get the flag for a particular name.
     fn from_name(name: &str) -> Option<Self> {
+        // Don't parse empty names as empty flags
+        if name.is_empty() {
+            return None;
+        }
+
         for flag in Self::FLAGS {
             if flag.name() == name {
                 return Some(Self::from_bits_retain(flag.value().bits()));
@@ -155,7 +144,7 @@ pub trait Flags: Sized + 'static {
     where
         Self: Sized,
     {
-        *self = Self::from_bits_retain(self.bits() | other.bits());
+        *self = Self::from_bits_retain(self.bits()).union(other);
     }
 
     /// Removes the specified flags in-place.
@@ -165,7 +154,7 @@ pub trait Flags: Sized + 'static {
     where
         Self: Sized,
     {
-        *self = Self::from_bits_retain(self.bits() & !other.bits());
+        *self = Self::from_bits_retain(self.bits()).difference(other);
     }
 
     /// Toggles the specified flags in-place.
@@ -175,7 +164,7 @@ pub trait Flags: Sized + 'static {
     where
         Self: Sized,
     {
-        *self = Self::from_bits_retain(self.bits() ^ other.bits());
+        *self = Self::from_bits_retain(self.bits()).symmetric_difference(other);
     }
 
     /// Inserts or removes the specified flags depending on the passed value.


Test patch:
diff --git a/src/tests.rs b/src/tests.rs
index cb41f75..ed52ad4 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -104,4 +104,28 @@ bitflags! {
         /// 2
         const D = 1 << 1;
     }
+
+    #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+    pub struct TestExternal: u8 {
+        /// 1
+        const A = 1;
+
+        /// 1 << 1
+        const B = 1 << 1;
+
+        /// 1 << 2
+        const C = 1 << 2;
+
+        /// 1 | (1 << 1) | (1 << 2)
+        const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
+
+        /// External
+        const _ = !0;
+    }
+
+    #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+    pub struct TestExternalFull: u8 {
+        /// External
+        const _ = !0;
+    }
 }
diff --git a/src/tests/all.rs b/src/tests/all.rs
index 66e4f9d..cceb93a 100644
--- a/src/tests/all.rs
+++ b/src/tests/all.rs
@@ -9,6 +9,8 @@ fn cases() {
     case(0, TestZero::all);
 
     case(0, TestEmpty::all);
+
+    case(!0, TestExternal::all);
 }
 
 #[track_caller]
diff --git a/src/tests/bits.rs b/src/tests/bits.rs
index 65184e5..678f153 100644
--- a/src/tests/bits.rs
+++ b/src/tests/bits.rs
@@ -15,6 +15,12 @@ fn cases() {
     case(1 << 3, TestZero::from_bits_retain(1 << 3), TestZero::bits);
 
     case(1 << 3, TestEmpty::from_bits_retain(1 << 3), TestEmpty::bits);
+
+    case(
+        1 << 4 | 1 << 6,
+        TestExternal::from_bits_retain(1 << 4 | 1 << 6),
+        TestExternal::bits,
+    );
 }
 
 #[track_caller]
diff --git a/src/tests/complement.rs b/src/tests/complement.rs
index 057aaf1..ac7a421 100644
--- a/src/tests/complement.rs
+++ b/src/tests/complement.rs
@@ -29,8 +29,9 @@ fn cases() {
 
     case(0, TestEmpty::empty(), TestEmpty::complement);
 
-    // Complement doesn't detect overlapping bits in multi-bit flags
-    case(0, TestOverlapping::AB, TestOverlapping::complement);
+    case(1 << 2, TestOverlapping::AB, TestOverlapping::complement);
+
+    case(!0, TestExternal::empty(), TestExternal::complement);
 }
 
 #[track_caller]
diff --git a/src/tests/contains.rs b/src/tests/contains.rs
index 6067e05..12428dd 100644
--- a/src/tests/contains.rs
+++ b/src/tests/contains.rs
@@ -70,6 +70,17 @@ fn cases() {
         ],
         TestOverlapping::contains,
     );
+
+    case(
+        TestExternal::all(),
+        &[
+            (TestExternal::A, true),
+            (TestExternal::B, true),
+            (TestExternal::C, true),
+            (TestExternal::from_bits_retain(1 << 5 | 1 << 7), true),
+        ],
+        TestExternal::contains,
+    );
 }
 
 #[track_caller]
diff --git a/src/tests/difference.rs b/src/tests/difference.rs
index 556bf13..6ce9c0b 100644
--- a/src/tests/difference.rs
+++ b/src/tests/difference.rs
@@ -23,6 +23,17 @@ fn cases() {
         TestFlags::difference,
     );
 
+    case(
+        TestExternal::from_bits_retain(!0),
+        &[(TestExternal::A, 0b1111_1110)],
+        TestExternal::difference,
+    );
+
+    assert_eq!(
+        0b1111_1110,
+        (TestExternal::from_bits_retain(!0) & !TestExternal::A).bits()
+    );
+
     assert_eq!(
         0b1111_1110,
         (TestFlags::from_bits_retain(!0).difference(TestFlags::A)).bits()
diff --git a/src/tests/empty.rs b/src/tests/empty.rs
index 9cf73eb..57fb1c7 100644
--- a/src/tests/empty.rs
+++ b/src/tests/empty.rs
@@ -9,6 +9,8 @@ fn cases() {
     case(0, TestZero::empty);
 
     case(0, TestEmpty::empty);
+
+    case(0, TestExternal::empty);
 }
 
 #[track_caller]
diff --git a/src/tests/extend.rs b/src/tests/extend.rs
index ed0fe03..869dc17 100644
--- a/src/tests/extend.rs
+++ b/src/tests/extend.rs
@@ -16,3 +16,27 @@ fn cases() {
 
     assert_eq!(TestFlags::ABC | TestFlags::from_bits_retain(1 << 5), flags);
 }
+
+mod external {
+    use super::*;
+
+    #[test]
+    fn cases() {
+        let mut flags = TestExternal::empty();
+
+        flags.extend(TestExternal::A);
+
+        assert_eq!(TestExternal::A, flags);
+
+        flags.extend(TestExternal::A | TestExternal::B | TestExternal::C);
+
+        assert_eq!(TestExternal::ABC, flags);
+
+        flags.extend(TestExternal::from_bits_retain(1 << 5));
+
+        assert_eq!(
+            TestExternal::ABC | TestExternal::from_bits_retain(1 << 5),
+            flags
+        );
+    }
+}
diff --git a/src/tests/flags.rs b/src/tests/flags.rs
index bd0c0a9..7a625b3 100644
--- a/src/tests/flags.rs
+++ b/src/tests/flags.rs
@@ -21,3 +21,26 @@ fn cases() {
 
     assert_eq!(0, TestEmpty::FLAGS.iter().count());
 }
+
+mod external {
+    use super::*;
+
+    #[test]
+    fn cases() {
+        let flags = TestExternal::FLAGS
+            .iter()
+            .map(|flag| (flag.name(), flag.value().bits()))
+            .collect::<Vec<_>>();
+
+        assert_eq!(
+            vec![
+                ("A", 1u8),
+                ("B", 1 << 1),
+                ("C", 1 << 2),
+                ("ABC", 1 | 1 << 1 | 1 << 2),
+                ("", !0),
+            ],
+            flags,
+        );
+    }
+}
diff --git a/src/tests/fmt.rs b/src/tests/fmt.rs
index eb36c17..ed45718 100644
--- a/src/tests/fmt.rs
+++ b/src/tests/fmt.rs
@@ -49,6 +49,33 @@ fn cases() {
         "2",
         "10",
     );
+
+    case(
+        TestExternal::from_bits_retain(1 | 1 << 1 | 1 << 3),
+        "TestExternal(A | B | 0x8)",
+        "B",
+        "b",
+        "13",
+        "1011",
+    );
+
+    case(
+        TestExternal::all(),
+        "TestExternal(A | B | C | 0xf8)",
+        "FF",
+        "ff",
+        "377",
+        "11111111",
+    );
+
+    case(
+        TestExternalFull::all(),
+        "TestExternalFull(0xff)",
+        "FF",
+        "ff",
+        "377",
+        "11111111",
+    );
 }
 
 #[track_caller]
diff --git a/src/tests/from_bits.rs b/src/tests/from_bits.rs
index b0f4ede..dada9af 100644
--- a/src/tests/from_bits.rs
+++ b/src/tests/from_bits.rs
@@ -17,7 +17,9 @@ fn cases() {
 
     case(Some(1 | 1 << 1), 1 | 1 << 1, TestOverlapping::from_bits);
 
-    case(None, 1 << 1, TestOverlapping::from_bits);
+    case(Some(1 << 1), 1 << 1, TestOverlapping::from_bits);
+
+    case(Some(1 << 5), 1 << 5, TestExternal::from_bits);
 }
 
 #[track_caller]
diff --git a/src/tests/from_bits_retain.rs b/src/tests/from_bits_retain.rs
index 4fc7726..1ae28a6 100644
--- a/src/tests/from_bits_retain.rs
+++ b/src/tests/from_bits_retain.rs
@@ -14,6 +14,8 @@ fn cases() {
     case(1 | 1 << 1, TestOverlapping::from_bits_retain);
 
     case(1 << 1, TestOverlapping::from_bits_retain);
+
+    case(1 << 5, TestExternal::from_bits_retain);
 }
 
 #[track_caller]
diff --git a/src/tests/from_bits_truncate.rs b/src/tests/from_bits_truncate.rs
index 7f4372a..e4f3e53 100644
--- a/src/tests/from_bits_truncate.rs
+++ b/src/tests/from_bits_truncate.rs
@@ -17,7 +17,9 @@ fn cases() {
 
     case(1 | 1 << 1, 1 | 1 << 1, TestOverlapping::from_bits_truncate);
 
-    case(0, 1 << 1, TestOverlapping::from_bits_truncate);
+    case(1 << 1, 1 << 1, TestOverlapping::from_bits_truncate);
+
+    case(1 << 5, 1 << 5, TestExternal::from_bits_truncate);
 }
 
 #[track_caller]
diff --git a/src/tests/from_name.rs b/src/tests/from_name.rs
index 11bba43..1d9a4e4 100644
--- a/src/tests/from_name.rs
+++ b/src/tests/from_name.rs
@@ -16,6 +16,10 @@ fn cases() {
     case(Some(0), "ZERO", TestZero::from_name);
 
     case(Some(2), "二", TestUnicode::from_name);
+
+    case(None, "_", TestExternal::from_name);
+
+    case(None, "", TestExternal::from_name);
 }
 
 #[track_caller]
diff --git a/src/tests/iter.rs b/src/tests/iter.rs
index b16d37f..889ebcb 100644
--- a/src/tests/iter.rs
+++ b/src/tests/iter.rs
@@ -13,6 +13,10 @@ fn roundtrip() {
                 TestFlags::from_bits_truncate(f.bits()),
                 f.iter_names().map(|(_, f)| f).collect::<TestFlags>()
             );
+
+            let f = TestExternal::from_bits_retain(a | b);
+
+            assert_eq!(f, f.iter().collect::<TestExternal>());
         }
     }
 }
@@ -45,6 +49,18 @@ mod collect {
             .collect::<TestFlags>()
             .bits()
         );
+
+        assert_eq!(
+            1 << 5 | 1 << 7,
+            [
+                TestExternal::empty(),
+                TestExternal::from_bits_retain(1 << 5),
+                TestExternal::from_bits_retain(1 << 7),
+            ]
+            .into_iter()
+            .collect::<TestExternal>()
+            .bits()
+        );
     }
 }
 
@@ -77,6 +93,12 @@ mod iter {
         );
 
         case(&[], TestZero::ZERO, TestZero::iter);
+
+        case(
+            &[1, 1 << 1, 1 << 2, 0b1111_1000],
+            TestExternal::all(),
+            TestExternal::iter,
+        );
     }
 
     #[track_caller]
diff --git a/tests/compile-fail/bitflags_missing_value.stderr b/tests/compile-fail/bitflags_missing_value.stderr
index 8c17c97..ec36d17 100644
--- a/tests/compile-fail/bitflags_missing_value.stderr
+++ b/tests/compile-fail/bitflags_missing_value.stderr
@@ -7,8 +7,8 @@ error: no rules expected the token `;`
 note: while trying to match `=`
  --> src/lib.rs
   |
-  |                 const $Flag:ident = $value:expr;
-  |                                   ^
+  |                 const $Flag:tt = $value:expr;
+  |                                ^
 
 error[E0601]: `main` function not found in crate `$CRATE`
  --> tests/compile-fail/bitflags_missing_value.rs:8:2
diff --git a/tests/compile-fail/unnamed_const.rs b/tests/compile-fail/unnamed_const.rs
new file mode 100644
index 0000000..494f104
--- /dev/null
+++ b/tests/compile-fail/unnamed_const.rs
@@ -0,0 +1,11 @@
+use bitflags::bitflags;
+
+bitflags! {
+    pub struct Unnamed: u8 {
+        const _ = 1;
+
+        const A = Self::_.bits();
+    }
+}
+
+fn main() {}
diff --git a/tests/compile-fail/unnamed_const.stderr b/tests/compile-fail/unnamed_const.stderr
new file mode 100644
index 0000000..4639786
--- /dev/null
+++ b/tests/compile-fail/unnamed_const.stderr
@@ -0,0 +1,21 @@
+error: expected identifier, found reserved identifier `_`
+ --> tests/compile-fail/unnamed_const.rs:7:25
+  |
+7 |         const A = Self::_.bits();
+  |                         ^ expected identifier, found reserved identifier
+
+error[E0599]: no associated item named `_` found for struct `Unnamed` in the current scope
+ --> tests/compile-fail/unnamed_const.rs:7:25
+  |
+3 | / bitflags! {
+4 | |     pub struct Unnamed: u8 {
+5 | |         const _ = 1;
+6 | |
+7 | |         const A = Self::_.bits();
+  | |                         ^
+  | |                         |
+  | |                         associated item not found in `Unnamed`
+  | |                         help: there is an associated constant with a similar name: `A`
+8 | |     }
+9 | | }
+  | |_- associated item `_` not found for this struct
diff --git a/tests/compile-pass/unnamed.rs b/tests/compile-pass/unnamed.rs
new file mode 100644
index 0000000..946110c
--- /dev/null
+++ b/tests/compile-pass/unnamed.rs
@@ -0,0 +1,23 @@
+use bitflags::bitflags;
+
+bitflags! {
+    pub struct Unnamed: u8 {
+        const A = 1;
+
+        const _ = Self::A.bits();
+        const _ = 2;
+        const _ = !0;
+    }
+
+    pub struct External: u8 {
+        const _ = !0;
+    }
+
+    pub struct Overlapping: u8 {
+        const _ = 1;
+        const _ = 2;
+        const _ = !0;
+    }
+}
+
+fn main() {}
diff --git a/tests/smoke-test/src/main.rs b/tests/smoke-test/src/main.rs
index 6f84ff3..5f14a0c 100644
--- a/tests/smoke-test/src/main.rs
+++ b/tests/smoke-test/src/main.rs
@@ -9,6 +9,8 @@ bitflags! {
         const B = 0b00000010;
         const C = 0b00000100;
         const ABC = Flags::A.bits() | Flags::B.bits() | Flags::C.bits();
+
+        const _ = !0;
     }
 }