blob: 0a85577eeaf74e332c25aba282cd97bbaa1ee782 [file] [log] [blame]
//! Intermediate representation for C/C++ enumerations.
use super::context::{BindgenContext, ItemId};
use super::item::Item;
use super::ty::TypeKind;
use clang;
use ir::annotations::Annotations;
use ir::int::IntKind;
use ir::layout::Layout;
use parse::{ClangItemParser, ParseError};
/// 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 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<ItemId>,
/// 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<ItemId>, variants: Vec<EnumVariant>) -> Self {
Enum {
repr: repr,
variants: variants,
}
}
/// Get this enumeration's representation.
pub fn repr(&self) -> Option<ItemId> {
self.repr
}
/// Get this enumeration's variants.
pub fn variants(&self) -> &[EnumVariant] {
&self.variants
}
/// Compute the layout of this type.
pub fn calc_layout(&self, ctx: &BindgenContext) -> Option<Layout> {
self.repr
.map(|repr| ctx.resolve_type(repr))
.and_then(|repr| match *repr.canonical_type(ctx).kind() {
TypeKind::Int(int_kind) => Some(int_kind),
_ => None,
})
.unwrap_or(IntKind::Int)
.known_size()
.map(|size| Layout::new(size, size))
}
/// Construct an enumeration from the given Clang type.
pub fn from_ty(ty: &clang::Type,
ctx: &mut BindgenContext)
-> Result<Self, ParseError> {
use clang_sys::*;
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, None, None, ctx).ok());
let mut variants = vec![];
// Assume signedness since the default type by the C standard is an int.
let is_signed =
repr.and_then(|r| ctx.resolve_type(r).safe_canonical_type(ctx))
.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_ref().map(String::as_str);
declaration.visit(|cursor| {
if cursor.kind() == CXCursor_EnumConstantDecl {
let value = 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 custom_behavior = ctx.type_chooser()
.and_then(|t| {
t.enum_variant_behavior(type_name, &name, val)
})
.or_else(|| {
Annotations::new(&cursor)
.and_then(|anno| if anno.hide() {
Some(EnumVariantCustomBehavior::Hide)
} else if
anno.constify_enum_variant() {
Some(EnumVariantCustomBehavior::Constify)
} else {
None
})
});
let comment = cursor.raw_comment();
variants.push(EnumVariant::new(name,
comment,
val,
custom_behavior));
}
}
CXChildVisit_Continue
});
Ok(Enum::new(repr, variants))
}
}
/// A single enum variant, to be contained only in an enum.
#[derive(Debug)]
pub struct EnumVariant {
/// The name of the variant.
name: 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 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,
comment: Option<String>,
val: EnumVariantValue,
custom_behavior: Option<EnumVariantCustomBehavior>)
-> Self {
EnumVariant {
name: name,
comment: comment,
val: val,
custom_behavior: custom_behavior,
}
}
/// Get this variant's name.
pub fn name(&self) -> &str {
&self.name
}
/// Get this variant's value.
pub fn val(&self) -> EnumVariantValue {
self.val
}
/// 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)
}
}