//! Intermediate representation for C/C++ enumerations. use super::super::codegen::EnumVariation; use super::context::{BindgenContext, TypeId}; use super::item::Item; use super::ty::{Type, TypeKind}; use crate::clang; use crate::ir::annotations::Annotations; use crate::parse::ParseError; use crate::regex_set::RegexSet; /// An enum representing custom handling that can be given to a variant. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum EnumVariantCustomBehavior { /// This variant will be a module containing constants. ModuleConstify, /// This variant will be constified, that is, forced to generate a constant. Constify, /// This variant will be hidden entirely from the resulting enum. Hide, } /// A C/C++ enumeration. #[derive(Debug)] pub struct Enum { /// The representation used for this enum; it should be an `IntKind` type or /// an alias to one. /// /// It's `None` if the enum is a forward declaration and isn't defined /// anywhere else, see `tests/headers/func_ptr_in_struct.h`. repr: Option, /// The different variants, with explicit values. variants: Vec, } impl Enum { /// Construct a new `Enum` with the given representation and variants. pub fn new(repr: Option, variants: Vec) -> Self { Enum { repr, variants } } /// Get this enumeration's representation. pub fn repr(&self) -> Option { self.repr } /// Get this enumeration's variants. pub fn variants(&self) -> &[EnumVariant] { &self.variants } /// Construct an enumeration from the given Clang type. pub fn from_ty( ty: &clang::Type, ctx: &mut BindgenContext, ) -> Result { use clang_sys::*; debug!("Enum::from_ty {:?}", ty); if ty.kind() != CXType_Enum { return Err(ParseError::Continue); } let declaration = ty.declaration().canonical(); let repr = declaration .enum_type() .and_then(|et| Item::from_ty(&et, declaration, None, ctx).ok()); let mut variants = vec![]; let variant_ty = repr.and_then(|r| ctx.resolve_type(r).safe_canonical_type(ctx)); let is_bool = variant_ty.map_or(false, Type::is_bool); // Assume signedness since the default type by the C standard is an int. let is_signed = variant_ty.map_or(true, |ty| match *ty.kind() { TypeKind::Int(ref int_kind) => int_kind.is_signed(), ref other => { panic!("Since when enums can be non-integers? {:?}", other) } }); let type_name = ty.spelling(); let type_name = if type_name.is_empty() { None } else { Some(type_name) }; let type_name = type_name.as_deref(); let definition = declaration.definition().unwrap_or(declaration); definition.visit(|cursor| { if cursor.kind() == CXCursor_EnumConstantDecl { let value = if is_bool { cursor.enum_val_boolean().map(EnumVariantValue::Boolean) } else if is_signed { cursor.enum_val_signed().map(EnumVariantValue::Signed) } else { cursor.enum_val_unsigned().map(EnumVariantValue::Unsigned) }; if let Some(val) = value { let name = cursor.spelling(); let annotations = Annotations::new(&cursor); let custom_behavior = ctx .options() .last_callback(|callbacks| { callbacks .enum_variant_behavior(type_name, &name, val) }) .or_else(|| { let annotations = annotations.as_ref()?; if annotations.hide() { Some(EnumVariantCustomBehavior::Hide) } else if annotations.constify_enum_variant() { Some(EnumVariantCustomBehavior::Constify) } else { None } }); let new_name = ctx .options() .last_callback(|callbacks| { callbacks.enum_variant_name(type_name, &name, val) }) .or_else(|| { annotations .as_ref()? .use_instead_of()? .last() .cloned() }) .unwrap_or_else(|| name.clone()); let comment = cursor.raw_comment(); variants.push(EnumVariant::new( new_name, name, comment, val, custom_behavior, )); } } CXChildVisit_Continue }); Ok(Enum::new(repr, variants)) } fn is_matching_enum( &self, ctx: &BindgenContext, enums: &RegexSet, item: &Item, ) -> bool { let path = item.path_for_allowlisting(ctx); let enum_ty = item.expect_type(); if enums.matches(path[1..].join("::")) { return true; } // Test the variants if the enum is anonymous. if enum_ty.name().is_some() { return false; } self.variants().iter().any(|v| enums.matches(v.name())) } /// Returns the final representation of the enum. pub fn computed_enum_variation( &self, ctx: &BindgenContext, item: &Item, ) -> EnumVariation { // ModuleConsts has higher precedence before Rust in order to avoid // problems with overlapping match patterns. if self.is_matching_enum( ctx, &ctx.options().constified_enum_modules, item, ) { EnumVariation::ModuleConsts } else if self.is_matching_enum( ctx, &ctx.options().bitfield_enums, item, ) { EnumVariation::NewType { is_bitfield: true, is_global: false, } } else if self.is_matching_enum(ctx, &ctx.options().newtype_enums, item) { EnumVariation::NewType { is_bitfield: false, is_global: false, } } else if self.is_matching_enum( ctx, &ctx.options().newtype_global_enums, item, ) { EnumVariation::NewType { is_bitfield: false, is_global: true, } } else if self.is_matching_enum( ctx, &ctx.options().rustified_enums, item, ) { EnumVariation::Rust { non_exhaustive: false, } } else if self.is_matching_enum( ctx, &ctx.options().rustified_non_exhaustive_enums, item, ) { EnumVariation::Rust { non_exhaustive: true, } } else if self.is_matching_enum( ctx, &ctx.options().constified_enums, item, ) { EnumVariation::Consts } else { ctx.options().default_enum_style } } } /// A single enum variant, to be contained only in an enum. #[derive(Debug)] pub struct EnumVariant { /// The name of the variant. name: String, /// The original name of the variant (without user mangling) name_for_allowlisting: String, /// An optional doc comment. comment: Option, /// The integer value of the variant. val: EnumVariantValue, /// The custom behavior this variant may have, if any. custom_behavior: Option, } /// A constant value assigned to an enumeration variant. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum EnumVariantValue { /// A boolean constant. Boolean(bool), /// A signed constant. Signed(i64), /// An unsigned constant. Unsigned(u64), } impl EnumVariant { /// Construct a new enumeration variant from the given parts. pub fn new( name: String, name_for_allowlisting: String, comment: Option, val: EnumVariantValue, custom_behavior: Option, ) -> Self { EnumVariant { name, name_for_allowlisting, comment, val, custom_behavior, } } /// Get this variant's name. pub fn name(&self) -> &str { &self.name } /// Get this variant's name. pub fn name_for_allowlisting(&self) -> &str { &self.name_for_allowlisting } /// Get this variant's value. pub fn val(&self) -> EnumVariantValue { self.val } /// Get this variant's documentation. pub fn comment(&self) -> Option<&str> { self.comment.as_deref() } /// Returns whether this variant should be enforced to be a constant by code /// generation. pub fn force_constification(&self) -> bool { self.custom_behavior .map_or(false, |b| b == EnumVariantCustomBehavior::Constify) } /// Returns whether the current variant should be hidden completely from the /// resulting rust enum. pub fn hidden(&self) -> bool { self.custom_behavior .map_or(false, |b| b == EnumVariantCustomBehavior::Hide) } }