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.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)]
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
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