1 //! Intermediate representation for C/C++ enumerations.
2 
3 use super::super::codegen::EnumVariation;
4 use super::context::{BindgenContext, TypeId};
5 use super::item::Item;
6 use super::ty::{Type, TypeKind};
7 use crate::clang;
8 use crate::ir::annotations::Annotations;
9 use crate::parse::ParseError;
10 use 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)]
14 pub 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)]
25 pub(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 
37 impl Enum {
38     /// Construct a new `Enum` with the given representation and variants.
new( repr: Option<TypeId>, variants: Vec<EnumVariant>, ) -> Self39     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.
repr(&self) -> Option<TypeId>47     pub(crate) fn repr(&self) -> Option<TypeId> {
48         self.repr
49     }
50 
51     /// Get this enumeration's variants.
variants(&self) -> &[EnumVariant]52     pub(crate) fn variants(&self) -> &[EnumVariant] {
53         &self.variants
54     }
55 
56     /// Construct an enumeration from the given Clang type.
from_ty( ty: &clang::Type, ctx: &mut BindgenContext, ) -> Result<Self, ParseError>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.map_or(false, 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 
is_matching_enum( &self, ctx: &BindgenContext, enums: &RegexSet, item: &Item, ) -> bool153     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.
computed_enum_variation( &self, ctx: &BindgenContext, item: &Item, ) -> EnumVariation175     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)]
242 pub(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)]
261 pub 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 
272 impl EnumVariant {
273     /// Construct a new enumeration variant from the given parts.
new( name: String, name_for_allowlisting: String, comment: Option<String>, val: EnumVariantValue, custom_behavior: Option<EnumVariantCustomBehavior>, ) -> Self274     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.
name(&self) -> &str291     pub(crate) fn name(&self) -> &str {
292         &self.name
293     }
294 
295     /// Get this variant's name.
name_for_allowlisting(&self) -> &str296     pub(crate) fn name_for_allowlisting(&self) -> &str {
297         &self.name_for_allowlisting
298     }
299 
300     /// Get this variant's value.
val(&self) -> EnumVariantValue301     pub(crate) fn val(&self) -> EnumVariantValue {
302         self.val
303     }
304 
305     /// Get this variant's documentation.
comment(&self) -> Option<&str>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.
force_constification(&self) -> bool312     pub(crate) fn force_constification(&self) -> bool {
313         self.custom_behavior
314             .map_or(false, |b| b == EnumVariantCustomBehavior::Constify)
315     }
316 
317     /// Returns whether the current variant should be hidden completely from the
318     /// resulting rust enum.
hidden(&self) -> bool319     pub(crate) fn hidden(&self) -> bool {
320         self.custom_behavior
321             .map_or(false, |b| b == EnumVariantCustomBehavior::Hide)
322     }
323 }
324