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. |
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.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 | |
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)] |
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. |
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 |
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. |
319 | pub(crate) fn hidden(&self) -> bool { |
320 | self.custom_behavior |
321 | .map_or(false, |b| b == EnumVariantCustomBehavior::Hide) |
322 | } |
323 | } |
324 | |