Initial support for tagged enums
This commit is contained in:
committed by
Ryan Hunt
parent
2ccaa3e956
commit
3b61c8ead5
+151
-38
@@ -7,7 +7,8 @@ use std::io::Write;
|
||||
use syn;
|
||||
|
||||
use bindgen::config::{Config, Language};
|
||||
use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, Item, ItemContainer, Repr};
|
||||
use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, GenericParams, GenericPath, Item,
|
||||
ItemContainer, Repr, Struct, Type};
|
||||
use bindgen::rename::{IdentifierType, RenameRule};
|
||||
use bindgen::utilities::find_first_some;
|
||||
use bindgen::writer::{Source, SourceWriter};
|
||||
@@ -16,7 +17,8 @@ use bindgen::writer::{Source, SourceWriter};
|
||||
pub struct Enum {
|
||||
pub name: String,
|
||||
pub repr: Repr,
|
||||
pub values: Vec<(String, u64, Documentation)>,
|
||||
pub values: Vec<(String, u64, Option<Struct>, Documentation)>,
|
||||
pub tag: Option<String>,
|
||||
pub cfg: Option<Cfg>,
|
||||
pub annotations: AnnotationSet,
|
||||
pub documentation: Documentation,
|
||||
@@ -40,38 +42,77 @@ impl Enum {
|
||||
|
||||
let mut values = Vec::new();
|
||||
let mut current = 0;
|
||||
let mut is_tagged = false;
|
||||
|
||||
for variant in variants {
|
||||
match variant.data {
|
||||
syn::VariantData::Unit => {
|
||||
match variant.discriminant {
|
||||
Some(syn::ConstExpr::Lit(syn::Lit::Int(i, _))) => {
|
||||
current = i;
|
||||
}
|
||||
Some(_) => {
|
||||
return Err("Unsupported discriminant.".to_owned());
|
||||
}
|
||||
None => { /* okay, we just use current */ }
|
||||
}
|
||||
match variant.discriminant {
|
||||
Some(syn::ConstExpr::Lit(syn::Lit::Int(i, _))) => {
|
||||
current = i;
|
||||
}
|
||||
Some(_) => {
|
||||
return Err("Unsupported discriminant.".to_owned());
|
||||
}
|
||||
None => { /* okay, we just use current */ }
|
||||
};
|
||||
let body = match variant.data {
|
||||
syn::VariantData::Unit => None,
|
||||
syn::VariantData::Struct(ref fields) | syn::VariantData::Tuple(ref fields) => {
|
||||
is_tagged = true;
|
||||
Some(Struct {
|
||||
name: format!("{}_Body", variant.ident),
|
||||
generic_params: GenericParams::default(),
|
||||
fields: {
|
||||
let mut res = vec![
|
||||
(
|
||||
"tag".to_string(),
|
||||
Type::Path(GenericPath {
|
||||
name: "Tag".to_string(),
|
||||
generics: vec![],
|
||||
}),
|
||||
Documentation::none(),
|
||||
),
|
||||
];
|
||||
|
||||
values.push((
|
||||
variant.ident.to_string(),
|
||||
current,
|
||||
Documentation::load(&variant.attrs),
|
||||
));
|
||||
current = current + 1;
|
||||
for (i, field) in fields.iter().enumerate() {
|
||||
if let Some(ty) = Type::load(&field.ty)? {
|
||||
res.push((
|
||||
match field.ident {
|
||||
Some(ref ident) => ident.to_string(),
|
||||
None => i.to_string(),
|
||||
},
|
||||
ty,
|
||||
Documentation::load(&field.attrs),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
},
|
||||
is_variant: true,
|
||||
tuple_struct: match variant.data {
|
||||
syn::VariantData::Tuple(_) => true,
|
||||
_ => false,
|
||||
},
|
||||
cfg: Cfg::append(mod_cfg, Cfg::load(attrs)),
|
||||
annotations: AnnotationSet::load(attrs)?,
|
||||
documentation: Documentation::none(),
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
return Err("Unsupported variant.".to_owned());
|
||||
}
|
||||
}
|
||||
};
|
||||
values.push((
|
||||
variant.ident.to_string(),
|
||||
current,
|
||||
body,
|
||||
Documentation::load(&variant.attrs),
|
||||
));
|
||||
current = current + 1;
|
||||
}
|
||||
|
||||
let annotations = AnnotationSet::load(attrs)?;
|
||||
|
||||
if let Some(variants) = annotations.list("enum-trailing-values") {
|
||||
for variant in variants {
|
||||
values.push((variant, current, Documentation::none()));
|
||||
values.push((variant, current, None, Documentation::none()));
|
||||
current = current + 1;
|
||||
}
|
||||
}
|
||||
@@ -80,6 +121,11 @@ impl Enum {
|
||||
name: name,
|
||||
repr: repr,
|
||||
values: values,
|
||||
tag: if is_tagged {
|
||||
Some("Tag".to_string())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
cfg: Cfg::append(mod_cfg, Cfg::load(attrs)),
|
||||
annotations: annotations,
|
||||
documentation: Documentation::load(attrs),
|
||||
@@ -109,16 +155,34 @@ impl Item for Enum {
|
||||
}
|
||||
|
||||
fn rename_for_config(&mut self, config: &Config) {
|
||||
config.export.rename(&mut self.name);
|
||||
if config.language == Language::C && self.tag.is_some() {
|
||||
// it makes sense to always prefix Tag with type name in C
|
||||
let new_tag = format!("{}_Tag", self.name);
|
||||
for value in &mut self.values {
|
||||
if let Some(ref mut body) = value.2 {
|
||||
body.fields[0].1 = Type::Path(GenericPath {
|
||||
name: new_tag.clone(),
|
||||
generics: vec![],
|
||||
});
|
||||
}
|
||||
}
|
||||
self.tag = Some(new_tag);
|
||||
}
|
||||
|
||||
if config.language == Language::C
|
||||
&& (config.enumeration.prefix_with_name
|
||||
|| self.annotations.bool("prefix-with-name").unwrap_or(false))
|
||||
for value in &mut self.values {
|
||||
if let Some(ref mut body) = value.2 {
|
||||
body.rename_for_config(config);
|
||||
}
|
||||
}
|
||||
|
||||
if config.enumeration.prefix_with_name
|
||||
|| self.annotations.bool("prefix-with-name").unwrap_or(false)
|
||||
{
|
||||
let old = ::std::mem::replace(&mut self.values, Vec::new());
|
||||
for (name, value, doc) in old {
|
||||
self.values
|
||||
.push((format!("{}_{}", self.name, name), value, doc));
|
||||
for value in &mut self.values {
|
||||
value.0 = format!("{}_{}", self.name, value.0);
|
||||
if let Some(ref mut body) = value.2 {
|
||||
body.name = format!("{}_{}", self.name, body.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,6 +199,7 @@ impl Item for Enum {
|
||||
r.apply_to_pascal_case(&x.0, IdentifierType::EnumVariant(self)),
|
||||
x.1.clone(),
|
||||
x.2.clone(),
|
||||
x.3.clone(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
@@ -148,6 +213,19 @@ impl Source for Enum {
|
||||
|
||||
self.documentation.write(config, out);
|
||||
|
||||
let is_tagged = self.tag.is_some();
|
||||
|
||||
if is_tagged && config.language == Language::Cxx {
|
||||
write!(out, "union {}", self.name);
|
||||
out.open_brace();
|
||||
}
|
||||
|
||||
let enum_name = if let Some(ref tag) = self.tag {
|
||||
tag
|
||||
} else {
|
||||
&self.name
|
||||
};
|
||||
|
||||
let size = match self.repr {
|
||||
Repr::C => None,
|
||||
Repr::USize => Some("uintptr_t"),
|
||||
@@ -165,13 +243,13 @@ impl Source for Enum {
|
||||
if size.is_none() {
|
||||
out.write("typedef enum");
|
||||
} else {
|
||||
write!(out, "enum {}", self.name);
|
||||
write!(out, "enum {}", enum_name);
|
||||
}
|
||||
} else {
|
||||
if let Some(prim) = size {
|
||||
write!(out, "enum class {} : {}", self.name, prim);
|
||||
write!(out, "enum class {} : {}", enum_name, prim);
|
||||
} else {
|
||||
write!(out, "enum class {}", self.name);
|
||||
write!(out, "enum class {}", enum_name);
|
||||
}
|
||||
}
|
||||
out.open_brace();
|
||||
@@ -179,7 +257,7 @@ impl Source for Enum {
|
||||
if i != 0 {
|
||||
out.new_line()
|
||||
}
|
||||
value.2.write(config, out);
|
||||
value.3.write(config, out);
|
||||
write!(out, "{} = {},", value.0, value.1);
|
||||
}
|
||||
if config.enumeration.add_sentinel(&self.annotations) {
|
||||
@@ -190,7 +268,7 @@ impl Source for Enum {
|
||||
|
||||
if config.language == Language::C && size.is_none() {
|
||||
out.close_brace(false);
|
||||
write!(out, " {};", self.name);
|
||||
write!(out, " {};", enum_name);
|
||||
} else {
|
||||
out.close_brace(true);
|
||||
}
|
||||
@@ -198,7 +276,42 @@ impl Source for Enum {
|
||||
if config.language == Language::C {
|
||||
if let Some(prim) = size {
|
||||
out.new_line();
|
||||
write!(out, "typedef {} {};", prim, self.name);
|
||||
write!(out, "typedef {} {};", prim, enum_name);
|
||||
}
|
||||
}
|
||||
|
||||
if is_tagged {
|
||||
for value in &self.values {
|
||||
if let Some(ref body) = value.2 {
|
||||
out.new_line();
|
||||
out.new_line();
|
||||
|
||||
body.write(config, out);
|
||||
}
|
||||
}
|
||||
|
||||
out.new_line();
|
||||
out.new_line();
|
||||
|
||||
if config.language == Language::C {
|
||||
out.write("typedef union");
|
||||
out.open_brace();
|
||||
}
|
||||
|
||||
write!(out, "{} tag;", enum_name);
|
||||
|
||||
for value in &self.values {
|
||||
if let Some(ref body) = value.2 {
|
||||
out.new_line();
|
||||
write!(out, "{} {};", body.name, value.0);
|
||||
}
|
||||
}
|
||||
|
||||
if config.language == Language::C {
|
||||
out.close_brace(false);
|
||||
write!(out, " {};", self.name);
|
||||
} else {
|
||||
out.close_brace(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-22
@@ -22,6 +22,7 @@ pub struct Struct {
|
||||
pub name: String,
|
||||
pub generic_params: GenericParams,
|
||||
pub fields: Vec<(String, Type, Documentation)>,
|
||||
pub is_variant: bool,
|
||||
pub tuple_struct: bool,
|
||||
pub cfg: Option<Cfg>,
|
||||
pub annotations: AnnotationSet,
|
||||
@@ -63,6 +64,7 @@ impl Struct {
|
||||
name: name,
|
||||
generic_params: GenericParams::new(generics),
|
||||
fields: fields,
|
||||
is_variant: false,
|
||||
tuple_struct: tuple_struct,
|
||||
cfg: Cfg::append(mod_cfg, Cfg::load(attrs)),
|
||||
annotations: AnnotationSet::load(attrs)?,
|
||||
@@ -131,33 +133,25 @@ impl Item for Struct {
|
||||
config.structure.rename_fields,
|
||||
];
|
||||
|
||||
if let Some(o) = self.annotations.list("field-names") {
|
||||
let mut overriden_fields = Vec::new();
|
||||
let mut names = self.fields.iter_mut().map(|field| &mut field.0);
|
||||
|
||||
for (i, &(ref name, ref ty, ref doc)) in self.fields.iter().enumerate() {
|
||||
if i >= o.len() {
|
||||
overriden_fields.push((name.clone(), ty.clone(), doc.clone()));
|
||||
} else {
|
||||
overriden_fields.push((o[i].clone(), ty.clone(), doc.clone()));
|
||||
}
|
||||
if let Some(o) = self.annotations.list("field-names") {
|
||||
for (dest, src) in names.zip(o) {
|
||||
*dest = src;
|
||||
}
|
||||
} else if let Some(r) = find_first_some(&field_rules) {
|
||||
for name in names {
|
||||
*name = r.apply_to_snake_case(name, IdentifierType::StructMember);
|
||||
}
|
||||
} else if self.tuple_struct {
|
||||
// If there is a tag field, skip it
|
||||
if self.is_variant {
|
||||
names.next();
|
||||
}
|
||||
|
||||
self.fields = overriden_fields;
|
||||
} else if let Some(r) = find_first_some(&field_rules) {
|
||||
self.fields = self.fields
|
||||
.iter()
|
||||
.map(|x| {
|
||||
(
|
||||
r.apply_to_snake_case(&x.0, IdentifierType::StructMember),
|
||||
x.1.clone(),
|
||||
x.2.clone(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
} else if self.tuple_struct {
|
||||
// If we don't have any rules for a tuple struct, prefix them with
|
||||
// an underscore so it still compiles
|
||||
for &mut (ref mut name, ..) in &mut self.fields {
|
||||
for name in names {
|
||||
name.insert(0, '_');
|
||||
}
|
||||
}
|
||||
@@ -189,6 +183,7 @@ impl Item for Struct {
|
||||
.iter()
|
||||
.map(|x| (x.0.clone(), x.1.specialize(&mappings), x.2.clone()))
|
||||
.collect(),
|
||||
is_variant: self.is_variant,
|
||||
tuple_struct: self.tuple_struct,
|
||||
cfg: self.cfg.clone(),
|
||||
annotations: self.annotations.clone(),
|
||||
|
||||
@@ -42,6 +42,30 @@ enum E {
|
||||
};
|
||||
typedef intptr_t E;
|
||||
|
||||
enum F_Tag {
|
||||
Foo = 0,
|
||||
Bar = 1,
|
||||
Baz = 2,
|
||||
};
|
||||
typedef uint8_t F_Tag;
|
||||
|
||||
typedef struct {
|
||||
F_Tag tag;
|
||||
int16_t _0;
|
||||
} Foo_Body;
|
||||
|
||||
typedef struct {
|
||||
F_Tag tag;
|
||||
uint8_t x;
|
||||
int16_t y;
|
||||
} Bar_Body;
|
||||
|
||||
typedef union {
|
||||
F_Tag tag;
|
||||
Foo_Body Foo;
|
||||
Bar_Body Bar;
|
||||
} F;
|
||||
|
||||
typedef struct Opaque Opaque;
|
||||
|
||||
void root(Opaque *o, A a, B b, C c, D d, E e);
|
||||
void root(Opaque *o, A a, B b, C c, D d, E e, F f);
|
||||
|
||||
@@ -36,10 +36,33 @@ enum class E : intptr_t {
|
||||
e4 = 5,
|
||||
};
|
||||
|
||||
union F {
|
||||
enum class Tag : uint8_t {
|
||||
Foo = 0,
|
||||
Bar = 1,
|
||||
Baz = 2,
|
||||
};
|
||||
|
||||
struct Foo_Body {
|
||||
Tag tag;
|
||||
int16_t _0;
|
||||
};
|
||||
|
||||
struct Bar_Body {
|
||||
Tag tag;
|
||||
uint8_t x;
|
||||
int16_t y;
|
||||
};
|
||||
|
||||
Tag tag;
|
||||
Foo_Body Foo;
|
||||
Bar_Body Bar;
|
||||
};
|
||||
|
||||
struct Opaque;
|
||||
|
||||
extern "C" {
|
||||
|
||||
void root(Opaque *o, A a, B b, C c, D d, E e);
|
||||
void root(Opaque *o, A a, B b, C c, D d, E e, F f);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
+9
-1
@@ -43,6 +43,13 @@ enum E {
|
||||
e4 = 5,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
enum F {
|
||||
Foo(i16),
|
||||
Bar { x: u8, y: i16 },
|
||||
Baz
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn root(
|
||||
o: *mut Opaque,
|
||||
@@ -50,5 +57,6 @@ pub extern "C" fn root(
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
e: E
|
||||
e: E,
|
||||
f: F
|
||||
) { }
|
||||
|
||||
Reference in New Issue
Block a user