rust-bindgen icon indicating copy to clipboard operation
rust-bindgen copied to clipboard

Derive `Default` for rustified enums.

Open Ddystopia opened this issue 1 month ago • 1 comments

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.

Ddystopia avatar Oct 30 '25 15:10 Ddystopia

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.

qinghon avatar Nov 06 '25 04:11 qinghon