diff options
Diffstat (limited to 'third_party/rust/bindgen/ir/enum_ty.rs')
-rw-r--r-- | third_party/rust/bindgen/ir/enum_ty.rs | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/third_party/rust/bindgen/ir/enum_ty.rs b/third_party/rust/bindgen/ir/enum_ty.rs new file mode 100644 index 0000000000..39677e93fd --- /dev/null +++ b/third_party/rust/bindgen/ir/enum_ty.rs @@ -0,0 +1,320 @@ +//! 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::{ClangItemParser, 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<TypeId>, + + /// The different variants, with explicit values. + variants: Vec<EnumVariant>, +} + +impl Enum { + /// Construct a new `Enum` with the given representation and variants. + pub fn new(repr: Option<TypeId>, variants: Vec<EnumVariant>) -> Self { + Enum { repr, variants } + } + + /// Get this enumeration's representation. + pub fn repr(&self) -> Option<TypeId> { + 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<Self, ParseError> { + 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<String>, + + /// The integer value of the variant. + val: EnumVariantValue, + + /// The custom behavior this variant may have, if any. + custom_behavior: Option<EnumVariantCustomBehavior>, +} + +/// 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<String>, + val: EnumVariantValue, + custom_behavior: Option<EnumVariantCustomBehavior>, + ) -> 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) + } +} |