Properly handle enum discriminant overflows.

This both checks for enum values that are illegal due to overflow and
ensures we do not overflow on valid enums.
This commit is contained in:
Joel Galenson 2020-05-01 10:00:31 -07:00
parent b3fcf7b724
commit 04fa0967e2
4 changed files with 56 additions and 16 deletions

View File

@ -127,14 +127,19 @@ fn expand_struct(strct: &Struct) -> TokenStream {
fn expand_enum(enm: &Enum) -> TokenStream {
let ident = &enm.ident;
let doc = &enm.doc;
let variants = enm.variants.iter().scan(0, |next_discriminant, variant| {
let variant_ident = &variant.ident;
let discriminant = variant.discriminant.unwrap_or(*next_discriminant);
*next_discriminant = discriminant + 1;
Some(quote! {
pub const #variant_ident: Self = #ident(#discriminant);
})
});
let variants = enm
.variants
.iter()
.scan(None, |prev_discriminant, variant| {
let variant_ident = &variant.ident;
let discriminant = variant
.discriminant
.unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1));
*prev_discriminant = Some(discriminant);
Some(quote! {
pub const #variant_ident: Self = #ident(#discriminant);
})
});
quote! {
#doc
#[derive(Copy, Clone, PartialEq, Eq)]

View File

@ -204,14 +204,23 @@ fn check_api_enum(cx: &mut Check, enm: &Enum) {
}
let mut discriminants = HashSet::new();
enm.variants.iter().fold(0, |next_discriminant, variant| {
let discriminant = variant.discriminant.unwrap_or(next_discriminant);
if !discriminants.insert(discriminant) {
let msg = format!("discriminant value `{}` already exists", discriminant);
cx.error(span_for_enum_error(enm), msg);
}
discriminant + 1
});
enm.variants
.iter()
.fold(None, |prev_discriminant, variant| {
if variant.discriminant.is_none() && prev_discriminant.unwrap_or(0) == u32::MAX {
let msg = format!("overflowed on value after {}", prev_discriminant.unwrap());
cx.error(span_for_enum_error(enm), msg);
return None;
}
let discriminant = variant
.discriminant
.unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1));
if !discriminants.insert(discriminant) {
let msg = format!("discriminant value `{}` already exists", discriminant);
cx.error(span_for_enum_error(enm), msg);
}
Some(discriminant)
});
}
fn check_api_type(cx: &mut Check, ty: &ExternType) {

View File

@ -0,0 +1,17 @@
#[cxx::bridge]
mod ffi {
enum Good1 {
A = 0xffffffff,
}
enum Good2 {
B = 0xffffffff,
C = 2020,
}
enum Bad {
D = 0xfffffffe,
E,
F,
}
}
fn main() {}

View File

@ -0,0 +1,9 @@
error: overflowed on value after 4294967295
--> $DIR/enum_overflows.rs:10:5
|
10 | / enum Bad {
11 | | D = 0xfffffffe,
12 | | E,
13 | | F,
14 | | }
| |_____^