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;
This commit is contained in:
Johan Anderholm 2018-03-15 16:29:25 +01:00 committed by Ryan Hunt
parent 1693519e39
commit 608a67b4b2
18 changed files with 377 additions and 32 deletions

View File

@ -85,6 +85,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

View File

@ -4,6 +4,7 @@
use std::io::Write;
use bindgen::declarationtyperesolver::DeclarationType;
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<DeclarationType>,
}
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("<");

View File

@ -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")]
@ -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(),

View File

@ -0,0 +1,62 @@
/* 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 DeclarationTypeResolver {
structs: HashSet<String>,
enums: HashSet<String>,
unions: HashSet<String>,
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
pub enum DeclarationType {
Struct,
Enum,
Union,
}
impl DeclarationType {
pub fn to_str(&self) -> &'static str {
match self {
&DeclarationType::Struct => "struct",
&DeclarationType::Enum => "enum",
&DeclarationType::Union => "union",
}
}
}
impl DeclarationTypeResolver {
pub fn new() -> DeclarationTypeResolver {
DeclarationTypeResolver {
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<DeclarationType> {
if self.structs.contains(name) {
Some(DeclarationType::Struct)
} else if self.enums.contains(name) {
Some(DeclarationType::Enum)
} else if self.unions.contains(name) {
Some(DeclarationType::Union)
} else {
None
}
}
}

View File

@ -9,6 +9,7 @@ use syn;
use bindgen::config::{Config, Language};
use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, Item, ItemContainer, Type};
use bindgen::declarationtyperesolver::DeclarationTypeResolver;
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 resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
self.ty.resolve_declaration_types(resolver);
}
}
impl Source for Constant {

View File

@ -7,6 +7,7 @@ use std::io::Write;
use syn;
use bindgen::config::{Config, Language};
use bindgen::declarationtyperesolver::DeclarationTypeResolver;
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 resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
if let Some((_, ref mut ty)) = self.body {
ty.resolve_declaration_types(resolver);
}
}
}
impl Source for EnumVariant {
@ -209,6 +217,22 @@ impl Item for Enum {
ItemContainer::Enum(self.clone())
}
fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
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 if self.repr.style == ReprStyle::C {
resolver.add_enum(&self.name);
}
}
fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
for &mut ref mut var in &mut self.variants {
var.resolve_declaration_types(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);
}

View File

@ -8,6 +8,7 @@ use syn;
use bindgen::cdecl;
use bindgen::config::{Config, Language, Layout};
use bindgen::declarationtyperesolver::DeclarationTypeResolver;
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 resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
self.ret.resolve_declaration_types(resolver);
for &mut (_, ref mut ty) in &mut self.args {
ty.resolve_declaration_types(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 {

View File

@ -7,6 +7,7 @@ use std::io::Write;
use syn;
use bindgen::config::Config;
use bindgen::declarationtyperesolver::DeclarationTypeResolver;
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 resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
self.ty.resolve_declaration_types(resolver);
}
fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
self.ty.add_dependencies(library, out);
}

View File

@ -6,6 +6,7 @@ use std::collections::BTreeMap;
use std::mem;
use bindgen::config::Config;
use bindgen::declarationtyperesolver::DeclarationTypeResolver;
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 collect_declaration_types(&self, _resolver: &mut DeclarationTypeResolver) { unimplemented!() }
fn resolve_declaration_types(&mut self, _resolver: &DeclarationTypeResolver) { unimplemented!() }
fn rename_for_config(&mut self, _config: &Config) {}
fn add_dependencies(&self, _library: &Library, _out: &mut Dependencies) {}
fn instantiate_monomorph(

View File

@ -7,6 +7,7 @@ use std::io::Write;
use syn;
use bindgen::config::{Config, Language};
use bindgen::declarationtyperesolver::DeclarationTypeResolver;
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 collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
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);

View File

@ -5,6 +5,7 @@
use syn;
use bindgen::ir::Type;
use bindgen::declarationtyperesolver::{DeclarationType, DeclarationTypeResolver};
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<DeclarationType>,
}
impl GenericPath {
@ -20,9 +22,14 @@ impl GenericPath {
GenericPath {
name: name,
generics: generics,
ctype: None,
}
}
pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
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();

View File

@ -7,9 +7,10 @@ use std::io::Write;
use syn;
use bindgen::config::{Config, Language};
use bindgen::declarationtyperesolver::DeclarationTypeResolver;
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 collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
resolver.add_struct(&self.name);
}
fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
for &mut (_, ref mut ty, _) in &mut self.fields {
ty.resolve_declaration_types(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 {

View File

@ -9,6 +9,7 @@ use syn;
use bindgen::cdecl;
use bindgen::config::Config;
use bindgen::declarationtyperesolver::DeclarationTypeResolver;
use bindgen::dependencies::Dependencies;
use bindgen::ir::{Documentation, GenericParams, GenericPath, Path};
use bindgen::library::Library;
@ -491,6 +492,30 @@ impl Type {
}
}
pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
match self {
&mut Type::ConstPtr(ref mut ty) => {
ty.resolve_declaration_types(resolver);
}
&mut Type::Ptr(ref mut ty) => {
ty.resolve_declaration_types(resolver);
}
&mut Type::Path(ref mut path) => {
path.resolve_declaration_types(resolver);
}
&mut Type::Primitive(_) => {}
&mut Type::Array(ref mut ty, _) => {
ty.resolve_declaration_types(resolver);
}
&mut Type::FuncPtr(ref mut ret, ref mut args) => {
ret.resolve_declaration_types(resolver);
for arg in args {
arg.resolve_declaration_types(resolver);
}
}
}
}
pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
match self {
&mut Type::ConstPtr(ref mut ty) => {

View File

@ -8,6 +8,7 @@ use std::io::Write;
use syn;
use bindgen::config::{Config, Language};
use bindgen::declarationtyperesolver::DeclarationTypeResolver;
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 resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
self.aliased.resolve_declaration_types(resolver);
}
fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
self.aliased
.add_dependencies_ignoring_generics(&self.generic_params, library, out);

View File

@ -7,6 +7,7 @@ use std::io::Write;
use syn;
use bindgen::config::{Config, Language};
use bindgen::declarationtyperesolver::DeclarationTypeResolver;
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 collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
resolver.add_union(&self.name);
}
fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
for &mut (_, ref mut ty, _) in &mut self.fields {
ty.resolve_declaration_types(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 {

View File

@ -7,6 +7,7 @@ use std::mem;
use bindgen::bindings::Bindings;
use bindgen::config::{Config, Language};
use bindgen::declarationtyperesolver::DeclarationTypeResolver;
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.resolve_declaration_types();
}
let mut dependencies = Dependencies::new();
@ -266,6 +269,49 @@ impl Library {
}
}
fn resolve_declaration_types(&mut self) {
if self.config.style.generate_typedef() {
return;
}
let mut resolver = DeclarationTypeResolver::new();
self.structs.for_all_items(|x| {
x.collect_declaration_types(&mut resolver);
});
self.opaque_items.for_all_items(|x| {
x.collect_declaration_types(&mut resolver);
});
self.enums.for_all_items(|x| {
x.collect_declaration_types(&mut resolver);
});
self.unions.for_all_items(|x| {
x.collect_declaration_types(&mut resolver);
});
self.enums
.for_all_items_mut(|x| x.resolve_declaration_types(&resolver));
self.structs
.for_all_items_mut(|x| x.resolve_declaration_types(&resolver));
self.unions
.for_all_items_mut(|x| x.resolve_declaration_types(&resolver));
self.typedefs
.for_all_items_mut(|x| x.resolve_declaration_types(&resolver));
self.globals
.for_all_items_mut(|x| x.resolve_declaration_types(&resolver));
for item in &mut self.functions {
item.resolve_declaration_types(&resolver);
}
}
fn simplify_option_to_ptr(&mut self) {
self.structs.for_all_items_mut(|x| {
x.simplify_option_to_ptr();

View File

@ -39,6 +39,7 @@ mod builder;
mod cargo;
mod cdecl;
mod config;
mod declarationtyperesolver;
mod dependencies;
mod error;
mod ir;

View File

@ -20,7 +20,7 @@ use clap::{App, Arg, ArgMatches};
mod logging;
mod bindgen;
use bindgen::{Bindings, Builder, Cargo, Config, Error, Language};
use bindgen::{Bindings, Builder, Cargo, Config, Error, Language, Style};
fn apply_config_overrides<'a>(config: &mut Config, matches: &ArgMatches<'a>) {
// We allow specifying a language to override the config default. This is
@ -38,6 +38,21 @@ fn apply_config_overrides<'a>(config: &mut Config, matches: &ArgMatches<'a>) {
};
}
if let Some(style) = matches.value_of("style") {
config.style = match style {
"Both" => Style::Both,
"both" => Style::Both,
"Tag" => Style::Tag,
"tag" => Style::Tag,
"Type" => Style::Type,
"type" => Style::Type,
_ => {
error!("Unknown style specified.");
return;
}
}
}
if matches.is_present("d") {
config.parse.parse_deps = true;
}
@ -111,6 +126,14 @@ fn main() {
.help("Specify the language to output bindings in")
.possible_values(&["c++", "C++", "c", "C"]),
)
.arg(
Arg::with_name("style")
.short("s")
.long("style")
.value_name("STYLE")
.help("Specify the declaration style to use for bindings")
.possible_values(&["Both", "both", "Tag", "tag", "Type", "type"]),
)
.arg(
Arg::with_name("d")
.short("d")