Skip to content

Commit 7feb96d

Browse files
committed
Disallow multiple bits in a flag
Also: fix repr(u64) on 32-bit platforms Also: replace a quadratic algorithm in the macro with a linear one
1 parent 04b4c4b commit 7feb96d

File tree

1 file changed

+33
-36
lines changed

1 file changed

+33
-36
lines changed

enumflags_derive/src/lib.rs

+33-36
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ pub fn derive_enum_flags(input: proc_macro::TokenStream) -> proc_macro::TokenStr
2020
}
2121
}
2222

23-
fn max_value_of(ty: &str) -> Option<usize> {
23+
fn max_value_of(ty: &str) -> Option<u64> {
2424
match ty {
25-
"u8" => Some(u8::max_value() as usize),
26-
"u16" => Some(u16::max_value() as usize),
27-
"u32" => Some(u32::max_value() as usize),
28-
"u64" => Some(u64::max_value() as usize),
29-
"usize" => Some(usize::max_value()),
25+
"u8" => Some(u8::max_value() as u64),
26+
"u16" => Some(u16::max_value() as u64),
27+
"u32" => Some(u32::max_value() as u64),
28+
"u64" => Some(u64::max_value() as u64),
29+
"usize" => Some(usize::max_value() as u64),
3030
_ => None,
3131
}
3232
}
@@ -85,39 +85,36 @@ fn gen_enumflags(ident: &Ident, item: &DeriveInput, data: &DataEnum) -> TokenStr
8585
.map(|d| fold_expr(&d.1)).expect("No discriminant"))
8686
.collect();
8787
let variants_len = flag_values.len();
88-
assert!(flag_values.iter().all(|&v| v != 0), "Null flag is not allowed");
8988
let names = flag_values.iter().map(|_| &ident);
9089
let ty = extract_repr(&item.attrs).unwrap_or(Ident::new("usize", span));
91-
let max_flag_value = flag_values.iter().max().unwrap();
9290
let max_allowed_value = max_value_of(&ty.to_string()).expect(&format!("{} is not supported", ty));
93-
assert!(
94-
*max_flag_value as usize <= max_allowed_value,
95-
format!(
96-
"Value '0b{val:b}' is too big for an {ty}",
97-
val = max_flag_value,
98-
ty = ty
99-
)
100-
);
101-
let wrong_flag_values: &Vec<_> = &flag_values
102-
.iter()
103-
.zip(variants.clone())
104-
.filter(|&(&val, _)| flag_values.iter().filter(|&&v| v & val != 0).count() > 1)
105-
.map(|(value, variant)| {
106-
format!(
107-
"{name}::{variant} = 0b{value:b}",
108-
name = ident,
109-
variant = variant,
110-
value = value
111-
)
112-
})
113-
.collect();
114-
assert!(
115-
wrong_flag_values.is_empty(),
116-
format!(
117-
"The following flags are not unique: {data:?}",
118-
data = wrong_flag_values
119-
)
120-
);
91+
92+
let mut flags_seen = 0;
93+
for (&flag, variant) in flag_values.iter().zip(variants.clone()) {
94+
if flag > max_allowed_value {
95+
panic!("Value {:#b} is too big for an {}",
96+
flag, ty
97+
);
98+
} else if flag == 0 || !flag.is_power_of_two() {
99+
panic!("Each flag must have exactly one bit set, and {ident}::{variant} = {flag:#b} doesn't",
100+
ident = ident,
101+
variant = variant,
102+
flag = flag
103+
);
104+
} else if flags_seen & flag != 0 {
105+
panic!("Flag {} collides with {}",
106+
variant,
107+
flag_values.iter()
108+
.zip(variants.clone())
109+
.find(|(&other_flag, _)| flag == other_flag)
110+
.unwrap()
111+
.1
112+
);
113+
}
114+
115+
flags_seen |= flag;
116+
}
117+
121118
let std_path = quote_spanned!(span=> ::enumflags2::_internal::core);
122119
quote_spanned!{
123120
span =>

0 commit comments

Comments
 (0)