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