Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide an API to constify enum variants. #393

Merged
merged 6 commits into from
Jan 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions libbindgen/src/chooser.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! A public API for more fine-grained customization of bindgen behavior.

pub use ir::int::IntKind;
pub use ir::enum_ty::{EnumVariantValue, EnumVariantCustomBehavior};
use std::fmt;

/// A trait to allow configuring different kinds of types in different
Expand All @@ -11,4 +12,15 @@ pub trait TypeChooser: fmt::Debug {
fn int_macro(&self, _name: &str, _value: i64) -> Option<IntKind> {
None
}

/// This function should return whether, given the a given enum variant
/// name, and value, returns whether this enum variant will forcibly be a
/// constant.
fn enum_variant_behavior(&self,
_enum_name: Option<&str>,
_variant_name: &str,
_variant_value: EnumVariantValue)
-> Option<EnumVariantCustomBehavior> {
None
}
}
28 changes: 22 additions & 6 deletions libbindgen/src/codegen/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
mod helpers;


use aster;

use ir::annotations::FieldAccessorKind;
Expand All @@ -21,7 +20,7 @@ use self::helpers::{BlobTyBuilder, attributes};

use std::borrow::Cow;
use std::cell::Cell;
use std::collections::HashSet;
use std::collections::{HashSet, VecDeque};
use std::collections::hash_map::{Entry, HashMap};
use std::fmt::Write;
use std::iter;
Expand Down Expand Up @@ -1667,7 +1666,22 @@ impl CodeGenerator for Enum {
Some(&name)
};

for variant in self.variants().iter() {
// NB: We defer the creation of constified variants, in case we find
// another variant with the same value (which is the common thing to
// do).
let mut constified_variants = VecDeque::new();

let mut iter = self.variants().iter().peekable();
while let Some(variant) = iter.next().or_else(|| constified_variants.pop_front()) {
if variant.hidden() {
continue;
}

if variant.force_constification() && iter.peek().is_some() {
constified_variants.push_back(variant);
continue;
}

match seen_values.entry(variant.val()) {
Entry::Occupied(ref entry) => {
if is_rust_enum {
Expand Down Expand Up @@ -1707,9 +1721,11 @@ impl CodeGenerator for Enum {

let variant_name = ctx.rust_mangle(variant.name());

// If it's an unnamed enum, we also generate a constant so
// it can be properly accessed.
if is_rust_enum && enum_ty.name().is_none() {
// If it's an unnamed enum, or constification is enforced,
// we also generate a constant so it can be properly
// accessed.
if (is_rust_enum && enum_ty.name().is_none()) ||
variant.force_constification() {
let mangled_name = if is_toplevel {
variant_name.clone()
} else {
Expand Down
28 changes: 27 additions & 1 deletion libbindgen/src/ir/annotations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@ pub enum FieldAccessorKind {
}

/// Annotations for a given item, or a field.
///
/// You can see the kind of comments that are accepted in the Doxygen
/// documentation:
///
/// http://www.stack.nl/~dimitri/doxygen/manual/docblocks.html
#[derive(Clone, PartialEq, Debug)]
pub struct Annotations {
/// Whether this item is marked as opaque. Only applies to types.
opaque: bool,
/// Whether this item should be hidden from the output. Only applies to
/// types.
/// types, or enum variants.
hide: bool,
/// Whether this type should be replaced by another. The name is a
/// namespace-aware path.
Expand All @@ -39,6 +44,20 @@ pub struct Annotations {
/// The kind of accessor this field will have. Also can be applied to
/// structs so all the fields inside share it by default.
accessor_kind: Option<FieldAccessorKind>,
/// Whether this enum variant should be constified.
///
/// This is controlled by the `constant` attribute, this way:
///
/// ```cpp
/// enum Foo {
/// Bar = 0, /**< <div rustbindgen constant></div> */
/// Baz = 0,
/// };
/// ```
///
/// In that case, bindgen will generate a constant for `Bar` instead of
/// `Baz`.
constify_enum_variant: bool,
}

fn parse_accessor(s: &str) -> FieldAccessorKind {
Expand All @@ -59,6 +78,7 @@ impl Default for Annotations {
disallow_copy: false,
private_fields: None,
accessor_kind: None,
constify_enum_variant: false,
}
}
}
Expand Down Expand Up @@ -150,6 +170,7 @@ impl Annotations {
"accessor" => {
self.accessor_kind = Some(parse_accessor(&attr.value))
}
"constant" => self.constify_enum_variant = true,
_ => {}
}
}
Expand All @@ -159,4 +180,9 @@ impl Annotations {
self.parse(&child, matched);
}
}

/// Returns whether we've parsed a "constant" attribute.
pub fn constify_enum_variant(&self) -> bool {
self.constify_enum_variant
}
}
6 changes: 6 additions & 0 deletions libbindgen/src/ir/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use BindgenOptions;
use cexpr;
use chooser::TypeChooser;
use clang::{self, Cursor};
use parse::ClangItemParser;
use std::borrow::Cow;
Expand Down Expand Up @@ -184,6 +185,11 @@ impl<'ctx> BindgenContext<'ctx> {
me
}

/// Get the user-provided type chooser by reference, if any.
pub fn type_chooser(&self) -> Option<&TypeChooser> {
self.options().type_chooser.as_ref().map(|t| &**t)
}

/// Define a new item.
///
/// This inserts it into the internal items set, and its type into the
Expand Down
54 changes: 52 additions & 2 deletions libbindgen/src/ir/enum_ty.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
//! Intermediate representation for C/C++ enumerations.

use clang;
use ir::annotations::Annotations;
use parse::{ClangItemParser, ParseError};
use super::context::{BindgenContext, ItemId};
use super::item::Item;
use super::ty::TypeKind;

/// 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 {
Expand Down Expand Up @@ -66,6 +76,10 @@ impl Enum {
}
});

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 {
Expand All @@ -75,8 +89,25 @@ impl Enum {
};
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));
variants.push(
EnumVariant::new(name, comment, val, custom_behavior));
}
}
CXChildVisit_Continue
Expand All @@ -96,6 +127,9 @@ pub struct EnumVariant {

/// 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.
Expand All @@ -112,12 +146,14 @@ impl EnumVariant {
/// Construct a new enumeration variant from the given parts.
pub fn new(name: String,
comment: Option<String>,
val: EnumVariantValue)
val: EnumVariantValue,
custom_behavior: Option<EnumVariantCustomBehavior>)
-> Self {
EnumVariant {
name: name,
comment: comment,
val: val,
custom_behavior: custom_behavior,
}
}

Expand All @@ -130,4 +166,18 @@ impl EnumVariant {
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)
}
}
4 changes: 1 addition & 3 deletions libbindgen/src/ir/var.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,7 @@ impl ClangSubItemParser for Var {
(TypeKind::Pointer(char_ty), VarType::String(val))
}
EvalResult::Int(Wrapping(value)) => {
let kind = ctx.options()
.type_chooser
.as_ref()
let kind = ctx.type_chooser()
.and_then(|c| c.int_macro(&name, value))
.unwrap_or_else(|| {
if value < 0 {
Expand Down
20 changes: 20 additions & 0 deletions libbindgen/tests/expectations/tests/constify-enum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* automatically generated by rust-bindgen */


#![allow(non_snake_case)]


pub const nsCSSPropertyID_eCSSProperty_COUNT_unexistingVariantValue:
nsCSSPropertyID =
nsCSSPropertyID::eCSSProperty_COUNT_unexistingVariantValue;
pub const nsCSSPropertyID_eCSSProperty_COUNT: nsCSSPropertyID =
nsCSSPropertyID::eCSSPropertyAlias_aa;
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum nsCSSPropertyID {
eCSSProperty_a = 0,
eCSSProperty_b = 1,
eCSSPropertyAlias_aa = 2,
eCSSPropertyAlias_bb = 3,
eCSSProperty_COUNT_unexistingVariantValue = 4,
}
13 changes: 13 additions & 0 deletions libbindgen/tests/headers/constify-enum.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

enum nsCSSPropertyID {
eCSSProperty_a,
eCSSProperty_b,

eCSSProperty_COUNT, /**< <div rustbindgen constant></div> */
eCSSProperty_COUNT_DUMMY2 = eCSSProperty_COUNT - 1, /**< <div rustbindgen hide></div> */

eCSSPropertyAlias_aa,
eCSSPropertyAlias_bb,

eCSSProperty_COUNT_unexistingVariantValue, /**< <div rustbindgen constant></div> */
};