Commit 35fa1b54 authored by Johan Anderholm's avatar Johan Anderholm

Introduce declaration styles on C structs and enums

Allow structs, enums and unions to be declared as:

struct Name {};
typedef struct {} Name;
typedef struct Name {} Name;

Opaque enums will be declared as:
struct Name;
typedef struct Name Name;
parent d78a4524
......@@ -87,6 +87,8 @@ line_length = 80
tab_width = 2
# The language to output bindings in
language = "[C|C++]"
# A rule to use to select style of declaration in C, tagname vs typedef
style = "[Both|Type|Tag]"
[parse]
# Whether to parse dependent crates and include their types in the generated
......
......@@ -4,6 +4,7 @@
use std::io::Write;
use bindgen::ctyperesolver::CType;
use bindgen::ir::{Function, Type};
use bindgen::writer::{ListType, SourceWriter};
......@@ -32,6 +33,7 @@ struct CDecl {
type_name: String,
type_generic_args: Vec<Type>,
declarators: Vec<CDeclarator>,
type_ctype: Option<CType>,
}
impl CDecl {
......@@ -41,6 +43,7 @@ impl CDecl {
type_name: String::new(),
type_generic_args: Vec::new(),
declarators: Vec::new(),
type_ctype: None,
}
}
......@@ -77,6 +80,7 @@ impl CDecl {
self.type_name = path.name.clone();
assert!(self.type_generic_args.len() == 0, "error generating cdecl for {:?}", t);
self.type_generic_args = path.generics.clone();
self.type_ctype = path.ctype;
}
&Type::Primitive(ref p) => {
if is_const {
......@@ -117,10 +121,14 @@ impl CDecl {
) {
// Write the type-specifier and type-qualifier first
if self.type_qualifers.len() != 0 {
write!(out, "{} {}", self.type_qualifers, self.type_name);
} else {
write!(out, "{}", self.type_name);
};
write!(out, "{} ", self.type_qualifers);
}
if let Some(ref ctype) = self.type_ctype {
write!(out, "{} ", ctype.to_str());
}
write!(out, "{}", self.type_name);
if !self.type_generic_args.is_empty() {
out.write("<");
......
......@@ -95,6 +95,50 @@ impl FromStr for Layout {
deserialize_enum_str!(Layout);
/// A style of Style to use when generating structs and enums.
#[derive(Debug, Clone, PartialEq)]
pub enum Style {
Both,
Tag,
Type,
}
impl Style {
pub fn generate_tag(&self) -> bool {
match self {
&Style::Both => true,
&Style::Tag => true,
&Style::Type => false,
}
}
pub fn generate_typedef(&self) -> bool {
match self {
&Style::Both => true,
&Style::Tag => false,
&Style::Type => true,
}
}
}
impl FromStr for Style {
type Err = String;
fn from_str(s: &str) -> Result<Style, Self::Err> {
match s {
"Both" => Ok(Style::Both),
"both" => Ok(Style::Both),
"Tag" => Ok(Style::Tag),
"tag" => Ok(Style::Tag),
"Type" => Ok(Style::Type),
"type" => Ok(Style::Type),
_ => Err(format!("Unrecognized Style: '{}'.", s)),
}
}
}
deserialize_enum_str!(Style);
/// Settings to apply when exporting items.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
......@@ -140,9 +184,9 @@ impl ExportConfig {
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct FunctionConfig {
/// Optional text to output before each function declaration
/// Optional text to output before each function Style
pub prefix: Option<String>,
/// Optional text to output after each function declaration
/// Optional text to output after each function Style
pub postfix: Option<String>,
/// The style to layout the args
pub args: Layout,
......@@ -380,6 +424,8 @@ pub struct Config {
pub tab_width: usize,
/// The language to output bindings for
pub language: Language,
/// The style to declare structs, enums and unions in for C
pub style: Style,
/// The configuration options for parsing
pub parse: ParseConfig,
/// The configuration options for exporting
......@@ -418,6 +464,7 @@ impl Default for Config {
line_length: 100,
tab_width: 2,
language: Language::Cxx,
style: Style::Type,
parse: ParseConfig::default(),
export: ExportConfig::default(),
function: FunctionConfig::default(),
......
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::collections::HashSet;
pub struct CTypeResolver {
structs: HashSet<String>,
enums: HashSet<String>,
unions: HashSet<String>,
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
pub enum CType {
Struct,
Enum,
Union,
}
impl CType {
pub fn to_str(&self) -> &'static str {
match self {
&CType::Struct => "struct",
&CType::Enum => "enum",
&CType::Union => "union",
}
}
}
impl CTypeResolver {
pub fn new() -> CTypeResolver {
CTypeResolver {
structs: HashSet::new(),
enums: HashSet::new(),
unions: HashSet::new(),
}
}
pub fn add_enum(&mut self, name: &str) {
self.enums.insert(name.to_owned());
}
pub fn add_struct(&mut self, name: &str) {
self.structs.insert(name.to_owned());
}
pub fn add_union(&mut self, name: &str) {
self.unions.insert(name.to_owned());
}
pub fn type_for(&self, name: &str) -> Option<CType> {
if self.structs.contains(name) {
Some(CType::Struct)
} else if self.enums.contains(name) {
Some(CType::Enum)
} else if self.unions.contains(name) {
Some(CType::Union)
} else {
None
}
}
}
\ No newline at end of file
......@@ -9,6 +9,7 @@ use syn;
use bindgen::config::{Config, Language};
use bindgen::ir::{AnnotationSet, Cfg, Documentation, Item, ItemContainer, Type};
use bindgen::ctyperesolver::CTypeResolver;
use bindgen::writer::{Source, SourceWriter};
#[derive(Debug, Clone)]
......@@ -128,6 +129,10 @@ impl Item for Constant {
fn rename_for_config(&mut self, config: &Config) {
config.export.rename(&mut self.name);
}
fn set_ctype(&mut self, resolver: &CTypeResolver) {
self.ty.set_ctype(resolver);
}
}
impl Source for Constant {
......
......@@ -7,6 +7,7 @@ use std::io::Write;
use syn;
use bindgen::config::{Config, Language};
use bindgen::ctyperesolver::CTypeResolver;
use bindgen::dependencies::Dependencies;
use bindgen::library::Library;
use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, GenericParams, GenericPath, Item,
......@@ -55,6 +56,7 @@ impl EnumVariant {
Type::Path(GenericPath {
name: "Tag".to_string(),
generics: vec![],
ctype: None,
}),
Documentation::none(),
));
......@@ -119,6 +121,12 @@ impl EnumVariant {
item.add_dependencies(library, out);
}
}
fn set_ctype(&mut self, resolver: &CTypeResolver) {
if let Some((_, ref mut ty)) = self.body {
ty.set_ctype(resolver);
}
}
}
impl Source for EnumVariant {
......@@ -209,6 +217,22 @@ impl Item for Enum {
ItemContainer::Enum(self.clone())
}
fn populate_ctyperesolver(&self, resolver: &mut CTypeResolver) {
if self.tag.is_some() && self.repr.style == ReprStyle::C {
resolver.add_struct(&self.name);
} else if self.tag.is_some() && self.repr.style != ReprStyle::C {
resolver.add_union(&self.name);
} else {
resolver.add_enum(&self.name);
}
}
fn set_ctype(&mut self, resolver: &CTypeResolver) {
for &mut ref mut var in &mut self.variants {
var.set_ctype(resolver);
}
}
fn rename_for_config(&mut self, config: &Config) {
config.export.rename(&mut self.name);
......@@ -221,6 +245,7 @@ impl Item for Enum {
body.fields[0].1 = Type::Path(GenericPath {
name: new_tag.clone(),
generics: vec![],
ctype: None,
});
}
}
......@@ -308,12 +333,16 @@ impl Source for Enum {
};
if config.language == Language::C {
if size.is_none() {
out.write("typedef enum");
} else {
write!(out, "enum {}", enum_name);
if size.is_none() && config.style.generate_typedef() {
out.write("typedef ");
}
} else {
out.write("enum");
if !size.is_none() || config.style.generate_tag() {
write!(out, " {}", enum_name);
}
} else {
if let Some(prim) = size {
write!(out, "enum class {} : {}", enum_name, prim);
} else {
......@@ -333,7 +362,7 @@ impl Source for Enum {
out.write("Sentinel /* this must be last for serialization purposes. */");
}
if config.language == Language::C && size.is_none() {
if config.language == Language::C && size.is_none() && config.style.generate_typedef() {
out.close_brace(false);
write!(out, " {};", enum_name);
} else {
......@@ -361,11 +390,16 @@ impl Source for Enum {
out.new_line();
if config.language == Language::C {
write!(
out,
"typedef {}",
if separate_tag { "struct" } else { "union" }
);
if config.style.generate_typedef() {
out.write("typedef ");
}
out.write(if separate_tag { "struct" } else { "union" });
if config.style.generate_tag() {
write!(out, " {}", self.name);
}
out.open_brace();
}
......@@ -378,6 +412,10 @@ impl Source for Enum {
out.open_brace();
}
if config.language == Language::C && !config.style.generate_typedef() {
out.write("enum ");
}
write!(out, "{} tag;", enum_name);
if wrap_tag {
......@@ -399,7 +437,11 @@ impl Source for Enum {
if i != 0 {
out.new_line();
}
write!(out, "{} {};", body.name, field_name);
if config.style.generate_typedef() {
write!(out, "{} {};", body.name, field_name);
} else {
write!(out, "struct {} {};", body.name, field_name);
}
}
if separate_tag {
......@@ -407,8 +449,12 @@ impl Source for Enum {
}
if config.language == Language::C {
out.close_brace(false);
write!(out, " {};", self.name);
if config.style.generate_typedef() {
out.close_brace(false);
write!(out, " {};", self.name);
} else {
out.close_brace(true);
}
} else {
out.close_brace(true);
}
......
......@@ -8,6 +8,7 @@ use syn;
use bindgen::cdecl;
use bindgen::config::{Config, Language, Layout};
use bindgen::ctyperesolver::CTypeResolver;
use bindgen::dependencies::Dependencies;
use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, PrimitiveType, Type};
use bindgen::library::Library;
......@@ -86,6 +87,13 @@ impl Function {
}
}
pub fn set_ctype(&mut self, resolver: &CTypeResolver) {
self.ret.set_ctype(resolver);
for &mut (_, ref mut ty) in &mut self.args {
ty.set_ctype(resolver);
}
}
pub fn rename_for_config(&mut self, config: &Config) {
self.ret.rename_for_config(config);
for &mut (_, ref mut ty) in &mut self.args {
......
......@@ -7,6 +7,7 @@ use std::io::Write;
use syn;
use bindgen::config::Config;
use bindgen::ctyperesolver::CTypeResolver;
use bindgen::dependencies::Dependencies;
use bindgen::ir::{AnnotationSet, Cfg, Documentation, Item, ItemContainer, Type};
use bindgen::library::Library;
......@@ -70,6 +71,10 @@ impl Item for Static {
self.ty.rename_for_config(config);
}
fn set_ctype(&mut self, resolver: &CTypeResolver) {
self.ty.set_ctype(resolver);
}
fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
self.ty.add_dependencies(library, out);
}
......
......@@ -6,6 +6,7 @@ use std::collections::BTreeMap;
use std::mem;
use bindgen::config::Config;
use bindgen::ctyperesolver::CTypeResolver;
use bindgen::dependencies::Dependencies;
use bindgen::ir::{AnnotationSet, Cfg, Constant, Enum, OpaqueItem, Static, Struct, Type, Typedef,
Union};
......@@ -21,6 +22,8 @@ pub trait Item {
fn container(&self) -> ItemContainer;
fn populate_ctyperesolver(&self, _resolver: &mut CTypeResolver) { unimplemented!() }
fn set_ctype(&mut self, _resolver: &CTypeResolver) { unimplemented!() }
fn rename_for_config(&mut self, _config: &Config) {}
fn add_dependencies(&self, _library: &Library, _out: &mut Dependencies) {}
fn instantiate_monomorph(
......
......@@ -7,6 +7,7 @@ use std::io::Write;
use syn;
use bindgen::config::{Config, Language};
use bindgen::ctyperesolver::CTypeResolver;
use bindgen::dependencies::Dependencies;
use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, GenericParams, Item, ItemContainer,
Path, Type};
......@@ -62,6 +63,10 @@ impl Item for OpaqueItem {
ItemContainer::OpaqueItem(self.clone())
}
fn populate_ctyperesolver(&self, resolver: &mut CTypeResolver) {
resolver.add_struct(&self.name);
}
fn rename_for_config(&mut self, config: &Config) {
config.export.rename(&mut self.name);
}
......@@ -101,7 +106,8 @@ impl Source for OpaqueItem {
self.generic_params.write(config, out);
if config.language == Language::C {
if config.style.generate_typedef() &&
config.language == Language::C {
write!(out, "typedef struct {} {};", self.name, self.name);
} else {
write!(out, "struct {};", self.name);
......
......@@ -5,6 +5,7 @@
use syn;
use bindgen::ir::Type;
use bindgen::ctyperesolver::{CType, CTypeResolver};
use bindgen::utilities::IterHelpers;
pub type Path = String;
......@@ -13,6 +14,7 @@ pub type Path = String;
pub struct GenericPath {
pub name: String,
pub generics: Vec<Type>,
pub ctype: Option<CType>,
}
impl GenericPath {
......@@ -20,9 +22,14 @@ impl GenericPath {
GenericPath {
name: name,
generics: generics,
ctype: None,
}
}
pub fn set_ctype(&mut self, resolver: &CTypeResolver) {
self.ctype = resolver.type_for(&self.name);
}
pub fn load(path: &syn::Path) -> Result<GenericPath, String> {
assert!(path.segments.len() > 0, "{:?} doesn't have any segments", path);
let last_segment_token = path.segments.last().unwrap();
......
......@@ -7,9 +7,10 @@ use std::io::Write;
use syn;
use bindgen::config::{Config, Language};
use bindgen::ctyperesolver::CTypeResolver;
use bindgen::dependencies::Dependencies;
use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, GenericParams, Item, ItemContainer,
Repr, Type};
use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, GenericParams, Item,
ItemContainer, Repr, Type};
use bindgen::library::Library;
use bindgen::mangle;
use bindgen::monomorph::Monomorphs;
......@@ -116,6 +117,16 @@ impl Item for Struct {
ItemContainer::Struct(self.clone())
}
fn populate_ctyperesolver(&self, resolver: &mut CTypeResolver) {
resolver.add_struct(&self.name);
}
fn set_ctype(&mut self, resolver: &CTypeResolver) {
for &mut (_, ref mut ty, _) in &mut self.fields {
ty.set_ctype(resolver);
}
}
fn rename_for_config(&mut self, config: &Config) {
config.export.rename(&mut self.name);
for &mut (_, ref mut ty, _) in &mut self.fields {
......@@ -217,11 +228,23 @@ impl Source for Struct {
self.generic_params.write(config, out);
if config.language == Language::C {
out.write("typedef struct");
} else {
write!(out, "struct {}", self.name);
// The following results in
// C++ or C with Tag as style:
// struct Name {
// C with Type only style:
// typedef struct {
// C with Both as style:
// typedef struct Name {
if config.language == Language::C && config.style.generate_typedef() {
out.write("typedef ");
}
out.write("struct");
if config.language == Language::Cxx || config.style.generate_tag() {
write!(out, " {}", self.name);
}
out.open_brace();
if config.documentation {
......@@ -345,7 +368,7 @@ impl Source for Struct {
}
}
if config.language == Language::C {
if config.language == Language::C && config.style.generate_typedef() {
out.close_brace(false);
write!(out, " {};", self.name);
} else {
......
......@@ -9,6 +9,7 @@ use syn;
use bindgen::cdecl;
use bindgen::config::Config;
use bindgen::ctyperesolver::CTypeResolver;
use bindgen::dependencies::Dependencies;
use bindgen::ir::{Documentation, GenericParams, GenericPath, Path};
use bindgen::library::Library;
......@@ -491,6 +492,30 @@ impl Type {
}
}
pub fn set_ctype(&mut self, resolver: &CTypeResolver) {
match self {
&mut Type::ConstPtr(ref mut ty) => {
ty.set_ctype(resolver);
}
&mut Type::Ptr(ref mut ty) => {
ty.set_ctype(resolver);
}
&mut Type::Path(ref mut path) => {
path.set_ctype(resolver);
}
&mut Type::Primitive(_) => {}
&mut Type::Array(ref mut ty, _) => {
ty.set_ctype(resolver);
}
&mut Type::FuncPtr(ref mut ret, ref mut args) => {
ret.set_ctype(resolver);
for arg in args {
arg.set_ctype(resolver);
}
}
}
}
pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
match self {
&mut Type::ConstPtr(ref mut ty) => {
......
......@@ -8,6 +8,7 @@ use std::io::Write;
use syn;
use bindgen::config::{Config, Language};
use bindgen::ctyperesolver::CTypeResolver;
use bindgen::dependencies::Dependencies;
use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, GenericParams, Item, ItemContainer,
Path, Type};
......@@ -114,6 +115,10 @@ impl Item for Typedef {
self.aliased.rename_for_config(config);
}
fn set_ctype(&mut self, resolver: &CTypeResolver) {
self.aliased.set_ctype(resolver);
}
fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
self.aliased
.add_dependencies_ignoring_generics(&self.generic_params, library, out);
......
......@@ -7,6 +7,7 @@ use std::io::Write;
use syn;
use bindgen::config::{Config, Language};
use bindgen::ctyperesolver::CTypeResolver;
use bindgen::dependencies::Dependencies;
use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, GenericParams, Item, ItemContainer,
Repr, Type};
......@@ -104,6 +105,16 @@ impl Item for Union {
ItemContainer::Union(self.clone())
}
fn populate_ctyperesolver(&self, resolver: &mut CTypeResolver) {
resolver.add_union(&self.name);
}
fn set_ctype(&mut self, resolver: &CTypeResolver) {
for &mut (_, ref mut ty, _) in &mut self.fields {
ty.set_ctype(resolver);
}
}
fn rename_for_config(&mut self, config: &Config) {
config.export.rename(&mut self.name);
for &mut (_, ref mut ty, _) in &mut self.fields {
......@@ -205,11 +216,23 @@ impl Source for Union {
self.generic_params.write(config, out);
if config.language == Language::C {
out.write("typedef union");
} else {
write!(out, "union {}", self.name);
// The following results in
// C++ or C with Tag as style:
// union Name {
// C with Type only style:
// typedef union {
// C with Both as style:
// typedef union Name {
if config.language == Language::C && config.style.generate_typedef() {
out.write("typedef ");
}
out.write("union");
if config.language == Language::Cxx || config.style.generate_tag() {
write!(out, " {}", self.name);
}
out.open_brace();
if config.documentation {
......@@ -224,7 +247,7 @@ impl Source for Union {
);
}
if config.language == Language::C {
if config.language == Language::C && config.style.generate_typedef() {
out.close_brace(false);
write!(out, " {};", self.name);
} else {
......
......@@ -7,6 +7,7 @@ use std::mem;
use bindgen::bindings::Bindings;
use bindgen::config::{Config, Language};
use bindgen::ctyperesolver::CTypeResolver;
use bindgen::dependencies::Dependencies;
use bindgen::error::Error;
use bindgen::ir::{Constant, Enum, Function, Item, ItemContainer, ItemMap};
......@@ -60,6 +61,8 @@ impl Library {
if self.config.language == Language::C {
self.instantiate_monomorphs();
self.set_ctype();
}
let mut dependencies = Dependencies::new();
......@@ -266,6 +269,49 @@ impl Library {
}
}
fn set_ctype(&mut self) {
if self.config.style.generate_typedef() {
return;
}
let mut resolver = CTypeResolver::new();
self.structs.for_all_items(|x| {
x.populate_ctyperesolver(&mut resolver);
});
self.opaque_items.for_all_items(|x| {
x.populate_ctyperesolver(&mut resolver);
});
self.enums.for_all_items(|x| {
x.populate_ctyperesolver(&mut resolver);
});
self.unions.for_all_items(|x| {
x.populate_ctyperesolver(&mut resolver);
});
self.enums
.for_all_items_mut(|x| x.set_ctype(&resolver));
self.structs
.for_all_items_mut(|x| x.set_ctype(&resolver));
self.unions
.for_all_items_mut(|x| x.set_ctype(&resolver));
self.typedefs
.for_all_items_mut(|x| x.set_ctype(&resolver));
self.globals
.for_all_items_mut(|x| x.set_ctype(&resolver));
for item in &mut self.functions {
item.set_ctype(&resolver);
}
}