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