summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/gen.rs333
-rw-r--r--src/parser.rs137
-rw-r--r--src/types.rs23
-rw-r--r--tests/headers/struct_with_anon_struct.h6
-rw-r--r--tests/headers/struct_with_anon_union.h6
-rw-r--r--tests/headers/struct_with_anon_unnamed_struct.h6
-rw-r--r--tests/headers/struct_with_anon_unnamed_union.h6
-rw-r--r--tests/headers/struct_with_nesting.h17
-rw-r--r--tests/headers/struct_with_struct.h6
-rw-r--r--tests/headers/union_with_anon_struct.h6
-rw-r--r--tests/headers/union_with_anon_union.h6
-rw-r--r--tests/headers/union_with_anon_unnamed_struct.h9
-rw-r--r--tests/headers/union_with_anon_unnamed_union.h7
-rw-r--r--tests/headers/union_with_nesting.h14
-rw-r--r--tests/struct.rs75
-rw-r--r--tests/union.rs84
16 files changed, 555 insertions, 186 deletions
diff --git a/src/gen.rs b/src/gen.rs
index cf1becc8..701ae707 100644
--- a/src/gen.rs
+++ b/src/gen.rs
@@ -9,7 +9,7 @@ use std::collections::hash_map::{Occupied, Vacant};
use syntax::abi;
use syntax::ast;
-use syntax::codemap::{Span, respan, ExpnInfo, NameAndSpan, MacroBang};
+use syntax::codemap::{Span, Spanned, respan, ExpnInfo, NameAndSpan, MacroBang};
use syntax::ext::base;
use syntax::ext::build::AstBuilder;
use syntax::ext::expand::ExpansionConfig;
@@ -93,15 +93,22 @@ fn unnamed_name(ctx: &mut GenCtx, name: String) -> String {
};
}
-fn struct_name(name: String) -> String {
+fn comp_name(kind: CompKind, name: &String) -> String {
+ match kind {
+ CompKind::Struct => struct_name(name),
+ CompKind::Union => union_name(name),
+ }
+}
+
+fn struct_name(name: &String) -> String {
format!("Struct_{}", name)
}
-fn union_name(name: String) -> String {
+fn union_name(name: &String) -> String {
format!("Union_{}", name)
}
-fn enum_name(name: String) -> String {
+fn enum_name(name: &String) -> String {
format!("Enum_{}", name)
}
@@ -156,11 +163,7 @@ pub fn gen_mod(links: &[(String, Option<String>)], globs: Vec<Global>, span: Spa
c.name = unnamed_name(&mut ctx, c.name.clone());
}
let c = ci.borrow().clone();
- if c.cstruct {
- defs.push(opaque_to_rs(&mut ctx, struct_name(c.name)));
- } else {
- defs.push(opaque_to_rs(&mut ctx, union_name(c.name)));
- }
+ defs.push(opaque_to_rs(&mut ctx, comp_name(c.kind, &c.name)));
},
GComp(ci) => {
{
@@ -168,16 +171,8 @@ pub fn gen_mod(links: &[(String, Option<String>)], globs: Vec<Global>, span: Spa
c.name = unnamed_name(&mut ctx, c.name.clone());
}
let c = ci.borrow().clone();
- if c.cstruct {
- defs.push(cstruct_to_rs(&mut ctx, struct_name(c.name.clone()),
- // this clone is necessary to prevent dynamic borrow
- // check errors.
- // FIXME: remove the @mut in types.rs to fix this
- c.fields.clone()))
- } else {
- defs.extend(cunion_to_rs(&mut ctx, union_name(c.name.clone()),
- c.layout, c.fields).into_iter())
- }
+ defs.extend(comp_to_rs(&mut ctx, c.kind, comp_name(c.kind, &c.name),
+ c.layout, c.members).into_iter())
},
GEnumDecl(ei) => {
{
@@ -185,7 +180,7 @@ pub fn gen_mod(links: &[(String, Option<String>)], globs: Vec<Global>, span: Spa
e.name = unnamed_name(&mut ctx, e.name.clone());
}
let e = ei.borrow().clone();
- defs.push(opaque_to_rs(&mut ctx, enum_name(e.name)));
+ defs.push(opaque_to_rs(&mut ctx, enum_name(&e.name)));
},
GEnum(ei) => {
{
@@ -193,7 +188,7 @@ pub fn gen_mod(links: &[(String, Option<String>)], globs: Vec<Global>, span: Spa
e.name = unnamed_name(&mut ctx, e.name.clone());
}
let e = ei.borrow().clone();
- defs.extend(cenum_to_rs(&mut ctx, enum_name(e.name.clone()), e.kind, e.items).into_iter())
+ defs.extend(cenum_to_rs(&mut ctx, enum_name(&e.name), e.kind, e.items).into_iter())
},
_ => { }
}
@@ -436,11 +431,7 @@ fn ctypedef_to_rs(ctx: &mut GenCtx, name: String, ty: &Type) -> Vec<P<ast::Item>
if is_empty {
ci.borrow_mut().name = name.clone();
let c = ci.borrow().clone();
- if c.cstruct {
- vec!(cstruct_to_rs(ctx, name, c.fields))
- } else {
- cunion_to_rs(ctx, name, c.layout, c.fields)
- }
+ comp_to_rs(ctx, c.kind, name, c.layout, c.members)
} else {
vec!(mk_item(ctx, name, ty))
}
@@ -459,46 +450,98 @@ fn ctypedef_to_rs(ctx: &mut GenCtx, name: String, ty: &Type) -> Vec<P<ast::Item>
}
}
-fn cstruct_to_rs(ctx: &mut GenCtx, name: String, fields: Vec<FieldInfo>) -> P<ast::Item> {
+fn comp_to_rs(ctx: &mut GenCtx, kind: CompKind, name: String,
+ layout: Layout, members: Vec<CompMember>) -> Vec<P<ast::Item>> {
+ match kind {
+ CompKind::Struct => cstruct_to_rs(ctx, name, members),
+ CompKind::Union => cunion_to_rs(ctx, name, layout, members),
+ }
+}
+
+fn cstruct_to_rs(ctx: &mut GenCtx, name: String, members: Vec<CompMember>) -> Vec<P<ast::Item>> {
let mut unnamed: uint = 0;
- let fs: Vec<ast::StructField> = fields.iter().map(|f| {
- let f_name = if f.name.is_empty() || "_" == f.name.as_slice() {
- unnamed += 1;
- format!("unnamed_field{}", unnamed)
- } else {
- rust_type_id(ctx, f.name.clone())
+
+ let mut fields = vec!();
+ let mut methods = vec!();
+ // Nested composites may need to emit declarations and implementations as
+ // they are encountered. The declarations end up in 'extra' and are emitted
+ // after the current struct.
+ let mut extra = vec!();
+
+ for m in members.iter() {
+ let (opt_rc_c, opt_f) = match m {
+ &CompMember::Field(ref f) => { (None, Some(f)) }
+ &CompMember::Comp(ref rc_c) => { (Some(rc_c), None) }
+ &CompMember::CompField(ref rc_c, ref f) => { (Some(rc_c), Some(f)) }
};
- let f_ty = P(cty_to_rs(ctx, &f.ty));
+ if let Some(f) = opt_f {
+ let f_name = rust_type_id(ctx, f.name.clone());
+ let f_ty = P(cty_to_rs(ctx, &f.ty));
- respan(ctx.span, ast::StructField_ {
- kind: ast::NamedField(
- ctx.ext_cx.ident_of(f_name.as_slice()),
- ast::Public,
- ),
- id: ast::DUMMY_NODE_ID,
- ty: f_ty,
- attrs: Vec::new()
- })
- }).collect();
+ fields.push(respan(ctx.span, ast::StructField_ {
+ kind: ast::NamedField(
+ ctx.ext_cx.ident_of(f_name.as_slice()),
+ ast::Public,
+ ),
+ id: ast::DUMMY_NODE_ID,
+ ty: f_ty,
+ attrs: Vec::new()
+ }));
+ }
- let ctor_id = if fs.is_empty() { Some(ast::DUMMY_NODE_ID) } else { None };
+ if let Some(rc_c) = opt_rc_c {
+ let c = rc_c.borrow();
+ if c.name.is_empty() {
+ unnamed += 1;
+ let field_name = format!("_bindgen_data_{}_", unnamed);
+ fields.push(mk_blob_field(ctx, field_name.as_slice(), c.layout));
+ methods.extend(gen_comp_methods(ctx, field_name.as_slice(), 0, c.kind, &c.members, &mut extra).into_iter());
+ } else {
+ extra.extend(comp_to_rs(ctx, c.kind, comp_name(c.kind, &c.name),
+ c.layout, c.members.clone()).into_iter());
+ }
+ }
+ }
+
+ let ctor_id = if fields.is_empty() { Some(ast::DUMMY_NODE_ID) } else { None };
let def = ast::ItemStruct(
P(ast::StructDef {
- fields: fs,
+ fields: fields,
ctor_id: ctor_id,
}),
empty_generics()
);
- let id = rust_type_id(ctx, name);
- return P(ast::Item { ident: ctx.ext_cx.ident_of(id.as_slice()),
+ let id = rust_type_id(ctx, name.clone());
+ let struct_def = P(ast::Item { ident: ctx.ext_cx.ident_of(id.as_slice()),
attrs: vec!(mk_repr_attr(ctx), mk_deriving_copy_attr(ctx)),
id: ast::DUMMY_NODE_ID,
node: def,
vis: ast::Public,
span: ctx.span
});
+
+ let mut items = vec!(struct_def);
+ if !methods.is_empty() {
+ let impl_ = ast::ItemImpl(
+ ast::Unsafety::Normal,
+ empty_generics(),
+ None,
+ P(mk_ty(ctx, false, vec!(id))),
+ methods
+ );
+ items.push(
+ P(ast::Item {
+ ident: ctx.ext_cx.ident_of(name.as_slice()),
+ attrs: vec!(),
+ id: ast::DUMMY_NODE_ID,
+ node: impl_,
+ vis: ast::Inherited,
+ span: ctx.span}));
+ }
+ items.extend(extra.into_iter());
+ items
}
fn opaque_to_rs(ctx: &mut GenCtx, name: String) -> P<ast::Item> {
@@ -519,7 +562,7 @@ fn opaque_to_rs(ctx: &mut GenCtx, name: String) -> P<ast::Item> {
});
}
-fn cunion_to_rs(ctx: &mut GenCtx, name: String, layout: Layout, fields: Vec<FieldInfo>) -> Vec<P<ast::Item>> {
+fn cunion_to_rs(ctx: &mut GenCtx, name: String, layout: Layout, members: Vec<CompMember>) -> Vec<P<ast::Item>> {
fn mk_item(ctx: &mut GenCtx, name: String, item: ast::Item_, vis:
ast::Visibility, attrs: Vec<ast::Attribute>) -> P<ast::Item> {
return P(ast::Item {
@@ -532,32 +575,20 @@ fn cunion_to_rs(ctx: &mut GenCtx, name: String, layout: Layout, fields: Vec<Fiel
});
}
- let ci = Rc::new(RefCell::new(CompInfo::new(name.clone(), false, fields.clone(), layout)));
+ let ci = Rc::new(RefCell::new(CompInfo::new(name.clone(), CompKind::Union, members.clone(), layout)));
let union = TNamed(Rc::new(RefCell::new(TypeInfo::new(name.clone(), TComp(ci)))));
- let ty_name = match layout.align {
- 1 => "u8",
- 2 => "u16",
- 4 => "u32",
- 8 => "u64",
- _ => "u8",
- };
- let data_len = if ty_name == "u8" { layout.size } else { layout.size / layout.align };
- let base_ty = mk_ty(ctx, false, vec!(ty_name.to_string()));
- let data_ty = P(mk_arrty(ctx, &base_ty, data_len));
- let data = respan(ctx.span, ast::StructField_ {
- kind: ast::NamedField(
- ctx.ext_cx.ident_of("data"),
- ast::Public,
- ),
- id: ast::DUMMY_NODE_ID,
- ty: data_ty,
- attrs: Vec::new()
- });
+ // Nested composites may need to emit declarations and implementations as
+ // they are encountered. The declarations end up in 'extra' and are emitted
+ // after the current union.
+ let mut extra = vec!();
+
+ let data_field_name = "_bindgen_data_";
+ let data_field = mk_blob_field(ctx, data_field_name, layout);
let def = ast::ItemStruct(
P(ast::StructDef {
- fields: Vec::from_elem(1, data),
+ fields: vec!(data_field),
ctor_id: None,
}),
empty_generics()
@@ -566,72 +597,20 @@ fn cunion_to_rs(ctx: &mut GenCtx, name: String, layout: Layout, fields: Vec<Fiel
let union_attrs = vec!(mk_repr_attr(ctx), mk_deriving_copy_attr(ctx));
let union_def = mk_item(ctx, union_id, def, ast::Public, union_attrs);
- let expr = quote_expr!(
- &ctx.ext_cx,
- unsafe { ::std::mem::transmute(self) }
- );
- let mut unnamed: uint = 0;
- let fs = fields.iter().map(|f| {
- let f_name = if f.name.is_empty() || "_" == f.name.as_slice() {
- unnamed += 1;
- format!("unnamed_field{}", unnamed)
- } else {
- first(rust_id(ctx, f.name.clone()))
- };
-
- let ret_ty = P(cty_to_rs(ctx, &TPtr(box f.ty.clone(), false, Layout::zero())));
- let body = P(ast::Block {
- view_items: Vec::new(),
- stmts: Vec::new(),
- expr: Some(expr.clone()),
- id: ast::DUMMY_NODE_ID,
- rules: ast::DefaultBlock,
- span: ctx.span
- });
-
- let decl = ast::MethDecl(
- ctx.ext_cx.ident_of(f_name.as_slice()),
- empty_generics(),
- abi::Rust,
- respan(
- ctx.span,
- ast::SelfRegion(None, ast::MutMutable, ctx.ext_cx.ident_of("self"))
- ),
- ast::Unsafety::Normal,
- P(ast::FnDecl {
- inputs: Vec::from_elem(
- 1, ast::Arg::new_self(
- ctx.span,
- ast::MutImmutable,
- ctx.ext_cx.ident_of("self")
- )),
- output: ast::Return(ret_ty),
- variadic: false
- }),
- body,
- ast::Public
- );
-
- ast::MethodImplItem(P(ast::Method {
- attrs: Vec::new(),
- id: ast::DUMMY_NODE_ID,
- span: ctx.span,
- node: decl,
- }))
- }).collect();
-
- let methods = ast::ItemImpl(
+ let union_impl = ast::ItemImpl(
ast::Unsafety::Normal,
empty_generics(),
None,
P(cty_to_rs(ctx, &union)),
- fs
+ gen_comp_methods(ctx, data_field_name, 0, CompKind::Union, &members, &mut extra),
);
- return vec!(
+ let mut items = vec!(
union_def,
- mk_item(ctx, "".to_string(), methods, ast::Inherited, Vec::new())
+ mk_item(ctx, "".to_string(), union_impl, ast::Inherited, Vec::new())
);
+ items.extend(extra.into_iter());
+ items
}
fn cenum_to_rs(ctx: &mut GenCtx, name: String, kind: IKind, items: Vec<EnumItem>) -> Vec<P<ast::Item>> {
@@ -670,6 +649,92 @@ fn cenum_to_rs(ctx: &mut GenCtx, name: String, kind: IKind, items: Vec<EnumItem>
return def;
}
+/// Generates accessors for fields in nested structs and unions which must be
+/// represented in Rust as an untyped array. This process may generate
+/// declarations and implementations that must be placed at the root level.
+/// These are emitted into `extra`.
+fn gen_comp_methods(ctx: &mut GenCtx, data_field: &str, data_offset: uint,
+ kind: CompKind, members: &Vec<CompMember>,
+ extra: &mut Vec<P<ast::Item>>) -> Vec<ast::ImplItem> {
+ let data_ident = ctx.ext_cx.ident_of(data_field);
+
+ let mk_field_method = |ctx: &mut GenCtx, f: &FieldInfo, offset: uint| {
+ let (f_name, _) = rust_id(ctx, f.name.clone());
+ let f_name_ident = ctx.ext_cx.ident_of(f_name.as_slice());
+ let ret_ty = P(cty_to_rs(ctx, &TPtr(box f.ty.clone(), false, Layout::zero())));
+
+ // When the offset is zero, generate slightly prettier code.
+ let method = if offset == 0 {
+ quote_method!(&ctx.ext_cx,
+ pub unsafe fn $f_name_ident(&mut self) -> $ret_ty {
+ ::std::mem::transmute(&self.$data_ident)
+ }
+ )
+ } else {
+ let offset_expr = &ctx.ext_cx.expr_int(ctx.span, offset.to_int().unwrap());
+ quote_method!(&ctx.ext_cx,
+ pub unsafe fn $f_name_ident(&mut self) -> $ret_ty {
+ let raw: *mut u8 = ::std::mem::transmute(&self.$data_ident);
+ ::std::mem::transmute(raw.offset($offset_expr))
+ }
+ )
+ };
+ ast::MethodImplItem(method)
+ };
+
+ let mut offset = data_offset;
+ let mut methods = vec!();
+ for m in members.iter() {
+ let advance_by = match m {
+ &CompMember::Field(ref f) => {
+ methods.push(mk_field_method(ctx, f, offset));
+ f.ty.size()
+ }
+ &CompMember::Comp(ref rc_c) => {
+ let ref c = rc_c.borrow();
+ methods.extend(gen_comp_methods(ctx, data_field, offset, c.kind,
+ &c.members, extra).into_iter());
+ c.layout.size
+ }
+ &CompMember::CompField(ref rc_c, ref f) => {
+ methods.push(mk_field_method(ctx, f, offset));
+
+ let c = rc_c.borrow();
+ extra.extend(comp_to_rs(ctx, c.kind, comp_name(c.kind, &c.name),
+ c.layout, c.members.clone()).into_iter());
+ f.ty.size()
+ }
+ };
+ match kind {
+ CompKind::Struct => { offset += advance_by; }
+ CompKind::Union => { }
+ }
+ }
+ methods
+}
+
+fn mk_blob_field(ctx: &GenCtx, name: &str, layout: Layout) -> Spanned<ast::StructField_> {
+ let ty_name = match layout.align {
+ 1 => "u8",
+ 2 => "u16",
+ 4 => "u32",
+ 8 => "u64",
+ _ => "u8",
+ };
+ let data_len = if ty_name == "u8" { layout.size } else { layout.size / layout.align };
+ let base_ty = mk_ty(ctx, false, vec!(ty_name.to_string()));
+ let data_ty = P(mk_arrty(ctx, &base_ty, data_len));
+ respan(ctx.span, ast::StructField_ {
+ kind: ast::NamedField(
+ ctx.ext_cx.ident_of(name),
+ ast::Public,
+ ),
+ id: ast::DUMMY_NODE_ID,
+ ty: data_ty,
+ attrs: Vec::new()
+ })
+}
+
fn mk_link_name_attr(ctx: &mut GenCtx, name: String) -> ast::Attribute {
let lit = respan(ctx.span, ast::LitStr(
to_intern_str(ctx, name),
@@ -859,21 +924,17 @@ fn cty_to_rs(ctx: &mut GenCtx, ty: &Type) -> ast::Ty {
&TComp(ref ci) => {
let mut c = ci.borrow_mut();
c.name = unnamed_name(ctx, c.name.clone());
- if c.cstruct {
- mk_ty(ctx, false, vec!(struct_name(c.name.clone())))
- } else {
- mk_ty(ctx, false, vec!(union_name(c.name.clone())))
- }
+ mk_ty(ctx, false, vec!(comp_name(c.kind, &c.name)))
},
&TEnum(ref ei) => {
let mut e = ei.borrow_mut();
e.name = unnamed_name(ctx, e.name.clone());
- mk_ty(ctx, false, vec!(enum_name(e.name.clone())))
+ mk_ty(ctx, false, vec!(enum_name(&e.name)))
}
};
}
-fn mk_ty(ctx: &mut GenCtx, global: bool, segments: Vec<String>) -> ast::Ty {
+fn mk_ty(ctx: &GenCtx, global: bool, segments: Vec<String>) -> ast::Ty {
let ty = ast::TyPath(
ast::Path {
span: ctx.span,
@@ -912,7 +973,7 @@ fn mk_ptrty(ctx: &mut GenCtx, base: &ast::Ty, is_const: bool) -> ast::Ty {
};
}
-fn mk_arrty(ctx: &mut GenCtx, base: &ast::Ty, n: uint) -> ast::Ty {
+fn mk_arrty(ctx: &GenCtx, base: &ast::Ty, n: uint) -> ast::Ty {
let int_lit = ast::LitInt(n as u64, ast::UnsignedIntLit(ast::TyU));
let sz = ast::ExprLit(P(respan(ctx.span, int_lit)));
let ty = ast::TyFixedLengthVec(
diff --git a/src/parser.rs b/src/parser.rs
index e26ce4f5..363e0b1a 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -72,11 +72,11 @@ fn decl_name(ctx: &mut ClangParserCtx, cursor: &Cursor) -> Global {
let glob_decl = match cursor.kind() {
CXCursor_StructDecl => {
- let ci = Rc::new(RefCell::new(CompInfo::new(spelling, true, vec!(), layout)));
+ let ci = Rc::new(RefCell::new(CompInfo::new(spelling, CompKind::Struct, vec!(), layout)));
GCompDecl(ci)
}
CXCursor_UnionDecl => {
- let ci = Rc::new(RefCell::new(CompInfo::new(spelling, false, vec!(), layout)));
+ let ci = Rc::new(RefCell::new(CompInfo::new(spelling, CompKind::Union, vec!(), layout)));
GCompDecl(ci)
}
CXCursor_EnumDecl => {
@@ -279,41 +279,83 @@ fn opaque_ty(ctx: &mut ClangParserCtx, ty: &cx::Type) {
}
}
-fn visit_struct(cursor: &Cursor,
- parent: &Cursor,
- ctx: &mut ClangParserCtx,
- fields: &mut Vec<FieldInfo>) -> Enum_CXVisitorResult {
- if cursor.kind() == CXCursor_FieldDecl {
- let ty = conv_ty(ctx, &cursor.cur_type(), cursor);
- let name = cursor.spelling();
- let bit = cursor.bit_width();
- // If we encounter a bitfield, and fail_on_bitfield is set, throw an
- // error and exit entirely.
- if bit != None {
- let fail = ctx.options.fail_on_bitfield;
- log_err_warn(ctx,
- format!("unsupported bitfield `{}` in struct `{}` ({})",
- name.as_slice(), parent.spelling().as_slice(), cursor.location()
- ).as_slice(),
- fail
- );
- }
- let field = FieldInfo::new(name, ty, bit);
- fields.push(field);
- }
- return CXChildVisit_Continue;
-}
+/// Recursively visits a cursor that represents a composite (struct or union)
+/// type and fills members with CompMember instances representing the fields and
+/// nested composites that make up the visited composite.
+fn visit_composite(cursor: &Cursor, parent: &Cursor,
+ ctx: &mut ClangParserCtx,
+ members: &mut Vec<CompMember>) -> Enum_CXVisitorResult {
+ match cursor.kind() {
+ CXCursor_FieldDecl => {
+ let ty = conv_ty(ctx, &cursor.cur_type(), cursor);
+ let name = cursor.spelling();
+ let bit = cursor.bit_width();
+ // If we encounter a bitfield, and fail_on_bitfield is set, throw an
+ // error and exit entirely.
+ if bit != None {
+ let fail = ctx.options.fail_on_bitfield;
+ log_err_warn(ctx,
+ format!("unsupported bitfield `{}` in `{}` ({})",
+ name, parent.spelling(), cursor.location()
+ ).as_slice(),
+ fail
+ );
+ }
-fn visit_union(cursor: &Cursor,
- ctx: &mut ClangParserCtx,
- fields: &mut Vec<FieldInfo>) -> Enum_CXVisitorResult {
- if cursor.kind() == CXCursor_FieldDecl {
- let ty = conv_ty(ctx, &cursor.cur_type(), cursor);
- let name = cursor.spelling();
- let field = FieldInfo::new(name, ty, None);
- fields.push(field);
+ // The Clang C api does not fully expose composite fields, but it
+ // does expose them in a way that can be detected. When the current
+ // field kind is CXType_Unexposed and the previous member is a
+ // composite type--the same type as this field-- then this is a
+ // composite field. e.g.:
+ //
+ // struct foo {
+ // union {
+ // int a;
+ // char b;
+ // } bar;
+ // };
+ let is_composite = if cursor.cur_type().kind() == CXType_Unexposed {
+ if let TComp(ref ty_compinfo) = ty {
+ match members.last() {
+ Some(&CompMember::Comp(ref c)) => c == ty_compinfo,
+ _ => false
+ }
+ } else { false }
+ } else { false };
+
+
+ let field = FieldInfo::new(name, ty.clone(), bit);
+ if is_composite {
+ if let Some(CompMember::Comp(c)) = members.pop() {
+ members.push(CompMember::CompField(c, field));
+ } else {
+ panic!(); // Checks in is_composite make this unreachable.
+ }
+ } else {
+ members.push(CompMember::Field(field));
+ }
+ }
+ CXCursor_StructDecl | CXCursor_UnionDecl => {
+ let decl = decl_name(ctx, cursor);
+ let ci = decl.compinfo();
+ cursor.visit(|c, p| {
+ let mut ci_ = ci.borrow_mut();
+ visit_composite(c, p, ctx, &mut ci_.members)
+ });
+ members.push(CompMember::Comp(ci));
+ }
+ _ => {
+ // XXX: Some kind of warning would be nice, but this produces far
+ // too many.
+ //log_err_warn(ctx,
+ // format!("unhandled composite member `{}` (kind {}) in `{}` ({})",
+ // cursor.spelling(), cursor.kind(), parent.spelling(), cursor.location()
+ // ).as_slice(),
+ // false
+ //);
+ }
}
- return CXChildVisit_Continue;
+ CXChildVisit_Continue
}
fn visit_enum(cursor: &Cursor,
@@ -347,27 +389,32 @@ fn visit_top<'r>(cur: &'r Cursor,
let ci = decl.compinfo();
cursor.visit(|c, p| {
let mut ci_ = ci.borrow_mut();
- visit_struct(c, p, ctx_, &mut ci_.fields)
+ visit_composite(c, p, ctx_, &mut ci_.members)
});
ctx_.globals.push(GComp(ci));
});
- return if cur.kind() == CXCursor_FieldDecl {
- CXChildVisit_Break
- } else {
- CXChildVisit_Recurse
- };
+
+ // XXX: Review this condition. I think it is no longer necessary.
+ // @chris-chambers
+ //return if cur.kind() == CXCursor_FieldDecl {
+ // CXChildVisit_Break
+ //} else {
+ // CXChildVisit_Continue
+ //};
+
+ CXChildVisit_Continue
}
CXCursor_UnionDecl => {
fwd_decl(ctx, cursor, |ctx_| {
let decl = decl_name(ctx_, cursor);
let ci = decl.compinfo();
- cursor.visit(|c, _| {
+ cursor.visit(|c, p| {
let mut ci_ = ci.borrow_mut();
- visit_union(c, ctx_, &mut ci_.fields)
+ visit_composite(c, p, ctx_, &mut ci_.members)
});
ctx_.globals.push(GComp(ci));
});
- return CXChildVisit_Recurse;
+ return CXChildVisit_Continue;
}
CXCursor_EnumDecl => {
fwd_decl(ctx, cursor, |ctx_| {
@@ -440,7 +487,7 @@ fn visit_top<'r>(cur: &'r Cursor,
CXCursor_FieldDecl => {
return CXChildVisit_Continue;
}
- _ => return CXChildVisit_Recurse,
+ _ => return CXChildVisit_Continue,
}
}
diff --git a/src/types.rs b/src/types.rs
index 37312d7a..1a0b353b 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -150,19 +150,32 @@ pub enum FKind {
}
#[deriving(Clone, PartialEq)]
+pub enum CompMember {
+ Field(FieldInfo),
+ Comp(Rc<RefCell<CompInfo>>),
+ CompField(Rc<RefCell<CompInfo>>, FieldInfo),
+}
+
+#[deriving(Copy, Clone, PartialEq)]
+pub enum CompKind {
+ Struct,
+ Union,
+}
+
+#[deriving(Clone, PartialEq)]
pub struct CompInfo {
- pub cstruct: bool,
+ pub kind: CompKind,
pub name: String,
- pub fields: Vec<FieldInfo>,
+ pub members: Vec<CompMember>,
pub layout: Layout,
}
impl CompInfo {
- pub fn new(name: String, cstruct: bool, fields: Vec<FieldInfo>, layout: Layout) -> CompInfo {
+ pub fn new(name: String, kind: CompKind, members: Vec<CompMember>, layout: Layout) -> CompInfo {
CompInfo {
- cstruct: cstruct,
+ kind: kind,
name: name,
- fields: fields,
+ members: members,
layout: layout,
}
}
diff --git a/tests/headers/struct_with_anon_struct.h b/tests/headers/struct_with_anon_struct.h
new file mode 100644
index 00000000..1617d7a8
--- /dev/null
+++ b/tests/headers/struct_with_anon_struct.h
@@ -0,0 +1,6 @@
+struct foo {
+ struct {
+ int a;
+ int b;
+ } bar;
+};
diff --git a/tests/headers/struct_with_anon_union.h b/tests/headers/struct_with_anon_union.h
new file mode 100644
index 00000000..3a92b940
--- /dev/null
+++ b/tests/headers/struct_with_anon_union.h
@@ -0,0 +1,6 @@
+struct foo {
+ union {
+ unsigned int a;
+ unsigned short b;
+ } bar;
+};
diff --git a/tests/headers/struct_with_anon_unnamed_struct.h b/tests/headers/struct_with_anon_unnamed_struct.h
new file mode 100644
index 00000000..f8ac4225
--- /dev/null
+++ b/tests/headers/struct_with_anon_unnamed_struct.h
@@ -0,0 +1,6 @@
+struct foo {
+ struct {
+ unsigned int a;
+ unsigned int b;
+ };
+};
diff --git a/tests/headers/struct_with_anon_unnamed_union.h b/tests/headers/struct_with_anon_unnamed_union.h
new file mode 100644
index 00000000..7158e727
--- /dev/null
+++ b/tests/headers/struct_with_anon_unnamed_union.h
@@ -0,0 +1,6 @@
+struct foo {
+ union {
+ unsigned int a;
+ unsigned short b;
+ };
+};
diff --git a/tests/headers/struct_with_nesting.h b/tests/headers/struct_with_nesting.h
new file mode 100644
index 00000000..9d7fa176
--- /dev/null
+++ b/tests/headers/struct_with_nesting.h
@@ -0,0 +1,17 @@
+struct foo {
+ unsigned int a;
+ union {
+ unsigned int b;
+ struct {
+ unsigned short c1;
+ unsigned short c2;
+ };
+
+ struct {
+ unsigned char d1;
+ unsigned char d2;
+ unsigned char d3;
+ unsigned char d4;
+ };
+ };
+};
diff --git a/tests/headers/struct_with_struct.h b/tests/headers/struct_with_struct.h
new file mode 100644
index 00000000..78b1cc81
--- /dev/null
+++ b/tests/headers/struct_with_struct.h
@@ -0,0 +1,6 @@
+struct foo {
+ struct {
+ unsigned int x;
+ unsigned int y;
+ } bar;
+};
diff --git a/tests/headers/union_with_anon_struct.h b/tests/headers/union_with_anon_struct.h
new file mode 100644
index 00000000..7f8dec95
--- /dev/null
+++ b/tests/headers/union_with_anon_struct.h
@@ -0,0 +1,6 @@
+union foo {
+ struct {
+ unsigned int a;
+ unsigned int b;
+ } bar;
+};
diff --git a/tests/headers/union_with_anon_union.h b/tests/headers/union_with_anon_union.h
new file mode 100644
index 00000000..212431b8
--- /dev/null
+++ b/tests/headers/union_with_anon_union.h
@@ -0,0 +1,6 @@
+union foo {
+ union {
+ unsigned int a;
+ unsigned short b;
+ } bar;
+};
diff --git a/tests/headers/union_with_anon_unnamed_struct.h b/tests/headers/union_with_anon_unnamed_struct.h
new file mode 100644
index 00000000..79558049
--- /dev/null
+++ b/tests/headers/union_with_anon_unnamed_struct.h
@@ -0,0 +1,9 @@
+union pixel {
+ unsigned int rgba;
+ struct {
+ unsigned char r;
+ unsigned char g;
+ unsigned char b;
+ unsigned char a;
+ };
+};
diff --git a/tests/headers/union_with_anon_unnamed_union.h b/tests/headers/union_with_anon_unnamed_union.h
new file mode 100644
index 00000000..7580771a
--- /dev/null
+++ b/tests/headers/union_with_anon_unnamed_union.h
@@ -0,0 +1,7 @@
+union foo {
+ unsigned int a;
+ union {
+ unsigned short b;
+ unsigned char c;
+ };
+};
diff --git a/tests/headers/union_with_nesting.h b/tests/headers/union_with_nesting.h
new file mode 100644
index 00000000..cd907d57
--- /dev/null
+++ b/tests/headers/union_with_nesting.h
@@ -0,0 +1,14 @@
+union foo {
+ unsigned int a;
+ struct {
+ union {
+ unsigned short b1;
+ unsigned short b2;
+ };
+
+ union {
+ unsigned short c1;
+ unsigned short c2;
+ };
+ };
+};
diff --git a/tests/struct.rs b/tests/struct.rs
new file mode 100644
index 00000000..ec1faa4f
--- /dev/null
+++ b/tests/struct.rs
@@ -0,0 +1,75 @@
+#![feature(phase)]
+
+#[phase(plugin)]
+extern crate bindgen;
+
+extern crate libc;
+
+#[test]
+fn test_struct_with_anon_struct() {
+ mod ffi { bindgen!("headers/struct_with_anon_struct.h") }
+ let mut x = ffi::Struct_foo { bar: ffi::Struct_Unnamed1 { a: 0, b: 0 } };
+
+ x.bar.a = 1;
+ x.bar.b = 2;
+
+ assert_eq!(x.bar.a, 1);
+ assert_eq!(x.bar.b, 2);
+}
+
+#[test]
+fn test_struct_with_anon_union() {
+ mod ffi { bindgen!("headers/struct_with_anon_union.h") }
+ let mut x = ffi::Struct_foo { bar: ffi::Union_Unnamed1 { _bindgen_data_: [0] } };
+
+ unsafe {
+ *x.bar.a() = 0x12345678;
+ assert_eq!(*x.bar.a(), 0x12345678);
+ assert_eq!(*x.bar.b(), 0x5678);
+ }
+}
+
+#[test]
+fn test_struct_with_anon_unnamed_struct() {
+ mod ffi { bindgen!("headers/struct_with_anon_unnamed_struct.h") }
+ let mut x = ffi::Struct_foo { _bindgen_data_1_: [0, 0] };
+
+ unsafe {
+ *x.a() = 0x12345678;
+ *x.b() = 0x87654321;
+ assert_eq!(*x.a(), 0x12345678);
+ assert_eq!(*x.b(), 0x87654321);
+ }
+}
+
+#[test]
+fn test_struct_with_anon_unnamed_union() {
+ mod ffi { bindgen!("headers/struct_with_anon_unnamed_union.h") }
+ let mut x = ffi::Struct_foo { _bindgen_data_1_: [0] };
+
+ unsafe {
+ *x.a() = 0x12345678;
+ assert_eq!(*x.a(), 0x12345678);
+ assert_eq!(*x.b(), 0x5678);
+ }
+}
+
+#[test]
+fn test_struct_with_nesting() {
+ mod ffi { bindgen!("headers/struct_with_nesting.h") }
+ let mut x = ffi::Struct_foo { a: 0, _bindgen_data_1_: [0] };
+
+ unsafe {
+ x.a = 0x12345678;
+ *x.b() = 0x87654321;
+
+ assert_eq!(x.a, 0x12345678);
+ assert_eq!(*x.b(), 0x87654321);
+ assert_eq!(*x.c1(), 0x4321);
+ assert_eq!(*x.c2(), 0x8765);
+ assert_eq!(*x.d1(), 0x21);
+ assert_eq!(*x.d2(), 0x43);
+ assert_eq!(*x.d3(), 0x65);
+ assert_eq!(*x.d4(), 0x87);
+ }
+}
diff --git a/tests/union.rs b/tests/union.rs
new file mode 100644
index 00000000..aae1e65d
--- /dev/null
+++ b/tests/union.rs
@@ -0,0 +1,84 @@
+#![feature(phase)]
+
+#[phase(plugin)]
+extern crate bindgen;
+
+extern crate libc;
+
+#[test]
+fn test_union_with_anon_struct() {
+ // XXX: Rustc thinks that the anonymous struct, bar, is unused.
+ #[allow(dead_code)]
+ mod ffi { bindgen!("headers/union_with_anon_struct.h") }
+ let mut x = ffi::Union_foo { _bindgen_data_: [0, 0] };
+
+ unsafe {
+ (*x.bar()).a = 0x12345678;
+ (*x.bar()).b = 0x87654321;
+
+ assert_eq!((*x.bar()).a, 0x12345678);
+ assert_eq!((*x.bar()).b, 0x87654321);
+ }
+}
+
+#[test]
+fn test_union_with_anon_union() {
+ mod ffi { bindgen!("headers/union_with_anon_union.h") }
+ let mut x = ffi::Union_foo { _bindgen_data_: [0] };
+
+ unsafe {
+ *(*x.bar()).a() = 0x12345678;
+
+ assert_eq!(*(*x.bar()).a(), 0x12345678);
+ assert_eq!(*(*x.bar()).b(), 0x5678);
+ }
+}
+
+#[test]
+fn test_union_with_anon_unnamed_struct() {
+ mod ffi { bindgen!("headers/union_with_anon_unnamed_struct.h") }
+ let mut x = ffi::Union_pixel { _bindgen_data_: [0] };
+
+ unsafe {
+ *x.r() = 0xca;
+ *x.g() = 0xcb;
+ *x.b() = 0xcc;
+ *x.a() = 0xcd;
+
+ assert_eq!(*x.rgba(), 0xcdcccbca);
+ assert_eq!(*x.r(), 0xca);
+ assert_eq!(*x.g(), 0xcb);
+ assert_eq!(*x.b(), 0xcc);
+ assert_eq!(*x.a(), 0xcd);
+ }
+}
+
+#[test]
+fn test_union_with_anon_unnamed_union() {
+ mod ffi { bindgen!("headers/union_with_anon_unnamed_union.h") }
+ let mut x = ffi::Union_foo { _bindgen_data_: [0] };
+
+ unsafe {
+ *x.a() = 0x12345678;
+
+ assert_eq!(*x.a(), 0x12345678);
+ assert_eq!(*x.b(), 0x5678);
+ assert_eq!(*x.c(), 0x78);
+ }
+}
+
+#[test]
+fn test_union_with_nesting() {
+ mod ffi { bindgen!("headers/union_with_nesting.h") }
+ let mut x = ffi::Union_foo { _bindgen_data_: [0] };
+
+ unsafe {
+ *x.a() = 0x12345678;
+
+ assert_eq!(*x.a(), 0x12345678);
+ assert_eq!(*x.b1(), 0x5678);
+ assert_eq!(*x.b2(), 0x5678);
+ assert_eq!(*x.c1(), 0x1234);
+ assert_eq!(*x.c2(), 0x1234);
+ }
+}