Skip to content

Commit 4fafa62

Browse files
committed
Don't panic on large shift counts
This also refactors the fold_expr function to avoid declaring any code erroneous. Instead, we defer the decision to rustc itself, which also has the side-effect of generating a nicer error.
1 parent 8fde754 commit 4fafa62

File tree

5 files changed

+48
-31
lines changed

5 files changed

+48
-31
lines changed

enumflags_derive/src/lib.rs

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ extern crate proc_macro;
33
#[macro_use]
44
extern crate quote;
55

6+
use std::convert::TryFrom;
67
use syn::{Data, Ident, DeriveInput, DataEnum, spanned::Spanned};
78
use proc_macro2::{TokenStream, Span};
89

@@ -46,42 +47,27 @@ pub fn bitflags_internal(
4647
}
4748

4849
/// Try to evaluate the expression given.
49-
///
50-
/// Returns `Err` when the expression is erroneous, and `Ok(None)` when
51-
/// information outside of the syntax, such as a `const`.
52-
fn fold_expr(expr: &syn::Expr) -> Result<Option<u64>, syn::Error> {
53-
/// Recurse, but bubble-up both kinds of errors.
54-
/// (I miss my monad transformers)
55-
macro_rules! fold_expr {
56-
($($x:tt)*) => {
57-
match fold_expr($($x)*)? {
58-
Some(x) => x,
59-
None => return Ok(None),
60-
}
61-
}
62-
}
63-
50+
fn fold_expr(expr: &syn::Expr) -> Option<u64> {
6451
use syn::Expr;
6552
match expr {
6653
Expr::Lit(ref expr_lit) => {
6754
match expr_lit.lit {
68-
syn::Lit::Int(ref lit_int) => {
69-
Ok(Some(lit_int.base10_parse()
70-
.map_err(|_| syn::Error::new_spanned(lit_int,
71-
"Integer literal out of range"))?))
72-
}
73-
_ => Ok(None),
55+
syn::Lit::Int(ref lit_int) => lit_int.base10_parse().ok(),
56+
_ => None,
7457
}
7558
},
7659
Expr::Binary(ref expr_binary) => {
77-
let l = fold_expr!(&expr_binary.left);
78-
let r = fold_expr!(&expr_binary.right);
60+
let l = fold_expr(&expr_binary.left)?;
61+
let r = fold_expr(&expr_binary.right)?;
7962
match &expr_binary.op {
80-
syn::BinOp::Shl(_) => Ok(Some(l << r)),
81-
_ => Ok(None),
63+
syn::BinOp::Shl(_) => {
64+
u32::try_from(r).ok()
65+
.and_then(|r| l.checked_shl(r))
66+
}
67+
_ => None,
8268
}
8369
}
84-
_ => Ok(None),
70+
_ => None,
8571
}
8672
}
8773

@@ -98,7 +84,7 @@ fn collect_flags<'a>(variants: impl Iterator<Item=&'a syn::Variant>)
9884
}
9985

10086
let value = if let Some(ref expr) = variant.discriminant {
101-
if let Some(n) = fold_expr(&expr.1)? {
87+
if let Some(n) = fold_expr(&expr.1) {
10288
FlagValue::Literal(n)
10389
} else {
10490
FlagValue::Deferred

test_suite/ui/literal_out_of_range.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
#[enumflags2::bitflags]
1+
//#[enumflags2::bitflags]
2+
#[repr(u64)]
23
#[derive(Copy, Clone)]
34
enum Foo {
45
BigNumber = 0xdeadbeefcafebabe1337,
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
error: Integer literal out of range
2-
--> $DIR/literal_out_of_range.rs:4:17
1+
error: literal out of range for u64
2+
--> $DIR/literal_out_of_range.rs:5:17
33
|
4-
4 | BigNumber = 0xdeadbeefcafebabe1337,
4+
5 | BigNumber = 0xdeadbeefcafebabe1337,
55
| ^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[deny(overflowing_literals)]` on by default
8+
= note: the literal `0xdeadbeefcafebabe1337` (decimal `1051570404360395033547575`) does not fit into the type `u64` and will become `13758438582043677495u64`
9+
= help: consider using `u128` instead
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#[enumflags2::bitflags]
2+
#[repr(u64)]
3+
#[derive(Copy, Clone)]
4+
enum Foo {
5+
BigNumber = 1 << 69,
6+
}
7+
8+
#[enumflags2::bitflags]
9+
#[repr(u16)]
10+
#[derive(Copy, Clone)]
11+
enum Bar {
12+
BigNumber = 1 << 20,
13+
}
14+
15+
fn main() {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0080]: evaluation of constant value failed
2+
--> $DIR/shift_out_of_range.rs:5:17
3+
|
4+
5 | BigNumber = 1 << 69,
5+
| ^^^^^^^ attempt to shift left with overflow
6+
7+
error[E0080]: evaluation of constant value failed
8+
--> $DIR/shift_out_of_range.rs:12:17
9+
|
10+
12 | BigNumber = 1 << 20,
11+
| ^^^^^^^ attempt to shift left with overflow

0 commit comments

Comments
 (0)