Skip to content
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
4 changes: 4 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# unreleased

# 0.29.4

* Support constant enums and arrays.

# 0.29.3

* Expose the line_endings config option to use with the builder
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cbindgen"
version = "0.29.3"
version = "0.29.4"
authors = [
"Emilio Cobos Álvarez <emilio@crisal.io>",
"Jeff Muizelaar <jmuizelaar@mozilla.com>",
Expand Down
39 changes: 38 additions & 1 deletion src/bindgen/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use std::rc::Rc;

use crate::bindgen::config::{Config, Language};
use crate::bindgen::ir::{
Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Type, Typedef,
Constant, Enum, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Type,
Typedef,
};
use crate::bindgen::language_backend::{
CLikeLanguageBackend, CythonLanguageBackend, LanguageBackend,
Expand All @@ -26,6 +27,9 @@ pub struct Bindings {
/// The map from path to struct, used to lookup whether a given type is a
/// transparent struct. This is needed to generate code for constants.
struct_map: ItemMap<Struct>,
/// The map from path to enum, used to validate enum-variant paths that
/// appear in constant literals.
enum_map: ItemMap<Enum>,
typedef_map: ItemMap<Typedef>,
struct_fileds_memo: RefCell<HashMap<BindgenPath, Rc<Vec<String>>>>,
pub globals: Vec<Static>,
Expand All @@ -44,6 +48,7 @@ impl Bindings {
pub(crate) fn new(
config: Config,
struct_map: ItemMap<Struct>,
enum_map: ItemMap<Enum>,
typedef_map: ItemMap<Typedef>,
constants: Vec<Constant>,
globals: Vec<Static>,
Expand All @@ -56,6 +61,7 @@ impl Bindings {
Bindings {
config,
struct_map,
enum_map,
typedef_map,
struct_fileds_memo: Default::default(),
globals,
Expand Down Expand Up @@ -100,6 +106,37 @@ impl Bindings {
any
}

pub fn enum_exists(&self, path: &BindgenPath) -> bool {
let mut any = false;
self.enum_map.for_items(path, |_| any = true);
any
}

/// Returns how the variant `variant_name` of the enum at `path` should be
/// referenced in generated code: `Enum::Variant` for a C++ `enum class`,
/// or the bare (possibly prefixed) variant name otherwise. Returns `None`
/// if the enum or the variant isn't known.
pub fn enum_variant_reference(&self, path: &BindgenPath, variant_name: &str) -> Option<String> {
let config = &self.config;
let mut result = None;
self.enum_map.for_items(path, |e| {
if result.is_some() {
return;
}
let Some(variant) = e.variants.iter().find(|v| v.name == variant_name) else {
return;
};
let qualify =
config.language == Language::Cxx && config.enumeration.enum_class(&e.annotations);
result = Some(if qualify {
format!("{}::{}", e.export_name, variant.export_name)
} else {
variant.export_name.clone()
});
});
result
}

pub fn struct_field_names(&self, path: &BindgenPath) -> Rc<Vec<String>> {
let mut memos = self.struct_fileds_memo.borrow_mut();
if let Some(memo) = memos.get(path) {
Expand Down
1 change: 1 addition & 0 deletions src/bindgen/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ impl Builder {
Default::default(),
Default::default(),
Default::default(),
Default::default(),
true,
String::new(),
));
Expand Down
50 changes: 43 additions & 7 deletions src/bindgen/ir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ pub enum Literal {
ty: Type,
value: Box<Literal>,
},
Array {
items: Vec<Literal>,
},
}

impl Literal {
Expand Down Expand Up @@ -182,19 +185,26 @@ impl Literal {
}
}
}
Literal::Array { ref mut items } => {
for item in items {
item.replace_self_with(self_ty);
}
}
Literal::Expr(..) => {}
}
}

fn is_valid(&self, bindings: &Bindings) -> bool {
match *self {
Literal::Expr(..) => true,
Literal::Array { ref items } => items.iter().all(|i| i.is_valid(bindings)),
Literal::Path {
ref associated_to,
ref name,
} => {
if let Some((ref path, _export_name)) = associated_to {
return bindings.struct_exists(path)
|| bindings.enum_exists(path)
|| to_known_assoc_constant(path, name).is_some();
}
true
Expand Down Expand Up @@ -227,6 +237,14 @@ impl Literal {
ref right,
..
} => left.visit(visitor) && right.visit(visitor),
Literal::Array { ref items } => {
for item in items {
if !item.visit(visitor) {
return false;
}
}
true
}
Literal::FieldAccess { ref base, .. } => base.visit(visitor),
Literal::Struct { ref fields, .. } => {
for (_name, field) in fields.iter() {
Expand Down Expand Up @@ -304,6 +322,11 @@ impl Literal {
left.rename_for_config(config);
right.rename_for_config(config);
}
Literal::Array { ref mut items } => {
for item in items {
item.rename_for_config(config);
}
}
Literal::Expr(_) => {}
Literal::Cast {
ref mut ty,
Expand Down Expand Up @@ -505,6 +528,14 @@ impl Literal {
}
}

syn::Expr::Array(syn::ExprArray { ref elems, .. }) => {
let mut items = vec![];
for elem in elems {
items.push(Self::load(elem)?);
}
Ok(Literal::Array { items })
}

_ => Err(format!("Unsupported expression. {:?}", *expr)),
}
}
Expand Down Expand Up @@ -664,8 +695,14 @@ impl Constant {
} else {
out.write("static const ");
}
language_backend.write_type(out, &self.ty);
write!(out, " {};", self.export_name());
crate::bindgen::cdecl::write_field(
language_backend,
out,
&self.ty,
self.export_name(),
config,
);
write!(out, ";");
condition.write_after(config, out);
}

Expand Down Expand Up @@ -758,9 +795,8 @@ impl Constant {
} else {
out.write("const ");
}

language_backend.write_type(out, &self.ty);
write!(out, " {name} = ");
crate::bindgen::cdecl::write_field(language_backend, out, &self.ty, &name, config);
write!(out, " = ");
language_backend.write_literal(out, value);
write!(out, ";");
}
Expand All @@ -770,10 +806,10 @@ impl Constant {
}
Language::Cython => {
out.write("const ");
language_backend.write_type(out, &self.ty);
// For extern Cython declarations the initializer is ignored,
// but still useful as documentation, so we write it as a comment.
write!(out, " {name} # = ");
crate::bindgen::cdecl::write_field(language_backend, out, &self.ty, &name, config);
write!(out, " # = ");
language_backend.write_literal(out, value);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/bindgen/ir/generic_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ impl GenericArgument {
pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) {
match *self {
GenericArgument::Type(ref mut ty) => ty.rename_for_config(config, generic_params),
GenericArgument::Const(ref mut expr) => expr.rename_for_config(config),
GenericArgument::Const(ref mut expr) => expr.rename_for_config(config, generic_params),
}
}
}
Expand Down
28 changes: 12 additions & 16 deletions src/bindgen/ir/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,20 +237,21 @@ impl PrimitiveType {
/// limited vocabulary here: only identifiers and literals.
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ConstExpr {
Name(String),
Path(GenericPath),
Value(String),
}

impl ConstExpr {
pub fn as_str(&self) -> &str {
match *self {
ConstExpr::Name(ref string) | ConstExpr::Value(ref string) => string,
ConstExpr::Path(ref path) => path.export_name(),
ConstExpr::Value(ref string) => string,
}
}

pub fn rename_for_config(&mut self, config: &Config) {
if let ConstExpr::Name(ref mut name) = self {
config.export.rename(name);
pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) {
if let ConstExpr::Path(ref mut path) = self {
path.rename_for_config(config, generic_params);
}
}

Expand All @@ -266,27 +267,23 @@ impl ConstExpr {
};
Ok(ConstExpr::Value(val))
}
syn::Expr::Path(ref path) => {
let generic_path = GenericPath::load(&path.path)?;
Ok(ConstExpr::Name(generic_path.export_name().to_owned()))
}
syn::Expr::Path(ref path) => Ok(ConstExpr::Path(GenericPath::load(&path.path)?)),
syn::Expr::Cast(ref cast) => Ok(ConstExpr::load(&cast.expr)?),
_ => Err(format!("can't handle const expression {expr:?}")),
}
}

pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> ConstExpr {
match *self {
ConstExpr::Name(ref name) => {
let path = Path::new(name);
if let ConstExpr::Path(ref path) = *self {
if path.is_single_identifier() {
for &(param, value) in mappings {
if path == *param {
if *param == *path.path() {
match *value {
GenericArgument::Type(Type::Path(ref path))
if path.is_single_identifier() =>
{
// This happens when the generic argument is a path.
return ConstExpr::Name(path.name().to_string());
return ConstExpr::Path(path.clone());
}
GenericArgument::Const(ref expr) => {
return expr.clone();
Expand All @@ -298,7 +295,6 @@ impl ConstExpr {
}
}
}
ConstExpr::Value(_) => {}
}
self.clone()
}
Expand Down Expand Up @@ -766,7 +762,7 @@ impl Type {
Type::Primitive(_) => {}
Type::Array(ref mut ty, ref mut len) => {
ty.rename_for_config(config, generic_params);
len.rename_for_config(config);
len.rename_for_config(config, generic_params);
}
Type::FuncPtr {
ref mut ret,
Expand Down
11 changes: 11 additions & 0 deletions src/bindgen/language_backend/clike.rs
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,14 @@ impl LanguageBackend for CLikeLanguageBackend<'_> {
fn write_literal<W: Write>(&mut self, out: &mut SourceWriter<W>, l: &Literal) {
match l {
Literal::Expr(v) => write!(out, "{v}"),
Literal::Array { items } => {
write!(out, "{{ ");
for item in items {
self.write_literal(out, item);
write!(out, ", ");
}
write!(out, "}}");
}
Literal::Path {
ref associated_to,
ref name,
Expand All @@ -848,6 +856,9 @@ impl LanguageBackend for CLikeLanguageBackend<'_> {
if let Some(known) = to_known_assoc_constant(path, name) {
return write!(out, "{known}");
}
if let Some(variant) = out.bindings().enum_variant_reference(path, name) {
return write!(out, "{variant}");
}
let path_separator = if self.config.language == Language::C {
"_"
} else if self.config.structure.associated_constants_in_body {
Expand Down
11 changes: 11 additions & 0 deletions src/bindgen/language_backend/cython.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,9 @@ impl LanguageBackend for CythonLanguageBackend<'_> {
if let Some(known) = to_known_assoc_constant(path, name) {
return write!(out, "{known}");
}
if let Some(variant) = out.bindings().enum_variant_reference(path, name) {
return write!(out, "{variant}");
}
write!(out, "{export_name}_")
}
write!(out, "{name}")
Expand Down Expand Up @@ -381,6 +384,14 @@ impl LanguageBackend for CythonLanguageBackend<'_> {
out.write(">");
self.write_literal(out, value);
}
Literal::Array { ref items } => {
write!(out, "[ ");
for item in items {
self.write_literal(out, item);
write!(out, ", ");
}
write!(out, "]");
}
Literal::Struct {
export_name,
fields,
Expand Down
1 change: 1 addition & 0 deletions src/bindgen/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ impl Library {
Ok(Bindings::new(
self.config,
self.structs,
self.enums,
self.typedefs,
constants,
globals,
Expand Down
Loading
Loading