Skip to content

Commit c2d64de

Browse files
committed
Check for overflow in computed flags
1 parent d0eb893 commit c2d64de

File tree

11 files changed

+54
-15
lines changed

11 files changed

+54
-15
lines changed

enumflags_derive/src/lib.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ use syn::{
1212
Ident, Item, ItemEnum, Token,
1313
};
1414

15+
#[derive(Debug)]
1516
struct Flag {
1617
name: Ident,
1718
span: Span,
1819
value: FlagValue,
1920
}
2021

22+
#[derive(Debug)]
2123
enum FlagValue {
2224
Literal(u128),
2325
Deferred,
@@ -88,6 +90,9 @@ fn fold_expr(expr: &syn::Expr) -> Option<u128> {
8890
_ => None,
8991
}
9092
}
93+
Expr::Paren(syn::ExprParen { expr, .. }) | Expr::Group(syn::ExprGroup { expr, .. }) => {
94+
fold_expr(&expr)
95+
}
9196
_ => None,
9297
}
9398
}
@@ -187,7 +192,7 @@ fn type_bits(ty: &Ident) -> Result<u8, syn::Error> {
187192
}
188193

189194
/// Returns deferred checks
190-
fn check_flag(type_name: &Ident, flag: &Flag) -> Result<Option<TokenStream>, syn::Error> {
195+
fn check_flag(type_name: &Ident, flag: &Flag, bits: u8) -> Result<Option<TokenStream>, syn::Error> {
191196
use FlagValue::*;
192197
match flag.value {
193198
Literal(n) => {
@@ -196,6 +201,11 @@ fn check_flag(type_name: &Ident, flag: &Flag) -> Result<Option<TokenStream>, syn
196201
flag.span,
197202
"Flags must have exactly one set bit",
198203
))
204+
} else if bits < 128 && n >= 1 << bits {
205+
Err(syn::Error::new(
206+
flag.span,
207+
format!("Flag value out of range for u{}", bits),
208+
))
199209
} else {
200210
Ok(None)
201211
}
@@ -235,17 +245,17 @@ fn gen_enumflags(ast: &ItemEnum, default: Vec<Ident>) -> Result<TokenStream, syn
235245
let variant_names = ast.variants.iter().map(|v| &v.ident).collect::<Vec<_>>();
236246
let repeated_name = vec![&ident; ast.variants.len()];
237247

238-
let variants = collect_flags(ast.variants.iter())?;
239-
let deferred = variants
240-
.iter()
241-
.flat_map(|variant| check_flag(ident, variant).transpose())
242-
.collect::<Result<Vec<_>, _>>()?;
243-
244248
let ty = extract_repr(&ast.attrs)?
245249
.ok_or_else(|| syn::Error::new_spanned(&ident,
246250
"repr attribute missing. Add #[repr(u64)] or a similar attribute to specify the size of the bitfield."))?;
247251
let bits = type_bits(&ty)?;
248252

253+
let variants = collect_flags(ast.variants.iter())?;
254+
let deferred = variants
255+
.iter()
256+
.flat_map(|variant| check_flag(ident, variant, bits).transpose())
257+
.collect::<Result<Vec<_>, _>>()?;
258+
249259
if (bits as usize) < variants.len() {
250260
return Err(syn::Error::new_spanned(
251261
&ty,

test_suite/ui/missing_disciminant.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#[enumflags2::bitflags]
2+
#[repr(u8)]
23
#[derive(Copy, Clone)]
34
enum Foo {
45
OhNoTheresNoDiscriminant,
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: Please add an explicit discriminant
2-
--> $DIR/missing_disciminant.rs:4:5
2+
--> $DIR/missing_disciminant.rs:5:5
33
|
4-
4 | OhNoTheresNoDiscriminant,
4+
5 | OhNoTheresNoDiscriminant,
55
| ^^^^^^^^^^^^^^^^^^^^^^^^

test_suite/ui/multiple_bits.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#[enumflags2::bitflags]
2+
#[repr(u8)]
23
#[derive(Copy, Clone)]
34
enum Foo {
45
SingleBit = 1,

test_suite/ui/multiple_bits.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: Flags must have exactly one set bit
2-
--> $DIR/multiple_bits.rs:5:5
2+
--> $DIR/multiple_bits.rs:6:5
33
|
4-
5 | MultipleBits = 6,
4+
6 | MultipleBits = 6,
55
| ^^^^^^^^^^^^^^^^

test_suite/ui/shift_out_of_range.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,11 @@ enum Bar {
1212
BigNumber = 1 << 20,
1313
}
1414

15+
#[enumflags2::bitflags]
16+
#[repr(u16)]
17+
#[derive(Copy, Clone)]
18+
enum Baz {
19+
BigNumber = (1 << 10) << 10,
20+
}
21+
1522
fn main() {}

test_suite/ui/shift_out_of_range.stderr

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
error: Flag value out of range for u64
2+
--> $DIR/shift_out_of_range.rs:5:5
3+
|
4+
5 | BigNumber = 1 << 69,
5+
| ^^^^^^^^^^^^^^^^^^^
6+
7+
error: Flag value out of range for u16
8+
--> $DIR/shift_out_of_range.rs:12:5
9+
|
10+
12 | BigNumber = 1 << 20,
11+
| ^^^^^^^^^^^^^^^^^^^
12+
13+
error: Flag value out of range for u16
14+
--> $DIR/shift_out_of_range.rs:19:5
15+
|
16+
19 | BigNumber = (1 << 10) << 10,
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
119
error[E0080]: evaluation of constant value failed
220
--> $DIR/shift_out_of_range.rs:5:17
321
|

test_suite/ui/with_fields.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#[enumflags2::bitflags]
2+
#[repr(u8)]
23
#[derive(Copy, Clone)]
34
enum Foo {
45
Bar(u32),

test_suite/ui/with_fields.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: Bitflag variants cannot contain additional data
2-
--> $DIR/with_fields.rs:4:8
2+
--> $DIR/with_fields.rs:5:8
33
|
4-
4 | Bar(u32),
4+
5 | Bar(u32),
55
| ^^^^^

test_suite/ui/zero_disciminant.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#[enumflags2::bitflags]
2+
#[repr(u8)]
23
#[derive(Copy, Clone)]
34
enum Foo {
45
Zero = 0,

0 commit comments

Comments
 (0)