Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a hidden trait to discourage manual impls of BitFlags #261

Merged
merged 7 commits into from Oct 26, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/bitflags_trait.rs
@@ -1,6 +1,13 @@
use core as _core;

KodrAus marked this conversation as resolved.
Show resolved Hide resolved
pub trait BitFlags {
#[doc(hidden)]
pub trait ImplementedByBitFlagsMacro {}

/// A flags enum.
///
/// Manual implementations of this trait are not supported.
/// It should only be automatically implemented through the `bitflags!` macro.
KodrAus marked this conversation as resolved.
Show resolved Hide resolved
pub trait BitFlags: ImplementedByBitFlagsMacro {
type Bits;
/// Returns an empty set of flags.
fn empty() -> Self;
Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Expand Up @@ -286,6 +286,11 @@ pub use bitflags_trait::BitFlags;

mod bitflags_trait;

#[doc(hidden)]
pub mod __private {
pub use crate::bitflags_trait::ImplementedByBitFlagsMacro;
}

/// The macro used to generate the flag structure.
///
/// See the [crate level docs](../bitflags/index.html) for complete documentation.
Expand Down Expand Up @@ -885,6 +890,8 @@ macro_rules! __impl_bitflags {
$BitFlags::set(self, other, value)
}
}

impl $crate::__private::ImplementedByBitFlagsMacro for $BitFlags {}
};

// Every attribute that the user writes on a const is applied to the
Expand Down
25 changes: 25 additions & 0 deletions tests/compile-fail/cfg/multi.rs
@@ -0,0 +1,25 @@
#[macro_use]
extern crate bitflags;

// NOTE: Ideally this would work, but our treatment of CFGs
// assumes flags may be missing but not redefined
bitflags! {
pub struct Flags: u32 {
#[cfg(target_os = "linux")]
const FOO = 1;
#[cfg(not(target_os = "linux"))]
const FOO = 2;
}
}

fn main() {
#[cfg(target_os = "linux")]
{
assert_eq!(1, Flags::FOO.bits());
}

#[cfg(not(target_os = "linux"))]
{
assert_eq!(1, Flags::FOO.bits());
}
}
35 changes: 35 additions & 0 deletions tests/compile-fail/cfg/multi.stderr.beta
@@ -0,0 +1,35 @@
error[E0428]: the name `FOO` is defined multiple times
--> $DIR/multi.rs:6:1
|
6 | / bitflags! {
7 | | pub struct Flags: u32 {
8 | | #[cfg(target_os = "linux")]
9 | | const FOO = 1;
... |
12 | | }
13 | | }
| | ^
| | |
| |_`FOO` redefined here
| previous definition of the value `FOO` here
|
= note: `FOO` must be defined only once in the value namespace of this trait
= note: this error originates in the macro `__impl_bitflags` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0428]: the name `FOO` is defined multiple times
--> $DIR/multi.rs:6:1
|
6 | / bitflags! {
7 | | pub struct Flags: u32 {
8 | | #[cfg(target_os = "linux")]
9 | | const FOO = 1;
... |
12 | | }
13 | | }
| | ^
| | |
| |_`FOO` redefined here
| previous definition of the value `FOO` here
|
= note: `FOO` must be defined only once in the value namespace of this trait
= note: this error originates in the macro `__impl_all_bitflags` (in Nightly builds, run with -Z macro-backtrace for more info)
4 changes: 2 additions & 2 deletions tests/compile-fail/non_integer_base/all_defined.stderr.beta
Expand Up @@ -10,7 +10,7 @@ error[E0308]: mismatched types
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)
= 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
Expand All @@ -24,4 +24,4 @@ error[E0308]: mismatched types
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)
= note: this error originates in the macro `__impl_all_bitflags` (in Nightly builds, run with -Z macro-backtrace for more info)
65 changes: 65 additions & 0 deletions tests/compile-fail/trait/custom_impl.rs
@@ -0,0 +1,65 @@
use bitflags::BitFlags;

pub struct BootlegFlags(u32);

impl BitFlags for BootlegFlags {
type Bits = u32;

fn empty() -> Self {
unimplemented!()
}

fn all() -> Self {
unimplemented!()
}

fn bits(&self) -> u32 {
unimplemented!()
}

fn from_bits(_: u32) -> Option<BootlegFlags> {
unimplemented!()
}

fn from_bits_truncate(_: u32) -> BootlegFlags {
unimplemented!()
}

unsafe fn from_bits_unchecked(_: u32) -> BootlegFlags {
unimplemented!()
}

fn is_empty(&self) -> bool {
unimplemented!()
}

fn is_all(&self) -> bool {
unimplemented!()
}

fn intersects(&self, _: BootlegFlags) -> bool {
unimplemented!()
}

fn contains(&self, _: BootlegFlags) -> bool {
unimplemented!()
}

fn insert(&mut self, _: BootlegFlags) {
unimplemented!()
}

fn remove(&mut self, _: BootlegFlags) {
unimplemented!()
}

fn toggle(&mut self, _: BootlegFlags) {
unimplemented!()
}

fn set(&mut self, _: BootlegFlags, value: bool) {
unimplemented!()
}
}

fn main() { }
11 changes: 11 additions & 0 deletions tests/compile-fail/trait/custom_impl.stderr.beta
@@ -0,0 +1,11 @@
error[E0277]: the trait bound `BootlegFlags: ImplementedByBitFlagsMacro` is not satisfied
--> $DIR/custom_impl.rs:5:6
|
5 | impl BitFlags for BootlegFlags {
| ^^^^^^^^ the trait `ImplementedByBitFlagsMacro` is not implemented for `BootlegFlags`
|
note: required by a bound in `BitFlags`
--> $DIR/bitflags_trait.rs:6:21
|
6 | pub trait BitFlags: ImplementedByBitFlagsMacro {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `BitFlags`
2 changes: 1 addition & 1 deletion tests/compile-fail/visibility/private_field.stderr.beta
Expand Up @@ -7,4 +7,4 @@ error[E0616]: field `bits` of struct `Flags1` is private
help: a method `bits` also exists, call it with parentheses
|
12 | let flag1 = example::Flags1::FLAG_A.bits();
| ^^
| ++
23 changes: 23 additions & 0 deletions tests/compile-pass/cfg/nested-value.rs
@@ -0,0 +1,23 @@
#[macro_use]
extern crate bitflags;

bitflags! {
pub struct Flags: u32 {
const FOO = {
#[cfg(target_os = "linux")] { 1 }
#[cfg(not(target_os = "linux"))] { 2 }
};
}
}

fn main() {
#[cfg(target_os = "linux")]
{
assert_eq!(1, Flags::FOO.bits());
}

#[cfg(not(target_os = "linux"))]
{
assert_eq!(2, Flags::FOO.bits());
}
}
22 changes: 22 additions & 0 deletions tests/compile-pass/trait/precedence.rs
@@ -0,0 +1,22 @@
use bitflags::{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 all_from_trait<F: BitFlags>() {
let _ = F::all();
}

fn main() {
all_from_trait::<Flags>();
let _ = Flags::all();
}