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. new( repr: Option<TypeId>, variants: Vec<EnumVariant>, ) -> Self39 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. repr(&self) -> Option<TypeId>47 pub(crate) fn repr(&self) -> Option<TypeId> { 48 self.repr 49 } 50 51 /// Get this enumeration's variants. variants(&self) -> &[EnumVariant]52 pub(crate) fn variants(&self) -> &[EnumVariant] { 53 &self.variants 54 } 55 56 /// Construct an enumeration from the given Clang type. from_ty( ty: &clang::Type, ctx: &mut BindgenContext, ) -> Result<Self, ParseError>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 is_matching_enum( &self, ctx: &BindgenContext, enums: &RegexSet, item: &Item, ) -> bool153 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. computed_enum_variation( &self, ctx: &BindgenContext, item: &Item, ) -> EnumVariation175 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. new( name: String, name_for_allowlisting: String, comment: Option<String>, val: EnumVariantValue, custom_behavior: Option<EnumVariantCustomBehavior>, ) -> Self274 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. name(&self) -> &str291 pub(crate) fn name(&self) -> &str { 292 &self.name 293 } 294 295 /// Get this variant's name. name_for_allowlisting(&self) -> &str296 pub(crate) fn name_for_allowlisting(&self) -> &str { 297 &self.name_for_allowlisting 298 } 299 300 /// Get this variant's value. val(&self) -> EnumVariantValue301 pub(crate) fn val(&self) -> EnumVariantValue { 302 self.val 303 } 304 305 /// Get this variant's documentation. comment(&self) -> Option<&str>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. force_constification(&self) -> bool312 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. hidden(&self) -> bool319 pub(crate) fn hidden(&self) -> bool { 320 self.custom_behavior 321 .map_or(false, |b| b == EnumVariantCustomBehavior::Hide) 322 } 323 } 324