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