Repository: rust-lang/rust-bindgen
Pull request #3112 (2025-02-05 08:03:09)

Refactor enum generation
Gather all the enum variants and let the builder generate everything at once.
This allows us to simplify some logic and makes the code more readable, by putting the actual code generation close together in the build method.

For users, the main benefit is that Newtype enums now only have a single impl block, instead of one impl block per enum variant.

emilio: Could you squash the commits as well? Thanks.
jschwe: > Looks fine to me, thanks!

@emilio Is there anything that still needs to be done to get this merged?
jschwe: > Looks fine to me, thanks!

@emilio Friendly ping

Patch:
diff --git a/.github/workflows/bindgen.yml b/.github/workflows/bindgen.yml
index df17f2c5..d0112e36 100644
--- a/.github/workflows/bindgen.yml
+++ b/.github/workflows/bindgen.yml
@@ -171,7 +171,7 @@ jobs:
           BINDGEN_FEATURE_RUNTIME: ${{matrix.feature_runtime}}
           BINDGEN_FEATURE_EXTRA_ASSERTS: ${{matrix.feature_extra_asserts}}
           BINDGEN_NO_DEFAULT_FEATURES: ${{matrix.no_default_features}}
-          BINDGEN_RUST_FOR_LINUX_TEST: ${{matrix.os == 'ubuntu-latest' && matrix.llvm_version == '16.0' && matrix.feature_extra_asserts == 0 && 1 || 0}}
+          BINDGEN_RUST_FOR_LINUX_TEST: 0
         run: ./ci/test.sh
 
   check-cfg:
diff --git a/bindgen/callbacks.rs b/bindgen/callbacks.rs
index c2be6682..cd7bb977 100644
--- a/bindgen/callbacks.rs
+++ b/bindgen/callbacks.rs
@@ -224,7 +224,21 @@ pub enum DiscoveredItem {
         /// The final name of the generated binding
         final_name: String,
     },
-    // functions, modules, etc.
+
+    /// A function or method.
+    Function {
+        /// The final name used.
+        final_name: String,
+    },
+
+    /// A method.
+    Method {
+        /// The final name used.
+        final_name: String,
+
+        /// Type to which this method belongs.
+        parent: DiscoveredItemId,
+    }, // modules, etc.
 }
 
 /// Relevant information about a type to which new derive attributes will be added using
diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
index f5518e43..13c0b605 100644
--- a/bindgen/codegen/mod.rs
+++ b/bindgen/codegen/mod.rs
@@ -2481,6 +2481,7 @@ impl CodeGenerator for CompInfo {
 
         let is_rust_union = is_union && struct_layout.is_rust_union();
 
+        let discovered_id = DiscoveredItemId::new(item.id().as_usize());
         ctx.options().for_each_callback(|cb| {
             let discovered_item = match self.kind() {
                 CompKind::Struct => DiscoveredItem::Struct {
@@ -2501,10 +2502,7 @@ impl CodeGenerator for CompInfo {
                 },
             };
 
-            cb.new_item_found(
-                DiscoveredItemId::new(item.id().as_usize()),
-                discovered_item,
-            );
+            cb.new_item_found(discovered_id, discovered_item);
         });
 
         // The custom derives callback may return a list of derive attributes;
@@ -2711,6 +2709,7 @@ impl CodeGenerator for CompInfo {
                         &mut method_names,
                         result,
                         self,
+                        discovered_id,
                     );
                 }
             }
@@ -2729,6 +2728,7 @@ impl CodeGenerator for CompInfo {
                         &mut method_names,
                         result,
                         self,
+                        discovered_id,
                     );
                 }
             }
@@ -2742,6 +2742,7 @@ impl CodeGenerator for CompInfo {
                         &mut method_names,
                         result,
                         self,
+                        discovered_id,
                     );
                 }
             }
@@ -2999,6 +3000,7 @@ impl Method {
         method_names: &mut HashSet<String>,
         result: &mut CodegenResult<'_>,
         _parent: &CompInfo,
+        parent_id: DiscoveredItemId,
     ) {
         assert!({
             let cc = &ctx.options().codegen_config;
@@ -3019,6 +3021,7 @@ impl Method {
 
         // First of all, output the actual function.
         let function_item = ctx.resolve_item(self.signature());
+        let id = DiscoveredItemId::new(function_item.id().as_usize());
         if !function_item.process_before_codegen(ctx, result) {
             return;
         }
@@ -3065,6 +3068,16 @@ impl Method {
 
         method_names.insert(name.clone());
 
+        ctx.options().for_each_callback(|cb| {
+            cb.new_item_found(
+                id,
+                DiscoveredItem::Method {
+                    parent: parent_id,
+                    final_name: name.clone(),
+                },
+            )
+        });
+
         let mut function_name = function_item.canonical_name(ctx);
         if times_seen > 0 {
             write!(&mut function_name, "{times_seen}").unwrap();
@@ -3266,111 +3279,106 @@ impl FromStr for EnumVariation {
     }
 }
 
+struct EnumBuilder {
+    /// Type identifier of the enum.
+    ///
+    /// This is the base name, i.e. for ModuleConst enums, this does not include the module name.
+    enum_type: Ident,
+    /// Attributes applying to the enum type
+    attrs: Vec<proc_macro2::TokenStream>,
+    /// The representation of the enum, e.g. `u32`.
+    repr: syn::Type,
+    /// The enum kind we are generating
+    kind: EnumBuilderKind,
+    /// A list of all variants this enum has.
+    enum_variants: Vec<EnumVariantInfo>,
+}
+
 /// A helper type to construct different enum variations.
-enum EnumBuilder<'a> {
+enum EnumBuilderKind {
     Rust {
-        attrs: Vec<proc_macro2::TokenStream>,
-        ident: Ident,
-        tokens: proc_macro2::TokenStream,
-        emitted_any_variants: bool,
+        non_exhaustive: bool,
     },
     NewType {
-        canonical_name: &'a str,
-        tokens: proc_macro2::TokenStream,
         is_bitfield: bool,
         is_global: bool,
+        /// if the enum is named or not.
+        is_anonymous: bool,
     },
     Consts {
-        variants: Vec<proc_macro2::TokenStream>,
+        needs_typedef: bool,
     },
     ModuleConsts {
-        module_name: &'a str,
-        module_items: Vec<proc_macro2::TokenStream>,
+        module_name: Ident,
     },
 }
 
-impl<'a> EnumBuilder<'a> {
+impl EnumBuilder {
     /// Returns true if the builder is for a rustified enum.
     fn is_rust_enum(&self) -> bool {
-        matches!(*self, EnumBuilder::Rust { .. })
+        matches!(self.kind, EnumBuilderKind::Rust { .. })
     }
 
     /// Create a new enum given an item builder, a canonical name, a name for
     /// the representation, and which variation it should be generated as.
     fn new(
-        name: &'a str,
-        mut attrs: Vec<proc_macro2::TokenStream>,
+        name: &str,
+        attrs: Vec<proc_macro2::TokenStream>,
         repr: &syn::Type,
         enum_variation: EnumVariation,
         has_typedef: bool,
+        enum_is_anonymous: bool,
     ) -> Self {
         let ident = Ident::new(name, Span::call_site());
+        // For most variants this is the same
+        let mut enum_ty = ident.clone();
 
-        match enum_variation {
+        let kind = match enum_variation {
             EnumVariation::NewType {
                 is_bitfield,
                 is_global,
-            } => EnumBuilder::NewType {
-                canonical_name: name,
-                tokens: quote! {
-                    #( #attrs )*
-                    pub struct #ident (pub #repr);
-                },
+            } => EnumBuilderKind::NewType {
                 is_bitfield,
                 is_global,
+                is_anonymous: enum_is_anonymous,
             },
 
-            EnumVariation::Rust { .. } => {
-                // `repr` is guaranteed to be Rustified in Enum::codegen
-                attrs.insert(0, quote! { #[repr( #repr )] });
-                let tokens = quote!();
-                EnumBuilder::Rust {
-                    attrs,
-                    ident,
-                    tokens,
-                    emitted_any_variants: false,
-                }
+            EnumVariation::Rust { non_exhaustive } => {
+                EnumBuilderKind::Rust { non_exhaustive }
             }
 
-            EnumVariation::Consts => {
-                let mut variants = Vec::new();
-
-                if !has_typedef {
-                    variants.push(quote! {
-                        #( #attrs )*
-                        pub type #ident = #repr;
-                    });
-                }
-
-                EnumBuilder::Consts { variants }
-            }
+            EnumVariation::Consts => EnumBuilderKind::Consts {
+                needs_typedef: !has_typedef,
+            },
 
             EnumVariation::ModuleConsts => {
-                let ident = Ident::new(
+                enum_ty = Ident::new(
                     CONSTIFIED_ENUM_MODULE_REPR_NAME,
                     Span::call_site(),
                 );
-                let type_definition = quote! {
-                    #( #attrs )*
-                    pub type #ident = #repr;
-                };
 
-                EnumBuilder::ModuleConsts {
-                    module_name: name,
-                    module_items: vec![type_definition],
+                EnumBuilderKind::ModuleConsts {
+                    module_name: ident.clone(),
                 }
             }
+        };
+        EnumBuilder {
+            enum_type: enum_ty,
+            attrs,
+            repr: repr.clone(),
+            kind,
+            enum_variants: vec![],
         }
     }
 
     /// Add a variant to this enum.
     fn with_variant(
-        self,
+        mut self,
         ctx: &BindgenContext,
         variant: &EnumVariant,
+        variant_doc: proc_macro2::TokenStream,
         mangling_prefix: Option<&str>,
         rust_ty: &syn::Type,
-        result: &mut CodegenResult<'_>,
         is_ty_named: bool,
     ) -> Self {
         let variant_name = ctx.rust_mangle(variant.name());
@@ -3384,66 +3392,38 @@ impl<'a> EnumBuilder<'a> {
             EnumVariantValue::Unsigned(v) => helpers::ast_ty::uint_expr(v),
         };
 
-        let mut doc = quote! {};
-        if ctx.options().generate_comments {
-            if let Some(raw_comment) = variant.comment() {
-                let comment = ctx.options().process_comment(raw_comment);
-                doc = attributes::doc(&comment);
-            }
-        }
-
-        match self {
-            EnumBuilder::Rust {
-                attrs,
-                ident,
-                tokens,
-                emitted_any_variants: _,
-            } => {
+        match self.kind {
+            EnumBuilderKind::Rust { .. } => {
                 let name = ctx.rust_ident(variant_name);
-                EnumBuilder::Rust {
-                    attrs,
-                    ident,
-                    tokens: quote! {
-                        #tokens
-                        #doc
-                        #name = #expr,
-                    },
-                    emitted_any_variants: true,
-                }
+                self.enum_variants.push(EnumVariantInfo {
+                    variant_name: name,
+                    variant_doc,
+                    value: expr,
+                });
+                self
             }
 
-            EnumBuilder::NewType {
-                canonical_name,
-                is_global,
-                ..
-            } => {
-                if is_ty_named && !is_global {
-                    let enum_ident = ctx.rust_ident(canonical_name);
-                    let variant_ident = ctx.rust_ident(variant_name);
-
-                    result.push(quote! {
-                        impl #enum_ident {
-                            #doc
-                            pub const #variant_ident : #rust_ty = #rust_ty ( #expr );
-                        }
-                    });
+            EnumBuilderKind::NewType { is_global, .. } => {
+                let variant_ident = if is_ty_named && !is_global {
+                    ctx.rust_ident(variant_name)
                 } else {
-                    let ident = ctx.rust_ident(match mangling_prefix {
+                    ctx.rust_ident(match mangling_prefix {
                         Some(prefix) => {
                             Cow::Owned(format!("{prefix}_{variant_name}"))
                         }
                         None => variant_name,
-                    });
-                    result.push(quote! {
-                        #doc
-                        pub const #ident : #rust_ty = #rust_ty ( #expr );
-                    });
-                }
+                    })
+                };
+                self.enum_variants.push(EnumVariantInfo {
+                    variant_name: variant_ident,
+                    variant_doc,
+                    value: quote! { #rust_ty ( #expr )},
+                });
 
                 self
             }
 
-            EnumBuilder::Consts { .. } => {
+            EnumBuilderKind::Consts { .. } => {
                 let constant_name = match mangling_prefix {
                     Some(prefix) => {
                         Cow::Owned(format!("{prefix}_{variant_name}"))
@@ -3452,27 +3432,58 @@ impl<'a> EnumBuilder<'a> {
                 };
 
                 let ident = ctx.rust_ident(constant_name);
-                result.push(quote! {
-                    #doc
-                    pub const #ident : #rust_ty = #expr ;
+                self.enum_variants.push(EnumVariantInfo {
+                    variant_name: ident,
+                    variant_doc,
+                    value: quote! { #expr },
                 });
 
                 self
             }
-            EnumBuilder::ModuleConsts {
-                module_name,
-                mut module_items,
-            } => {
+            EnumBuilderKind::ModuleConsts { .. } => {
                 let name = ctx.rust_ident(variant_name);
-                let ty = ctx.rust_ident(CONSTIFIED_ENUM_MODULE_REPR_NAME);
-                module_items.push(quote! {
-                    #doc
-                    pub const #name : #ty = #expr ;
+                self.enum_variants.push(EnumVariantInfo {
+                    variant_name: name,
+                    variant_doc,
+                    value: quote! { #expr },
                 });
+                self
+            }
+        }
+    }
+
+    fn newtype_bitfield_impl(
+        prefix: Ident,
+        rust_ty: &syn::Type,
+    ) -> proc_macro2::TokenStream {
+        let rust_ty_name = &rust_ty;
+        quote! {
+            impl ::#prefix::ops::BitOr<#rust_ty> for #rust_ty {
+                type Output = Self;
 
-                EnumBuilder::ModuleConsts {
-                    module_name,
-                    module_items,
+                #[inline]
+                fn bitor(self, other: Self) -> Self {
+                    #rust_ty_name(self.0 | other.0)
+                }
+            }
+            impl ::#prefix::ops::BitOrAssign for #rust_ty {
+                #[inline]
+                fn bitor_assign(&mut self, rhs: #rust_ty) {
+                    self.0 |= rhs.0;
+                }
+            }
+            impl ::#prefix::ops::BitAnd<#rust_ty> for #rust_ty {
+                type Output = Self;
+
+                #[inline]
+                fn bitand(self, other: Self) -> Self {
+                    #rust_ty_name(self.0 & other.0)
+                }
+            }
+            impl ::#prefix::ops::BitAndAssign for #rust_ty {
+                #[inline]
+                fn bitand_assign(&mut self, rhs: #rust_ty) {
+                    self.0 &= rhs.0;
                 }
             }
         }
@@ -3482,94 +3493,141 @@ impl<'a> EnumBuilder<'a> {
         self,
         ctx: &BindgenContext,
         rust_ty: &syn::Type,
-        result: &mut CodegenResult<'_>,
     ) -> proc_macro2::TokenStream {
-        match self {
-            EnumBuilder::Rust {
-                attrs,
-                ident,
-                tokens,
-                emitted_any_variants,
-                ..
-            } => {
-                let variants = if emitted_any_variants {
-                    tokens
-                } else {
-                    quote!(__bindgen_cannot_repr_c_on_empty_enum = 0)
-                };
+        let enum_ident = self.enum_type;
+
+        // 1. Construct a list of the enum variants
+        let variants = match self.kind {
+            EnumBuilderKind::Rust { .. } => {
+                let mut variants = vec![];
+
+                for v in self.enum_variants {
+                    let variant_doc = &v.variant_doc;
+                    let variant_ident = &v.variant_name;
+                    let variant_value = &v.value;
+
+                    variants.push(quote! {
+                        #variant_doc
+                        #variant_ident = #variant_value,
+                    });
+                }
+
+                if variants.is_empty() {
+                    variants.push(
+                        quote! {__bindgen_cannot_repr_c_on_empty_enum = 0,},
+                    );
+                }
+                variants
+            }
+            EnumBuilderKind::NewType { .. } => {
+                let mut variants = vec![];
+
+                for v in self.enum_variants {
+                    let variant_doc = &v.variant_doc;
+                    let variant_ident = &v.variant_name;
+                    let variant_value = &v.value;
+
+                    variants.push(quote! {
+                        #variant_doc
+                        pub const #variant_ident: #enum_ident = #variant_value;
+                    });
+                }
+                variants
+            }
+            EnumBuilderKind::Consts { .. } |
+            EnumBuilderKind::ModuleConsts { .. } => {
+                let mut variants = vec![];
+
+                for v in self.enum_variants {
+                    let variant_doc = &v.variant_doc;
+                    let variant_ident = &v.variant_name;
+                    let variant_value = &v.value;
+
+                    variants.push(quote! {
+                        #variant_doc
+                        pub const #variant_ident: #enum_ident = #variant_value;
+                    });
+                }
+                variants
+            }
+        };
+        let attrs = self.attrs;
+        let enum_repr = &self.repr;
+
+        // 2. Generate the enum representation
+        match self.kind {
+            EnumBuilderKind::Rust { non_exhaustive } => {
+                let non_exhaustive_opt =
+                    non_exhaustive.then(attributes::non_exhaustive);
 
                 quote! {
+                    // Note: repr is on top of attrs to keep the test expectations diff small.
+                    // a future commit could move it further down.
+                    #[repr(#enum_repr)]
+                    #non_exhaustive_opt
                     #( #attrs )*
-                    pub enum #ident {
-                        #variants
+                    pub enum #enum_ident {
+                        #( #variants )*
                     }
                 }
             }
-            EnumBuilder::NewType {
-                canonical_name,
-                tokens,
+            EnumBuilderKind::NewType {
                 is_bitfield,
-                ..
+                is_global,
+                is_anonymous,
             } => {
-                if !is_bitfield {
-                    return tokens;
-                }
-
-                let rust_ty_name = ctx.rust_ident_raw(canonical_name);
-                let prefix = ctx.trait_prefix();
-
-                result.push(quote! {
-                    impl ::#prefix::ops::BitOr<#rust_ty> for #rust_ty {
-                        type Output = Self;
-
-                        #[inline]
-                        fn bitor(self, other: Self) -> Self {
-                            #rust_ty_name(self.0 | other.0)
-                        }
+                // There doesn't seem to be a technical reason why we generate
+                // anon enum variants as global constants.
+                // We keep this behavior to avoid breaking changes in the bindings.
+                let impl_variants = if is_anonymous || is_global {
+                    quote! {
+                        #( #variants )*
                     }
-                });
-
-                result.push(quote! {
-                    impl ::#prefix::ops::BitOrAssign for #rust_ty {
-                        #[inline]
-                        fn bitor_assign(&mut self, rhs: #rust_ty) {
-                            self.0 |= rhs.0;
+                } else {
+                    quote! {
+                        impl #enum_ident {
+                            #( #variants )*
                         }
                     }
-                });
+                };
 
-                result.push(quote! {
-                    impl ::#prefix::ops::BitAnd<#rust_ty> for #rust_ty {
-                        type Output = Self;
+                let prefix = ctx.trait_prefix();
+                let bitfield_impl_opt = is_bitfield
+                    .then(|| Self::newtype_bitfield_impl(prefix, rust_ty));
 
-                        #[inline]
-                        fn bitand(self, other: Self) -> Self {
-                            #rust_ty_name(self.0 & other.0)
-                        }
-                    }
-                });
+                quote! {
+                    // Previously variant impls where before the enum definition.
+                    // lets keep this as is for now, to reduce the diff in generated bindings.
+                    #impl_variants
 
-                result.push(quote! {
-                    impl ::#prefix::ops::BitAndAssign for #rust_ty {
-                        #[inline]
-                        fn bitand_assign(&mut self, rhs: #rust_ty) {
-                            self.0 &= rhs.0;
-                        }
+                    #bitfield_impl_opt
+
+                    #[repr(transparent)]
+                    #( #attrs )*
+                    pub struct #enum_ident (pub #enum_repr);
+                }
+            }
+            EnumBuilderKind::Consts { needs_typedef } => {
+                let typedef_opt = needs_typedef.then(|| {
+                    quote! {
+                        #( #attrs )*
+                        pub type #enum_ident = #enum_repr;
                     }
                 });
+                quote! {
+                    #( #variants )*
 
-                tokens
+                    #typedef_opt
+                }
             }
-            EnumBuilder::Consts { variants, .. } => quote! { #( #variants )* },
-            EnumBuilder::ModuleConsts {
-                module_items,
-                module_name,
-                ..
-            } => {
-                let ident = ctx.rust_ident(module_name);
+            EnumBuilderKind::ModuleConsts { module_name, .. } => {
                 quote! {
-                    pub mod #ident {
-                        #( #module_items )*
+                    // todo: Probably some attributes, e.g. `cfg` should apply to the `mod`.
+                    pub mod #module_name {
+                        #( #attrs )*
+                        pub type #enum_ident = #enum_repr;
+
+                        #( #variants )*
                     }
                 }
             }
@@ -3656,29 +3714,6 @@ impl CodeGenerator for Enum {
 
         let mut attrs = vec![];
 
-        // TODO(emilio): Delegate this to the builders?
-        match variation {
-            EnumVariation::Rust { non_exhaustive } => {
-                if non_exhaustive &&
-                    ctx.options().rust_features().non_exhaustive
-                {
-                    attrs.push(attributes::non_exhaustive());
-                } else if non_exhaustive &&
-                    !ctx.options().rust_features().non_exhaustive
-                {
-                    panic!("The rust target you're using doesn't seem to support non_exhaustive enums");
-                }
-            }
-            EnumVariation::NewType { .. } => {
-                if true {
-                    attrs.push(attributes::repr("transparent"));
-                } else {
-                    attrs.push(attributes::repr("C"));
-                }
-            }
-            _ => {}
-        };
-
         if let Some(comment) = item.comment(ctx) {
             attrs.push(attributes::doc(&comment));
         }
@@ -3779,8 +3814,14 @@ impl CodeGenerator for Enum {
             );
         });
 
-        let mut builder =
-            EnumBuilder::new(&name, attrs, &repr, variation, has_typedef);
+        let mut builder = EnumBuilder::new(
+            &name,
+            attrs,
+            &repr,
+            variation,
+            has_typedef,
+            enum_ty.name().is_none(),
+        );
 
         // A map where we keep a value -> variant relation.
         let mut seen_values = HashMap::<_, Ident>::default();
@@ -3822,6 +3863,15 @@ impl CodeGenerator for Enum {
                 continue;
             }
 
+            let mut variant_doc = quote! {};
+            if ctx.options().generate_comments {
+                if let Some(raw_comment) = variant.comment() {
+                    let processed_comment =
+                        ctx.options().process_comment(raw_comment);
+                    variant_doc = attributes::doc(&processed_comment);
+                }
+            }
+
             match seen_values.entry(variant.val()) {
                 Entry::Occupied(ref entry) => {
                     if variation.is_rust() {
@@ -3864,9 +3914,9 @@ impl CodeGenerator for Enum {
                         builder = builder.with_variant(
                             ctx,
                             variant,
+                            variant_doc,
                             constant_mangling_prefix,
                             &enum_rust_ty,
-                            result,
                             enum_ty.name().is_some(),
                         );
                     }
@@ -3875,9 +3925,9 @@ impl CodeGenerator for Enum {
                     builder = builder.with_variant(
                         ctx,
                         variant,
+                        variant_doc,
                         constant_mangling_prefix,
                         &enum_rust_ty,
-                        result,
                         enum_ty.name().is_some(),
                     );
 
@@ -3917,11 +3967,17 @@ impl CodeGenerator for Enum {
             }
         }
 
-        let item = builder.build(ctx, &enum_rust_ty, result);
+        let item = builder.build(ctx, &enum_rust_ty);
         result.push(item);
     }
 }
 
+struct EnumVariantInfo {
+    variant_name: Ident,
+    variant_doc: proc_macro2::TokenStream,
+    value: proc_macro2::TokenStream,
+}
+
 /// Enum for the default type of macro constants.
 #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
 pub enum MacroTypeVariation {
@@ -4540,6 +4596,7 @@ impl CodeGenerator for Function {
     ) -> Self::Return {
         debug!("<Function as CodeGenerator>::codegen: item = {item:?}");
         debug_assert!(item.is_enabled_for_codegen(ctx));
+        let id = DiscoveredItemId::new(item.id().as_usize());
 
         let is_internal = matches!(self.linkage(), Linkage::Internal);
 
@@ -4564,14 +4621,20 @@ impl CodeGenerator for Function {
             }
         }
 
-        // Pure virtual methods have no actual symbol, so we can't generate
-        // something meaningful for them.
-        let is_dynamic_function = match self.kind() {
-            FunctionKind::Method(ref method_kind)
-                if method_kind.is_pure_virtual() =>
-            {
-                return None;
+        let is_pure_virtual = match self.kind() {
+            FunctionKind::Method(ref method_kind) => {
+                method_kind.is_pure_virtual()
             }
+            _ => false,
+        };
+        if is_pure_virtual && !ctx.options().generate_pure_virtual_functions {
+            // Pure virtual methods have no actual symbol, so we can't generate
+            // something meaningful for them. Downstream code postprocessors
+            // might want to find out about them.
+            return None;
+        }
+
+        let is_dynamic_function = match self.kind() {
             FunctionKind::Function => {
                 ctx.options().dynamic_library_name.is_some()
             }
@@ -4650,6 +4713,14 @@ impl CodeGenerator for Function {
         if times_seen > 0 {
             write!(&mut canonical_name, "{times_seen}").unwrap();
         }
+        ctx.options().for_each_callback(|cb| {
+            cb.new_item_found(
+                id,
+                DiscoveredItem::Function {
+                    final_name: canonical_name.to_string(),
+                },
+            );
+        });
 
         let link_name_attr = self.link_name().or_else(|| {
             let mangled_name = mangled_name.unwrap_or(name);
diff --git a/bindgen/ir/function.rs b/bindgen/ir/function.rs
index 52fc94d9..65a12d4b 100644
--- a/bindgen/ir/function.rs
+++ b/bindgen/ir/function.rs
@@ -433,7 +433,7 @@ impl FunctionSig {
             spelling.starts_with("operator") &&
                 !clang::is_valid_identifier(spelling)
         };
-        if is_operator(&spelling) {
+        if is_operator(&spelling) && !ctx.options().represent_cxx_operators {
             return Err(ParseError::Continue);
         }
 
@@ -734,8 +734,9 @@ impl ClangSubItemParser for Function {
         if visibility != CXVisibility_Default {
             return Err(ParseError::Continue);
         }
-
-        if cursor.access_specifier() == CX_CXXPrivate {
+        if cursor.access_specifier() == CX_CXXPrivate &&
+            !context.options().generate_private_functions
+        {
             return Err(ParseError::Continue);
         }
 
@@ -755,7 +756,9 @@ impl ClangSubItemParser for Function {
                 return Err(ParseError::Continue);
             }
 
-            if cursor.is_deleted_function() {
+            if cursor.is_deleted_function() &&
+                !context.options().generate_deleted_functions
+            {
                 return Err(ParseError::Continue);
             }
 
diff --git a/bindgen/ir/ty.rs b/bindgen/ir/ty.rs
index 2589a56f..baaa4a49 100644
--- a/bindgen/ir/ty.rs
+++ b/bindgen/ir/ty.rs
@@ -1117,7 +1117,7 @@ impl Type {
 
                     TypeKind::Comp(complex)
                 }
-                CXType_Vector => {
+                CXType_Vector | CXType_ExtVector => {
                     let inner = Item::from_ty(
                         ty.elem_type().as_ref().unwrap(),
                         location,
diff --git a/bindgen/lib.rs b/bindgen/lib.rs
index 1a15d51d..5a946e6d 100644
--- a/bindgen/lib.rs
+++ b/bindgen/lib.rs
@@ -684,29 +684,44 @@ pub(crate) const HOST_TARGET: &str =
 fn rust_to_clang_target(rust_target: &str) -> Box<str> {
     const TRIPLE_HYPHENS_MESSAGE: &str = "Target triple should contain hyphens";
 
-    let mut clang_target = rust_target.to_owned();
+    let mut triple: Vec<&str> = rust_target.split_terminator('-').collect();
 
-    if clang_target.starts_with("riscv32") {
-        let idx = clang_target.find('-').expect(TRIPLE_HYPHENS_MESSAGE);
+    assert!(!triple.is_empty(), "{}", TRIPLE_HYPHENS_MESSAGE);
+    triple.resize(4, "");
 
-        clang_target.replace_range(..idx, "riscv32");
-    } else if clang_target.starts_with("riscv64") {
-        let idx = clang_target.find('-').expect(TRIPLE_HYPHENS_MESSAGE);
-
-        clang_target.replace_range(..idx, "riscv64");
-    } else if clang_target.starts_with("aarch64-apple-") {
-        let idx = clang_target.find('-').expect(TRIPLE_HYPHENS_MESSAGE);
-
-        clang_target.replace_range(..idx, "arm64");
+    // RISC-V
+    if triple[0].starts_with("riscv32") {
+        triple[0] = "riscv32";
+    } else if triple[0].starts_with("riscv64") {
+        triple[0] = "riscv64";
     }
 
-    if clang_target.ends_with("-espidf") {
-        let idx = clang_target.rfind('-').expect(TRIPLE_HYPHENS_MESSAGE);
+    // Apple
+    if triple[1] == "apple" {
+        if triple[0] == "aarch64" {
+            triple[0] = "arm64";
+        }
+        if triple[3] == "sim" {
+            triple[3] = "simulator";
+        }
+    }
 
-        clang_target.replace_range((idx + 1).., "elf");
+    // ESP-IDF
+    if triple[2] == "espidf" {
+        triple[2] = "elf";
     }
 
-    clang_target.into()
+    triple
+        .iter()
+        .skip(1)
+        .fold(triple[0].to_string(), |triple, part| {
+            if part.is_empty() {
+                triple
+            } else {
+                triple + "-" + part
+            }
+        })
+        .into()
 }
 
 /// Returns the effective target, and whether it was explicitly specified on the
@@ -1389,3 +1404,19 @@ fn test_rust_to_clang_target_espidf() {
         "xtensa-esp32-elf"
     );
 }
+
+#[test]
+fn test_rust_to_clang_target_simulator() {
+    assert_eq!(
+        rust_to_clang_target("aarch64-apple-ios-sim").as_ref(),
+        "arm64-apple-ios-simulator"
+    );
+    assert_eq!(
+        rust_to_clang_target("aarch64-apple-tvos-sim").as_ref(),
+        "arm64-apple-tvos-simulator"
+    );
+    assert_eq!(
+        rust_to_clang_target("aarch64-apple-watchos-sim").as_ref(),
+        "arm64-apple-watchos-simulator"
+    );
+}
diff --git a/bindgen/log_stubs.rs b/bindgen/log_stubs.rs
index 83159831..51d2f81f 100644
--- a/bindgen/log_stubs.rs
+++ b/bindgen/log_stubs.rs
@@ -1,5 +1,6 @@
 #![allow(unused)]
 
+#[clippy::format_args]
 macro_rules! log {
     (target: $target:expr, $lvl:expr, $($arg:tt)+) => {{
         let _ = $target;
@@ -10,22 +11,27 @@ macro_rules! log {
         let _ = format_args!($($arg)+);
     }};
 }
+#[clippy::format_args]
 macro_rules! error {
     (target: $target:expr, $($arg:tt)+) => { log!(target: $target, "", $($arg)+) };
     ($($arg:tt)+) => { log!("", $($arg)+) };
 }
+#[clippy::format_args]
 macro_rules! warn {
     (target: $target:expr, $($arg:tt)*) => { log!(target: $target, "", $($arg)*) };
     ($($arg:tt)*) => { log!("", $($arg)*) };
 }
+#[clippy::format_args]
 macro_rules! info {
     (target: $target:expr, $($arg:tt)+) => { log!(target: $target, "", $($arg)+) };
     ($($arg:tt)+) => { log!("", $($arg)+) };
 }
+#[clippy::format_args]
 macro_rules! debug {
     (target: $target:expr, $($arg:tt)+) => { log!(target: $target, "", $($arg)+) };
     ($($arg:tt)+) => { log!("", $($arg)+) };
 }
+#[clippy::format_args]
 macro_rules! trace {
     (target: $target:expr, $($arg:tt)+) => { log!(target: $target, "", $($arg)+) };
     ($($arg:tt)+) => { log!("", $($arg)+) };
diff --git a/bindgen/options/cli.rs b/bindgen/options/cli.rs
index f9a85729..d3cf5d09 100644
--- a/bindgen/options/cli.rs
+++ b/bindgen/options/cli.rs
@@ -447,6 +447,9 @@ struct BindgenCommand {
     /// Use distinct char16_t
     #[arg(long)]
     use_distinct_char16_t: bool,
+    /// Output C++ overloaded operators
+    #[arg(long)]
+    represent_cxx_operators: bool,
     /// Enables generation of vtable functions.
     #[arg(long)]
     vtable_generation: bool,
@@ -510,6 +513,18 @@ struct BindgenCommand {
     /// bitfields. This flag is ignored if the `--respect-cxx-access-specs` flag is used.
     #[arg(long, value_name = "VISIBILITY")]
     default_visibility: Option<FieldVisibilityKind>,
+    /// Whether to generate C++ functions marked with "=delete" even though they
+    /// can't be called.
+    #[arg(long)]
+    generate_deleted_functions: bool,
+    /// Whether to generate C++ "pure virtual" functions even though they can't
+    /// be called.
+    #[arg(long)]
+    generate_pure_virtual_functions: bool,
+    /// Whether to generate C++ private functions even though they can't
+    /// be called.
+    #[arg(long)]
+    generate_private_functions: bool,
     /// Whether to emit diagnostics or not.
     #[cfg(feature = "experimental")]
     #[arg(long, requires = "experimental")]
@@ -637,6 +652,7 @@ where
         explicit_padding,
         use_specific_virtual_function_receiver,
         use_distinct_char16_t,
+        represent_cxx_operators,
         vtable_generation,
         sort_semantically,
         merge_extern_blocks,
@@ -657,6 +673,9 @@ where
         wrap_static_fns_path,
         wrap_static_fns_suffix,
         default_visibility,
+        generate_deleted_functions,
+        generate_pure_virtual_functions,
+        generate_private_functions,
         #[cfg(feature = "experimental")]
         emit_diagnostics,
         generate_shell_completions,
@@ -936,6 +955,7 @@ where
             explicit_padding,
             use_specific_virtual_function_receiver,
             use_distinct_char16_t,
+            represent_cxx_operators,
             vtable_generation,
             sort_semantically,
             merge_extern_blocks,
@@ -948,6 +968,9 @@ where
             wrap_static_fns_path,
             wrap_static_fns_suffix,
             default_visibility,
+            generate_deleted_functions,
+            generate_pure_virtual_functions,
+            generate_private_functions,
         }
     );
 
diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs
index ab6b232e..c9ef7c8b 100644
--- a/bindgen/options/mod.rs
+++ b/bindgen/options/mod.rs
@@ -192,6 +192,24 @@ options! {
         },
         as_args: "--use-distinct-char16-t",
     },
+    /// Whether we should output C++ overloaded operators. By itself,
+    /// this option is not sufficient to produce valid output, because
+    /// such operators will have names that are not acceptable Rust
+    /// names (for example `operator=`). If you use this option, you'll also
+    /// have to rename the resulting functions - for example by using
+    /// [`ParseCallbacks::generated_name_override`].
+    represent_cxx_operators: bool {
+        methods: {
+            /// If this is true, output existence of C++ overloaded operators.
+            /// At present, only operator= is noted.
+            /// Disabled by default.
+            pub fn represent_cxx_operators(mut self, doit: bool) -> Builder {
+                self.options.represent_cxx_operators = doit;
+                self
+            }
+        },
+        as_args: "--represent-cxx-operators",
+    },
 
     /// Types that have been blocklisted and should not appear anywhere in the generated code.
     blocklisted_types: RegexSet {
@@ -2211,4 +2229,58 @@ options! {
         },
         as_args: "--clang-macro-fallback-build-dir",
     }
+    /// Whether to always report C++ "deleted" functions.
+    generate_deleted_functions: bool {
+        methods: {
+            /// Set whether to generate C++ functions even marked "=deleted"
+            ///
+            /// Although not useful to call these functions, downstream code
+            /// generators may need to know whether they've been deleted in
+            /// order to determine the relocatability of a C++ type
+            /// (specifically by virtue of which constructors exist.)
+            pub fn generate_deleted_functions(mut self, doit: bool) -> Self {
+                self.options.generate_deleted_functions = doit;
+                self
+            }
+
+        },
+        as_args: "--generate-deleted-functions",
+    },
+    /// Whether to always report C++ "pure virtual" functions.
+    generate_pure_virtual_functions: bool {
+        methods: {
+            /// Set whether to generate C++ functions that are pure virtual.
+            ///
+            /// These functions can't be called, so the only reason
+            /// to generate them is if downstream postprocessors
+            /// need to know of their existence. This is necessary,
+            /// for instance, to determine whether a type itself is
+            /// pure virtual and thus can't be allocated.
+            /// Downstream code generators may choose to make code to
+            /// allow types to be allocated but need to avoid doing so
+            /// if the type contains pure virtual functions.
+            pub fn generate_pure_virtual_functions(mut self, doit: bool) -> Self {
+                self.options.generate_pure_virtual_functions = doit;
+                self
+            }
+
+        },
+        as_args: "--generate-pure-virtual-functions",
+    },
+    /// Whether to always report C++ "private" functions.
+    generate_private_functions: bool {
+        methods: {
+            /// Set whether to generate C++ functions that are private.
+            ///
+            /// These functions can't be called, so the only reason
+            /// to generate them is if downstream postprocessors
+            /// need to know of their existence.
+            pub fn generate_private_functions(mut self, doit: bool) -> Self {
+                self.options.generate_private_functions = doit;
+                self
+            }
+
+        },
+        as_args: "--generate-private-functions",
+    },
 }


Test patch:
diff --git a/bindgen-tests/tests/expectations/tests/bitfield-enum-basic.rs b/bindgen-tests/tests/expectations/tests/bitfield-enum-basic.rs
index adc4690c..aecb9dc6 100644
--- a/bindgen-tests/tests/expectations/tests/bitfield-enum-basic.rs
+++ b/bindgen-tests/tests/expectations/tests/bitfield-enum-basic.rs
@@ -1,14 +1,8 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
 impl Foo {
     pub const Bar: Foo = Foo(2);
-}
-impl Foo {
     pub const Baz: Foo = Foo(4);
-}
-impl Foo {
     pub const Duplicated: Foo = Foo(4);
-}
-impl Foo {
     pub const Negative: Foo = Foo(-3);
 }
 impl ::std::ops::BitOr<Foo> for Foo {
@@ -42,14 +36,8 @@ impl ::std::ops::BitAndAssign for Foo {
 pub struct Foo(pub ::std::os::raw::c_int);
 impl Buz {
     pub const Bar: Buz = Buz(2);
-}
-impl Buz {
     pub const Baz: Buz = Buz(4);
-}
-impl Buz {
     pub const Duplicated: Buz = Buz(4);
-}
-impl Buz {
     pub const Negative: Buz = Buz(-3);
 }
 impl ::std::ops::BitOr<Buz> for Buz {
diff --git a/bindgen-tests/tests/expectations/tests/bitfield-enum-repr-c.rs b/bindgen-tests/tests/expectations/tests/bitfield-enum-repr-c.rs
index 0b5202df..df5a69ed 100644
--- a/bindgen-tests/tests/expectations/tests/bitfield-enum-repr-c.rs
+++ b/bindgen-tests/tests/expectations/tests/bitfield-enum-repr-c.rs
@@ -1,14 +1,8 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
 impl Foo {
     pub const Bar: Foo = Foo(2);
-}
-impl Foo {
     pub const Baz: Foo = Foo(4);
-}
-impl Foo {
     pub const Duplicated: Foo = Foo(4);
-}
-impl Foo {
     pub const Negative: Foo = Foo(-3);
 }
 impl ::std::ops::BitOr<Foo> for Foo {
diff --git a/bindgen-tests/tests/expectations/tests/bitfield-enum-repr-transparent.rs b/bindgen-tests/tests/expectations/tests/bitfield-enum-repr-transparent.rs
index 0b5202df..df5a69ed 100644
--- a/bindgen-tests/tests/expectations/tests/bitfield-enum-repr-transparent.rs
+++ b/bindgen-tests/tests/expectations/tests/bitfield-enum-repr-transparent.rs
@@ -1,14 +1,8 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
 impl Foo {
     pub const Bar: Foo = Foo(2);
-}
-impl Foo {
     pub const Baz: Foo = Foo(4);
-}
-impl Foo {
     pub const Duplicated: Foo = Foo(4);
-}
-impl Foo {
     pub const Negative: Foo = Foo(-3);
 }
 impl ::std::ops::BitOr<Foo> for Foo {
diff --git a/bindgen-tests/tests/expectations/tests/enum-default-bitfield.rs b/bindgen-tests/tests/expectations/tests/enum-default-bitfield.rs
index 58b8bf09..b7b14fbc 100644
--- a/bindgen-tests/tests/expectations/tests/enum-default-bitfield.rs
+++ b/bindgen-tests/tests/expectations/tests/enum-default-bitfield.rs
@@ -52,8 +52,6 @@ impl Default for foo {
 }
 impl Foo {
     pub const Bar: Foo = Foo(0);
-}
-impl Foo {
     pub const Qux: Foo = Foo(1);
 }
 impl ::std::ops::BitOr<Foo> for Foo {
@@ -92,8 +90,6 @@ pub mod Neg {
 }
 impl NoDebug {
     pub const NoDebug1: NoDebug = NoDebug(0);
-}
-impl NoDebug {
     pub const NoDebug2: NoDebug = NoDebug(1);
 }
 impl ::std::ops::BitOr<NoDebug> for NoDebug {
@@ -128,8 +124,6 @@ impl ::std::ops::BitAndAssign for NoDebug {
 pub struct NoDebug(pub ::std::os::raw::c_uint);
 impl Debug {
     pub const Debug1: Debug = Debug(0);
-}
-impl Debug {
     pub const Debug2: Debug = Debug(1);
 }
 impl ::std::ops::BitOr<Debug> for Debug {
diff --git a/bindgen-tests/tests/expectations/tests/enum-doc-bitfield.rs b/bindgen-tests/tests/expectations/tests/enum-doc-bitfield.rs
index ba73a8ea..33eec3e4 100644
--- a/bindgen-tests/tests/expectations/tests/enum-doc-bitfield.rs
+++ b/bindgen-tests/tests/expectations/tests/enum-doc-bitfield.rs
@@ -2,24 +2,14 @@
 impl B {
     /// Document field with three slashes
     pub const VAR_A: B = B(0);
-}
-impl B {
     /// Document field with preceding star
     pub const VAR_B: B = B(1);
-}
-impl B {
     /// Document field with preceding exclamation
     pub const VAR_C: B = B(2);
-}
-impl B {
     ///< Document field with following star
     pub const VAR_D: B = B(3);
-}
-impl B {
     ///< Document field with following exclamation
     pub const VAR_E: B = B(4);
-}
-impl B {
     /** Document field with preceding star, with a loong long multiline
  comment.
 
diff --git a/bindgen-tests/tests/expectations/tests/issue-1198-alias-rust-bitfield-enum.rs b/bindgen-tests/tests/expectations/tests/issue-1198-alias-rust-bitfield-enum.rs
index 7bbcb0d5..5aaff691 100644
--- a/bindgen-tests/tests/expectations/tests/issue-1198-alias-rust-bitfield-enum.rs
+++ b/bindgen-tests/tests/expectations/tests/issue-1198-alias-rust-bitfield-enum.rs
@@ -1,11 +1,7 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
 impl MyDupeEnum {
     pub const A: MyDupeEnum = MyDupeEnum(0);
-}
-impl MyDupeEnum {
     pub const A_alias: MyDupeEnum = MyDupeEnum(0);
-}
-impl MyDupeEnum {
     pub const B: MyDupeEnum = MyDupeEnum(1);
 }
 impl ::std::ops::BitOr<MyDupeEnum> for MyDupeEnum {
@@ -39,11 +35,7 @@ impl ::std::ops::BitAndAssign for MyDupeEnum {
 pub struct MyDupeEnum(pub ::std::os::raw::c_uint);
 impl MyOtherDupeEnum {
     pub const C: MyOtherDupeEnum = MyOtherDupeEnum(0);
-}
-impl MyOtherDupeEnum {
     pub const C_alias: MyOtherDupeEnum = MyOtherDupeEnum(0);
-}
-impl MyOtherDupeEnum {
     pub const D: MyOtherDupeEnum = MyOtherDupeEnum(1);
 }
 impl ::std::ops::BitOr<MyOtherDupeEnum> for MyOtherDupeEnum {
diff --git a/bindgen-tests/tests/expectations/tests/issue-1435.rs b/bindgen-tests/tests/expectations/tests/issue-1435.rs
index 7b9bfe84..b50e1f88 100644
--- a/bindgen-tests/tests/expectations/tests/issue-1435.rs
+++ b/bindgen-tests/tests/expectations/tests/issue-1435.rs
@@ -6,8 +6,8 @@ pub mod root {
     pub mod ns {
         #[allow(unused_imports)]
         use self::super::super::root;
-        pub const AB_A: root::ns::AB = 0;
-        pub const AB_B: root::ns::AB = 1;
+        pub const AB_A: AB = 0;
+        pub const AB_B: AB = 1;
         pub type AB = ::std::os::raw::c_int;
     }
     pub use self::super::root::ns::AB as AB;
diff --git a/bindgen-tests/tests/expectations/tests/newtype-enum.rs b/bindgen-tests/tests/expectations/tests/newtype-enum.rs
index ea25950d..0045c52a 100644
--- a/bindgen-tests/tests/expectations/tests/newtype-enum.rs
+++ b/bindgen-tests/tests/expectations/tests/newtype-enum.rs
@@ -1,14 +1,8 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
 impl Foo {
     pub const Bar: Foo = Foo(2);
-}
-impl Foo {
     pub const Baz: Foo = Foo(4);
-}
-impl Foo {
     pub const Duplicated: Foo = Foo(4);
-}
-impl Foo {
     pub const Negative: Foo = Foo(-3);
 }
 #[repr(transparent)]
diff --git a/bindgen-tests/tests/expectations/tests/opencl_vector.rs b/bindgen-tests/tests/expectations/tests/opencl_vector.rs
new file mode 100644
index 00000000..3c2525c8
--- /dev/null
+++ b/bindgen-tests/tests/expectations/tests/opencl_vector.rs
@@ -0,0 +1,6 @@
+#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+pub type float4 = [f32; 4usize];
+pub type float2 = [f32; 2usize];
+unsafe extern "C" {
+    pub fn foo(a: float2, b: float2) -> float4;
+}
diff --git a/bindgen-tests/tests/expectations/tests/operator_equals.rs b/bindgen-tests/tests/expectations/tests/operator_equals.rs
new file mode 100644
index 00000000..ff9f4fdd
--- /dev/null
+++ b/bindgen-tests/tests/expectations/tests/operator_equals.rs
@@ -0,0 +1,24 @@
+#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct SomeClass {
+    pub _address: u8,
+}
+#[allow(clippy::unnecessary_operation, clippy::identity_op)]
+const _: () = {
+    ["Size of SomeClass"][::std::mem::size_of::<SomeClass>() - 1usize];
+    ["Alignment of SomeClass"][::std::mem::align_of::<SomeClass>() - 1usize];
+};
+unsafe extern "C" {
+    #[link_name = "\u{1}_ZN9SomeClassaSERKS_"]
+    pub fn SomeClass_operatorequals(
+        this: *mut SomeClass,
+        another: *const SomeClass,
+    ) -> bool;
+}
+impl SomeClass {
+    #[inline]
+    pub unsafe fn operatorequals(&mut self, another: *const SomeClass) -> bool {
+        SomeClass_operatorequals(self, another)
+    }
+}
diff --git a/bindgen-tests/tests/expectations/tests/uncallable_functions.rs b/bindgen-tests/tests/expectations/tests/uncallable_functions.rs
new file mode 100644
index 00000000..fddb5d41
--- /dev/null
+++ b/bindgen-tests/tests/expectations/tests/uncallable_functions.rs
@@ -0,0 +1,46 @@
+#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#[repr(C)]
+pub struct Test__bindgen_vtable {
+    pub Test_a: unsafe extern "C" fn(this: *mut Test),
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Test {
+    pub vtable_: *const Test__bindgen_vtable,
+}
+#[allow(clippy::unnecessary_operation, clippy::identity_op)]
+const _: () = {
+    ["Size of Test"][::std::mem::size_of::<Test>() - 8usize];
+    ["Alignment of Test"][::std::mem::align_of::<Test>() - 8usize];
+};
+unsafe extern "C" {
+    #[link_name = "\u{1}_ZN4Test1bEv"]
+    pub fn Test_b(this: *mut Test);
+}
+unsafe extern "C" {
+    #[link_name = "\u{1}_ZN4Test1cEv"]
+    pub fn Test_c(this: *mut Test);
+}
+impl Default for Test {
+    fn default() -> Self {
+        let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
+        unsafe {
+            ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+            s.assume_init()
+        }
+    }
+}
+impl Test {
+    #[inline]
+    pub unsafe fn b(&mut self) {
+        Test_b(self)
+    }
+    #[inline]
+    pub unsafe fn c(&mut self) {
+        Test_c(self)
+    }
+}
+unsafe extern "C" {
+    #[link_name = "\u{1}_ZN4Test1aEv"]
+    pub fn Test_a(this: *mut ::std::os::raw::c_void);
+}
diff --git a/bindgen-tests/tests/headers/opencl_vector.h b/bindgen-tests/tests/headers/opencl_vector.h
new file mode 100644
index 00000000..79022c76
--- /dev/null
+++ b/bindgen-tests/tests/headers/opencl_vector.h
@@ -0,0 +1,9 @@
+typedef float float4 __attribute__((ext_vector_type(4)));
+typedef float float2 __attribute__((ext_vector_type(2)));
+
+float4 foo(float2 a, float2 b) {
+  float4 c;
+  c.xz = a;
+  c.yw = b;
+  return c;
+}
\ No newline at end of file
diff --git a/bindgen-tests/tests/headers/operator_equals.hpp b/bindgen-tests/tests/headers/operator_equals.hpp
new file mode 100644
index 00000000..38a3a092
--- /dev/null
+++ b/bindgen-tests/tests/headers/operator_equals.hpp
@@ -0,0 +1,9 @@
+// bindgen-flags: --represent-cxx-operators --generate-inline-functions -- -x c++
+// bindgen-parse-callbacks: operator-rename
+
+class SomeClass {
+public:
+    bool operator=(const SomeClass& another) {
+        return false;
+    }
+};
diff --git a/bindgen-tests/tests/headers/uncallable_functions.hpp b/bindgen-tests/tests/headers/uncallable_functions.hpp
new file mode 100644
index 00000000..9187470c
--- /dev/null
+++ b/bindgen-tests/tests/headers/uncallable_functions.hpp
@@ -0,0 +1,9 @@
+// bindgen-flags: --generate-deleted-functions --generate-private-functions --generate-pure-virtual-functions --generate-inline-functions -- -x c++ -std=c++14
+
+class Test {
+public:
+    virtual void a() = 0;
+    void b() = delete;
+private:
+    void c() {}
+};
diff --git a/bindgen-tests/tests/parse_callbacks/item_discovery_callback/header_item_discovery.h b/bindgen-tests/tests/parse_callbacks/item_discovery_callback/header_item_discovery.h
index b2bb04f1..eb44e5fc 100644
--- a/bindgen-tests/tests/parse_callbacks/item_discovery_callback/header_item_discovery.h
+++ b/bindgen-tests/tests/parse_callbacks/item_discovery_callback/header_item_discovery.h
@@ -25,4 +25,8 @@ enum NamedEnum {
     Fish,
 };
 
-typedef enum NamedEnum AliasOfNamedEnum;
\ No newline at end of file
+typedef enum NamedEnum AliasOfNamedEnum;
+
+// Functions
+
+void named_function();
diff --git a/bindgen-tests/tests/parse_callbacks/item_discovery_callback/header_item_discovery.hpp b/bindgen-tests/tests/parse_callbacks/item_discovery_callback/header_item_discovery.hpp
new file mode 100644
index 00000000..1de8d99e
--- /dev/null
+++ b/bindgen-tests/tests/parse_callbacks/item_discovery_callback/header_item_discovery.hpp
@@ -0,0 +1,6 @@
+// Methods
+
+class SomeClass {
+public:
+    void named_method();
+};
\ No newline at end of file
diff --git a/bindgen-tests/tests/parse_callbacks/item_discovery_callback/mod.rs b/bindgen-tests/tests/parse_callbacks/item_discovery_callback/mod.rs
index 93a2b029..645235b2 100644
--- a/bindgen-tests/tests/parse_callbacks/item_discovery_callback/mod.rs
+++ b/bindgen-tests/tests/parse_callbacks/item_discovery_callback/mod.rs
@@ -17,20 +17,28 @@ impl ParseCallbacks for ItemDiscovery {
         self.0.borrow_mut().insert(_id, _item);
     }
 }
-#[test]
-pub fn test_item_discovery_callback() {
+
+fn test_item_discovery_callback(
+    header: &str,
+    expected: HashMap<DiscoveredItemId, DiscoveredItem>,
+) {
     let discovery = ItemDiscovery::default();
     let info = Rc::clone(&discovery.0);
 
+    let mut header_path = env!("CARGO_MANIFEST_DIR").to_string();
+    header_path.push_str(header);
+
     Builder::default()
-        .header(concat!(
-        env!("CARGO_MANIFEST_DIR"),
-        "/tests/parse_callbacks/item_discovery_callback/header_item_discovery.h"
-        ))
+        .header(header_path)
         .parse_callbacks(Box::new(discovery))
         .generate()
         .expect("TODO: panic message");
 
+    compare_item_caches(&info.borrow(), &expected);
+}
+
+#[test]
+fn test_item_discovery_callback_c() {
     let expected = ItemCache::from([
         (
             DiscoveredItemId::new(10),
@@ -87,9 +95,37 @@ pub fn test_item_discovery_callback() {
                 final_name: "_bindgen_ty_*".to_string(),
             },
         ),
+        (
+            DiscoveredItemId::new(41),
+            DiscoveredItem::Function {
+                final_name: "named_function".to_string(),
+            },
+        ),
     ]);
+    test_item_discovery_callback(
+        "/tests/parse_callbacks/item_discovery_callback/header_item_discovery.h", expected);
+}
 
-    compare_item_caches(&info.borrow(), &expected);
+#[test]
+fn test_item_discovery_callback_cpp() {
+    let expected = ItemCache::from([
+        (
+            DiscoveredItemId::new(1),
+            DiscoveredItem::Struct {
+                original_name: Some("SomeClass".to_string()),
+                final_name: "SomeClass".to_string(),
+            },
+        ),
+        (
+            DiscoveredItemId::new(2),
+            DiscoveredItem::Method {
+                final_name: "named_method".to_string(),
+                parent: DiscoveredItemId::new(1),
+            },
+        ),
+    ]);
+    test_item_discovery_callback(
+        "/tests/parse_callbacks/item_discovery_callback/header_item_discovery.hpp", expected);
 }
 
 pub fn compare_item_caches(generated: &ItemCache, expected: &ItemCache) {
@@ -142,6 +178,12 @@ fn compare_item_info(
         DiscoveredItem::Enum { .. } => {
             compare_enum_info(expected_item, generated_item)
         }
+        DiscoveredItem::Function { .. } => {
+            compare_function_info(expected_item, generated_item)
+        }
+        DiscoveredItem::Method { .. } => {
+            compare_method_info(expected_item, generated_item)
+        }
     }
 }
 
@@ -279,3 +321,57 @@ pub fn compare_alias_info(
 
     compare_item_info(expected_aliased, generated_aliased, expected, generated)
 }
+
+pub fn compare_function_info(
+    expected_item: &DiscoveredItem,
+    generated_item: &DiscoveredItem,
+) -> bool {
+    let DiscoveredItem::Function {
+        final_name: expected_final_name,
+    } = expected_item
+    else {
+        unreachable!()
+    };
+
+    let DiscoveredItem::Function {
+        final_name: generated_final_name,
+    } = generated_item
+    else {
+        unreachable!()
+    };
+
+    if !compare_names(expected_final_name, generated_final_name) {
+        return false;
+    }
+    true
+}
+
+pub fn compare_method_info(
+    expected_item: &DiscoveredItem,
+    generated_item: &DiscoveredItem,
+) -> bool {
+    let DiscoveredItem::Method {
+        final_name: expected_final_name,
+        parent: expected_parent,
+    } = expected_item
+    else {
+        unreachable!()
+    };
+
+    let DiscoveredItem::Method {
+        final_name: generated_final_name,
+        parent: generated_parent,
+    } = generated_item
+    else {
+        unreachable!()
+    };
+
+    if expected_parent != generated_parent {
+        return false;
+    }
+
+    if !compare_names(expected_final_name, generated_final_name) {
+        return false;
+    }
+    true
+}
diff --git a/bindgen-tests/tests/parse_callbacks/mod.rs b/bindgen-tests/tests/parse_callbacks/mod.rs
index 7aca0fd1..5fe8d90d 100644
--- a/bindgen-tests/tests/parse_callbacks/mod.rs
+++ b/bindgen-tests/tests/parse_callbacks/mod.rs
@@ -146,6 +146,19 @@ impl ParseCallbacks for WrapAsVariadicFn {
     }
 }
 
+#[derive(Debug)]
+pub(super) struct OperatorRename;
+
+impl ParseCallbacks for OperatorRename {
+    fn generated_name_override(&self, info: ItemInfo) -> Option<String> {
+        if info.name == "operator=" {
+            Some("operatorequals".to_string())
+        } else {
+            None
+        }
+    }
+}
+
 pub fn lookup(cb: &str) -> Box<dyn ParseCallbacks> {
     match cb {
         "enum-variant-rename" => Box::new(EnumVariantRename),
@@ -154,6 +167,7 @@ pub fn lookup(cb: &str) -> Box<dyn ParseCallbacks> {
         }
         "wrap-as-variadic-fn" => Box::new(WrapAsVariadicFn),
         "type-visibility" => Box::new(TypeVisibility),
+        "operator-rename" => Box::new(OperatorRename),
         call_back => {
             if let Some(prefix) =
                 call_back.strip_prefix("remove-function-prefix-")