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 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_deref(); 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 .options() 106 .last_callback(|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 .options() 123 .last_callback(|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 { 191 is_bitfield: true, 192 is_global: false, 193 } 194 } else if self.is_matching_enum(ctx, &ctx.options().newtype_enums, item) 195 { 196 EnumVariation::NewType { 197 is_bitfield: false, 198 is_global: false, 199 } 200 } else if self.is_matching_enum( 201 ctx, 202 &ctx.options().newtype_global_enums, 203 item, 204 ) { 205 EnumVariation::NewType { 206 is_bitfield: false, 207 is_global: true, 208 } 209 } else if self.is_matching_enum( 210 ctx, 211 &ctx.options().rustified_enums, 212 item, 213 ) { 214 EnumVariation::Rust { 215 non_exhaustive: false, 216 } 217 } else if self.is_matching_enum( 218 ctx, 219 &ctx.options().rustified_non_exhaustive_enums, 220 item, 221 ) { 222 EnumVariation::Rust { 223 non_exhaustive: true, 224 } 225 } else if self.is_matching_enum( 226 ctx, 227 &ctx.options().constified_enums, 228 item, 229 ) { 230 EnumVariation::Consts 231 } else { 232 ctx.options().default_enum_style 233 } 234 } 235 } 236 237 /// A single enum variant, to be contained only in an enum. 238 #[derive(Debug)] 239 pub struct EnumVariant { 240 /// The name of the variant. 241 name: String, 242 243 /// The original name of the variant (without user mangling) 244 name_for_allowlisting: String, 245 246 /// An optional doc comment. 247 comment: Option<String>, 248 249 /// The integer value of the variant. 250 val: EnumVariantValue, 251 252 /// The custom behavior this variant may have, if any. 253 custom_behavior: Option<EnumVariantCustomBehavior>, 254 } 255 256 /// A constant value assigned to an enumeration variant. 257 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 258 pub enum EnumVariantValue { 259 /// A boolean constant. 260 Boolean(bool), 261 262 /// A signed constant. 263 Signed(i64), 264 265 /// An unsigned constant. 266 Unsigned(u64), 267 } 268 269 impl EnumVariant { 270 /// 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>, ) -> Self271 pub fn new( 272 name: String, 273 name_for_allowlisting: String, 274 comment: Option<String>, 275 val: EnumVariantValue, 276 custom_behavior: Option<EnumVariantCustomBehavior>, 277 ) -> Self { 278 EnumVariant { 279 name, 280 name_for_allowlisting, 281 comment, 282 val, 283 custom_behavior, 284 } 285 } 286 287 /// Get this variant's name. name(&self) -> &str288 pub fn name(&self) -> &str { 289 &self.name 290 } 291 292 /// Get this variant's name. name_for_allowlisting(&self) -> &str293 pub fn name_for_allowlisting(&self) -> &str { 294 &self.name_for_allowlisting 295 } 296 297 /// Get this variant's value. val(&self) -> EnumVariantValue298 pub fn val(&self) -> EnumVariantValue { 299 self.val 300 } 301 302 /// Get this variant's documentation. comment(&self) -> Option<&str>303 pub fn comment(&self) -> Option<&str> { 304 self.comment.as_deref() 305 } 306 307 /// Returns whether this variant should be enforced to be a constant by code 308 /// generation. force_constification(&self) -> bool309 pub fn force_constification(&self) -> bool { 310 self.custom_behavior 311 .map_or(false, |b| b == EnumVariantCustomBehavior::Constify) 312 } 313 314 /// Returns whether the current variant should be hidden completely from the 315 /// resulting rust enum. hidden(&self) -> bool316 pub fn hidden(&self) -> bool { 317 self.custom_behavior 318 .map_or(false, |b| b == EnumVariantCustomBehavior::Hide) 319 } 320 } 321