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