rust-bindgen
rust-bindgen copied to clipboard
Derive `Default` for rustified enums.
Hello, I have bindgen.derive_default(true), but rustified_enum types don't have a default impl.
Can we either:
- Derive default if there is a variant that equals to 0
- Derive default if a first variant that equals to 0
- Derive default to be the first variant
I tried looking through the source code of bindgen, but I didn't manage to grasp where the changes should be made.
this generate by cursor , looks pretty good。 However, I don't think this is necessary. Enums in the C standard are essentially equivalent to plain integers. The current default implementation of bindgen is already sufficient to represent C enums. Introducing more of Rust's complexity into the bindings is, in my opinion, not ideal.
Subject: [PATCH] cargo clippy
---
Index: bindgen/ir/analysis/derive.rs
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/bindgen/ir/analysis/derive.rs b/bindgen/ir/analysis/derive.rs
--- a/bindgen/ir/analysis/derive.rs (revision 6bb851216378aaed1add619af3b3690d9d0f91f2)
+++ b/bindgen/ir/analysis/derive.rs (date 1762399980715)
@@ -536,7 +536,6 @@
DeriveTrait::Default,
TypeKind::Void |
TypeKind::NullPtr |
- TypeKind::Enum(..) |
TypeKind::Reference(..) |
TypeKind::TypeParam |
TypeKind::ObjCInterface(..) |
@@ -546,6 +545,10 @@
trace!(" types that always cannot derive Default");
CanDerive::No
}
+ (DeriveTrait::Default, TypeKind::Enum(..)) => {
+ trace!(" enum can derive Default");
+ CanDerive::Yes
+ }
(DeriveTrait::Default, TypeKind::UnresolvedTypeRef(..)) => {
unreachable!(
"Type with unresolved type ref can't reach derive default"
Index: bindgen/codegen/helpers.rs
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/bindgen/codegen/helpers.rs b/bindgen/codegen/helpers.rs
--- a/bindgen/codegen/helpers.rs (revision 21ef9d6ad06f0d05cd72032812d4568482e519c6)
+++ b/bindgen/codegen/helpers.rs (date 1762400127780)
@@ -53,6 +53,12 @@
}
}
+ pub(crate) fn default() -> TokenStream {
+ quote! {
+ #[default]
+ }
+ }
+
pub(crate) fn doc(comment: &str) -> TokenStream {
if comment.is_empty() {
quote!()
Index: bindgen/codegen/mod.rs
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
--- a/bindgen/codegen/mod.rs (revision 21ef9d6ad06f0d05cd72032812d4568482e519c6)
+++ b/bindgen/codegen/mod.rs (date 1762400127780)
@@ -3444,6 +3444,7 @@
variant_name: name,
variant_doc,
value: expr,
+ original_value: Some(variant.val()),
});
self
}
@@ -3463,6 +3464,7 @@
variant_name: variant_ident,
variant_doc,
value: quote! { #rust_ty ( #expr )},
+ original_value: None, // Not needed for NewType
});
self
@@ -3481,6 +3483,7 @@
variant_name: ident,
variant_doc,
value: quote! { #expr },
+ original_value: None, // Not needed for Consts
});
self
@@ -3491,6 +3494,7 @@
variant_name: name,
variant_doc,
value: quote! { #expr },
+ original_value: None, // Not needed for ModuleConsts
});
self
}
@@ -3538,6 +3542,7 @@
self,
ctx: &BindgenContext,
rust_ty: &syn::Type,
+ should_add_default: bool,
) -> proc_macro2::TokenStream {
let enum_ident = self.enum_type;
@@ -3546,13 +3551,61 @@
EnumBuilderKind::Rust { .. } => {
let mut variants = vec![];
- for v in self.enum_variants {
+ // Determine which variant should be marked with #[default]
+ let default_variant_index = if should_add_default && !self.enum_variants.is_empty() {
+ // Strategy:
+ // 1. If first variant equals 0, use it
+ // 2. Else if any variant equals 0, use the first such variant
+ // 3. Else use the first variant
+ let mut default_index = 0;
+ let mut found_zero = false;
+
+ // Check if first variant is 0
+ if let Some(first_val) = self.enum_variants.first()
+ .and_then(|v| v.original_value) {
+ if matches!(first_val,
+ EnumVariantValue::Boolean(false) |
+ EnumVariantValue::Signed(0) |
+ EnumVariantValue::Unsigned(0)) {
+ found_zero = true;
+ }
+ }
+
+ if !found_zero {
+ // Look for any variant with value 0
+ for (idx, v) in self.enum_variants.iter().enumerate().skip(1) {
+ if let Some(val) = v.original_value {
+ if matches!(val,
+ EnumVariantValue::Boolean(false) |
+ EnumVariantValue::Signed(0) |
+ EnumVariantValue::Unsigned(0)) {
+ default_index = idx;
+ found_zero = true;
+ break;
+ }
+ }
+ }
+ }
+ // If no zero variant found, default_index is already 0 (first variant)
+
+ Some(default_index)
+ } else {
+ None
+ };
+
+ for (idx, v) in self.enum_variants.into_iter().enumerate() {
let variant_doc = &v.variant_doc;
let variant_ident = &v.variant_name;
let variant_value = &v.value;
+ let default_attr = if default_variant_index == Some(idx) {
+ Some(attributes::default())
+ } else {
+ None
+ };
variants.push(quote! {
#variant_doc
+ #default_attr
#variant_ident = #variant_value,
});
}
@@ -4012,7 +4065,16 @@
}
}
- let item = builder.build(ctx, &enum_rust_ty);
+ // Check if enum should derive Default
+ let should_add_default = if !variation.is_const() {
+ let packed = false;
+ let derives = derives_of_item(item, ctx, packed);
+ derives.contains(DerivableTraits::DEFAULT)
+ } else {
+ false
+ };
+
+ let item = builder.build(ctx, &enum_rust_ty, should_add_default);
result.push(item);
}
}
@@ -4021,6 +4083,8 @@
variant_name: Ident,
variant_doc: proc_macro2::TokenStream,
value: proc_macro2::TokenStream,
+ /// The original enum variant value, used to determine if this variant should be marked as #[default]
+ original_value: Option<EnumVariantValue>,
}
/// Enum for the default type of macro constants.