From d562a9d1465e334f4a6154f07a736dfd31d3b661 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sun, 26 Jul 2026 15:37:40 -0700 Subject: [PATCH] Add `ParseCallbacks` method to override enum type representation Introduce `ParseCallbacks::enum_type_override()` to override the type used to represent an enum to force it to be a specific integer kind. Fixes #2711 --- .../tests/enum-default-bitfield.rs | 99 +++++++++++++++++++ .../expectations/tests/enum-default-consts.rs | 9 ++ .../expectations/tests/enum-default-module.rs | 21 ++++ .../expectations/tests/enum-default-rust.rs | 18 ++++ .../expectations/tests/enum-no-debug-rust.rs | 18 ++++ .../tests/expectations/tests/enum.rs | 9 ++ .../tests/headers/enum-default-bitfield.h | 1 + .../tests/headers/enum-default-consts.h | 1 + .../tests/headers/enum-default-module.h | 1 + .../tests/headers/enum-default-rust.h | 1 + .../tests/headers/enum-no-debug-rust.h | 1 + bindgen-tests/tests/headers/enum.h | 17 +++- bindgen-tests/tests/parse_callbacks/mod.rs | 15 +++ bindgen/callbacks.rs | 6 ++ bindgen/codegen/mod.rs | 14 ++- 15 files changed, 229 insertions(+), 2 deletions(-) diff --git a/bindgen-tests/tests/expectations/tests/enum-default-bitfield.rs b/bindgen-tests/tests/expectations/tests/enum-default-bitfield.rs index dda688617c..bf21cc2340 100644 --- a/bindgen-tests/tests/expectations/tests/enum-default-bitfield.rs +++ b/bindgen-tests/tests/expectations/tests/enum-default-bitfield.rs @@ -158,3 +158,102 @@ impl ::std::ops::BitAndAssign for Debug { ///
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Debug(pub ::std::os::raw::c_uint); +impl should_be_u8 { + pub const FirstU8: should_be_u8 = should_be_u8(0); + pub const SecondU8: should_be_u8 = should_be_u8(1); +} +impl ::std::ops::BitOr for should_be_u8 { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + should_be_u8(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for should_be_u8 { + #[inline] + fn bitor_assign(&mut self, rhs: should_be_u8) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for should_be_u8 { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + should_be_u8(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for should_be_u8 { + #[inline] + fn bitand_assign(&mut self, rhs: should_be_u8) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct should_be_u8(pub u8); +impl should_be_u16 { + pub const FirstU16: should_be_u16 = should_be_u16(0); + pub const SecondU16: should_be_u16 = should_be_u16(1); +} +impl ::std::ops::BitOr for should_be_u16 { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + should_be_u16(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for should_be_u16 { + #[inline] + fn bitor_assign(&mut self, rhs: should_be_u16) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for should_be_u16 { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + should_be_u16(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for should_be_u16 { + #[inline] + fn bitand_assign(&mut self, rhs: should_be_u16) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct should_be_u16(pub u16); +impl should_be_i32 { + pub const FirstI32: should_be_i32 = should_be_i32(0); + pub const SecondI32: should_be_i32 = should_be_i32(1); +} +impl ::std::ops::BitOr for should_be_i32 { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + should_be_i32(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for should_be_i32 { + #[inline] + fn bitor_assign(&mut self, rhs: should_be_i32) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for should_be_i32 { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + should_be_i32(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for should_be_i32 { + #[inline] + fn bitand_assign(&mut self, rhs: should_be_i32) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct should_be_i32(pub i32); diff --git a/bindgen-tests/tests/expectations/tests/enum-default-consts.rs b/bindgen-tests/tests/expectations/tests/enum-default-consts.rs index 1432182310..b47703d964 100644 --- a/bindgen-tests/tests/expectations/tests/enum-default-consts.rs +++ b/bindgen-tests/tests/expectations/tests/enum-default-consts.rs @@ -40,3 +40,12 @@ pub const Debug_Debug1: Debug = 0; pub const Debug_Debug2: Debug = 1; ///
pub type Debug = ::std::os::raw::c_uint; +pub const should_be_u8_FirstU8: should_be_u8 = 0; +pub const should_be_u8_SecondU8: should_be_u8 = 1; +pub type should_be_u8 = u8; +pub const should_be_u16_FirstU16: should_be_u16 = 0; +pub const should_be_u16_SecondU16: should_be_u16 = 1; +pub type should_be_u16 = u16; +pub const should_be_i32_FirstI32: should_be_i32 = 0; +pub const should_be_i32_SecondI32: should_be_i32 = 1; +pub type should_be_i32 = i32; diff --git a/bindgen-tests/tests/expectations/tests/enum-default-module.rs b/bindgen-tests/tests/expectations/tests/enum-default-module.rs index 23fbd22c6e..82c2c72eb3 100644 --- a/bindgen-tests/tests/expectations/tests/enum-default-module.rs +++ b/bindgen-tests/tests/expectations/tests/enum-default-module.rs @@ -56,3 +56,24 @@ pub mod Debug { pub const Debug1: Type = 0; pub const Debug2: Type = 1; } +pub mod should_be_u8 { + #[allow(unused_imports)] + use super::*; + pub type Type = u8; + pub const FirstU8: Type = 0; + pub const SecondU8: Type = 1; +} +pub mod should_be_u16 { + #[allow(unused_imports)] + use super::*; + pub type Type = u16; + pub const FirstU16: Type = 0; + pub const SecondU16: Type = 1; +} +pub mod should_be_i32 { + #[allow(unused_imports)] + use super::*; + pub type Type = i32; + pub const FirstI32: Type = 0; + pub const SecondI32: Type = 1; +} diff --git a/bindgen-tests/tests/expectations/tests/enum-default-rust.rs b/bindgen-tests/tests/expectations/tests/enum-default-rust.rs index 59901a78ac..a9d62b7400 100644 --- a/bindgen-tests/tests/expectations/tests/enum-default-rust.rs +++ b/bindgen-tests/tests/expectations/tests/enum-default-rust.rs @@ -54,3 +54,21 @@ pub enum Debug { Debug1 = 0, Debug2 = 1, } +#[repr(u8)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum should_be_u8 { + FirstU8 = 0, + SecondU8 = 1, +} +#[repr(u16)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum should_be_u16 { + FirstU16 = 0, + SecondU16 = 1, +} +#[repr(i32)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum should_be_i32 { + FirstI32 = 0, + SecondI32 = 1, +} diff --git a/bindgen-tests/tests/expectations/tests/enum-no-debug-rust.rs b/bindgen-tests/tests/expectations/tests/enum-no-debug-rust.rs index 643577e1e3..202a04e74e 100644 --- a/bindgen-tests/tests/expectations/tests/enum-no-debug-rust.rs +++ b/bindgen-tests/tests/expectations/tests/enum-no-debug-rust.rs @@ -54,3 +54,21 @@ pub enum Debug { Debug1 = 0, Debug2 = 1, } +#[repr(u8)] +#[derive(Copy, Clone, Hash, PartialEq, Eq)] +pub enum should_be_u8 { + FirstU8 = 0, + SecondU8 = 1, +} +#[repr(u16)] +#[derive(Copy, Clone, Hash, PartialEq, Eq)] +pub enum should_be_u16 { + FirstU16 = 0, + SecondU16 = 1, +} +#[repr(i32)] +#[derive(Copy, Clone, Hash, PartialEq, Eq)] +pub enum should_be_i32 { + FirstI32 = 0, + SecondI32 = 1, +} diff --git a/bindgen-tests/tests/expectations/tests/enum.rs b/bindgen-tests/tests/expectations/tests/enum.rs index 820182125a..b0e549f05c 100644 --- a/bindgen-tests/tests/expectations/tests/enum.rs +++ b/bindgen-tests/tests/expectations/tests/enum.rs @@ -36,3 +36,12 @@ pub const Debug_Debug1: Debug = 0; pub const Debug_Debug2: Debug = 1; ///
pub type Debug = ::std::os::raw::c_uint; +pub const should_be_u8_FirstU8: should_be_u8 = 0; +pub const should_be_u8_SecondU8: should_be_u8 = 1; +pub type should_be_u8 = ::std::os::raw::c_uint; +pub const should_be_u16_FirstU16: should_be_u16 = 0; +pub const should_be_u16_SecondU16: should_be_u16 = 1; +pub type should_be_u16 = ::std::os::raw::c_uint; +pub const should_be_i32_FirstI32: should_be_i32 = 0; +pub const should_be_i32_SecondI32: should_be_i32 = 1; +pub type should_be_i32 = ::std::os::raw::c_uint; diff --git a/bindgen-tests/tests/headers/enum-default-bitfield.h b/bindgen-tests/tests/headers/enum-default-bitfield.h index 5f3cb95468..8250563fa8 100644 --- a/bindgen-tests/tests/headers/enum-default-bitfield.h +++ b/bindgen-tests/tests/headers/enum-default-bitfield.h @@ -1,3 +1,4 @@ // bindgen-flags: --default-enum-style=bitfield --constified-enum-module=Neg +// bindgen-parse-callbacks: enum-type-changer #include "enum.h" diff --git a/bindgen-tests/tests/headers/enum-default-consts.h b/bindgen-tests/tests/headers/enum-default-consts.h index 233d2718a4..5228ca7aa5 100644 --- a/bindgen-tests/tests/headers/enum-default-consts.h +++ b/bindgen-tests/tests/headers/enum-default-consts.h @@ -1,3 +1,4 @@ // bindgen-flags: --default-enum-style=consts --constified-enum-module=Neg +// bindgen-parse-callbacks: enum-type-changer #include "enum.h" diff --git a/bindgen-tests/tests/headers/enum-default-module.h b/bindgen-tests/tests/headers/enum-default-module.h index 8c2ec1bca4..eae2d69a54 100644 --- a/bindgen-tests/tests/headers/enum-default-module.h +++ b/bindgen-tests/tests/headers/enum-default-module.h @@ -1,3 +1,4 @@ // bindgen-flags: --default-enum-style=moduleconsts --constified-enum-module=Neg +// bindgen-parse-callbacks: enum-type-changer #include "enum.h" diff --git a/bindgen-tests/tests/headers/enum-default-rust.h b/bindgen-tests/tests/headers/enum-default-rust.h index 7fd2999fe0..4e95166d18 100644 --- a/bindgen-tests/tests/headers/enum-default-rust.h +++ b/bindgen-tests/tests/headers/enum-default-rust.h @@ -1,3 +1,4 @@ // bindgen-flags: --default-enum-style=rust --constified-enum-module=Neg +// bindgen-parse-callbacks: enum-type-changer #include "enum.h" diff --git a/bindgen-tests/tests/headers/enum-no-debug-rust.h b/bindgen-tests/tests/headers/enum-no-debug-rust.h index 7cb7398029..c5e3e0d48b 100644 --- a/bindgen-tests/tests/headers/enum-no-debug-rust.h +++ b/bindgen-tests/tests/headers/enum-no-debug-rust.h @@ -1,3 +1,4 @@ // bindgen-flags: --no-derive-debug --default-enum-style=rust --constified-enum-module=Neg +// bindgen-parse-callbacks: enum-type-changer #include "enum.h" diff --git a/bindgen-tests/tests/headers/enum.h b/bindgen-tests/tests/headers/enum.h index 0147433ee9..2acd6adbdf 100644 --- a/bindgen-tests/tests/headers/enum.h +++ b/bindgen-tests/tests/headers/enum.h @@ -28,4 +28,19 @@ enum NoDebug { enum Debug { Debug1, Debug2, -}; \ No newline at end of file +}; + +enum should_be_u8 { + FirstU8, + SecondU8, +}; + +enum should_be_u16 { + FirstU16, + SecondU16, +}; + +enum should_be_i32 { + FirstI32, + SecondI32, +}; diff --git a/bindgen-tests/tests/parse_callbacks/mod.rs b/bindgen-tests/tests/parse_callbacks/mod.rs index 7e9f011812..ca22b7cb9a 100644 --- a/bindgen-tests/tests/parse_callbacks/mod.rs +++ b/bindgen-tests/tests/parse_callbacks/mod.rs @@ -94,6 +94,20 @@ impl ParseCallbacks for StructFieldRename { } } +#[derive(Debug)] +struct EnumTypeChanger; + +impl ParseCallbacks for EnumTypeChanger { + fn enum_type_override(&self, enum_name: &str) -> Option { + match enum_name { + "should_be_u8" => Some(IntKind::U8), + "should_be_u16" => Some(IntKind::U16), + "should_be_i32" => Some(IntKind::I32), + _ => None, + } + } +} + #[derive(Debug)] struct BlocklistedTypeImplementsTrait; @@ -191,6 +205,7 @@ pub fn lookup(cb: &str) -> Box { "wrap-as-variadic-fn" => Box::new(WrapAsVariadicFn), "type-visibility" => Box::new(TypeVisibility), "operator-rename" => Box::new(OperatorRename), + "enum-type-changer" => Box::new(EnumTypeChanger), call_back => { if let Some(prefix) = call_back.strip_prefix("remove-function-prefix-") diff --git a/bindgen/callbacks.rs b/bindgen/callbacks.rs index 4df316b9d1..3df6a8c63b 100644 --- a/bindgen/callbacks.rs +++ b/bindgen/callbacks.rs @@ -73,6 +73,12 @@ pub trait ParseCallbacks: fmt::Debug { /// the expansion of the macro as a sequence of tokens. fn func_macro(&self, _name: &str, _value: &[&[u8]]) {} + /// The integer kind an enum should have, given the name of the enum type, + /// or `None` if you want the default to be chosen. + fn enum_type_override(&self, _enum_name: &str) -> Option { + None + } + /// This function should return whether, given an enum variant /// name, and value, this enum variant will forcibly be a constant. fn enum_variant_behavior( diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 6034b0e6ba..87faaf1042 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -3759,7 +3759,7 @@ impl CodeGenerator for Enum { let variation = self.computed_enum_variation(ctx, item); let repr_translated; - let repr = match self.repr().map(|repr| ctx.resolve_type(repr)) { + let mut repr = match self.repr().map(|repr| ctx.resolve_type(repr)) { Some(repr) if !ctx.options().translate_enum_integer_types && !variation.is_rust() => @@ -3906,6 +3906,18 @@ impl CodeGenerator for Enum { }); } + let repr_from_callback; + if !ctx.options().parse_callbacks.is_empty() { + if let Some(callback_type) = ctx + .options() + .last_callback(|cb| cb.enum_type_override(&name)) + { + repr_from_callback = + Type::new(None, None, TypeKind::Int(callback_type), false); + repr = &repr_from_callback; + } + } + let repr = repr.to_rust_ty_or_opaque(ctx, item); let has_typedef = ctx.is_enum_typedef_combo(item.id());