bindgen/ir/
enum_ty.rs

1//! Intermediate representation for C/C++ enumerations.
2
3use super::super::codegen::EnumVariation;
4use super::context::{BindgenContext, TypeId};
5use super::item::Item;
6use super::ty::{Type, TypeKind};
7use crate::clang;
8use crate::ir::annotations::Annotations;
9use crate::parse::ParseError;
10use crate::regex_set::RegexSet;
11
12/// An enum representing custom handling that can be given to a variant.
13#[derive(Copy, Clone, Debug, PartialEq, Eq)]
14pub enum EnumVariantCustomBehavior {
15    /// This variant will be a module containing constants.
16    ModuleConstify,
17    /// This variant will be constified, that is, forced to generate a constant.
18    Constify,
19    /// This variant will be hidden entirely from the resulting enum.
20    Hide,
21}
22
23/// A C/C++ enumeration.
24#[derive(Debug)]
25pub(crate) struct Enum {
26    /// The representation used for this enum; it should be an `IntKind` type or
27    /// an alias to one.
28    ///
29    /// It's `None` if the enum is a forward declaration and isn't defined
30    /// anywhere else, see `tests/headers/func_ptr_in_struct.h`.
31    repr: Option<TypeId>,
32
33    /// The different variants, with explicit values.
34    variants: Vec<EnumVariant>,
35}
36
37impl Enum {
38    /// Construct a new `Enum` with the given representation and variants.
39    pub(crate) fn new(
40        repr: Option<TypeId>,
41        variants: Vec<EnumVariant>,
42    ) -> Self {
43        Enum { repr, variants }
44    }
45
46    /// Get this enumeration's representation.
47    pub(crate) fn repr(&self) -> Option<TypeId> {
48        self.repr
49    }
50
51    /// Get this enumeration's variants.
52    pub(crate) fn variants(&self) -> &[EnumVariant] {
53        &self.variants
54    }
55
56    /// Construct an enumeration from the given Clang type.
57    pub(crate) fn from_ty(
58        ty: &clang::Type,
59        ctx: &mut BindgenContext,
60    ) -> Result<Self, ParseError> {
61        use clang_sys::*;
62        debug!("Enum::from_ty {ty:?}");
63
64        if ty.kind() != CXType_Enum {
65            return Err(ParseError::Continue);
66        }
67
68        let declaration = ty.declaration().canonical();
69        let repr = declaration
70            .enum_type()
71            .and_then(|et| Item::from_ty(&et, declaration, None, ctx).ok());
72        let mut variants = vec![];
73
74        let variant_ty =
75            repr.and_then(|r| ctx.resolve_type(r).safe_canonical_type(ctx));
76        let is_bool = variant_ty.is_some_and(Type::is_bool);
77
78        // Assume signedness since the default type by the C standard is an int.
79        let is_signed = variant_ty.map_or(true, |ty| match *ty.kind() {
80            TypeKind::Int(ref int_kind) => int_kind.is_signed(),
81            ref other => {
82                panic!("Since when enums can be non-integers? {other:?}")
83            }
84        });
85
86        let type_name = ty.spelling();
87        let type_name = if type_name.is_empty() {
88            None
89        } else {
90            Some(type_name)
91        };
92        let type_name = type_name.as_deref();
93
94        let definition = declaration.definition().unwrap_or(declaration);
95        definition.visit(|cursor| {
96            if cursor.kind() == CXCursor_EnumConstantDecl {
97                let value = if is_bool {
98                    cursor.enum_val_boolean().map(EnumVariantValue::Boolean)
99                } else if is_signed {
100                    cursor.enum_val_signed().map(EnumVariantValue::Signed)
101                } else {
102                    cursor.enum_val_unsigned().map(EnumVariantValue::Unsigned)
103                };
104                if let Some(val) = value {
105                    let name = cursor.spelling();
106                    let annotations = Annotations::new(&cursor);
107                    let custom_behavior = ctx
108                        .options()
109                        .last_callback(|callbacks| {
110                            callbacks
111                                .enum_variant_behavior(type_name, &name, val)
112                        })
113                        .or_else(|| {
114                            let annotations = annotations.as_ref()?;
115                            if annotations.hide() {
116                                Some(EnumVariantCustomBehavior::Hide)
117                            } else if annotations.constify_enum_variant() {
118                                Some(EnumVariantCustomBehavior::Constify)
119                            } else {
120                                None
121                            }
122                        });
123
124                    let new_name = ctx
125                        .options()
126                        .last_callback(|callbacks| {
127                            callbacks.enum_variant_name(type_name, &name, val)
128                        })
129                        .or_else(|| {
130                            annotations
131                                .as_ref()?
132                                .use_instead_of()?
133                                .last()
134                                .cloned()
135                        })
136                        .unwrap_or_else(|| name.clone());
137
138                    let comment = cursor.raw_comment();
139                    variants.push(EnumVariant::new(
140                        new_name,
141                        name,
142                        comment,
143                        val,
144                        custom_behavior,
145                    ));
146                }
147            }
148            CXChildVisit_Continue
149        });
150        Ok(Enum::new(repr, variants))
151    }
152
153    fn is_matching_enum(
154        &self,
155        ctx: &BindgenContext,
156        enums: &RegexSet,
157        item: &Item,
158    ) -> bool {
159        let path = item.path_for_allowlisting(ctx);
160        let enum_ty = item.expect_type();
161
162        if enums.matches(path[1..].join("::")) {
163            return true;
164        }
165
166        // Test the variants if the enum is anonymous.
167        if enum_ty.name().is_some() {
168            return false;
169        }
170
171        self.variants().iter().any(|v| enums.matches(v.name()))
172    }
173
174    /// Returns the final representation of the enum.
175    pub(crate) fn computed_enum_variation(
176        &self,
177        ctx: &BindgenContext,
178        item: &Item,
179    ) -> EnumVariation {
180        // ModuleConsts has higher precedence before Rust in order to avoid
181        // problems with overlapping match patterns.
182        if self.is_matching_enum(
183            ctx,
184            &ctx.options().constified_enum_modules,
185            item,
186        ) {
187            EnumVariation::ModuleConsts
188        } else if self.is_matching_enum(
189            ctx,
190            &ctx.options().bitfield_enums,
191            item,
192        ) {
193            EnumVariation::NewType {
194                is_bitfield: true,
195                is_global: false,
196            }
197        } else if self.is_matching_enum(ctx, &ctx.options().newtype_enums, item)
198        {
199            EnumVariation::NewType {
200                is_bitfield: false,
201                is_global: false,
202            }
203        } else if self.is_matching_enum(
204            ctx,
205            &ctx.options().newtype_global_enums,
206            item,
207        ) {
208            EnumVariation::NewType {
209                is_bitfield: false,
210                is_global: true,
211            }
212        } else if self.is_matching_enum(
213            ctx,
214            &ctx.options().rustified_enums,
215            item,
216        ) {
217            EnumVariation::Rust {
218                non_exhaustive: false,
219            }
220        } else if self.is_matching_enum(
221            ctx,
222            &ctx.options().rustified_non_exhaustive_enums,
223            item,
224        ) {
225            EnumVariation::Rust {
226                non_exhaustive: true,
227            }
228        } else if self.is_matching_enum(
229            ctx,
230            &ctx.options().constified_enums,
231            item,
232        ) {
233            EnumVariation::Consts
234        } else {
235            ctx.options().default_enum_style
236        }
237    }
238}
239
240/// A single enum variant, to be contained only in an enum.
241#[derive(Debug)]
242pub(crate) struct EnumVariant {
243    /// The name of the variant.
244    name: String,
245
246    /// The original name of the variant (without user mangling)
247    name_for_allowlisting: String,
248
249    /// An optional doc comment.
250    comment: Option<String>,
251
252    /// The integer value of the variant.
253    val: EnumVariantValue,
254
255    /// The custom behavior this variant may have, if any.
256    custom_behavior: Option<EnumVariantCustomBehavior>,
257}
258
259/// A constant value assigned to an enumeration variant.
260#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
261pub enum EnumVariantValue {
262    /// A boolean constant.
263    Boolean(bool),
264
265    /// A signed constant.
266    Signed(i64),
267
268    /// An unsigned constant.
269    Unsigned(u64),
270}
271
272impl EnumVariant {
273    /// Construct a new enumeration variant from the given parts.
274    pub(crate) fn new(
275        name: String,
276        name_for_allowlisting: String,
277        comment: Option<String>,
278        val: EnumVariantValue,
279        custom_behavior: Option<EnumVariantCustomBehavior>,
280    ) -> Self {
281        EnumVariant {
282            name,
283            name_for_allowlisting,
284            comment,
285            val,
286            custom_behavior,
287        }
288    }
289
290    /// Get this variant's name.
291    pub(crate) fn name(&self) -> &str {
292        &self.name
293    }
294
295    /// Get this variant's name.
296    pub(crate) fn name_for_allowlisting(&self) -> &str {
297        &self.name_for_allowlisting
298    }
299
300    /// Get this variant's value.
301    pub(crate) fn val(&self) -> EnumVariantValue {
302        self.val
303    }
304
305    /// Get this variant's documentation.
306    pub(crate) fn comment(&self) -> Option<&str> {
307        self.comment.as_deref()
308    }
309
310    /// Returns whether this variant should be enforced to be a constant by code
311    /// generation.
312    pub(crate) fn force_constification(&self) -> bool {
313        self.custom_behavior == Some(EnumVariantCustomBehavior::Constify)
314    }
315
316    /// Returns whether the current variant should be hidden completely from the
317    /// resulting rust enum.
318    pub(crate) fn hidden(&self) -> bool {
319        self.custom_behavior == Some(EnumVariantCustomBehavior::Hide)
320    }
321}