summaryrefslogtreecommitdiffstats
path: root/src/bindgen
diff options
context:
space:
mode:
Diffstat (limited to 'src/bindgen')
-rw-r--r--src/bindgen/bindings.rs537
-rw-r--r--src/bindgen/bitflags.rs289
-rw-r--r--src/bindgen/builder.rs424
-rw-r--r--src/bindgen/cargo/cargo.rs252
-rw-r--r--src/bindgen/cargo/cargo_expand.rs145
-rw-r--r--src/bindgen/cargo/cargo_lock.rs51
-rw-r--r--src/bindgen/cargo/cargo_metadata.rs253
-rw-r--r--src/bindgen/cargo/cargo_toml.rs67
-rw-r--r--src/bindgen/cargo/mod.rs12
-rw-r--r--src/bindgen/cdecl.rs372
-rw-r--r--src/bindgen/config.rs1114
-rw-r--r--src/bindgen/declarationtyperesolver.rs57
-rw-r--r--src/bindgen/dependencies.rs46
-rw-r--r--src/bindgen/error.rs88
-rw-r--r--src/bindgen/ir/annotation.rs217
-rw-r--r--src/bindgen/ir/cfg.rs365
-rw-r--r--src/bindgen/ir/constant.rs813
-rw-r--r--src/bindgen/ir/documentation.rs111
-rw-r--r--src/bindgen/ir/enumeration.rs1556
-rw-r--r--src/bindgen/ir/field.rs80
-rw-r--r--src/bindgen/ir/function.rs390
-rw-r--r--src/bindgen/ir/generic_path.rs303
-rw-r--r--src/bindgen/ir/global.rs121
-rw-r--r--src/bindgen/ir/item.rs250
-rw-r--r--src/bindgen/ir/mod.rs39
-rw-r--r--src/bindgen/ir/opaque.rs176
-rw-r--r--src/bindgen/ir/path.rs50
-rw-r--r--src/bindgen/ir/repr.rs182
-rw-r--r--src/bindgen/ir/structure.rs717
-rw-r--r--src/bindgen/ir/ty.rs1017
-rw-r--r--src/bindgen/ir/typedef.rs208
-rw-r--r--src/bindgen/ir/union.rs336
-rw-r--r--src/bindgen/library.rs446
-rw-r--r--src/bindgen/mangle.rs348
-rw-r--r--src/bindgen/mod.rs65
-rw-r--r--src/bindgen/monomorph.rs147
-rw-r--r--src/bindgen/parser.rs1011
-rw-r--r--src/bindgen/rename.rs140
-rw-r--r--src/bindgen/reserved.rs91
-rw-r--r--src/bindgen/utilities.rs348
-rw-r--r--src/bindgen/writer.rs258
41 files changed, 13492 insertions, 0 deletions
diff --git a/src/bindgen/bindings.rs b/src/bindgen/bindings.rs
new file mode 100644
index 0000000..410b11f
--- /dev/null
+++ b/src/bindgen/bindings.rs
@@ -0,0 +1,537 @@
+/* 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::borrow::Cow;
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::fs;
+use std::fs::File;
+use std::io::{Read, Write};
+use std::path;
+use std::rc::Rc;
+
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::ir::{
+ Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Typedef,
+};
+use crate::bindgen::writer::{Source, SourceWriter};
+
+/// A bindings header that can be written.
+pub struct Bindings {
+ pub config: Config,
+ /// 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>,
+ typedef_map: ItemMap<Typedef>,
+ struct_fileds_memo: RefCell<HashMap<BindgenPath, Rc<Vec<String>>>>,
+ globals: Vec<Static>,
+ constants: Vec<Constant>,
+ items: Vec<ItemContainer>,
+ functions: Vec<Function>,
+ source_files: Vec<path::PathBuf>,
+ /// Bindings are generated by a recursive call to cbindgen
+ /// and shouldn't do anything when written anywhere.
+ noop: bool,
+}
+
+#[derive(PartialEq, Eq)]
+enum NamespaceOperation {
+ Open,
+ Close,
+}
+
+impl Bindings {
+ #[allow(clippy::too_many_arguments)]
+ pub(crate) fn new(
+ config: Config,
+ struct_map: ItemMap<Struct>,
+ typedef_map: ItemMap<Typedef>,
+ constants: Vec<Constant>,
+ globals: Vec<Static>,
+ items: Vec<ItemContainer>,
+ functions: Vec<Function>,
+ source_files: Vec<path::PathBuf>,
+ noop: bool,
+ ) -> Bindings {
+ Bindings {
+ config,
+ struct_map,
+ typedef_map,
+ struct_fileds_memo: Default::default(),
+ globals,
+ constants,
+ items,
+ functions,
+ source_files,
+ noop,
+ }
+ }
+
+ // FIXME(emilio): What to do when the configuration doesn't match?
+ pub fn struct_is_transparent(&self, path: &BindgenPath) -> bool {
+ let mut any = false;
+ self.struct_map.for_items(path, |s| any |= s.is_transparent);
+ any
+ }
+
+ /// Peels through typedefs to allow resolving structs.
+ fn resolved_struct_path<'a>(&self, path: &'a BindgenPath) -> Cow<'a, BindgenPath> {
+ use crate::bindgen::ir::Type;
+
+ let mut resolved_path = Cow::Borrowed(path);
+ loop {
+ let mut found = None;
+ self.typedef_map.for_items(&resolved_path, |item| {
+ if let Type::Path(ref p) = item.aliased {
+ found = Some(p.path().clone());
+ }
+ });
+ resolved_path = match found {
+ Some(p) => Cow::Owned(p),
+ None => break,
+ }
+ }
+ resolved_path
+ }
+
+ pub fn struct_exists(&self, path: &BindgenPath) -> bool {
+ let mut any = false;
+ self.struct_map
+ .for_items(&self.resolved_struct_path(path), |_| any = true);
+ any
+ }
+
+ 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) {
+ return memo.clone();
+ }
+
+ let resolved_path = self.resolved_struct_path(path);
+
+ let mut fields = Vec::<String>::new();
+ self.struct_map.for_items(&resolved_path, |st| {
+ let mut pos: usize = 0;
+ for field in &st.fields {
+ if let Some(found_pos) = fields.iter().position(|v| *v == field.name) {
+ pos = found_pos + 1;
+ } else {
+ fields.insert(pos, field.name.clone());
+ pos += 1;
+ }
+ }
+ });
+
+ let fields = Rc::new(fields);
+ memos.insert(path.clone(), fields.clone());
+ if let Cow::Owned(p) = resolved_path {
+ memos.insert(p, fields.clone());
+ }
+ fields
+ }
+
+ pub fn generate_depfile<P: AsRef<path::Path>>(&self, header_path: P, depfile_path: P) {
+ if let Some(dir) = depfile_path.as_ref().parent() {
+ if !dir.exists() {
+ std::fs::create_dir_all(dir).unwrap()
+ }
+ }
+ let canon_header_path = header_path.as_ref().canonicalize().unwrap();
+ let mut canon_source_files: Vec<_> = self
+ .source_files
+ .iter()
+ .chain(self.config.config_path.as_ref())
+ .map(|p| p.canonicalize().unwrap())
+ .collect();
+ // Sorting makes testing easier by ensuring the output is ordered.
+ canon_source_files.sort_unstable();
+
+ // When writing the depfile we must escape whitespace in paths to avoid it being interpreted
+ // as a seperator.
+ // It is not clear how to otherwise _correctly_ replace whitespace in a non-unicode
+ // compliant slice, without knowing the encoding, so we lossy convert such cases,
+ // to avoid panics.
+ let mut depfile = File::create(depfile_path).unwrap();
+ write!(
+ &mut depfile,
+ "{}:",
+ canon_header_path.to_string_lossy().replace(' ', "\\ ")
+ )
+ .expect("Writing header name to depfile failed");
+ canon_source_files.into_iter().for_each(|source_file| {
+ // Add line-continue and line-break and then indent with 4 spaces.
+ // This makes the output more human-readable.
+ depfile.write_all(b" \\\n ").unwrap();
+ let escaped_path = source_file.to_string_lossy().replace(' ', "\\ ");
+ depfile.write_all(escaped_path.as_bytes()).unwrap();
+ });
+
+ writeln!(&mut depfile).unwrap();
+
+ depfile.flush().unwrap();
+ }
+
+ pub fn write_to_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
+ if self.noop {
+ return false;
+ }
+
+ // Don't compare files if we've never written this file before
+ if !path.as_ref().is_file() {
+ if let Some(parent) = path::Path::new(path.as_ref()).parent() {
+ fs::create_dir_all(parent).unwrap();
+ }
+ self.write(File::create(path).unwrap());
+ return true;
+ }
+
+ let mut new_file_contents = Vec::new();
+ self.write(&mut new_file_contents);
+
+ let mut old_file_contents = Vec::new();
+ {
+ let mut old_file = File::open(&path).unwrap();
+ old_file.read_to_end(&mut old_file_contents).unwrap();
+ }
+
+ if old_file_contents != new_file_contents {
+ let mut new_file = File::create(&path).unwrap();
+ new_file.write_all(&new_file_contents).unwrap();
+ true
+ } else {
+ false
+ }
+ }
+
+ pub fn write_headers<F: Write>(&self, out: &mut SourceWriter<F>) {
+ if self.noop {
+ return;
+ }
+
+ if let Some(ref f) = self.config.header {
+ out.new_line_if_not_start();
+ write!(out, "{}", f);
+ out.new_line();
+ }
+ if let Some(f) = self.config.include_guard() {
+ out.new_line_if_not_start();
+ write!(out, "#ifndef {}", f);
+ out.new_line();
+ write!(out, "#define {}", f);
+ out.new_line();
+ }
+ if self.config.pragma_once && self.config.language != Language::Cython {
+ out.new_line_if_not_start();
+ write!(out, "#pragma once");
+ out.new_line();
+ }
+ if self.config.include_version {
+ out.new_line_if_not_start();
+ write!(
+ out,
+ "/* Generated with cbindgen:{} */",
+ crate::bindgen::config::VERSION
+ );
+ out.new_line();
+ }
+ if let Some(ref f) = self.config.autogen_warning {
+ out.new_line_if_not_start();
+ write!(out, "{}", f);
+ out.new_line();
+ }
+
+ if self.config.no_includes
+ && self.config.sys_includes().is_empty()
+ && self.config.includes().is_empty()
+ && (self.config.cython.cimports.is_empty() || self.config.language != Language::Cython)
+ && self.config.after_includes.is_none()
+ {
+ return;
+ }
+
+ out.new_line_if_not_start();
+
+ if !self.config.no_includes {
+ match self.config.language {
+ Language::C => {
+ out.write("#include <stdarg.h>");
+ out.new_line();
+ out.write("#include <stdbool.h>");
+ out.new_line();
+ if self.config.usize_is_size_t {
+ out.write("#include <stddef.h>");
+ out.new_line();
+ }
+ out.write("#include <stdint.h>");
+ out.new_line();
+ out.write("#include <stdlib.h>");
+ out.new_line();
+ }
+ Language::Cxx => {
+ out.write("#include <cstdarg>");
+ out.new_line();
+ if self.config.usize_is_size_t {
+ out.write("#include <cstddef>");
+ out.new_line();
+ }
+ out.write("#include <cstdint>");
+ out.new_line();
+ out.write("#include <cstdlib>");
+ out.new_line();
+ out.write("#include <ostream>");
+ out.new_line();
+ out.write("#include <new>");
+ out.new_line();
+ if self.config.enumeration.cast_assert_name.is_none()
+ && (self.config.enumeration.derive_mut_casts
+ || self.config.enumeration.derive_const_casts)
+ {
+ out.write("#include <cassert>");
+ out.new_line();
+ }
+ }
+ Language::Cython => {
+ out.write(
+ "from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t",
+ );
+ out.new_line();
+ out.write(
+ "from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t",
+ );
+ out.new_line();
+ out.write("cdef extern from *");
+ out.open_brace();
+ out.write("ctypedef bint bool");
+ out.new_line();
+ out.write("ctypedef struct va_list");
+ out.new_line();
+ out.close_brace(false);
+ }
+ }
+ }
+
+ for include in self.config.sys_includes() {
+ write!(out, "#include <{}>", include);
+ out.new_line();
+ }
+
+ for include in self.config.includes() {
+ write!(out, "#include \"{}\"", include);
+ out.new_line();
+ }
+
+ if self.config.language == Language::Cython {
+ for (module, names) in &self.config.cython.cimports {
+ write!(out, "from {} cimport {}", module, names.join(", "));
+ out.new_line();
+ }
+ }
+
+ if let Some(ref line) = self.config.after_includes {
+ write!(out, "{}", line);
+ out.new_line();
+ }
+ }
+
+ pub fn write<F: Write>(&self, file: F) {
+ if self.noop {
+ return;
+ }
+
+ let mut out = SourceWriter::new(file, self);
+
+ self.write_headers(&mut out);
+
+ self.open_namespaces(&mut out);
+
+ for constant in &self.constants {
+ if constant.uses_only_primitive_types() {
+ out.new_line_if_not_start();
+ constant.write(&self.config, &mut out, None);
+ out.new_line();
+ }
+ }
+
+ for item in &self.items {
+ if item
+ .deref()
+ .annotations()
+ .bool("no-export")
+ .unwrap_or(false)
+ {
+ continue;
+ }
+
+ out.new_line_if_not_start();
+ match *item {
+ ItemContainer::Constant(..) => unreachable!(),
+ ItemContainer::Static(..) => unreachable!(),
+ ItemContainer::Enum(ref x) => x.write(&self.config, &mut out),
+ ItemContainer::Struct(ref x) => x.write(&self.config, &mut out),
+ ItemContainer::Union(ref x) => x.write(&self.config, &mut out),
+ ItemContainer::OpaqueItem(ref x) => x.write(&self.config, &mut out),
+ ItemContainer::Typedef(ref x) => x.write(&self.config, &mut out),
+ }
+ out.new_line();
+ }
+
+ for constant in &self.constants {
+ if !constant.uses_only_primitive_types() {
+ out.new_line_if_not_start();
+ constant.write(&self.config, &mut out, None);
+ out.new_line();
+ }
+ }
+
+ if !self.functions.is_empty() || !self.globals.is_empty() {
+ if self.config.cpp_compatible_c() {
+ out.new_line_if_not_start();
+ out.write("#ifdef __cplusplus");
+ }
+
+ if self.config.language == Language::Cxx {
+ if let Some(ref using_namespaces) = self.config.using_namespaces {
+ for namespace in using_namespaces {
+ out.new_line();
+ write!(out, "using namespace {};", namespace);
+ }
+ out.new_line();
+ }
+ }
+
+ if self.config.language == Language::Cxx || self.config.cpp_compatible_c() {
+ out.new_line();
+ out.write("extern \"C\" {");
+ out.new_line();
+ }
+
+ if self.config.cpp_compatible_c() {
+ out.write("#endif // __cplusplus");
+ out.new_line();
+ }
+
+ for global in &self.globals {
+ out.new_line_if_not_start();
+ global.write(&self.config, &mut out);
+ out.new_line();
+ }
+
+ for function in &self.functions {
+ out.new_line_if_not_start();
+ function.write(&self.config, &mut out);
+ out.new_line();
+ }
+
+ if self.config.cpp_compatible_c() {
+ out.new_line();
+ out.write("#ifdef __cplusplus");
+ }
+
+ if self.config.language == Language::Cxx || self.config.cpp_compatible_c() {
+ out.new_line();
+ out.write("} // extern \"C\"");
+ out.new_line();
+ }
+
+ if self.config.cpp_compatible_c() {
+ out.write("#endif // __cplusplus");
+ out.new_line();
+ }
+ }
+
+ if self.config.language == Language::Cython
+ && self.globals.is_empty()
+ && self.constants.is_empty()
+ && self.items.is_empty()
+ && self.functions.is_empty()
+ {
+ out.write("pass");
+ }
+
+ self.close_namespaces(&mut out);
+
+ if let Some(f) = self.config.include_guard() {
+ out.new_line_if_not_start();
+ if self.config.language == Language::C {
+ write!(out, "#endif /* {} */", f);
+ } else {
+ write!(out, "#endif // {}", f);
+ }
+ out.new_line();
+ }
+ if let Some(ref f) = self.config.trailer {
+ out.new_line_if_not_start();
+ write!(out, "{}", f);
+ if !f.ends_with('\n') {
+ out.new_line();
+ }
+ }
+ }
+
+ fn all_namespaces(&self) -> Vec<&str> {
+ if self.config.language != Language::Cxx && !self.config.cpp_compatible_c() {
+ return vec![];
+ }
+ let mut ret = vec![];
+ if let Some(ref namespace) = self.config.namespace {
+ ret.push(&**namespace);
+ }
+ if let Some(ref namespaces) = self.config.namespaces {
+ for namespace in namespaces {
+ ret.push(&**namespace);
+ }
+ }
+ ret
+ }
+
+ fn open_close_namespaces<F: Write>(&self, op: NamespaceOperation, out: &mut SourceWriter<F>) {
+ if self.config.language == Language::Cython {
+ if op == NamespaceOperation::Open {
+ out.new_line();
+ let header = self.config.cython.header.as_deref().unwrap_or("*");
+ write!(out, "cdef extern from {}", header);
+ out.open_brace();
+ } else {
+ out.close_brace(false);
+ }
+ return;
+ }
+
+ let mut namespaces = self.all_namespaces();
+ if namespaces.is_empty() {
+ return;
+ }
+
+ if op == NamespaceOperation::Close {
+ namespaces.reverse();
+ }
+
+ if self.config.cpp_compatible_c() {
+ out.new_line_if_not_start();
+ out.write("#ifdef __cplusplus");
+ }
+
+ for namespace in namespaces {
+ out.new_line();
+ match op {
+ NamespaceOperation::Open => write!(out, "namespace {} {{", namespace),
+ NamespaceOperation::Close => write!(out, "}} // namespace {}", namespace),
+ }
+ }
+
+ out.new_line();
+ if self.config.cpp_compatible_c() {
+ out.write("#endif // __cplusplus");
+ out.new_line();
+ }
+ }
+
+ pub(crate) fn open_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
+ self.open_close_namespaces(NamespaceOperation::Open, out);
+ }
+
+ pub(crate) fn close_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
+ self.open_close_namespaces(NamespaceOperation::Close, out);
+ }
+}
diff --git a/src/bindgen/bitflags.rs b/src/bindgen/bitflags.rs
new file mode 100644
index 0000000..2ca0be6
--- /dev/null
+++ b/src/bindgen/bitflags.rs
@@ -0,0 +1,289 @@
+/* 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 proc_macro2::TokenStream;
+use std::collections::HashSet;
+use syn::fold::Fold;
+use syn::parse::{Parse, ParseStream, Parser, Result as ParseResult};
+
+// $(#[$outer:meta])*
+// ($($vis:tt)*) $BitFlags:ident: $T:ty {
+// $(
+// $(#[$inner:ident $($args:tt)*])*
+// const $Flag:ident = $value:expr;
+// )+
+// }
+#[derive(Debug)]
+pub struct BitflagsStruct {
+ attrs: Vec<syn::Attribute>,
+ vis: syn::Visibility,
+ #[allow(dead_code)]
+ struct_token: Token![struct],
+ name: syn::Ident,
+ #[allow(dead_code)]
+ colon_token: Token![:],
+ repr: syn::Type,
+ flags: Flags,
+}
+
+// impl $BitFlags:ident: $T:ty {
+// $(
+// $(#[$inner:ident $($args:tt)*])*
+// const $Flag:ident = $value:expr;
+// )+
+// }
+#[derive(Debug)]
+pub struct BitflagsImpl {
+ #[allow(dead_code)]
+ impl_token: Token![impl],
+ name: syn::Ident,
+ #[allow(dead_code)]
+ colon_token: Token![:],
+ repr: syn::Type,
+ flags: Flags,
+}
+
+#[derive(Debug)]
+pub enum Bitflags {
+ Struct(BitflagsStruct),
+ Impl(BitflagsImpl),
+}
+
+impl Bitflags {
+ pub fn expand(&self) -> (Option<syn::ItemStruct>, syn::ItemImpl) {
+ match self {
+ Bitflags::Struct(BitflagsStruct {
+ attrs,
+ vis,
+ name,
+ repr,
+ flags,
+ ..
+ }) => {
+ let struct_ = parse_quote! {
+ #(#attrs)*
+ #vis struct #name {
+ bits: #repr,
+ }
+ };
+
+ let consts = flags.expand(name, repr, false);
+ let impl_ = parse_quote! {
+ impl #name {
+ #consts
+ }
+ };
+
+ (Some(struct_), impl_)
+ }
+ Bitflags::Impl(BitflagsImpl {
+ name, repr, flags, ..
+ }) => {
+ let consts = flags.expand(name, repr, true);
+ let impl_: syn::ItemImpl = parse_quote! {
+ impl #name {
+ #consts
+ }
+ };
+ (None, impl_)
+ }
+ }
+ }
+}
+
+impl Parse for Bitflags {
+ fn parse(input: ParseStream) -> ParseResult<Self> {
+ Ok(if input.peek(Token![impl]) {
+ Self::Impl(BitflagsImpl {
+ impl_token: input.parse()?,
+ name: input.parse()?,
+ colon_token: input.parse()?,
+ repr: input.parse()?,
+ flags: input.parse()?,
+ })
+ } else {
+ Self::Struct(BitflagsStruct {
+ attrs: input.call(syn::Attribute::parse_outer)?,
+ vis: input.parse()?,
+ struct_token: input.parse()?,
+ name: input.parse()?,
+ colon_token: input.parse()?,
+ repr: input.parse()?,
+ flags: input.parse()?,
+ })
+ })
+ }
+}
+
+// $(#[$inner:ident $($args:tt)*])*
+// const $Flag:ident = $value:expr;
+#[derive(Debug)]
+struct Flag {
+ attrs: Vec<syn::Attribute>,
+ #[allow(dead_code)]
+ const_token: Token![const],
+ name: syn::Ident,
+ #[allow(dead_code)]
+ equals_token: Token![=],
+ value: syn::Expr,
+ #[allow(dead_code)]
+ semicolon_token: Token![;],
+}
+
+struct FlagValueFold<'a> {
+ struct_name: &'a syn::Ident,
+ flag_names: &'a HashSet<String>,
+ out_of_line: bool,
+}
+
+impl<'a> FlagValueFold<'a> {
+ fn is_self(&self, ident: &syn::Ident) -> bool {
+ ident == self.struct_name || ident == "Self"
+ }
+}
+
+impl<'a> Fold for FlagValueFold<'a> {
+ fn fold_expr(&mut self, node: syn::Expr) -> syn::Expr {
+ // bitflags 2 doesn't expose `bits` publically anymore, and the documented way to
+ // combine flags is using the `bits` method, e.g.
+ // ```
+ // bitflags! {
+ // struct Flags: u8 {
+ // const A = 1;
+ // const B = 1 << 1;
+ // const AB = Flags::A.bits() | Flags::B.bits();
+ // }
+ // }
+ // ```
+ // As we're transforming the struct definition into `struct StructName { bits: T }`
+ // as far as our bindings generation is concerned, `bits` is available as a field,
+ // so by replacing `StructName::FLAG.bits()` with `StructName::FLAG.bits`, we make
+ // e.g. `Flags::AB` available in the generated bindings.
+ // For out-of-line definitions of the struct(*), where the struct is defined as a
+ // newtype, we replace it with `StructName::FLAGS.0`.
+ // * definitions like:
+ // ```
+ // struct Flags(u8);
+ // bitflags! {
+ // impl Flags: u8 {
+ // const A = 1;
+ // const B = 1 << 1;
+ // const AB = Flags::A.bits() | Flags::B.bits();
+ // }
+ // }
+ // ```
+ match node {
+ syn::Expr::MethodCall(syn::ExprMethodCall {
+ attrs,
+ receiver,
+ dot_token,
+ method,
+ args,
+ ..
+ }) if method == "bits"
+ && args.is_empty()
+ && matches!(&*receiver,
+ syn::Expr::Path(syn::ExprPath { path, .. })
+ if path.segments.len() == 2
+ && self.is_self(&path.segments.first().unwrap().ident)
+ && self
+ .flag_names
+ .contains(&path.segments.last().unwrap().ident.to_string())) =>
+ {
+ return syn::Expr::Field(syn::ExprField {
+ attrs,
+ base: receiver,
+ dot_token,
+ member: if self.out_of_line {
+ syn::Member::Unnamed(parse_quote! {0})
+ } else {
+ syn::Member::Named(method)
+ },
+ });
+ }
+ _ => {}
+ }
+ syn::fold::fold_expr(self, node)
+ }
+}
+
+impl Flag {
+ fn expand(
+ &self,
+ struct_name: &syn::Ident,
+ repr: &syn::Type,
+ flag_names: &HashSet<String>,
+ out_of_line: bool,
+ ) -> TokenStream {
+ let Flag {
+ ref attrs,
+ ref name,
+ ref value,
+ ..
+ } = *self;
+ let folded_value = FlagValueFold {
+ struct_name,
+ flag_names,
+ out_of_line,
+ }
+ .fold_expr(value.clone());
+ let value = if out_of_line {
+ quote! { ((#folded_value) as #repr) }
+ } else {
+ quote! { { bits: (#folded_value) as #repr } }
+ };
+ quote! {
+ #(#attrs)*
+ pub const #name : #struct_name = #struct_name #value;
+ }
+ }
+}
+
+impl Parse for Flag {
+ fn parse(input: ParseStream) -> ParseResult<Self> {
+ Ok(Self {
+ attrs: input.call(syn::Attribute::parse_outer)?,
+ const_token: input.parse()?,
+ name: input.parse()?,
+ equals_token: input.parse()?,
+ value: input.parse()?,
+ semicolon_token: input.parse()?,
+ })
+ }
+}
+
+#[derive(Debug)]
+struct Flags(Vec<Flag>);
+
+impl Parse for Flags {
+ fn parse(input: ParseStream) -> ParseResult<Self> {
+ let content;
+ let _ = braced!(content in input);
+ let mut flags = vec![];
+ while !content.is_empty() {
+ flags.push(content.parse()?);
+ }
+ Ok(Flags(flags))
+ }
+}
+
+impl Flags {
+ fn expand(&self, struct_name: &syn::Ident, repr: &syn::Type, out_of_line: bool) -> TokenStream {
+ let mut ts = quote! {};
+ let flag_names = self
+ .0
+ .iter()
+ .map(|flag| flag.name.to_string())
+ .collect::<HashSet<_>>();
+ for flag in &self.0 {
+ ts.extend(flag.expand(struct_name, repr, &flag_names, out_of_line));
+ }
+ ts
+ }
+}
+
+pub fn parse(tokens: TokenStream) -> ParseResult<Bitflags> {
+ let parser = Bitflags::parse;
+ parser.parse2(tokens)
+}
diff --git a/src/bindgen/builder.rs b/src/bindgen/builder.rs
new file mode 100644
index 0000000..a0328b4
--- /dev/null
+++ b/src/bindgen/builder.rs
@@ -0,0 +1,424 @@
+/* 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::path;
+
+use crate::bindgen::bindings::Bindings;
+use crate::bindgen::cargo::Cargo;
+use crate::bindgen::config::{Braces, Config, Language, Profile, Style};
+use crate::bindgen::error::Error;
+use crate::bindgen::library::Library;
+use crate::bindgen::parser::{self, Parse};
+
+/// A builder for generating a bindings header.
+#[derive(Debug, Clone)]
+pub struct Builder {
+ config: Config,
+ srcs: Vec<path::PathBuf>,
+ lib: Option<(path::PathBuf, Option<String>)>,
+ lib_cargo: Option<Cargo>,
+ std_types: bool,
+ lockfile: Option<path::PathBuf>,
+}
+
+impl Builder {
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> Builder {
+ Builder {
+ config: Config::default(),
+ srcs: Vec::new(),
+ lib: None,
+ lib_cargo: None,
+ std_types: true,
+ lockfile: None,
+ }
+ }
+
+ #[allow(unused)]
+ pub fn with_header<S: AsRef<str>>(mut self, header: S) -> Builder {
+ self.config.header = Some(String::from(header.as_ref()));
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_no_includes(mut self) -> Builder {
+ self.config.no_includes = true;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_include<S: AsRef<str>>(mut self, include: S) -> Builder {
+ self.config.includes.push(String::from(include.as_ref()));
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_sys_include<S: AsRef<str>>(mut self, include: S) -> Builder {
+ self.config
+ .sys_includes
+ .push(String::from(include.as_ref()));
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_after_include<S: AsRef<str>>(mut self, line: S) -> Builder {
+ self.config.after_includes = Some(String::from(line.as_ref()));
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_trailer<S: AsRef<str>>(mut self, trailer: S) -> Builder {
+ self.config.trailer = Some(String::from(trailer.as_ref()));
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_include_guard<S: AsRef<str>>(mut self, include_guard: S) -> Builder {
+ self.config.include_guard = Some(String::from(include_guard.as_ref()));
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_pragma_once(mut self, pragma_once: bool) -> Builder {
+ self.config.pragma_once = pragma_once;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_autogen_warning<S: AsRef<str>>(mut self, autogen_warning: S) -> Builder {
+ self.config.autogen_warning = Some(String::from(autogen_warning.as_ref()));
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_include_version(mut self, include_version: bool) -> Builder {
+ self.config.include_version = include_version;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_namespace<S: AsRef<str>>(mut self, namespace: S) -> Builder {
+ self.config.namespace = Some(String::from(namespace.as_ref()));
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_namespaces<S: AsRef<str>>(mut self, namespaces: &[S]) -> Builder {
+ self.config.namespaces = Some(
+ namespaces
+ .iter()
+ .map(|x| String::from(x.as_ref()))
+ .collect(),
+ );
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_using_namespaces<S: AsRef<str>>(mut self, namespaces: &[S]) -> Builder {
+ self.config.using_namespaces = Some(
+ namespaces
+ .iter()
+ .map(|x| String::from(x.as_ref()))
+ .collect(),
+ );
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_braces(mut self, braces: Braces) -> Builder {
+ self.config.braces = braces;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_line_length(mut self, line_length: usize) -> Builder {
+ self.config.line_length = line_length;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_tab_width(mut self, tab_width: usize) -> Builder {
+ self.config.tab_width = tab_width;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_language(mut self, language: Language) -> Builder {
+ self.config.language = language;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_cpp_compat(mut self, cpp_compat: bool) -> Builder {
+ self.config.cpp_compat = cpp_compat;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_style(mut self, style: Style) -> Builder {
+ self.config.style = style;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn include_item<S: AsRef<str>>(mut self, item_name: S) -> Builder {
+ self.config
+ .export
+ .include
+ .push(String::from(item_name.as_ref()));
+ self
+ }
+
+ #[allow(unused)]
+ pub fn exclude_item<S: AsRef<str>>(mut self, item_name: S) -> Builder {
+ self.config
+ .export
+ .exclude
+ .push(String::from(item_name.as_ref()));
+ self
+ }
+
+ #[allow(unused)]
+ pub fn rename_item<S: AsRef<str>>(mut self, from: S, to: S) -> Builder {
+ self.config
+ .export
+ .rename
+ .insert(String::from(from.as_ref()), String::from(to.as_ref()));
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_item_prefix<S: AsRef<str>>(mut self, prefix: S) -> Builder {
+ self.config.export.prefix = Some(String::from(prefix.as_ref()));
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_parse_deps(mut self, parse_deps: bool) -> Builder {
+ self.config.parse.parse_deps = parse_deps;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_parse_include<S: AsRef<str>>(mut self, include: &[S]) -> Builder {
+ self.config.parse.include =
+ Some(include.iter().map(|x| String::from(x.as_ref())).collect());
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_parse_exclude<S: AsRef<str>>(mut self, exclude: &[S]) -> Builder {
+ self.config.parse.exclude = exclude.iter().map(|x| String::from(x.as_ref())).collect();
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_parse_expand<S: AsRef<str>>(mut self, expand: &[S]) -> Builder {
+ self.config.parse.expand.crates = expand.iter().map(|x| String::from(x.as_ref())).collect();
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_parse_expand_all_features(mut self, expand_all_features: bool) -> Builder {
+ self.config.parse.expand.all_features = expand_all_features;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_parse_expand_default_features(mut self, expand_default_features: bool) -> Builder {
+ self.config.parse.expand.default_features = expand_default_features;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_parse_expand_features<S: AsRef<str>>(mut self, expand_features: &[S]) -> Builder {
+ self.config.parse.expand.features = Some(
+ expand_features
+ .iter()
+ .map(|x| String::from(x.as_ref()))
+ .collect(),
+ );
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_parse_expand_profile(mut self, profile: Profile) -> Builder {
+ self.config.parse.expand.profile = profile;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_parse_extra_bindings<S: AsRef<str>>(mut self, extra_bindings: &[S]) -> Builder {
+ self.config.parse.extra_bindings = extra_bindings
+ .iter()
+ .map(|x| String::from(x.as_ref()))
+ .collect();
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_only_target_dependencies(mut self, only_target_dependencies: bool) -> Builder {
+ self.config.only_target_dependencies = only_target_dependencies;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_documentation(mut self, documentation: bool) -> Builder {
+ self.config.documentation = documentation;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_target_os_define(mut self, platform: &str, preprocessor_define: &str) -> Builder {
+ self.config.defines.insert(
+ format!("target_os = {}", platform),
+ preprocessor_define.to_owned(),
+ );
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_define(mut self, key: &str, value: &str, preprocessor_define: &str) -> Builder {
+ self.config.defines.insert(
+ format!("{} = {}", key, value),
+ preprocessor_define.to_owned(),
+ );
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_config(mut self, config: Config) -> Builder {
+ self.config = config;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_std_types(mut self, std_types: bool) -> Builder {
+ self.std_types = std_types;
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_src<P: AsRef<path::Path>>(mut self, src: P) -> Builder {
+ self.srcs.push(src.as_ref().to_owned());
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_crate<P: AsRef<path::Path>>(mut self, lib_dir: P) -> Builder {
+ debug_assert!(self.lib.is_none());
+ debug_assert!(self.lib_cargo.is_none());
+ self.lib = Some((path::PathBuf::from(lib_dir.as_ref()), None));
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_crate_and_name<P: AsRef<path::Path>, S: AsRef<str>>(
+ mut self,
+ lib_dir: P,
+ binding_lib_name: S,
+ ) -> Builder {
+ debug_assert!(self.lib.is_none());
+ debug_assert!(self.lib_cargo.is_none());
+ self.lib = Some((
+ path::PathBuf::from(lib_dir.as_ref()),
+ Some(String::from(binding_lib_name.as_ref())),
+ ));
+ self
+ }
+
+ #[allow(unused)]
+ pub(crate) fn with_cargo(mut self, lib: Cargo) -> Builder {
+ debug_assert!(self.lib.is_none());
+ debug_assert!(self.lib_cargo.is_none());
+ self.lib_cargo = Some(lib);
+ self
+ }
+
+ #[allow(unused)]
+ pub fn with_lockfile<P: AsRef<path::Path>>(mut self, lockfile: P) -> Builder {
+ debug_assert!(self.lockfile.is_none());
+ debug_assert!(self.lib_cargo.is_none());
+ self.lockfile = Some(path::PathBuf::from(lockfile.as_ref()));
+ self
+ }
+
+ pub fn generate(self) -> Result<Bindings, Error> {
+ // If macro expansion is enabled, then cbindgen will attempt to build the crate
+ // and will run its build script which may run cbindgen again. That second run may start
+ // infinite recursion, or overwrite previously written files with bindings.
+ // So if we are called recursively, we are skipping the whole generation
+ // and produce "noop" bindings that won't be able to overwrite anything.
+ if std::env::var("_CBINDGEN_IS_RUNNING").is_ok() {
+ return Ok(Bindings::new(
+ self.config,
+ Default::default(),
+ Default::default(),
+ Default::default(),
+ Default::default(),
+ Default::default(),
+ Default::default(),
+ Default::default(),
+ true,
+ ));
+ }
+
+ let mut result = Parse::new();
+
+ if self.std_types {
+ result.add_std_types();
+ }
+
+ for x in &self.srcs {
+ result.extend_with(&parser::parse_src(x, &self.config)?);
+ }
+
+ if let Some((lib_dir, binding_lib_name)) = self.lib.clone() {
+ let lockfile = self.lockfile.as_ref().and_then(|p| p.to_str());
+
+ let cargo = Cargo::load(
+ &lib_dir,
+ lockfile,
+ binding_lib_name.as_deref(),
+ self.config.parse.parse_deps,
+ self.config.parse.clean,
+ self.config.only_target_dependencies,
+ /* existing_metadata = */ None,
+ )?;
+
+ result.extend_with(&parser::parse_lib(cargo, &self.config)?);
+ } else if let Some(cargo) = self.lib_cargo.clone() {
+ result.extend_with(&parser::parse_lib(cargo, &self.config)?);
+ }
+
+ result.source_files.extend_from_slice(self.srcs.as_slice());
+
+ Library::new(
+ self.config,
+ result.constants,
+ result.globals,
+ result.enums,
+ result.structs,
+ result.unions,
+ result.opaque_items,
+ result.typedefs,
+ result.functions,
+ result.source_files,
+ )
+ .generate()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn with_style() {
+ assert_eq!(
+ Style::Tag,
+ Builder::new().with_style(Style::Tag).config.style
+ );
+ }
+}
diff --git a/src/bindgen/cargo/cargo.rs b/src/bindgen/cargo/cargo.rs
new file mode 100644
index 0000000..69cf938
--- /dev/null
+++ b/src/bindgen/cargo/cargo.rs
@@ -0,0 +1,252 @@
+/* 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::path::{Path, PathBuf};
+
+use crate::bindgen::cargo::cargo_expand;
+use crate::bindgen::cargo::cargo_lock::{self, Lock};
+pub(crate) use crate::bindgen::cargo::cargo_metadata::PackageRef;
+use crate::bindgen::cargo::cargo_metadata::{self, Metadata};
+use crate::bindgen::cargo::cargo_toml;
+use crate::bindgen::config::Profile;
+use crate::bindgen::error::Error;
+use crate::bindgen::ir::Cfg;
+
+/// Parse a dependency string used in Cargo.lock
+fn parse_dep_string(dep_string: &str) -> (&str, Option<&str>) {
+ let split: Vec<&str> = dep_string.split_whitespace().collect();
+
+ (split[0], split.get(1).cloned())
+}
+
+/// A collection of metadata for a library from cargo.
+#[derive(Clone, Debug)]
+pub(crate) struct Cargo {
+ manifest_path: PathBuf,
+ binding_crate_name: String,
+ lock: Option<Lock>,
+ metadata: Metadata,
+ clean: bool,
+}
+
+impl Cargo {
+ /// Gather metadata from cargo for a specific library and binding crate
+ /// name. If dependency finding isn't needed then Cargo.lock files don't
+ /// need to be parsed.
+ pub(crate) fn load(
+ crate_dir: &Path,
+ lock_file: Option<&str>,
+ binding_crate_name: Option<&str>,
+ use_cargo_lock: bool,
+ clean: bool,
+ only_target_dependencies: bool,
+ existing_metadata_file: Option<&Path>,
+ ) -> Result<Cargo, Error> {
+ let toml_path = crate_dir.join("Cargo.toml");
+ let metadata =
+ cargo_metadata::metadata(&toml_path, existing_metadata_file, only_target_dependencies)
+ .map_err(|x| Error::CargoMetadata(toml_path.to_str().unwrap().to_owned(), x))?;
+ let lock_path = lock_file
+ .map(PathBuf::from)
+ .unwrap_or_else(|| Path::new(&metadata.workspace_root).join("Cargo.lock"));
+
+ let lock = if use_cargo_lock {
+ match cargo_lock::lock(&lock_path) {
+ Ok(lock) => Some(lock),
+ Err(x) => {
+ warn!("Couldn't load lock file {:?}: {:?}", lock_path, x);
+ None
+ }
+ }
+ } else {
+ None
+ };
+
+ // Use the specified binding crate name or infer it from the manifest
+ let binding_crate_name = match binding_crate_name {
+ Some(s) => s.to_owned(),
+ None => {
+ let manifest = cargo_toml::manifest(&toml_path)
+ .map_err(|x| Error::CargoToml(toml_path.to_str().unwrap().to_owned(), x))?;
+ manifest.package.name
+ }
+ };
+
+ Ok(Cargo {
+ manifest_path: toml_path,
+ binding_crate_name,
+ lock,
+ metadata,
+ clean,
+ })
+ }
+
+ pub(crate) fn binding_crate_name(&self) -> &str {
+ &self.binding_crate_name
+ }
+
+ pub(crate) fn binding_crate_ref(&self) -> PackageRef {
+ match self.find_pkg_ref(&self.binding_crate_name) {
+ Some(pkg_ref) => pkg_ref,
+ None => panic!(
+ "Unable to find {} for {:?}",
+ self.binding_crate_name, self.manifest_path
+ ),
+ }
+ }
+
+ pub(crate) fn dependencies(&self, package: &PackageRef) -> Vec<(PackageRef, Option<Cfg>)> {
+ let lock = match self.lock {
+ Some(ref lock) => lock,
+ None => return vec![],
+ };
+
+ let mut dependencies = None;
+
+ // Find the dependencies listing in the lockfile
+ if let Some(ref root) = lock.root {
+ // If the version is not on the lockfile then it shouldn't be
+ // ambiguous.
+ if root.name == package.name
+ && package
+ .version
+ .as_ref()
+ .map_or(true, |v| *v == root.version)
+ {
+ dependencies = root.dependencies.as_ref();
+ }
+ }
+ if dependencies.is_none() {
+ if let Some(ref lock_packages) = lock.package {
+ for lock_package in lock_packages {
+ if lock_package.name == package.name
+ && package
+ .version
+ .as_ref()
+ .map_or(true, |v| *v == lock_package.version)
+ {
+ dependencies = lock_package.dependencies.as_ref();
+ break;
+ }
+ }
+ }
+ }
+ if dependencies.is_none() {
+ return vec![];
+ }
+
+ dependencies
+ .unwrap()
+ .iter()
+ .map(|dep| {
+ let (dep_name, dep_version) = parse_dep_string(dep);
+
+ // If a version was not specified find the only package with the name of the dependency
+ let dep_version = dep_version.or_else(|| {
+ let mut versions = self.metadata.packages.iter().filter_map(|package| {
+ if package.name_and_version.name != dep_name {
+ return None;
+ }
+ package.name_and_version.version.as_deref()
+ });
+
+ // If the iterator contains more items, meaning multiple versions of the same
+ // package are present, warn! amd abort.
+ let version = versions.next();
+ if versions.next().is_none() {
+ version
+ } else {
+ warn!("when looking for a version for package {}, multiple versions where found", dep_name);
+ None
+ }
+ });
+
+ // Try to find the cfgs in the Cargo.toml
+ let cfg = self
+ .metadata
+ .packages
+ .get(package)
+ .and_then(|meta_package| meta_package.dependencies.get(dep_name))
+ .and_then(Cfg::load_metadata);
+
+ let package_ref = PackageRef {
+ name: dep_name.to_owned(),
+ version: dep_version.map(|v| v.to_owned()),
+ };
+
+ (package_ref, cfg)
+ })
+ .collect()
+ }
+
+ /// Finds the package reference in `cargo metadata` that has `package_name`
+ /// ignoring the version.
+ fn find_pkg_ref(&self, package_name: &str) -> Option<PackageRef> {
+ for package in &self.metadata.packages {
+ if package.name_and_version.name == package_name {
+ return Some(package.name_and_version.clone());
+ }
+ }
+ None
+ }
+
+ /// Finds the directory for a specified package reference.
+ #[allow(unused)]
+ pub(crate) fn find_crate_dir(&self, package: &PackageRef) -> Option<PathBuf> {
+ self.metadata
+ .packages
+ .get(package)
+ .and_then(|meta_package| {
+ Path::new(&meta_package.manifest_path)
+ .parent()
+ .map(|x| x.to_owned())
+ })
+ }
+
+ /// Finds `src/lib.rs` for a specified package reference.
+ pub(crate) fn find_crate_src(&self, package: &PackageRef) -> Option<PathBuf> {
+ let kind_lib = String::from("lib");
+ let kind_staticlib = String::from("staticlib");
+ let kind_rlib = String::from("rlib");
+ let kind_cdylib = String::from("cdylib");
+ let kind_dylib = String::from("dylib");
+
+ self.metadata
+ .packages
+ .get(package)
+ .and_then(|meta_package| {
+ for target in &meta_package.targets {
+ if target.kind.contains(&kind_lib)
+ || target.kind.contains(&kind_staticlib)
+ || target.kind.contains(&kind_rlib)
+ || target.kind.contains(&kind_cdylib)
+ || target.kind.contains(&kind_dylib)
+ {
+ return Some(PathBuf::from(&target.src_path));
+ }
+ }
+ None
+ })
+ }
+
+ pub(crate) fn expand_crate(
+ &self,
+ package: &PackageRef,
+ expand_all_features: bool,
+ expand_default_features: bool,
+ expand_features: &Option<Vec<String>>,
+ profile: Profile,
+ ) -> Result<String, cargo_expand::Error> {
+ cargo_expand::expand(
+ &self.manifest_path,
+ &package.name,
+ package.version.as_deref(),
+ self.clean,
+ expand_all_features,
+ expand_default_features,
+ expand_features,
+ profile,
+ )
+ }
+}
diff --git a/src/bindgen/cargo/cargo_expand.rs b/src/bindgen/cargo/cargo_expand.rs
new file mode 100644
index 0000000..565a0d1
--- /dev/null
+++ b/src/bindgen/cargo/cargo_expand.rs
@@ -0,0 +1,145 @@
+/* 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 crate::bindgen::config::Profile;
+use std::env;
+use std::error;
+use std::fmt;
+use std::io;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+use std::str::{from_utf8, Utf8Error};
+
+extern crate tempfile;
+use self::tempfile::Builder;
+
+#[derive(Debug)]
+/// Possible errors that can occur during `rustc -Zunpretty=expanded`.
+pub enum Error {
+ /// Error during creation of temporary directory
+ Io(io::Error),
+ /// Output of `cargo metadata` was not valid utf8
+ Utf8(Utf8Error),
+ /// Error during execution of `cargo rustc -Zunpretty=expanded`
+ Compile(String),
+}
+
+impl From<io::Error> for Error {
+ fn from(err: io::Error) -> Self {
+ Error::Io(err)
+ }
+}
+impl From<Utf8Error> for Error {
+ fn from(err: Utf8Error) -> Self {
+ Error::Utf8(err)
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Error::Io(ref err) => err.fmt(f),
+ Error::Utf8(ref err) => err.fmt(f),
+ Error::Compile(ref err) => write!(f, "{}", err),
+ }
+ }
+}
+
+impl error::Error for Error {
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match self {
+ Error::Io(ref err) => Some(err),
+ Error::Utf8(ref err) => Some(err),
+ Error::Compile(..) => None,
+ }
+ }
+}
+
+/// Use rustc to expand and pretty print the crate into a single file,
+/// removing any macros in the process.
+#[allow(clippy::too_many_arguments)]
+pub fn expand(
+ manifest_path: &Path,
+ crate_name: &str,
+ version: Option<&str>,
+ use_tempdir: bool,
+ expand_all_features: bool,
+ expand_default_features: bool,
+ expand_features: &Option<Vec<String>>,
+ profile: Profile,
+) -> Result<String, Error> {
+ let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));
+ let mut cmd = Command::new(cargo);
+
+ let mut _temp_dir = None; // drop guard
+ if use_tempdir {
+ _temp_dir = Some(Builder::new().prefix("cbindgen-expand").tempdir()?);
+ cmd.env("CARGO_TARGET_DIR", _temp_dir.unwrap().path());
+ } else if let Ok(ref path) = env::var("CARGO_EXPAND_TARGET_DIR") {
+ cmd.env("CARGO_TARGET_DIR", path);
+ } else if let Ok(ref path) = env::var("OUT_DIR") {
+ // When cbindgen was started programatically from a build.rs file, Cargo is running and
+ // locking the default target directory. In this case we need to use another directory,
+ // else we would end up in a deadlock. If Cargo is running `OUT_DIR` will be set, so we
+ // can use a directory relative to that.
+ cmd.env("CARGO_TARGET_DIR", PathBuf::from(path).join("expanded"));
+ }
+
+ // Set this variable so that we don't call it recursively if we expand a crate that is using
+ // cbindgen
+ cmd.env("_CBINDGEN_IS_RUNNING", "1");
+
+ cmd.arg("rustc");
+ cmd.arg("--lib");
+ // When build with the release profile we can't choose the `check` profile.
+ if profile != Profile::Release {
+ cmd.arg("--profile=check");
+ }
+ cmd.arg("--manifest-path");
+ cmd.arg(manifest_path);
+ if let Some(features) = expand_features {
+ cmd.arg("--features");
+ let mut features_str = String::new();
+ for (index, feature) in features.iter().enumerate() {
+ if index != 0 {
+ features_str.push(' ');
+ }
+ features_str.push_str(feature);
+ }
+ cmd.arg(features_str);
+ }
+ if expand_all_features {
+ cmd.arg("--all-features");
+ }
+ if !expand_default_features {
+ cmd.arg("--no-default-features");
+ }
+ match profile {
+ Profile::Debug => {}
+ Profile::Release => {
+ cmd.arg("--release");
+ }
+ }
+ cmd.arg("-p");
+ let mut package = crate_name.to_owned();
+ if let Some(version) = version {
+ package.push(':');
+ package.push_str(version);
+ }
+ cmd.arg(&package);
+ cmd.arg("--verbose");
+ cmd.arg("--");
+ cmd.arg("-Zunpretty=expanded");
+ info!("Command: {:?}", cmd);
+ let output = cmd.output()?;
+
+ let src = from_utf8(&output.stdout)?.to_owned();
+ let error = from_utf8(&output.stderr)?.to_owned();
+
+ if src.is_empty() {
+ Err(Error::Compile(error))
+ } else {
+ Ok(src)
+ }
+}
diff --git a/src/bindgen/cargo/cargo_lock.rs b/src/bindgen/cargo/cargo_lock.rs
new file mode 100644
index 0000000..9302082
--- /dev/null
+++ b/src/bindgen/cargo/cargo_lock.rs
@@ -0,0 +1,51 @@
+/* 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::fs::File;
+use std::io;
+use std::io::Read;
+use std::path::Path;
+
+#[derive(Debug)]
+/// Possible errors that can occur during Cargo.toml parsing.
+pub enum Error {
+ /// Error during reading of Cargo.toml
+ Io(io::Error),
+ /// Deserialization error
+ Toml(toml::de::Error),
+}
+
+impl From<io::Error> for Error {
+ fn from(err: io::Error) -> Self {
+ Error::Io(err)
+ }
+}
+impl From<toml::de::Error> for Error {
+ fn from(err: toml::de::Error) -> Self {
+ Error::Toml(err)
+ }
+}
+
+#[derive(Clone, Deserialize, Debug)]
+pub struct Lock {
+ pub root: Option<Package>,
+ pub package: Option<Vec<Package>>,
+}
+
+#[derive(Clone, Deserialize, Debug)]
+pub struct Package {
+ pub name: String,
+ pub version: String,
+ /// A list of dependencies formatted like "NAME VERSION-OPT REGISTRY-OPT"
+ pub dependencies: Option<Vec<String>>,
+}
+
+/// Parse the Cargo.toml for a given path
+pub fn lock(manifest_path: &Path) -> Result<Lock, Error> {
+ let mut s = String::new();
+ let mut f = File::open(manifest_path)?;
+ f.read_to_string(&mut s)?;
+
+ toml::from_str::<Lock>(&s).map_err(|x| x.into())
+}
diff --git a/src/bindgen/cargo/cargo_metadata.rs b/src/bindgen/cargo/cargo_metadata.rs
new file mode 100644
index 0000000..01ab80f
--- /dev/null
+++ b/src/bindgen/cargo/cargo_metadata.rs
@@ -0,0 +1,253 @@
+#![deny(missing_docs)]
+#![allow(dead_code)]
+//! Structured access to the output of `cargo metadata`
+//! Usually used from within a `cargo-*` executable
+
+// Forked from `https://github.com/oli-obk/cargo_metadata`
+// Modifications:
+// 1. Remove `resolve` from Metadata because it was causing parse failures
+// 2. Fix the `manifest-path` argument
+// 3. Add `--all-features` argument
+// 4. Remove the `--no-deps` argument
+
+use std::borrow::{Borrow, Cow};
+use std::collections::{HashMap, HashSet};
+use std::env;
+use std::error;
+use std::fmt;
+use std::hash::{Hash, Hasher};
+use std::io;
+use std::path::Path;
+use std::process::{Command, Output};
+use std::str::Utf8Error;
+
+#[derive(Clone, Deserialize, Debug)]
+/// Starting point for metadata returned by `cargo metadata`
+pub struct Metadata {
+ /// A list of all crates referenced by this crate (and the crate itself)
+ pub packages: HashSet<Package>,
+ version: usize,
+ /// path to the workspace containing the `Cargo.lock`
+ pub workspace_root: String,
+}
+
+/// A reference to a package including it's name and the specific version.
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
+pub struct PackageRef {
+ pub name: String,
+ pub version: Option<String>,
+}
+
+#[derive(Clone, Deserialize, Debug)]
+/// A crate
+pub struct Package {
+ #[serde(flatten)]
+ pub name_and_version: PackageRef,
+ id: String,
+ source: Option<String>,
+ /// List of dependencies of this particular package
+ pub dependencies: HashSet<Dependency>,
+ /// Targets provided by the crate (lib, bin, example, test, ...)
+ pub targets: Vec<Target>,
+ features: HashMap<String, Vec<String>>,
+ /// path containing the `Cargo.toml`
+ pub manifest_path: String,
+}
+
+#[derive(Clone, Deserialize, Debug)]
+/// A dependency of the main crate
+pub struct Dependency {
+ /// Name as given in the `Cargo.toml`
+ pub name: String,
+ source: Option<String>,
+ /// Whether this is required or optional
+ pub req: String,
+ kind: Option<String>,
+ optional: bool,
+ uses_default_features: bool,
+ features: Vec<String>,
+ pub target: Option<String>,
+}
+
+#[derive(Clone, Deserialize, Debug)]
+/// A single target (lib, bin, example, ...) provided by a crate
+pub struct Target {
+ /// Name as given in the `Cargo.toml` or generated from the file name
+ pub name: String,
+ /// Kind of target ("bin", "example", "test", "bench", "lib")
+ pub kind: Vec<String>,
+ /// Almost the same as `kind`, except when an example is a library instad of an executable.
+ /// In that case `crate_types` contains things like `rlib` and `dylib` while `kind` is `example`
+ #[serde(default)]
+ pub crate_types: Vec<String>,
+ /// Path to the main source file of the target
+ pub src_path: String,
+}
+
+#[derive(Debug)]
+/// Possible errors that can occur during metadata parsing.
+pub enum Error {
+ /// Error during execution of `cargo metadata`
+ Io(io::Error),
+ /// Metadata extraction failure
+ Metadata(Output),
+ /// Output of `cargo metadata` was not valid utf8
+ Utf8(Utf8Error),
+ /// Deserialization error (structure of json did not match expected structure)
+ Json(serde_json::Error),
+}
+
+impl From<io::Error> for Error {
+ fn from(err: io::Error) -> Self {
+ Error::Io(err)
+ }
+}
+impl From<Utf8Error> for Error {
+ fn from(err: Utf8Error) -> Self {
+ Error::Utf8(err)
+ }
+}
+impl From<serde_json::Error> for Error {
+ fn from(err: serde_json::Error) -> Self {
+ Error::Json(err)
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Error::Io(ref err) => err.fmt(f),
+ Error::Metadata(_) => write!(f, "Metadata error"),
+ Error::Utf8(ref err) => err.fmt(f),
+ Error::Json(ref err) => err.fmt(f),
+ }
+ }
+}
+
+impl error::Error for Error {
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match self {
+ Error::Io(ref err) => Some(err),
+ Error::Metadata(_) => None,
+ Error::Utf8(ref err) => Some(err),
+ Error::Json(ref err) => Some(err),
+ }
+ }
+}
+
+// Implementations that let us lookup Packages and Dependencies by name (string)
+
+impl Borrow<PackageRef> for Package {
+ fn borrow(&self) -> &PackageRef {
+ &self.name_and_version
+ }
+}
+
+impl Hash for Package {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.name_and_version.hash(state);
+ }
+}
+
+impl PartialEq for Package {
+ fn eq(&self, other: &Self) -> bool {
+ self.name_and_version == other.name_and_version
+ }
+}
+
+impl Eq for Package {}
+
+impl Borrow<str> for Dependency {
+ fn borrow(&self) -> &str {
+ &self.name
+ }
+}
+
+impl Hash for Dependency {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.name.hash(state);
+ }
+}
+
+impl PartialEq for Dependency {
+ fn eq(&self, other: &Self) -> bool {
+ self.name == other.name
+ }
+}
+
+impl Eq for Dependency {}
+
+fn discover_target(manifest_path: &Path) -> Option<String> {
+ if let Ok(target) = std::env::var("TARGET") {
+ return Some(target);
+ }
+
+ // We must be running as a standalone script, not under cargo.
+ // Let's use the host platform instead.
+ // We figure out the host platform through rustc and use that.
+ // We unfortunatelly cannot go through cargo, since cargo rustc _also_ builds.
+ // If `rustc` fails to run, we just fall back to not passing --filter-platforms.
+ //
+ // NOTE: We set the current directory in case of rustup shenanigans.
+ let rustc = env::var("RUSTC").unwrap_or_else(|_| String::from("rustc"));
+ debug!("Discovering host platform by {:?}", rustc);
+
+ let rustc_output = Command::new(rustc)
+ .current_dir(manifest_path.parent().unwrap())
+ .arg("-vV")
+ .output();
+ let rustc_output = match rustc_output {
+ Ok(ref out) => String::from_utf8_lossy(&out.stdout),
+ Err(..) => return None,
+ };
+
+ let field = "host: ";
+ rustc_output
+ .lines()
+ .find_map(|l| l.strip_prefix(field).map(|stripped| stripped.to_string()))
+}
+
+/// The main entry point to obtaining metadata
+pub fn metadata(
+ manifest_path: &Path,
+ existing_metadata_file: Option<&Path>,
+ only_target: bool,
+) -> Result<Metadata, Error> {
+ let output;
+ let metadata = match existing_metadata_file {
+ Some(path) => Cow::Owned(std::fs::read_to_string(path)?),
+ None => {
+ let target = if only_target {
+ let target = discover_target(manifest_path);
+ if target.is_none() {
+ warn!(
+ "Failed to discover host platform for cargo metadata; \
+ will fetch dependencies for all platforms."
+ );
+ }
+ target
+ } else {
+ None
+ };
+
+ let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));
+ let mut cmd = Command::new(cargo);
+ cmd.arg("metadata");
+ cmd.arg("--all-features");
+ cmd.arg("--format-version").arg("1");
+ if let Some(target) = target {
+ cmd.arg("--filter-platform").arg(target);
+ }
+ cmd.arg("--manifest-path");
+ cmd.arg(manifest_path);
+ output = cmd.output()?;
+ if !output.status.success() {
+ return Err(Error::Metadata(output));
+ }
+ Cow::Borrowed(std::str::from_utf8(&output.stdout)?)
+ }
+ };
+
+ let meta: Metadata = serde_json::from_str(&metadata)?;
+ Ok(meta)
+}
diff --git a/src/bindgen/cargo/cargo_toml.rs b/src/bindgen/cargo/cargo_toml.rs
new file mode 100644
index 0000000..998176e
--- /dev/null
+++ b/src/bindgen/cargo/cargo_toml.rs
@@ -0,0 +1,67 @@
+/* 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::error;
+use std::fmt;
+use std::fs::File;
+use std::io;
+use std::io::Read;
+use std::path::Path;
+
+#[derive(Debug)]
+/// Possible errors that can occur during Cargo.toml parsing.
+pub enum Error {
+ /// Error during reading of Cargo.toml
+ Io(io::Error),
+ /// Deserialization error
+ Toml(toml::de::Error),
+}
+
+impl From<io::Error> for Error {
+ fn from(err: io::Error) -> Self {
+ Error::Io(err)
+ }
+}
+impl From<toml::de::Error> for Error {
+ fn from(err: toml::de::Error) -> Self {
+ Error::Toml(err)
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Error::Io(ref err) => err.fmt(f),
+ Error::Toml(ref err) => err.fmt(f),
+ }
+ }
+}
+
+impl error::Error for Error {
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match self {
+ Error::Io(ref err) => Some(err),
+ Error::Toml(ref err) => Some(err),
+ }
+ }
+}
+
+#[derive(Clone, Deserialize, Debug)]
+pub struct Manifest {
+ pub package: Package,
+}
+
+#[derive(Clone, Deserialize, Debug)]
+pub struct Package {
+ pub name: String,
+}
+
+/// Parse the Cargo.toml for a given path
+pub fn manifest(manifest_path: &Path) -> Result<Manifest, Error> {
+ let mut s = String::new();
+ let mut f = File::open(manifest_path)?;
+ f.read_to_string(&mut s)?;
+
+ toml::from_str::<Manifest>(&s).map_err(|x| x.into())
+}
diff --git a/src/bindgen/cargo/mod.rs b/src/bindgen/cargo/mod.rs
new file mode 100644
index 0000000..19fef54
--- /dev/null
+++ b/src/bindgen/cargo/mod.rs
@@ -0,0 +1,12 @@
+/* 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/. */
+
+#[allow(clippy::module_inception)]
+mod cargo;
+pub(crate) mod cargo_expand;
+pub(crate) mod cargo_lock;
+pub(crate) mod cargo_metadata;
+pub(crate) mod cargo_toml;
+
+pub(crate) use self::cargo::*;
diff --git a/src/bindgen/cdecl.rs b/src/bindgen/cdecl.rs
new file mode 100644
index 0000000..a9c5a0f
--- /dev/null
+++ b/src/bindgen/cdecl.rs
@@ -0,0 +1,372 @@
+/* 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::io::Write;
+
+use crate::bindgen::config::Layout;
+use crate::bindgen::declarationtyperesolver::DeclarationType;
+use crate::bindgen::ir::{ConstExpr, Function, GenericArgument, Type};
+use crate::bindgen::writer::{ListType, SourceWriter};
+use crate::bindgen::{Config, Language};
+
+// This code is for translating Rust types into C declarations.
+// See Section 6.7, Declarations, in the C standard for background.
+// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
+
+enum CDeclarator {
+ Ptr {
+ is_const: bool,
+ is_nullable: bool,
+ is_ref: bool,
+ },
+ Array(String),
+ Func {
+ args: Vec<(Option<String>, CDecl)>,
+ layout: Layout,
+ never_return: bool,
+ },
+}
+
+impl CDeclarator {
+ fn is_ptr(&self) -> bool {
+ matches!(self, CDeclarator::Ptr { .. } | CDeclarator::Func { .. })
+ }
+}
+
+struct CDecl {
+ type_qualifers: String,
+ type_name: String,
+ type_generic_args: Vec<GenericArgument>,
+ declarators: Vec<CDeclarator>,
+ type_ctype: Option<DeclarationType>,
+ deprecated: Option<String>,
+}
+
+impl CDecl {
+ fn new() -> CDecl {
+ CDecl {
+ type_qualifers: String::new(),
+ type_name: String::new(),
+ type_generic_args: Vec::new(),
+ declarators: Vec::new(),
+ type_ctype: None,
+ deprecated: None,
+ }
+ }
+
+ fn from_type(t: &Type, config: &Config) -> CDecl {
+ let mut cdecl = CDecl::new();
+ cdecl.build_type(t, false, config);
+ cdecl
+ }
+
+ fn from_func_arg(t: &Type, array_length: Option<&str>, config: &Config) -> CDecl {
+ let mut cdecl = CDecl::new();
+ let length = match array_length {
+ Some(l) => l,
+ None => return CDecl::from_type(t, config),
+ };
+ let (ty, is_const) = match t {
+ Type::Ptr { ty, is_const, .. } => (ty, is_const),
+ _ => unreachable!(
+ "Should never have an array length for a non pointer type {:?}",
+ t
+ ),
+ };
+ let ptr_as_array = Type::Array(ty.clone(), ConstExpr::Value(length.to_string()));
+ cdecl.build_type(&ptr_as_array, *is_const, config);
+ cdecl
+ }
+
+ fn from_func(f: &Function, layout: Layout, config: &Config) -> CDecl {
+ let mut cdecl = CDecl::new();
+ cdecl.build_func(f, layout, config);
+ cdecl
+ }
+
+ fn build_func(&mut self, f: &Function, layout: Layout, config: &Config) {
+ let args = f
+ .args
+ .iter()
+ .map(|arg| {
+ (
+ arg.name.clone(),
+ CDecl::from_func_arg(&arg.ty, arg.array_length.as_deref(), config),
+ )
+ })
+ .collect();
+ self.declarators.push(CDeclarator::Func {
+ args,
+ layout,
+ never_return: f.never_return,
+ });
+ self.deprecated = f.annotations.deprecated.clone();
+ self.build_type(&f.ret, false, config);
+ }
+
+ fn build_type(&mut self, t: &Type, is_const: bool, config: &Config) {
+ match t {
+ Type::Path(ref generic) => {
+ if is_const {
+ assert!(
+ self.type_qualifers.is_empty(),
+ "error generating cdecl for {:?}",
+ t
+ );
+ self.type_qualifers = "const".to_owned();
+ }
+
+ assert!(
+ self.type_name.is_empty(),
+ "error generating cdecl for {:?}",
+ t
+ );
+ self.type_name = generic.export_name().to_owned();
+ assert!(
+ self.type_generic_args.is_empty(),
+ "error generating cdecl for {:?}",
+ t
+ );
+ self.type_generic_args = generic.generics().to_owned();
+ self.type_ctype = generic.ctype().cloned();
+ }
+ Type::Primitive(ref p) => {
+ if is_const {
+ assert!(
+ self.type_qualifers.is_empty(),
+ "error generating cdecl for {:?}",
+ t
+ );
+ self.type_qualifers = "const".to_owned();
+ }
+
+ assert!(
+ self.type_name.is_empty(),
+ "error generating cdecl for {:?}",
+ t
+ );
+ self.type_name = p.to_repr_c(config).to_string();
+ }
+ Type::Ptr {
+ ref ty,
+ is_nullable,
+ is_const: ptr_is_const,
+ is_ref,
+ } => {
+ self.declarators.push(CDeclarator::Ptr {
+ is_const,
+ is_nullable: *is_nullable,
+ is_ref: *is_ref,
+ });
+ self.build_type(ty, *ptr_is_const, config);
+ }
+ Type::Array(ref t, ref constant) => {
+ let len = constant.as_str().to_owned();
+ self.declarators.push(CDeclarator::Array(len));
+ self.build_type(t, is_const, config);
+ }
+ Type::FuncPtr {
+ ref ret,
+ ref args,
+ is_nullable: _,
+ never_return,
+ } => {
+ let args = args
+ .iter()
+ .map(|(ref name, ref ty)| (name.clone(), CDecl::from_type(ty, config)))
+ .collect();
+ self.declarators.push(CDeclarator::Ptr {
+ is_const: false,
+ is_nullable: true,
+ is_ref: false,
+ });
+ self.declarators.push(CDeclarator::Func {
+ args,
+ layout: config.function.args.clone(),
+ never_return: *never_return,
+ });
+ self.build_type(ret, false, config);
+ }
+ }
+ }
+
+ fn write<F: Write>(&self, out: &mut SourceWriter<F>, ident: Option<&str>, config: &Config) {
+ // Write the type-specifier and type-qualifier first
+ if !self.type_qualifers.is_empty() {
+ write!(out, "{} ", self.type_qualifers);
+ }
+
+ if config.language != Language::Cython {
+ 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("<");
+ out.write_horizontal_source_list(&self.type_generic_args, ListType::Join(", "));
+ out.write(">");
+ }
+
+ // When we have an identifier, put a space between the type and the declarators
+ if ident.is_some() {
+ out.write(" ");
+ }
+
+ // Write the left part of declarators before the identifier
+ let mut iter_rev = self.declarators.iter().rev().peekable();
+
+ #[allow(clippy::while_let_on_iterator)]
+ while let Some(declarator) = iter_rev.next() {
+ let next_is_pointer = iter_rev.peek().map_or(false, |x| x.is_ptr());
+
+ match *declarator {
+ CDeclarator::Ptr {
+ is_const,
+ is_nullable,
+ is_ref,
+ } => {
+ out.write(if is_ref { "&" } else { "*" });
+ if is_const {
+ out.write("const ");
+ }
+ if !is_nullable && !is_ref && config.language != Language::Cython {
+ if let Some(attr) = &config.pointer.non_null_attribute {
+ write!(out, "{} ", attr);
+ }
+ }
+ }
+ CDeclarator::Array(..) => {
+ if next_is_pointer {
+ out.write("(");
+ }
+ }
+ CDeclarator::Func { .. } => {
+ if next_is_pointer {
+ out.write("(");
+ }
+ }
+ }
+ }
+
+ // Write the identifier
+ if let Some(ident) = ident {
+ write!(out, "{}", ident);
+ }
+
+ // Write the right part of declarators after the identifier
+ let mut iter = self.declarators.iter();
+ let mut last_was_pointer = false;
+
+ #[allow(clippy::while_let_on_iterator)]
+ while let Some(declarator) = iter.next() {
+ match *declarator {
+ CDeclarator::Ptr { .. } => {
+ last_was_pointer = true;
+ }
+ CDeclarator::Array(ref constant) => {
+ if last_was_pointer {
+ out.write(")");
+ }
+ write!(out, "[{}]", constant);
+
+ last_was_pointer = false;
+ }
+ CDeclarator::Func {
+ ref args,
+ ref layout,
+ never_return,
+ } => {
+ if last_was_pointer {
+ out.write(")");
+ }
+
+ out.write("(");
+ if args.is_empty() && config.language == Language::C {
+ out.write("void");
+ }
+
+ fn write_vertical<F: Write>(
+ out: &mut SourceWriter<F>,
+ config: &Config,
+ args: &[(Option<String>, CDecl)],
+ ) {
+ let align_length = out.line_length_for_align();
+ out.push_set_spaces(align_length);
+ for (i, (arg_ident, arg_ty)) in args.iter().enumerate() {
+ if i != 0 {
+ out.write(",");
+ out.new_line();
+ }
+
+ // Convert &Option<String> to Option<&str>
+ let arg_ident = arg_ident.as_ref().map(|x| x.as_ref());
+
+ arg_ty.write(out, arg_ident, config);
+ }
+ out.pop_tab();
+ }
+
+ fn write_horizontal<F: Write>(
+ out: &mut SourceWriter<F>,
+ config: &Config,
+ args: &[(Option<String>, CDecl)],
+ ) {
+ for (i, (arg_ident, arg_ty)) in args.iter().enumerate() {
+ if i != 0 {
+ out.write(", ");
+ }
+
+ // Convert &Option<String> to Option<&str>
+ let arg_ident = arg_ident.as_ref().map(|x| x.as_ref());
+
+ arg_ty.write(out, arg_ident, config);
+ }
+ }
+
+ match layout {
+ Layout::Vertical => write_vertical(out, config, args),
+ Layout::Horizontal => write_horizontal(out, config, args),
+ Layout::Auto => {
+ if !out.try_write(
+ |out| write_horizontal(out, config, args),
+ config.line_length,
+ ) {
+ write_vertical(out, config, args)
+ }
+ }
+ }
+ out.write(")");
+
+ if never_return && config.language != Language::Cython {
+ if let Some(ref no_return_attr) = config.function.no_return {
+ out.write_fmt(format_args!(" {}", no_return_attr));
+ }
+ }
+
+ last_was_pointer = true;
+ }
+ }
+ }
+ }
+}
+
+pub fn write_func<F: Write>(
+ out: &mut SourceWriter<F>,
+ f: &Function,
+ layout: Layout,
+ config: &Config,
+) {
+ CDecl::from_func(f, layout, config).write(out, Some(f.path().name()), config);
+}
+
+pub fn write_field<F: Write>(out: &mut SourceWriter<F>, t: &Type, ident: &str, config: &Config) {
+ CDecl::from_type(t, config).write(out, Some(ident), config);
+}
+
+pub fn write_type<F: Write>(out: &mut SourceWriter<F>, t: &Type, config: &Config) {
+ CDecl::from_type(t, config).write(out, None, config);
+}
diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs
new file mode 100644
index 0000000..5012414
--- /dev/null
+++ b/src/bindgen/config.rs
@@ -0,0 +1,1114 @@
+/* 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::{BTreeMap, HashMap};
+use std::default::Default;
+use std::str::FromStr;
+use std::{fmt, fs, path::Path as StdPath, path::PathBuf as StdPathBuf};
+
+use serde::de::value::{MapAccessDeserializer, SeqAccessDeserializer};
+use serde::de::{Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
+
+use crate::bindgen::ir::annotation::AnnotationSet;
+use crate::bindgen::ir::path::Path;
+use crate::bindgen::ir::repr::ReprAlign;
+pub use crate::bindgen::rename::RenameRule;
+
+pub const VERSION: &str = env!("CARGO_PKG_VERSION");
+
+/// A language type to generate bindings for.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum Language {
+ Cxx,
+ C,
+ Cython,
+}
+
+impl FromStr for Language {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<Language, Self::Err> {
+ match s {
+ "cxx" => Ok(Language::Cxx),
+ "Cxx" => Ok(Language::Cxx),
+ "CXX" => Ok(Language::Cxx),
+ "cpp" => Ok(Language::Cxx),
+ "Cpp" => Ok(Language::Cxx),
+ "CPP" => Ok(Language::Cxx),
+ "c++" => Ok(Language::Cxx),
+ "C++" => Ok(Language::Cxx),
+ "c" => Ok(Language::C),
+ "C" => Ok(Language::C),
+ "cython" => Ok(Language::Cython),
+ "Cython" => Ok(Language::Cython),
+ _ => Err(format!("Unrecognized Language: '{}'.", s)),
+ }
+ }
+}
+
+deserialize_enum_str!(Language);
+
+impl Language {
+ pub(crate) fn typedef(self) -> &'static str {
+ match self {
+ Language::Cxx | Language::C => "typedef",
+ Language::Cython => "ctypedef",
+ }
+ }
+}
+
+/// Controls what type of line endings are used in the generated code.
+#[derive(Debug, Clone, Copy)]
+#[allow(clippy::upper_case_acronyms)]
+#[derive(Default)]
+pub enum LineEndingStyle {
+ /// Use Unix-style linefeed characters
+ #[default]
+ LF,
+ /// Use classic Mac-style carriage-return characters
+ CR,
+ /// Use Windows-style carriage-return and linefeed characters
+ CRLF,
+ /// Use the native mode for the platform: CRLF on Windows, LF everywhere else.
+ Native,
+}
+
+impl LineEndingStyle {
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ Self::LF => "\n",
+ Self::CR => "\r",
+ Self::CRLF => "\r\n",
+ Self::Native => {
+ #[cfg(target_os = "windows")]
+ {
+ Self::CRLF.as_str()
+ }
+ #[cfg(not(target_os = "windows"))]
+ {
+ Self::LF.as_str()
+ }
+ }
+ }
+ }
+}
+
+impl FromStr for LineEndingStyle {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s.to_lowercase().as_ref() {
+ "native" => Ok(Self::Native),
+ "lf" => Ok(Self::LF),
+ "crlf" => Ok(Self::CRLF),
+ "cr" => Ok(Self::CR),
+ _ => Err(format!("Unrecognized line ending style: '{}'.", s)),
+ }
+ }
+}
+
+deserialize_enum_str!(LineEndingStyle);
+
+/// A style of braces to use for generating code.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Braces {
+ SameLine,
+ NextLine,
+}
+
+impl FromStr for Braces {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<Braces, Self::Err> {
+ match s {
+ "SameLine" => Ok(Braces::SameLine),
+ "same_line" => Ok(Braces::SameLine),
+ "NextLine" => Ok(Braces::NextLine),
+ "next_line" => Ok(Braces::NextLine),
+ _ => Err(format!("Unrecognized Braces: '{}'.", s)),
+ }
+ }
+}
+
+deserialize_enum_str!(Braces);
+
+/// A type of layout to use when generating long lines of code.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Layout {
+ Horizontal,
+ Vertical,
+ Auto,
+}
+
+impl FromStr for Layout {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<Layout, Self::Err> {
+ match s {
+ "Horizontal" => Ok(Layout::Horizontal),
+ "horizontal" => Ok(Layout::Horizontal),
+ "Vertical" => Ok(Layout::Vertical),
+ "vertical" => Ok(Layout::Vertical),
+ "Auto" => Ok(Layout::Auto),
+ "auto" => Ok(Layout::Auto),
+ _ => Err(format!("Unrecognized Layout: '{}'.", s)),
+ }
+ }
+}
+
+deserialize_enum_str!(Layout);
+
+/// How the comments containing documentation should be styled.
+#[derive(Debug, Clone, PartialEq, Eq, Copy)]
+pub enum DocumentationStyle {
+ C,
+ C99,
+ Doxy,
+ Cxx,
+ Auto,
+}
+
+impl FromStr for DocumentationStyle {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<DocumentationStyle, Self::Err> {
+ match s.to_lowercase().as_ref() {
+ "c" => Ok(DocumentationStyle::C),
+ "c99" => Ok(DocumentationStyle::C99),
+ "cxx" => Ok(DocumentationStyle::Cxx),
+ "c++" => Ok(DocumentationStyle::Cxx),
+ "doxy" => Ok(DocumentationStyle::Doxy),
+ "auto" => Ok(DocumentationStyle::Auto),
+ _ => Err(format!("Unrecognized documentation style: '{}'.", s)),
+ }
+ }
+}
+
+deserialize_enum_str!(DocumentationStyle);
+
+/// How much of the documentation to include in the header file.
+#[derive(Debug, Clone, Copy)]
+pub enum DocumentationLength {
+ Short,
+ Full,
+}
+
+impl FromStr for DocumentationLength {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<DocumentationLength, Self::Err> {
+ match s.to_lowercase().as_ref() {
+ "short" => Ok(DocumentationLength::Short),
+ "full" => Ok(DocumentationLength::Full),
+ _ => Err(format!("Unrecognized documentation style: '{}'.", s)),
+ }
+ }
+}
+
+deserialize_enum_str!(DocumentationLength);
+
+/// A style of Style to use when generating structs and enums.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
+pub enum Style {
+ #[default]
+ Both,
+ Tag,
+ Type,
+}
+
+impl Style {
+ pub fn generate_tag(self) -> bool {
+ match self {
+ Style::Both | Style::Tag => true,
+ Style::Type => false,
+ }
+ }
+
+ pub fn generate_typedef(self) -> bool {
+ match self {
+ Style::Both | Style::Type => true,
+ Style::Tag => false,
+ }
+ }
+
+ // https://cython.readthedocs.io/en/latest/src/userguide/external_C_code.html#styles-of-struct-union-and-enum-declaration
+ pub fn cython_def(self) -> &'static str {
+ if self.generate_tag() {
+ "cdef "
+ } else {
+ "ctypedef "
+ }
+ }
+}
+
+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);
+
+/// Different item types that we can generate and filter.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ItemType {
+ Constants,
+ Globals,
+ Enums,
+ Structs,
+ Unions,
+ Typedefs,
+ OpaqueItems,
+ Functions,
+}
+
+impl FromStr for ItemType {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ use self::ItemType::*;
+ Ok(match &*s.to_lowercase() {
+ "constants" => Constants,
+ "globals" => Globals,
+ "enums" => Enums,
+ "structs" => Structs,
+ "unions" => Unions,
+ "typedefs" => Typedefs,
+ "opaque" => OpaqueItems,
+ "functions" => Functions,
+ _ => return Err(format!("Unrecognized Style: '{}'.", s)),
+ })
+ }
+}
+
+deserialize_enum_str!(ItemType);
+
+/// Type which specifies the sort order of functions
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum SortKey {
+ Name,
+ None,
+}
+
+impl FromStr for SortKey {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ use self::SortKey::*;
+ Ok(match &*s.to_lowercase() {
+ "name" => Name,
+ "none" => None,
+ _ => return Err(format!("Unrecognized sort option: '{}'.", s)),
+ })
+ }
+}
+
+deserialize_enum_str!(SortKey);
+
+/// Settings to apply when exporting items.
+#[derive(Debug, Clone, Deserialize, Default)]
+#[serde(rename_all = "snake_case")]
+#[serde(deny_unknown_fields)]
+#[serde(default)]
+pub struct ExportConfig {
+ /// A list of additional items not used by exported functions to include in
+ /// the generated bindings
+ pub include: Vec<String>,
+ /// A list of items to not include in the generated bindings
+ pub exclude: Vec<String>,
+ /// Table of name conversions to apply to item names
+ pub rename: HashMap<String, String>,
+ /// Table of raw strings to prepend to the body of items.
+ pub pre_body: HashMap<String, String>,
+ /// Table of raw strings to append to the body of items.
+ pub body: HashMap<String, String>,
+ /// A prefix to add before the name of every item
+ pub prefix: Option<String>,
+ /// Types of items to generate.
+ pub item_types: Vec<ItemType>,
+ /// Whether renaming overrides or extends prefixing.
+ pub renaming_overrides_prefixing: bool,
+ /// Mangling configuration.
+ pub mangle: MangleConfig,
+}
+
+/// Mangling-specific configuration.
+#[derive(Debug, Clone, Deserialize, Default)]
+#[serde(rename_all = "snake_case")]
+#[serde(deny_unknown_fields)]
+#[serde(default)]
+pub struct MangleConfig {
+ /// The rename rule to apply to the type names mangled.
+ pub rename_types: RenameRule,
+ /// Remove the underscores used for name mangling.
+ pub remove_underscores: bool,
+}
+
+impl ExportConfig {
+ pub(crate) fn should_generate(&self, item_type: ItemType) -> bool {
+ self.item_types.is_empty() || self.item_types.contains(&item_type)
+ }
+
+ pub(crate) fn pre_body(&self, path: &Path) -> Option<&str> {
+ self.pre_body.get(path.name()).map(|s| s.trim_matches('\n'))
+ }
+
+ pub(crate) fn post_body(&self, path: &Path) -> Option<&str> {
+ self.body.get(path.name()).map(|s| s.trim_matches('\n'))
+ }
+
+ pub(crate) fn rename(&self, item_name: &mut String) {
+ if let Some(name) = self.rename.get(item_name) {
+ *item_name = name.clone();
+ if self.renaming_overrides_prefixing {
+ return;
+ }
+ }
+ if let Some(ref prefix) = self.prefix {
+ item_name.insert_str(0, prefix);
+ }
+ }
+}
+
+/// Settings to apply to generated types with layout modifiers.
+#[derive(Debug, Default, Clone, Deserialize)]
+#[serde(rename_all = "snake_case")]
+#[serde(deny_unknown_fields)]
+#[serde(default)]
+pub struct LayoutConfig {
+ /// The way to annotate C types as #[repr(packed)].
+ pub packed: Option<String>,
+ /// The way to annotate C types as #[repr(align(...))]. This is assumed to be a functional
+ /// macro which takes a single argument (the alignment).
+ pub aligned_n: Option<String>,
+}
+
+impl LayoutConfig {
+ pub(crate) fn ensure_safe_to_represent(&self, align: &ReprAlign) -> Result<(), String> {
+ match (align, &self.packed, &self.aligned_n) {
+ (ReprAlign::Packed, None, _) => Err("Cannot safely represent #[repr(packed)] type without configured 'packed' annotation.".to_string()),
+ (ReprAlign::Align(_), _, None) => Err("Cannot safely represent #[repr(aligned(...))] type without configured 'aligned_n' annotation.".to_string()),
+ _ => Ok(()),
+ }
+ }
+}
+
+/// Settings to apply to generated functions.
+#[derive(Debug, Clone, Deserialize)]
+#[serde(rename_all = "snake_case")]
+#[serde(deny_unknown_fields)]
+#[serde(default)]
+pub struct FunctionConfig {
+ /// Optional text to output before each function declaration
+ pub prefix: Option<String>,
+ /// Optional text to output after each function declaration
+ pub postfix: Option<String>,
+ /// The way to annotation this function as #[must_use]
+ pub must_use: Option<String>,
+ /// The way to annotation this function as #[deprecated] without notes
+ pub deprecated: Option<String>,
+ /// The way to annotation this function as #[deprecated] with notes
+ pub deprecated_with_note: Option<String>,
+ /// The style to layout the args
+ pub args: Layout,
+ /// The rename rule to apply to function args
+ pub rename_args: RenameRule,
+ /// An optional macro to use when generating Swift function name attributes
+ pub swift_name_macro: Option<String>,
+ /// Sort key for functions
+ pub sort_by: Option<SortKey>,
+ /// Optional text to output after functions which return `!`.
+ pub no_return: Option<String>,
+}
+
+impl Default for FunctionConfig {
+ fn default() -> FunctionConfig {
+ FunctionConfig {
+ prefix: None,
+ postfix: None,
+ must_use: None,
+ deprecated: None,
+ deprecated_with_note: None,
+ args: Layout::Auto,
+ rename_args: RenameRule::None,
+ swift_name_macro: None,
+ sort_by: None,
+ no_return: None,
+ }
+ }
+}
+
+impl FunctionConfig {
+ pub(crate) fn prefix(&self, annotations: &AnnotationSet) -> Option<String> {
+ if let Some(x) = annotations.atom("prefix") {
+ return x;
+ }
+ self.prefix.clone()
+ }
+
+ pub(crate) fn postfix(&self, annotations: &AnnotationSet) -> Option<String> {
+ if let Some(x) = annotations.atom("postfix") {
+ return x;
+ }
+ self.postfix.clone()
+ }
+}
+
+/// Settings to apply to generated structs.
+#[derive(Debug, Default, Clone, Deserialize)]
+#[serde(rename_all = "snake_case")]
+#[serde(deny_unknown_fields)]
+#[serde(default)]
+pub struct StructConfig {
+ /// The rename rule to apply to the name of struct fields
+ pub rename_fields: RenameRule,
+ /// Whether to generate a constructor for the struct (which takes
+ /// arguments to initialize all the members)
+ pub derive_constructor: bool,
+ /// Whether to generate a piecewise equality operator
+ pub derive_eq: bool,
+ /// Whether to generate a piecewise inequality operator
+ pub derive_neq: bool,
+ /// Whether to generate a less than operator on structs with one field
+ pub derive_lt: bool,
+ /// Whether to generate a less than or equal to operator on structs with one field
+ pub derive_lte: bool,
+ /// Whether to generate a greater than operator on structs with one field
+ pub derive_gt: bool,
+ /// Whether to generate a greater than or equal to operator on structs with one field
+ pub derive_gte: bool,
+ /// Whether to generate a ostream serializer for the struct
+ pub derive_ostream: bool,
+ /// Whether associated constants should be in the body. Only applicable to
+ /// non-transparent structs, and in C++-only.
+ pub associated_constants_in_body: bool,
+ /// The way to annotate this struct as #[must_use].
+ pub must_use: Option<String>,
+ /// The way to annotation this function as #[deprecated] without notes
+ pub deprecated: Option<String>,
+ /// The way to annotation this function as #[deprecated] with notes
+ pub deprecated_with_note: Option<String>,
+}
+
+impl StructConfig {
+ pub(crate) fn derive_constructor(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("derive-constructor") {
+ return x;
+ }
+ self.derive_constructor
+ }
+ pub(crate) fn derive_eq(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("derive-eq") {
+ return x;
+ }
+ self.derive_eq
+ }
+ pub(crate) fn derive_neq(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("derive-neq") {
+ return x;
+ }
+ self.derive_neq
+ }
+ pub(crate) fn derive_lt(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("derive-lt") {
+ return x;
+ }
+ self.derive_lt
+ }
+ pub(crate) fn derive_lte(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("derive-lte") {
+ return x;
+ }
+ self.derive_lte
+ }
+ pub(crate) fn derive_gt(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("derive-gt") {
+ return x;
+ }
+ self.derive_gt
+ }
+ pub(crate) fn derive_gte(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("derive-gte") {
+ return x;
+ }
+ self.derive_gte
+ }
+ pub(crate) fn derive_ostream(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("derive-ostream") {
+ return x;
+ }
+ self.derive_ostream
+ }
+}
+
+/// Settings to apply to generated enums.
+#[derive(Debug, Clone, Deserialize)]
+#[serde(rename_all = "snake_case")]
+#[serde(deny_unknown_fields)]
+#[serde(default)]
+pub struct EnumConfig {
+ /// The rename rule to apply to the name of enum variants
+ pub rename_variants: RenameRule,
+ /// The rename rule to apply to the names of the union fields in C/C++
+ /// generated from the Rust enum. Applied before rename_variants
+ /// rename rule. Defaults to SnakeCase.
+ pub rename_variant_name_fields: RenameRule,
+ /// Whether to add a `Sentinel` value at the end of every enum
+ /// This is useful in Gecko for IPC serialization
+ pub add_sentinel: bool,
+ /// Whether the enum variants should be prefixed with the enum name
+ pub prefix_with_name: bool,
+ /// Whether to generate static `::X(..)` constructors and `IsX()`
+ /// methods for tagged enums.
+ pub derive_helper_methods: bool,
+ /// Whether to generate `AsX() const` methods for tagged enums.
+ pub derive_const_casts: bool,
+ /// Whether to generate `AsX()` methods for tagged enums.
+ pub derive_mut_casts: bool,
+ /// The name of the macro to use for `derive_{const,mut}casts`. If custom, you're
+ /// responsible to provide the necessary header, otherwise `assert` will be
+ /// used, and `<cassert>` will be included.
+ pub cast_assert_name: Option<String>,
+ /// The way to annotation this enum as #[must_use].
+ pub must_use: Option<String>,
+ /// The way to annotation this function as #[deprecated] without notes
+ pub deprecated: Option<String>,
+ /// The way to annotation this function as #[deprecated] with notes
+ pub deprecated_with_note: Option<String>,
+ /// Whether to generate destructors of tagged enums.
+ pub derive_tagged_enum_destructor: bool,
+ /// Whether to generate copy-constructors of tagged enums.
+ pub derive_tagged_enum_copy_constructor: bool,
+ /// Whether to generate copy-assignment operators of tagged enums.
+ ///
+ /// This is only generated if a copy constructor for the same tagged enum is
+ /// generated as well.
+ pub derive_tagged_enum_copy_assignment: bool,
+ /// Whether to generate a ostream serializer for the struct
+ pub derive_ostream: bool,
+ /// Declare the enum as an enum class.
+ /// Only relevant when targeting C++.
+ pub enum_class: bool,
+ /// Whether to generate empty, private default-constructors for tagged
+ /// enums.
+ pub private_default_tagged_enum_constructor: bool,
+}
+
+impl Default for EnumConfig {
+ fn default() -> EnumConfig {
+ EnumConfig {
+ rename_variants: RenameRule::None,
+ rename_variant_name_fields: RenameRule::SnakeCase,
+ add_sentinel: false,
+ prefix_with_name: false,
+ derive_helper_methods: false,
+ derive_const_casts: false,
+ derive_mut_casts: false,
+ cast_assert_name: None,
+ must_use: None,
+ deprecated: None,
+ deprecated_with_note: None,
+ derive_tagged_enum_destructor: false,
+ derive_tagged_enum_copy_constructor: false,
+ derive_tagged_enum_copy_assignment: false,
+ derive_ostream: false,
+ enum_class: true,
+ private_default_tagged_enum_constructor: false,
+ }
+ }
+}
+
+impl EnumConfig {
+ pub(crate) fn add_sentinel(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("add-sentinel") {
+ return x;
+ }
+ self.add_sentinel
+ }
+ pub(crate) fn derive_helper_methods(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("derive-helper-methods") {
+ return x;
+ }
+ self.derive_helper_methods
+ }
+ pub(crate) fn derive_const_casts(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("derive-const-casts") {
+ return x;
+ }
+ self.derive_const_casts
+ }
+ pub(crate) fn derive_mut_casts(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("derive-mut-casts") {
+ return x;
+ }
+ self.derive_mut_casts
+ }
+ pub(crate) fn derive_tagged_enum_destructor(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("derive-tagged-enum-destructor") {
+ return x;
+ }
+ self.derive_tagged_enum_destructor
+ }
+ pub(crate) fn derive_tagged_enum_copy_constructor(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("derive-tagged-enum-copy-constructor") {
+ return x;
+ }
+ self.derive_tagged_enum_copy_constructor
+ }
+ pub(crate) fn derive_tagged_enum_copy_assignment(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("derive-tagged-enum-copy-assignment") {
+ return x;
+ }
+ self.derive_tagged_enum_copy_assignment
+ }
+ pub(crate) fn derive_ostream(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("derive-ostream") {
+ return x;
+ }
+ self.derive_ostream
+ }
+ pub(crate) fn enum_class(&self, annotations: &AnnotationSet) -> bool {
+ if let Some(x) = annotations.bool("enum-class") {
+ return x;
+ }
+ self.enum_class
+ }
+ pub(crate) fn private_default_tagged_enum_constructor(
+ &self,
+ annotations: &AnnotationSet,
+ ) -> bool {
+ if let Some(x) = annotations.bool("private-default-tagged-enum-constructor") {
+ return x;
+ }
+ self.private_default_tagged_enum_constructor
+ }
+}
+
+/// Settings to apply to generated constants.
+#[derive(Debug, Clone, Deserialize)]
+#[serde(rename_all = "snake_case")]
+#[serde(deny_unknown_fields)]
+#[serde(default)]
+pub struct ConstantConfig {
+ /// Whether a generated constant can be a static const in C++ mode.
+ pub allow_static_const: bool,
+ /// Whether a generated constant should be constexpr in C++ mode.
+ pub allow_constexpr: bool,
+ /// Sort key for constants
+ pub sort_by: Option<SortKey>,
+}
+
+impl Default for ConstantConfig {
+ fn default() -> ConstantConfig {
+ ConstantConfig {
+ allow_static_const: true,
+ allow_constexpr: true,
+ sort_by: None,
+ }
+ }
+}
+
+/// Settings for custom macro expansion.
+#[derive(Debug, Clone, Deserialize, Default)]
+#[serde(rename_all = "snake_case")]
+#[serde(deny_unknown_fields)]
+#[serde(default)]
+pub struct MacroExpansionConfig {
+ /// Whether the `bitflags` macro should be expanded.
+ pub bitflags: bool,
+}
+
+/// Controls which Cargo profile is used for macro expansion.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum Profile {
+ Debug,
+ Release,
+}
+
+impl FromStr for Profile {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<Profile, Self::Err> {
+ match s {
+ "debug" | "Debug" => Ok(Profile::Debug),
+ "release" | "Release" => Ok(Profile::Release),
+ _ => Err(format!("Unrecognized Profile: '{}'.", s)),
+ }
+ }
+}
+
+deserialize_enum_str!(Profile);
+
+/// Settings to apply when running `rustc -Zunpretty=expanded`
+#[derive(Debug, Clone, Deserialize)]
+#[serde(rename_all = "snake_case")]
+#[serde(deny_unknown_fields)]
+#[serde(default)]
+pub struct ParseExpandConfig {
+ /// The names of crates to parse with `rustc -Zunpretty=expanded`
+ pub crates: Vec<String>,
+ /// Whether to enable all the features when expanding.
+ pub all_features: bool,
+ /// Whether to use the default feature set when expanding.
+ pub default_features: bool,
+ /// List of features to use when expanding. Combines with `default_features` like in
+ /// `Cargo.toml`.
+ pub features: Option<Vec<String>>,
+ /// Controls whether or not to pass `--release` when expanding.
+ pub profile: Profile,
+}
+
+impl Default for ParseExpandConfig {
+ fn default() -> ParseExpandConfig {
+ ParseExpandConfig {
+ crates: Vec::new(),
+ all_features: false,
+ default_features: true,
+ features: None,
+ profile: Profile::Debug,
+ }
+ }
+}
+
+// Backwards-compatibility deserializer for ParseExpandConfig. This allows accepting both the
+// simple `expand = ["crate"]` and the more complex `expand = {"crates": ["crate"],
+// "default_features": false}` format for the `expand` key.
+//
+// Note that one (major) difference between the two forms is that, for backwards-compatibility
+// reasons, the `expand = ["crate"]` form will enable the `--all-features` flag by default while
+// the `expand = {"crates": ["crate"]}` form will use the default feature set by default.
+fn retrocomp_parse_expand_config_deserialize<'de, D: Deserializer<'de>>(
+ deserializer: D,
+) -> Result<ParseExpandConfig, D::Error> {
+ struct ParseExpandVisitor;
+
+ impl<'de> Visitor<'de> for ParseExpandVisitor {
+ type Value = ParseExpandConfig;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("a map or sequence of string")
+ }
+
+ fn visit_seq<A: SeqAccess<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {
+ let crates =
+ <Vec<String> as Deserialize>::deserialize(SeqAccessDeserializer::new(seq))?;
+ Ok(ParseExpandConfig {
+ crates,
+ all_features: true,
+ default_features: true,
+ features: None,
+ profile: Profile::Debug,
+ })
+ }
+
+ fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
+ <ParseExpandConfig as Deserialize>::deserialize(MapAccessDeserializer::new(map))
+ }
+ }
+
+ deserializer.deserialize_any(ParseExpandVisitor)
+}
+
+/// Settings to apply when parsing.
+#[derive(Debug, Default, Clone, Deserialize)]
+#[serde(rename_all = "snake_case")]
+#[serde(deny_unknown_fields)]
+#[serde(default)]
+pub struct ParseConfig {
+ /// Whether to parse dependencies when generating bindings. When this is true,
+ /// each dependent crate is found using a combination of `cargo metadata` and
+ /// `Cargo.lock`. To further control this behavior, crates can be whitelisted or
+ /// blacklisted using `include` and `exclude` respectively. Additionally in cases
+ /// where crates have types to expose in bindings hidden in macros, a crate can
+ /// be marked in `expand` and `cargo expand` will be used to expand the macros
+ /// before parsing. A crate marked in `expand` doesn't need to be added to any
+ /// whitelist.
+ pub parse_deps: bool,
+ /// An optional whitelist of names of crates to parse
+ pub include: Option<Vec<String>>,
+ /// The names of crates to not parse
+ pub exclude: Vec<String>,
+ /// The configuration options for `rustc -Zunpretty=expanded`
+ #[serde(deserialize_with = "retrocomp_parse_expand_config_deserialize")]
+ pub expand: ParseExpandConfig,
+ /// Whether to use a new temporary target directory when running `rustc -Zunpretty=expanded`.
+ /// This may be required for some build processes.
+ pub clean: bool,
+ /// List of crate names which generate consts, statics, and fns. By default
+ /// no dependent crates generate them.
+ pub extra_bindings: Vec<String>,
+}
+
+impl ParseConfig {
+ pub(crate) fn should_generate_top_level_item(
+ &self,
+ crate_name: &str,
+ binding_crate_name: &str,
+ ) -> bool {
+ if crate_name == binding_crate_name {
+ // Always generate items for the binding crate.
+ return true;
+ }
+
+ self.extra_bindings.iter().any(|dep| dep == crate_name)
+ }
+}
+
+/// Settings to apply to pointers
+#[derive(Debug, Clone, Default, Deserialize)]
+#[serde(rename_all = "snake_case")]
+#[serde(deny_unknown_fields)]
+#[serde(default)]
+pub struct PtrConfig {
+ /// Optional attribute to apply to pointers that are required to not be null
+ pub non_null_attribute: Option<String>,
+}
+
+/// Settings specific to Cython bindings.
+#[derive(Debug, Clone, Default, Deserialize)]
+#[serde(rename_all = "snake_case")]
+#[serde(deny_unknown_fields)]
+#[serde(default)]
+pub struct CythonConfig {
+ /// Header specified in the top level `cdef extern from header:` declaration.
+ pub header: Option<String>,
+ /// `from module cimport name1, name2, ...` declarations added in the same place
+ /// where you'd get includes in C.
+ pub cimports: BTreeMap<String, Vec<String>>,
+}
+
+/// A collection of settings to customize the generated bindings.
+#[derive(Debug, Clone, Deserialize)]
+#[serde(rename_all = "snake_case")]
+#[serde(deny_unknown_fields)]
+#[serde(default)]
+pub struct Config {
+ /// Optional text to output at the beginning of the file
+ pub header: Option<String>,
+ /// A list of additional includes to put at the beginning of the generated header
+ pub includes: Vec<String>,
+ /// A list of additional system includes to put at the beginning of the generated header
+ pub sys_includes: Vec<String>,
+ /// Optional verbatim code added after the include blocks
+ pub after_includes: Option<String>,
+ /// Optional text to output at the end of the file
+ pub trailer: Option<String>,
+ /// Optional name to use for an include guard
+ pub include_guard: Option<String>,
+ /// Add a `#pragma once` guard
+ pub pragma_once: bool,
+ /// Generates no includes at all. Overrides all other include options
+ ///
+ /// This option is useful when using cbindgen with tools such as python's cffi which
+ /// doesn't understand include directives
+ pub no_includes: bool,
+ /// Optional text to output at major sections to deter manual editing
+ pub autogen_warning: Option<String>,
+ /// Include a comment with the version of cbindgen used to generate the file
+ pub include_version: bool,
+ /// An optional name for the root namespace. Only applicable when language="C++"
+ pub namespace: Option<String>,
+ /// An optional list of namespaces. Only applicable when language="C++"
+ pub namespaces: Option<Vec<String>>,
+ /// An optional list of namespaces to declare as using. Only applicable when language="C++"
+ pub using_namespaces: Option<Vec<String>>,
+ /// The style to use for braces
+ pub braces: Braces,
+ /// The preferred length of a line, used for auto breaking function arguments
+ pub line_length: usize,
+ /// The amount of spaces in a tab
+ pub tab_width: usize,
+ /// The type of line endings to generate
+ pub line_endings: LineEndingStyle,
+ /// The language to output bindings for
+ pub language: Language,
+ /// Include preprocessor defines in C bindings to ensure C++ compatibility
+ pub cpp_compat: bool,
+ /// The style to declare structs, enums and unions in for C
+ pub style: Style,
+ /// Default sort key for functions and constants.
+ pub sort_by: SortKey,
+ /// If this option is true `usize` and `isize` will be converted into `size_t` and `ptrdiff_t`
+ /// instead of `uintptr_t` and `intptr_t` respectively.
+ pub usize_is_size_t: bool,
+ /// The configuration options for parsing
+ pub parse: ParseConfig,
+ /// The configuration options for exporting
+ pub export: ExportConfig,
+ /// The configuration options for macros.
+ pub macro_expansion: MacroExpansionConfig,
+ /// The configuration options for type layouts.
+ pub layout: LayoutConfig,
+ /// The configuration options for functions
+ #[serde(rename = "fn")]
+ pub function: FunctionConfig,
+ /// The configuration options for structs
+ #[serde(rename = "struct")]
+ pub structure: StructConfig,
+ /// The configuration options for enums
+ #[serde(rename = "enum")]
+ pub enumeration: EnumConfig,
+ /// The configuration options for constants
+ #[serde(rename = "const")]
+ pub constant: ConstantConfig,
+ /// Preprocessor defines to use when generating #ifdef's for #[cfg]
+ pub defines: HashMap<String, String>,
+ /// Include doc comments from Rust as documentation
+ pub documentation: bool,
+ /// How documentation comments should be styled.
+ pub documentation_style: DocumentationStyle,
+ /// How much of the documentation should be output for each item.
+ pub documentation_length: DocumentationLength,
+ /// Configuration options for pointers
+ #[serde(rename = "ptr")]
+ pub pointer: PtrConfig,
+ /// Only download sources for dependencies needed for the target platform.
+ ///
+ /// By default, cbindgen will fetch sources for dependencies used on any platform so that if a
+ /// type is defined in terms of a type from a dependency on another target (probably behind a
+ /// `#[cfg]`), cbindgen will be able to generate the appropriate binding as it can see the
+ /// nested type's definition. However, this makes calling cbindgen slower, as it may have to
+ /// download a number of additional dependencies.
+ ///
+ /// As an example, consider this Cargo.toml:
+ ///
+ /// ```toml
+ /// [target.'cfg(windows)'.dependencies]
+ /// windows = "0.7"
+ /// ```
+ ///
+ /// with this declaration in one of the `.rs` files that cbindgen is asked to generate bindings
+ /// for:
+ ///
+ /// ```rust,ignore
+ /// #[cfg(windows)]
+ /// pub struct Error(windows::ErrorCode);
+ /// ```
+ ///
+ /// With the default value (`false`), cbindgen will download the `windows` dependency even when
+ /// not compiling for Windows, and will thus be able to generate the binding for `Error`
+ /// (behind a `#define`).
+ ///
+ /// If this value is instead to `true`, cbindgen will _not_ download the `windows` dependency
+ /// if it's not compiling for Windows, but will also fail to generate a Windows binding for
+ /// `Error` as it does not know the definition for `ErrorCode`.
+ ///
+ /// The target can be chosen via the `TARGET` environment variable (if used
+ /// via the CLI, when ran from a build script cargo sets this variable
+ /// appropriately).
+ pub only_target_dependencies: bool,
+ /// Configuration options specific to Cython.
+ pub cython: CythonConfig,
+ #[serde(skip)]
+ pub(crate) config_path: Option<StdPathBuf>,
+}
+
+impl Default for Config {
+ fn default() -> Config {
+ Config {
+ header: None,
+ includes: Vec::new(),
+ sys_includes: Vec::new(),
+ after_includes: None,
+ trailer: None,
+ include_guard: None,
+ pragma_once: false,
+ autogen_warning: None,
+ include_version: false,
+ no_includes: false,
+ namespace: None,
+ namespaces: None,
+ using_namespaces: None,
+ braces: Braces::SameLine,
+ line_length: 100,
+ tab_width: 2,
+ line_endings: LineEndingStyle::default(),
+ language: Language::Cxx,
+ cpp_compat: false,
+ style: Style::default(),
+ usize_is_size_t: false,
+ sort_by: SortKey::None,
+ macro_expansion: Default::default(),
+ parse: ParseConfig::default(),
+ export: ExportConfig::default(),
+ layout: LayoutConfig::default(),
+ function: FunctionConfig::default(),
+ structure: StructConfig::default(),
+ enumeration: EnumConfig::default(),
+ constant: ConstantConfig::default(),
+ defines: HashMap::new(),
+ documentation: true,
+ documentation_style: DocumentationStyle::Auto,
+ documentation_length: DocumentationLength::Full,
+ pointer: PtrConfig::default(),
+ only_target_dependencies: false,
+ cython: CythonConfig::default(),
+ config_path: None,
+ }
+ }
+}
+
+impl Config {
+ pub(crate) fn cpp_compatible_c(&self) -> bool {
+ self.language == Language::C && self.cpp_compat
+ }
+
+ pub(crate) fn include_guard(&self) -> Option<&str> {
+ if self.language == Language::Cython {
+ None
+ } else {
+ self.include_guard.as_deref()
+ }
+ }
+
+ pub(crate) fn includes(&self) -> &[String] {
+ if self.language == Language::Cython {
+ &[]
+ } else {
+ &self.includes
+ }
+ }
+
+ pub(crate) fn sys_includes(&self) -> &[String] {
+ if self.language == Language::Cython {
+ &[]
+ } else {
+ &self.sys_includes
+ }
+ }
+
+ pub fn from_file<P: AsRef<StdPath>>(file_name: P) -> Result<Config, String> {
+ let config_text = fs::read_to_string(file_name.as_ref()).map_err(|_| {
+ format!(
+ "Couldn't open config file: {}.",
+ file_name.as_ref().display()
+ )
+ })?;
+
+ let mut config = toml::from_str::<Config>(&config_text)
+ .map_err(|e| format!("Couldn't parse config file: {}.", e))?;
+ config.config_path = Some(StdPathBuf::from(file_name.as_ref()));
+ Ok(config)
+ }
+
+ pub fn from_root_or_default<P: AsRef<StdPath>>(root: P) -> Config {
+ let c = root.as_ref().join("cbindgen.toml");
+
+ if c.exists() {
+ Config::from_file(c).unwrap()
+ } else {
+ Config::default()
+ }
+ }
+}
diff --git a/src/bindgen/declarationtyperesolver.rs b/src/bindgen/declarationtyperesolver.rs
new file mode 100644
index 0000000..e72761e
--- /dev/null
+++ b/src/bindgen/declarationtyperesolver.rs
@@ -0,0 +1,57 @@
+/* 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 crate::bindgen::ir::Path;
+use std::collections::hash_map::Entry;
+use std::collections::HashMap;
+
+impl DeclarationType {
+ pub fn to_str(self) -> &'static str {
+ match self {
+ DeclarationType::Struct => "struct",
+ DeclarationType::Enum => "enum",
+ DeclarationType::Union => "union",
+ }
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
+pub enum DeclarationType {
+ Struct,
+ Enum,
+ Union,
+}
+
+#[derive(Default)]
+pub struct DeclarationTypeResolver {
+ types: HashMap<Path, Option<DeclarationType>>,
+}
+
+impl DeclarationTypeResolver {
+ fn insert(&mut self, path: &Path, ty: Option<DeclarationType>) {
+ if let Entry::Vacant(vacant_entry) = self.types.entry(path.clone()) {
+ vacant_entry.insert(ty);
+ }
+ }
+
+ pub fn add_enum(&mut self, path: &Path) {
+ self.insert(path, Some(DeclarationType::Enum));
+ }
+
+ pub fn add_struct(&mut self, path: &Path) {
+ self.insert(path, Some(DeclarationType::Struct));
+ }
+
+ pub fn add_union(&mut self, path: &Path) {
+ self.insert(path, Some(DeclarationType::Union));
+ }
+
+ pub fn add_none(&mut self, path: &Path) {
+ self.insert(path, None);
+ }
+
+ pub fn type_for(&self, path: &Path) -> Option<DeclarationType> {
+ *self.types.get(path)?
+ }
+}
diff --git a/src/bindgen/dependencies.rs b/src/bindgen/dependencies.rs
new file mode 100644
index 0000000..6a98738
--- /dev/null
+++ b/src/bindgen/dependencies.rs
@@ -0,0 +1,46 @@
+/* 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::cmp::Ordering;
+use std::collections::HashSet;
+
+use crate::bindgen::ir::{ItemContainer, Path};
+
+/// A dependency list is used for gathering what order to output the types.
+#[derive(Default)]
+pub struct Dependencies {
+ pub order: Vec<ItemContainer>,
+ pub items: HashSet<Path>,
+}
+
+impl Dependencies {
+ pub fn new() -> Dependencies {
+ Dependencies {
+ order: Vec::new(),
+ items: HashSet::new(),
+ }
+ }
+
+ pub fn sort(&mut self) {
+ // Sort untagged enums and opaque structs into their own layers because they don't
+ // depend on each other or anything else.
+ let ordering = |a: &ItemContainer, b: &ItemContainer| match (a, b) {
+ (ItemContainer::Enum(x), ItemContainer::Enum(y))
+ if x.tag.is_none() && y.tag.is_none() =>
+ {
+ x.path.cmp(&y.path)
+ }
+ (ItemContainer::Enum(x), _) if x.tag.is_none() => Ordering::Less,
+ (_, ItemContainer::Enum(x)) if x.tag.is_none() => Ordering::Greater,
+
+ (ItemContainer::OpaqueItem(x), ItemContainer::OpaqueItem(y)) => x.path.cmp(&y.path),
+ (&ItemContainer::OpaqueItem(_), _) => Ordering::Less,
+ (_, &ItemContainer::OpaqueItem(_)) => Ordering::Greater,
+
+ _ => Ordering::Equal,
+ };
+
+ self.order.sort_by(ordering);
+ }
+}
diff --git a/src/bindgen/error.rs b/src/bindgen/error.rs
new file mode 100644
index 0000000..e4002f6
--- /dev/null
+++ b/src/bindgen/error.rs
@@ -0,0 +1,88 @@
+/* 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::error;
+use std::fmt;
+
+pub use crate::bindgen::cargo::cargo_expand::Error as CargoExpandError;
+pub use crate::bindgen::cargo::cargo_metadata::Error as CargoMetadataError;
+pub use crate::bindgen::cargo::cargo_toml::Error as CargoTomlError;
+pub use syn::parse::Error as ParseError;
+
+#[derive(Debug)]
+#[allow(clippy::enum_variant_names)]
+pub enum Error {
+ CargoMetadata(String, CargoMetadataError),
+ CargoToml(String, CargoTomlError),
+ CargoExpand(String, CargoExpandError),
+ ParseSyntaxError {
+ crate_name: String,
+ src_path: String,
+ error: ParseError,
+ },
+ ParseCannotOpenFile {
+ crate_name: String,
+ src_path: String,
+ },
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ Error::CargoMetadata(ref path, ref error) => write!(
+ f,
+ "Couldn't execute `cargo metadata` with manifest {:?}: {:?}",
+ path, error
+ ),
+ Error::CargoToml(ref path, ref error) => {
+ write!(f, "Couldn't load manifest file {:?}: {:?}", path, error)
+ }
+ Error::CargoExpand(ref crate_name, ref error) => write!(
+ f,
+ "Parsing crate `{}`: couldn't run `cargo rustc -Zunpretty=expanded`: {:?}",
+ crate_name, error
+ ),
+ Error::ParseSyntaxError {
+ ref crate_name,
+ ref src_path,
+ ref error,
+ } => {
+ write!(
+ f,
+ "Parsing crate `{}`:`{}`:\n{:?}",
+ crate_name, src_path, error
+ )?;
+
+ if !src_path.is_empty() {
+ write!(
+ f,
+ "\nTry running `rustc -Z parse-only {}` to see a nicer error message",
+ src_path,
+ )?
+ }
+ Ok(())
+ }
+ Error::ParseCannotOpenFile {
+ ref crate_name,
+ ref src_path,
+ } => write!(
+ f,
+ "Parsing crate `{}`: cannot open file `{}`.",
+ crate_name, src_path
+ ),
+ }
+ }
+}
+
+impl error::Error for Error {
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match self {
+ Error::CargoMetadata(_, ref error) => Some(error),
+ Error::CargoToml(_, ref error) => Some(error),
+ Error::CargoExpand(_, ref error) => Some(error),
+ Error::ParseSyntaxError { ref error, .. } => Some(error),
+ Error::ParseCannotOpenFile { .. } => None,
+ }
+ }
+}
diff --git a/src/bindgen/ir/annotation.rs b/src/bindgen/ir/annotation.rs
new file mode 100644
index 0000000..48e3e4b
--- /dev/null
+++ b/src/bindgen/ir/annotation.rs
@@ -0,0 +1,217 @@
+/* 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::borrow::Cow;
+use std::collections::hash_map::Entry;
+use std::collections::HashMap;
+use std::str::FromStr;
+
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::utilities::SynAttributeHelpers;
+
+// A system for specifying properties on items. Annotations are
+// given through document comments and parsed by this code.
+//
+// An annotation is in the form cbindgen:PROPERTY=VALUE
+// Where PROPERTY depends on the item
+// Where VALUE can be
+// * list - [Item1, Item2, Item3, ...]
+// * atom - Foo
+// * bool - true,false
+// Examples:
+// * cbindgen:field-names=[mHandle, mNamespace]
+// * cbindgen:function-postfix=WR_DESTRUCTOR_SAFE
+
+/// A value specified by an annotation.
+#[derive(Debug, Clone)]
+pub enum AnnotationValue {
+ List(Vec<String>),
+ Atom(Option<String>),
+ Bool(bool),
+}
+
+/// A set of annotations specified by a document comment.
+#[derive(Debug, Default, Clone)]
+pub struct AnnotationSet {
+ annotations: HashMap<String, AnnotationValue>,
+ pub must_use: bool,
+ pub deprecated: Option<String>,
+}
+
+pub enum DeprecatedNoteKind {
+ Function,
+ Struct,
+ Enum,
+}
+
+impl AnnotationSet {
+ pub fn new() -> AnnotationSet {
+ AnnotationSet {
+ annotations: HashMap::new(),
+ must_use: false,
+ deprecated: None,
+ }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.annotations.is_empty() && !self.must_use
+ }
+
+ pub(crate) fn must_use(&self, config: &Config) -> bool {
+ self.must_use && config.language != Language::Cython
+ }
+
+ pub(crate) fn deprecated_note<'c>(
+ &self,
+ config: &'c Config,
+ kind: DeprecatedNoteKind,
+ ) -> Option<Cow<'c, str>> {
+ let note = self.deprecated.as_deref()?;
+
+ if config.language == Language::Cython {
+ return None;
+ }
+
+ if note.is_empty() {
+ return Some(Cow::Borrowed(match kind {
+ DeprecatedNoteKind::Enum => config.enumeration.deprecated.as_deref()?,
+ DeprecatedNoteKind::Function => config.function.deprecated.as_deref()?,
+ DeprecatedNoteKind::Struct => config.structure.deprecated.as_deref()?,
+ }));
+ }
+
+ let format = match kind {
+ DeprecatedNoteKind::Enum => &config.enumeration.deprecated_with_note,
+ DeprecatedNoteKind::Function => &config.function.deprecated_with_note,
+ DeprecatedNoteKind::Struct => &config.structure.deprecated_with_note,
+ }
+ .as_ref()?;
+ Some(Cow::Owned(format.replace("{}", &format!("{:?}", note))))
+ }
+
+ pub fn load(attrs: &[syn::Attribute]) -> Result<AnnotationSet, String> {
+ let lines = attrs.get_comment_lines();
+ let lines: Vec<&str> = lines
+ .iter()
+ .filter_map(|line| {
+ let line = line.trim_start();
+ if !line.starts_with("cbindgen:") {
+ return None;
+ }
+
+ Some(line)
+ })
+ .collect();
+
+ let must_use = attrs.has_attr_word("must_use");
+ let deprecated = attrs.find_deprecated_note();
+ let mut annotations = HashMap::new();
+
+ // Look at each line for an annotation
+ for line in lines {
+ debug_assert!(line.starts_with("cbindgen:"));
+
+ // Remove the "cbindgen:" prefix
+ let annotation = &line[9..];
+
+ // Split the annotation in two
+ let parts: Vec<&str> = annotation.split('=').map(|x| x.trim()).collect();
+
+ if parts.len() > 2 {
+ return Err(format!("Couldn't parse {}.", line));
+ }
+
+ // Grab the name that this annotation is modifying
+ let name = parts[0];
+
+ // If the annotation only has a name, assume it's setting a bool flag
+ if parts.len() == 1 {
+ annotations.insert(name.to_string(), AnnotationValue::Bool(true));
+ continue;
+ }
+
+ // Parse the value we're setting the name to
+ let value = parts[1];
+
+ if let Some(x) = parse_list(value) {
+ annotations.insert(name.to_string(), AnnotationValue::List(x));
+ continue;
+ }
+ if let Ok(x) = value.parse::<bool>() {
+ annotations.insert(name.to_string(), AnnotationValue::Bool(x));
+ continue;
+ }
+ annotations.insert(
+ name.to_string(),
+ if value.is_empty() {
+ AnnotationValue::Atom(None)
+ } else {
+ AnnotationValue::Atom(Some(value.to_string()))
+ },
+ );
+ }
+
+ Ok(AnnotationSet {
+ annotations,
+ must_use,
+ deprecated,
+ })
+ }
+
+ /// Adds an annotation value if none is specified.
+ pub fn add_default(&mut self, name: &str, value: AnnotationValue) {
+ if let Entry::Vacant(e) = self.annotations.entry(name.to_string()) {
+ e.insert(value);
+ }
+ }
+
+ pub fn list(&self, name: &str) -> Option<Vec<String>> {
+ match self.annotations.get(name) {
+ Some(AnnotationValue::List(x)) => Some(x.clone()),
+ _ => None,
+ }
+ }
+ pub fn atom(&self, name: &str) -> Option<Option<String>> {
+ match self.annotations.get(name) {
+ Some(AnnotationValue::Atom(x)) => Some(x.clone()),
+ _ => None,
+ }
+ }
+ pub fn bool(&self, name: &str) -> Option<bool> {
+ match self.annotations.get(name) {
+ Some(AnnotationValue::Bool(x)) => Some(*x),
+ _ => None,
+ }
+ }
+
+ pub fn parse_atom<T>(&self, name: &str) -> Option<T>
+ where
+ T: Default + FromStr,
+ {
+ match self.annotations.get(name) {
+ Some(AnnotationValue::Atom(x)) => Some(
+ x.as_ref()
+ .map_or(T::default(), |y| y.parse::<T>().ok().unwrap()),
+ ),
+ _ => None,
+ }
+ }
+}
+
+/// Parse lists like "[x, y, z]". This is not implemented efficiently or well.
+fn parse_list(list: &str) -> Option<Vec<String>> {
+ if list.len() < 2 {
+ return None;
+ }
+
+ match (list.chars().next(), list.chars().last()) {
+ (Some('['), Some(']')) => Some(
+ list[1..list.len() - 1]
+ .split(',')
+ .map(|x| x.trim().to_string())
+ .collect(),
+ ),
+ _ => None,
+ }
+}
diff --git a/src/bindgen/ir/cfg.rs b/src/bindgen/ir/cfg.rs
new file mode 100644
index 0000000..e8aa412
--- /dev/null
+++ b/src/bindgen/ir/cfg.rs
@@ -0,0 +1,365 @@
+/* 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::fmt;
+use std::io::Write;
+
+use crate::bindgen::cargo::cargo_metadata::Dependency;
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::writer::SourceWriter;
+
+#[derive(PartialEq, Eq)]
+enum DefineKey<'a> {
+ Boolean(&'a str),
+ Named(&'a str, &'a str),
+}
+
+impl<'a> DefineKey<'a> {
+ fn load(key: &str) -> DefineKey {
+ // TODO: dirty parser
+ if !key.contains('=') {
+ return DefineKey::Boolean(key);
+ }
+
+ let mut splits = key.trim().split('=');
+
+ let name = match splits.next() {
+ Some(n) => n.trim(),
+ None => return DefineKey::Boolean(key),
+ };
+
+ let value = match splits.next() {
+ Some(v) => v.trim(),
+ None => return DefineKey::Boolean(key),
+ };
+
+ if splits.next().is_some() {
+ return DefineKey::Boolean(key);
+ }
+
+ DefineKey::Named(name, value)
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum Cfg {
+ Boolean(String),
+ Named(String, String),
+ Any(Vec<Cfg>),
+ All(Vec<Cfg>),
+ Not(Box<Cfg>),
+}
+
+impl fmt::Display for Cfg {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Cfg::Boolean(key) => write!(f, "{}", key),
+ Cfg::Named(key, value) => write!(f, "{} = {:?}", key, value),
+ Cfg::Any(cfgs) => {
+ write!(f, "any(")?;
+ for (index, cfg) in cfgs.iter().enumerate() {
+ if index > 0 {
+ write!(f, ", ")?;
+ }
+ write!(f, "{}", cfg)?;
+ }
+ write!(f, ")")
+ }
+ Cfg::All(cfgs) => {
+ write!(f, "all(")?;
+ for (index, cfg) in cfgs.iter().enumerate() {
+ if index > 0 {
+ write!(f, ", ")?;
+ }
+ write!(f, "{}", cfg)?;
+ }
+ write!(f, ")")
+ }
+ Cfg::Not(cfg) => write!(f, "not({})", cfg),
+ }
+ }
+}
+
+impl Cfg {
+ pub fn join(cfgs: &[Cfg]) -> Option<Cfg> {
+ if cfgs.is_empty() {
+ None
+ } else {
+ Some(Cfg::All(cfgs.to_owned()))
+ }
+ }
+
+ pub fn append(parent: Option<&Cfg>, child: Option<Cfg>) -> Option<Cfg> {
+ match (parent, child) {
+ (None, None) => None,
+ (None, Some(child)) => Some(child),
+ (Some(parent), None) => Some(parent.clone()),
+ (Some(parent), Some(child)) => Some(Cfg::All(vec![parent.clone(), child])),
+ }
+ }
+
+ pub fn load(attrs: &[syn::Attribute]) -> Option<Cfg> {
+ let mut configs = Vec::new();
+
+ for attr in attrs {
+ if let Ok(syn::Meta::List(syn::MetaList { path, nested, .. })) = attr.parse_meta() {
+ if !path.is_ident("cfg") || nested.len() != 1 {
+ continue;
+ }
+
+ if let Some(config) = Cfg::load_single(nested.first().unwrap()) {
+ configs.push(config);
+ }
+ }
+ }
+
+ match configs.len() {
+ 0 => None,
+ 1 => Some(configs.pop().unwrap()),
+ _ => Some(Cfg::All(configs)),
+ }
+ }
+
+ pub fn load_metadata(dependency: &Dependency) -> Option<Cfg> {
+ let target = dependency.target.as_ref()?;
+ match syn::parse_str::<syn::Meta>(target) {
+ Ok(target) => {
+ // Parsing succeeded using the #[cfg] syntax
+ if let syn::Meta::List(syn::MetaList { path, nested, .. }) = target {
+ if !path.is_ident("cfg") || nested.len() != 1 {
+ return None;
+ }
+ Cfg::load_single(nested.first().unwrap())
+ } else {
+ None
+ }
+ }
+ Err(_) => {
+ // Parsing failed using #[cfg], this may be a literal target
+ // name
+ Cfg::load_single(&syn::NestedMeta::Lit(syn::Lit::Str(syn::LitStr::new(
+ target,
+ proc_macro2::Span::call_site(),
+ ))))
+ }
+ }
+ }
+
+ fn load_single(item: &syn::NestedMeta) -> Option<Cfg> {
+ Some(match *item {
+ syn::NestedMeta::Meta(syn::Meta::Path(ref path)) => {
+ Cfg::Boolean(format!("{}", path.segments.first().unwrap().ident))
+ }
+ syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
+ ref path,
+ lit: syn::Lit::Str(ref value),
+ ..
+ })) => Cfg::Named(
+ format!("{}", path.segments.first().unwrap().ident),
+ value.value(),
+ ),
+ syn::NestedMeta::Meta(syn::Meta::List(syn::MetaList {
+ ref path,
+ ref nested,
+ ..
+ })) => {
+ if path.is_ident("any") {
+ Cfg::Any(Cfg::load_list(nested.iter())?)
+ } else if path.is_ident("all") {
+ Cfg::All(Cfg::load_list(nested.iter())?)
+ } else if path.is_ident("not") {
+ if nested.len() != 1 {
+ return None;
+ }
+
+ Cfg::Not(Box::new(Cfg::load_single(&nested[0])?))
+ } else {
+ return None;
+ }
+ }
+ _ => return None,
+ })
+ }
+
+ fn load_list<'a, I: Iterator<Item = &'a syn::NestedMeta>>(attrs: I) -> Option<Vec<Cfg>> {
+ let mut configs = Vec::new();
+
+ for attr in attrs {
+ configs.push(Cfg::load_single(attr)?);
+ }
+
+ if configs.is_empty() {
+ None
+ } else {
+ Some(configs)
+ }
+ }
+}
+
+pub trait ToCondition: Sized {
+ fn to_condition(&self, config: &Config) -> Option<Condition>;
+}
+
+impl ToCondition for Option<Cfg> {
+ fn to_condition(&self, config: &Config) -> Option<Condition> {
+ self.as_ref()?.to_condition(config)
+ }
+}
+
+impl ToCondition for Cfg {
+ fn to_condition(&self, config: &Config) -> Option<Condition> {
+ match *self {
+ Cfg::Boolean(ref cfg_name) => {
+ let define = config
+ .defines
+ .iter()
+ .find(|(key, ..)| DefineKey::Boolean(cfg_name) == DefineKey::load(key));
+ if let Some((_, define)) = define {
+ Some(Condition::Define(define.to_owned()))
+ } else {
+ warn!(
+ "Missing `[defines]` entry for `{}` in cbindgen config.",
+ self,
+ );
+ None
+ }
+ }
+ Cfg::Named(ref cfg_name, ref cfg_value) => {
+ let define = config.defines.iter().find(|(key, ..)| {
+ DefineKey::Named(cfg_name, cfg_value) == DefineKey::load(key)
+ });
+ if let Some((_, define)) = define {
+ Some(Condition::Define(define.to_owned()))
+ } else {
+ warn!(
+ "Missing `[defines]` entry for `{}` in cbindgen config.",
+ self,
+ );
+ None
+ }
+ }
+ Cfg::Any(ref children) => {
+ let conditions: Vec<_> = children
+ .iter()
+ .filter_map(|x| x.to_condition(config))
+ .collect();
+ match conditions.len() {
+ 0 => None,
+ 1 => conditions.into_iter().next(),
+ _ => Some(Condition::Any(conditions)),
+ }
+ }
+ Cfg::All(ref children) => {
+ let cfgs: Vec<_> = children
+ .iter()
+ .filter_map(|x| x.to_condition(config))
+ .collect();
+ match cfgs.len() {
+ 0 => None,
+ 1 => cfgs.into_iter().next(),
+ _ => Some(Condition::All(cfgs)),
+ }
+ }
+ Cfg::Not(ref child) => child
+ .to_condition(config)
+ .map(|cfg| Condition::Not(Box::new(cfg))),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum Condition {
+ Define(String),
+ Any(Vec<Condition>),
+ All(Vec<Condition>),
+ Not(Box<Condition>),
+}
+
+impl Condition {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ match *self {
+ Condition::Define(ref define) => {
+ if config.language == Language::Cython {
+ write!(out, "{}", define);
+ } else {
+ out.write("defined(");
+ write!(out, "{}", define);
+ out.write(")");
+ }
+ }
+ Condition::Any(ref conditions) => {
+ out.write("(");
+ for (i, condition) in conditions.iter().enumerate() {
+ if i != 0 {
+ out.write(if config.language == Language::Cython {
+ " or "
+ } else {
+ " || "
+ });
+ }
+ condition.write(config, out);
+ }
+ out.write(")");
+ }
+ Condition::All(ref conditions) => {
+ out.write("(");
+ for (i, condition) in conditions.iter().enumerate() {
+ if i != 0 {
+ out.write(if config.language == Language::Cython {
+ " and "
+ } else {
+ " && "
+ });
+ }
+ condition.write(config, out);
+ }
+ out.write(")");
+ }
+ Condition::Not(ref condition) => {
+ out.write(if config.language == Language::Cython {
+ "not "
+ } else {
+ "!"
+ });
+ condition.write(config, out);
+ }
+ }
+ }
+}
+
+pub trait ConditionWrite {
+ fn write_before<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>);
+ fn write_after<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>);
+}
+
+impl ConditionWrite for Option<Condition> {
+ fn write_before<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ if let Some(ref cfg) = *self {
+ if config.language == Language::Cython {
+ out.write("IF ");
+ cfg.write(config, out);
+ out.open_brace();
+ } else {
+ out.push_set_spaces(0);
+ out.write("#if ");
+ cfg.write(config, out);
+ out.pop_set_spaces();
+ out.new_line();
+ }
+ }
+ }
+
+ fn write_after<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ if self.is_some() {
+ if config.language == Language::Cython {
+ out.close_brace(false);
+ } else {
+ out.new_line();
+ out.push_set_spaces(0);
+ out.write("#endif");
+ out.pop_set_spaces();
+ }
+ }
+ }
+}
diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs
new file mode 100644
index 0000000..d3b9bd4
--- /dev/null
+++ b/src/bindgen/ir/constant.rs
@@ -0,0 +1,813 @@
+/* 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::borrow::Cow;
+use std::collections::HashMap;
+use std::io::Write;
+
+use syn::ext::IdentExt;
+use syn::{self, UnOp};
+
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{
+ AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path,
+ Struct, ToCondition, Type,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::writer::{Source, SourceWriter};
+use crate::bindgen::Bindings;
+
+fn member_to_ident(member: &syn::Member) -> String {
+ match member {
+ syn::Member::Named(ref name) => name.unraw().to_string(),
+ syn::Member::Unnamed(ref index) => format!("_{}", index.index),
+ }
+}
+
+// TODO: Maybe add support to more std associated constants.
+fn to_known_assoc_constant(associated_to: &Path, name: &str) -> Option<String> {
+ use crate::bindgen::ir::{IntKind, PrimitiveType};
+
+ if name != "MAX" && name != "MIN" {
+ return None;
+ }
+
+ let prim = PrimitiveType::maybe(associated_to.name())?;
+ let prefix = match prim {
+ PrimitiveType::Integer {
+ kind,
+ signed,
+ zeroable: _,
+ } => match kind {
+ IntKind::B8 => {
+ if signed {
+ "INT8"
+ } else {
+ "UINT8"
+ }
+ }
+ IntKind::B16 => {
+ if signed {
+ "INT16"
+ } else {
+ "UINT16"
+ }
+ }
+ IntKind::B32 => {
+ if signed {
+ "INT32"
+ } else {
+ "UINT32"
+ }
+ }
+ IntKind::B64 => {
+ if signed {
+ "INT64"
+ } else {
+ "UINT64"
+ }
+ }
+ _ => return None,
+ },
+ _ => return None,
+ };
+ Some(format!("{}_{}", prefix, name))
+}
+
+#[derive(Debug, Clone)]
+pub enum Literal {
+ Expr(String),
+ Path {
+ associated_to: Option<(Path, String)>,
+ name: String,
+ },
+ PostfixUnaryOp {
+ op: &'static str,
+ value: Box<Literal>,
+ },
+ BinOp {
+ left: Box<Literal>,
+ op: &'static str,
+ right: Box<Literal>,
+ },
+ FieldAccess {
+ base: Box<Literal>,
+ field: String,
+ },
+ Struct {
+ path: Path,
+ export_name: String,
+ fields: HashMap<String, Literal>,
+ },
+ Cast {
+ ty: Type,
+ value: Box<Literal>,
+ },
+}
+
+impl Literal {
+ fn replace_self_with(&mut self, self_ty: &Path) {
+ match *self {
+ Literal::PostfixUnaryOp { ref mut value, .. } => {
+ value.replace_self_with(self_ty);
+ }
+ Literal::BinOp {
+ ref mut left,
+ ref mut right,
+ ..
+ } => {
+ left.replace_self_with(self_ty);
+ right.replace_self_with(self_ty);
+ }
+ Literal::FieldAccess { ref mut base, .. } => {
+ base.replace_self_with(self_ty);
+ }
+ Literal::Struct {
+ ref mut path,
+ ref mut export_name,
+ ref mut fields,
+ } => {
+ if path.replace_self_with(self_ty) {
+ *export_name = self_ty.name().to_owned();
+ }
+ for ref mut expr in fields.values_mut() {
+ expr.replace_self_with(self_ty);
+ }
+ }
+ Literal::Cast {
+ ref mut ty,
+ ref mut value,
+ } => {
+ ty.replace_self_with(self_ty);
+ value.replace_self_with(self_ty);
+ }
+ Literal::Path {
+ ref mut associated_to,
+ ..
+ } => {
+ if let Some((ref mut path, ref mut export_name)) = *associated_to {
+ if path.replace_self_with(self_ty) {
+ *export_name = self_ty.name().to_owned();
+ }
+ }
+ }
+ Literal::Expr(..) => {}
+ }
+ }
+
+ fn is_valid(&self, bindings: &Bindings) -> bool {
+ match *self {
+ Literal::Expr(..) => true,
+ Literal::Path {
+ ref associated_to,
+ ref name,
+ } => {
+ if let Some((ref path, _export_name)) = associated_to {
+ return bindings.struct_exists(path)
+ || to_known_assoc_constant(path, name).is_some();
+ }
+ true
+ }
+ Literal::PostfixUnaryOp { ref value, .. } => value.is_valid(bindings),
+ Literal::BinOp {
+ ref left,
+ ref right,
+ ..
+ } => left.is_valid(bindings) && right.is_valid(bindings),
+ Literal::FieldAccess { ref base, .. } => base.is_valid(bindings),
+ Literal::Struct { ref path, .. } => bindings.struct_exists(path),
+ Literal::Cast { ref value, .. } => value.is_valid(bindings),
+ }
+ }
+
+ fn can_be_constexpr(&self) -> bool {
+ !self.has_pointer_casts()
+ }
+
+ fn visit(&self, visitor: &mut impl FnMut(&Self) -> bool) -> bool {
+ if !visitor(self) {
+ return false;
+ }
+ match self {
+ Literal::Expr(..) | Literal::Path { .. } => true,
+ Literal::PostfixUnaryOp { ref value, .. } => value.visit(visitor),
+ Literal::BinOp {
+ ref left,
+ ref right,
+ ..
+ } => left.visit(visitor) && right.visit(visitor),
+ Literal::FieldAccess { ref base, .. } => base.visit(visitor),
+ Literal::Struct { ref fields, .. } => {
+ for (_name, field) in fields.iter() {
+ if !field.visit(visitor) {
+ return false;
+ }
+ }
+ true
+ }
+ Literal::Cast { ref value, .. } => value.visit(visitor),
+ }
+ }
+
+ fn has_pointer_casts(&self) -> bool {
+ let mut has_pointer_casts = false;
+ self.visit(&mut |lit| {
+ if let Literal::Cast { ref ty, .. } = *lit {
+ has_pointer_casts = has_pointer_casts || ty.is_ptr();
+ }
+ !has_pointer_casts
+ });
+ has_pointer_casts
+ }
+
+ pub fn uses_only_primitive_types(&self) -> bool {
+ let mut uses_only_primitive_types = true;
+ self.visit(&mut |lit| {
+ // XXX This is a bit sketchy, but alas.
+ uses_only_primitive_types = uses_only_primitive_types
+ && match *lit {
+ Literal::Struct { .. } => false,
+ Literal::Cast { ref ty, .. } => ty.is_primitive_or_ptr_primitive(),
+ _ => true,
+ };
+ uses_only_primitive_types
+ });
+ uses_only_primitive_types
+ }
+}
+
+impl Literal {
+ pub fn rename_for_config(&mut self, config: &Config) {
+ match self {
+ Literal::Struct {
+ ref mut export_name,
+ fields,
+ ..
+ } => {
+ config.export.rename(export_name);
+ for lit in fields.values_mut() {
+ lit.rename_for_config(config);
+ }
+ }
+ Literal::FieldAccess { ref mut base, .. } => {
+ base.rename_for_config(config);
+ }
+ Literal::Path {
+ ref mut associated_to,
+ ref mut name,
+ } => {
+ if let Some((_path, ref mut export_name)) = associated_to {
+ config.export.rename(export_name);
+ } else {
+ config.export.rename(name);
+ }
+ }
+ Literal::PostfixUnaryOp { ref mut value, .. } => {
+ value.rename_for_config(config);
+ }
+ Literal::BinOp {
+ ref mut left,
+ ref mut right,
+ ..
+ } => {
+ left.rename_for_config(config);
+ right.rename_for_config(config);
+ }
+ Literal::Expr(_) => {}
+ Literal::Cast {
+ ref mut ty,
+ ref mut value,
+ } => {
+ ty.rename_for_config(config, &GenericParams::default());
+ value.rename_for_config(config);
+ }
+ }
+ }
+
+ // Translate from full blown `syn::Expr` into a simpler `Literal` type
+ pub fn load(expr: &syn::Expr) -> Result<Literal, String> {
+ match *expr {
+ // Match binary expressions of the form `a * b`
+ syn::Expr::Binary(ref bin_expr) => {
+ let l = Self::load(&bin_expr.left)?;
+ let r = Self::load(&bin_expr.right)?;
+ let op = match bin_expr.op {
+ syn::BinOp::Add(..) => "+",
+ syn::BinOp::Sub(..) => "-",
+ syn::BinOp::Mul(..) => "*",
+ syn::BinOp::Div(..) => "/",
+ syn::BinOp::Rem(..) => "%",
+ syn::BinOp::And(..) => "&&",
+ syn::BinOp::Or(..) => "||",
+ syn::BinOp::BitXor(..) => "^",
+ syn::BinOp::BitAnd(..) => "&",
+ syn::BinOp::BitOr(..) => "|",
+ syn::BinOp::Shl(..) => "<<",
+ syn::BinOp::Shr(..) => ">>",
+ syn::BinOp::Eq(..) => "==",
+ syn::BinOp::Lt(..) => "<",
+ syn::BinOp::Le(..) => "<=",
+ syn::BinOp::Ne(..) => "!=",
+ syn::BinOp::Ge(..) => ">=",
+ syn::BinOp::Gt(..) => ">",
+ syn::BinOp::AddEq(..) => "+=",
+ syn::BinOp::SubEq(..) => "-=",
+ syn::BinOp::MulEq(..) => "*=",
+ syn::BinOp::DivEq(..) => "/=",
+ syn::BinOp::RemEq(..) => "%=",
+ syn::BinOp::BitXorEq(..) => "^=",
+ syn::BinOp::BitAndEq(..) => "&=",
+ syn::BinOp::BitOrEq(..) => "|=",
+ syn::BinOp::ShlEq(..) => "<<=",
+ syn::BinOp::ShrEq(..) => ">>=",
+ };
+ Ok(Literal::BinOp {
+ left: Box::new(l),
+ op,
+ right: Box::new(r),
+ })
+ }
+
+ // Match literals like true, 'a', 32 etc
+ syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
+ match lit {
+ syn::Lit::Byte(ref value) => Ok(Literal::Expr(format!("{}", value.value()))),
+ syn::Lit::Char(ref value) => Ok(Literal::Expr(match value.value() as u32 {
+ 0..=255 => format!("'{}'", value.value().escape_default()),
+ other_code => format!(r"U'\U{:08X}'", other_code),
+ })),
+ syn::Lit::Int(ref value) => {
+ let suffix = match value.suffix() {
+ "u64" => "ull",
+ "i64" => "ll",
+ "u32" => "u",
+ _ if value.base10_parse::<i64>().is_err() => "ull",
+ _ => "",
+ };
+ Ok(Literal::Expr(format!(
+ "{}{}",
+ value.base10_digits(),
+ suffix
+ )))
+ }
+ syn::Lit::Float(ref value) => {
+ Ok(Literal::Expr(value.base10_digits().to_string()))
+ }
+ syn::Lit::Bool(ref value) => Ok(Literal::Expr(format!("{}", value.value))),
+ // TODO: Add support for byte string and Verbatim
+ _ => Err(format!("Unsupported literal expression. {:?}", *lit)),
+ }
+ }
+
+ syn::Expr::Field(syn::ExprField {
+ ref base,
+ ref member,
+ ..
+ }) => Ok(Literal::FieldAccess {
+ base: Box::new(Literal::load(base)?),
+ field: member_to_ident(member),
+ }),
+
+ syn::Expr::Call(syn::ExprCall {
+ ref func, ref args, ..
+ }) => {
+ let struct_name = match Literal::load(func)? {
+ Literal::Path {
+ associated_to: None,
+ name,
+ } => name,
+ _ => return Err(format!("Unsupported call expression. {:?}", *expr)),
+ };
+ let mut fields = HashMap::<String, Literal>::default();
+ for (index, arg) in args.iter().enumerate() {
+ let ident =
+ member_to_ident(&syn::Member::Unnamed(syn::Index::from(index))).to_string();
+ let value = Literal::load(arg)?;
+ fields.insert(ident, value);
+ }
+ Ok(Literal::Struct {
+ path: Path::new(struct_name.clone()),
+ export_name: struct_name,
+ fields,
+ })
+ }
+
+ syn::Expr::Struct(syn::ExprStruct {
+ ref path,
+ ref fields,
+ ..
+ }) => {
+ let struct_name = path.segments[0].ident.unraw().to_string();
+ let mut field_map = HashMap::<String, Literal>::default();
+ for field in fields {
+ let ident = member_to_ident(&field.member).to_string();
+ let value = Literal::load(&field.expr)?;
+ field_map.insert(ident, value);
+ }
+ Ok(Literal::Struct {
+ path: Path::new(struct_name.clone()),
+ export_name: struct_name,
+ fields: field_map,
+ })
+ }
+
+ syn::Expr::Unary(syn::ExprUnary {
+ ref op, ref expr, ..
+ }) => match *op {
+ UnOp::Not(_) => {
+ let val = Self::load(expr)?;
+ Ok(Literal::PostfixUnaryOp {
+ op: "~",
+ value: Box::new(val),
+ })
+ }
+ UnOp::Neg(_) => {
+ let val = Self::load(expr)?;
+ Ok(Literal::PostfixUnaryOp {
+ op: "-",
+ value: Box::new(val),
+ })
+ }
+ _ => Err(format!("Unsupported Unary expression. {:?}", *op)),
+ },
+
+ // Match identifiers, like `5 << SHIFT`
+ syn::Expr::Path(syn::ExprPath { ref path, .. }) => {
+ // Handle only the simplest identifiers and Associated::IDENT
+ // kind of syntax.
+ Ok(match path.segments.len() {
+ 1 => Literal::Path {
+ associated_to: None,
+ name: path.segments[0].ident.to_string(),
+ },
+ 2 => {
+ let struct_name = path.segments[0].ident.to_string();
+ Literal::Path {
+ associated_to: Some((Path::new(&struct_name), struct_name)),
+ name: path.segments[1].ident.to_string(),
+ }
+ }
+ _ => return Err(format!("Unsupported path expression. {:?}", path)),
+ })
+ }
+
+ syn::Expr::Paren(syn::ExprParen { ref expr, .. }) => Self::load(expr),
+
+ syn::Expr::Cast(syn::ExprCast {
+ ref expr, ref ty, ..
+ }) => {
+ let val = Self::load(expr)?;
+ match Type::load(ty)? {
+ Some(ty) => Ok(Literal::Cast {
+ ty,
+ value: Box::new(val),
+ }),
+ None => Err("Cannot cast to zero sized type.".to_owned()),
+ }
+ }
+
+ _ => Err(format!("Unsupported expression. {:?}", *expr)),
+ }
+ }
+
+ pub(crate) fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ match self {
+ Literal::Expr(v) => match (&**v, config.language) {
+ ("true", Language::Cython) => write!(out, "True"),
+ ("false", Language::Cython) => write!(out, "False"),
+ (v, _) => write!(out, "{}", v),
+ },
+ Literal::Path {
+ ref associated_to,
+ ref name,
+ } => {
+ if let Some((ref path, ref export_name)) = associated_to {
+ if let Some(known) = to_known_assoc_constant(path, name) {
+ return write!(out, "{}", known);
+ }
+ let path_separator = match config.language {
+ Language::Cython | Language::C => "_",
+ Language::Cxx => {
+ if config.structure.associated_constants_in_body {
+ "::"
+ } else {
+ "_"
+ }
+ }
+ };
+ write!(out, "{}{}", export_name, path_separator)
+ }
+ write!(out, "{}", name)
+ }
+ Literal::FieldAccess {
+ ref base,
+ ref field,
+ } => {
+ write!(out, "(");
+ base.write(config, out);
+ write!(out, ").{}", field);
+ }
+ Literal::PostfixUnaryOp { op, ref value } => {
+ write!(out, "{}", op);
+ value.write(config, out);
+ }
+ Literal::BinOp {
+ ref left,
+ op,
+ ref right,
+ } => {
+ write!(out, "(");
+ left.write(config, out);
+ write!(out, " {} ", op);
+ right.write(config, out);
+ write!(out, ")");
+ }
+ Literal::Cast { ref ty, ref value } => {
+ out.write(if config.language == Language::Cython {
+ "<"
+ } else {
+ "("
+ });
+ ty.write(config, out);
+ out.write(if config.language == Language::Cython {
+ ">"
+ } else {
+ ")"
+ });
+ value.write(config, out);
+ }
+ Literal::Struct {
+ export_name,
+ fields,
+ path,
+ } => {
+ match config.language {
+ Language::C => write!(out, "({})", export_name),
+ Language::Cxx => write!(out, "{}", export_name),
+ Language::Cython => write!(out, "<{}>", export_name),
+ }
+
+ write!(out, "{{ ");
+ let mut is_first_field = true;
+ // In C++, same order as defined is required.
+ let ordered_fields = out.bindings().struct_field_names(path);
+ for ordered_key in ordered_fields.iter() {
+ if let Some(lit) = fields.get(ordered_key) {
+ if !is_first_field {
+ write!(out, ", ");
+ } else {
+ is_first_field = false;
+ }
+ match config.language {
+ Language::Cxx => write!(out, "/* .{} = */ ", ordered_key),
+ Language::C => write!(out, ".{} = ", ordered_key),
+ Language::Cython => {}
+ }
+ lit.write(config, out);
+ }
+ }
+ write!(out, " }}");
+ }
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Constant {
+ pub path: Path,
+ pub export_name: String,
+ pub ty: Type,
+ pub value: Literal,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+ pub associated_to: Option<Path>,
+}
+
+impl Constant {
+ pub fn load(
+ path: Path,
+ mod_cfg: Option<&Cfg>,
+ ty: &syn::Type,
+ expr: &syn::Expr,
+ attrs: &[syn::Attribute],
+ associated_to: Option<Path>,
+ ) -> Result<Constant, String> {
+ let ty = Type::load(ty)?;
+ let mut ty = match ty {
+ Some(ty) => ty,
+ None => {
+ return Err("Cannot have a zero sized const definition.".to_owned());
+ }
+ };
+
+ let mut lit = Literal::load(expr)?;
+
+ if let Some(ref associated_to) = associated_to {
+ ty.replace_self_with(associated_to);
+ lit.replace_self_with(associated_to);
+ }
+
+ Ok(Constant::new(
+ path,
+ ty,
+ lit,
+ Cfg::append(mod_cfg, Cfg::load(attrs)),
+ AnnotationSet::load(attrs)?,
+ Documentation::load(attrs),
+ associated_to,
+ ))
+ }
+
+ pub fn new(
+ path: Path,
+ ty: Type,
+ value: Literal,
+ cfg: Option<Cfg>,
+ annotations: AnnotationSet,
+ documentation: Documentation,
+ associated_to: Option<Path>,
+ ) -> Self {
+ let export_name = path.name().to_owned();
+ Self {
+ path,
+ export_name,
+ ty,
+ value,
+ cfg,
+ annotations,
+ documentation,
+ associated_to,
+ }
+ }
+
+ pub fn uses_only_primitive_types(&self) -> bool {
+ self.value.uses_only_primitive_types() && self.ty.is_primitive_or_ptr_primitive()
+ }
+}
+
+impl Item for Constant {
+ fn path(&self) -> &Path {
+ &self.path
+ }
+
+ fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ self.ty.add_dependencies(library, out);
+ }
+
+ fn export_name(&self) -> &str {
+ &self.export_name
+ }
+
+ fn cfg(&self) -> Option<&Cfg> {
+ self.cfg.as_ref()
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ &self.annotations
+ }
+
+ fn annotations_mut(&mut self) -> &mut AnnotationSet {
+ &mut self.annotations
+ }
+
+ fn container(&self) -> ItemContainer {
+ ItemContainer::Constant(self.clone())
+ }
+
+ fn rename_for_config(&mut self, config: &Config) {
+ if self.associated_to.is_none() {
+ config.export.rename(&mut self.export_name);
+ }
+ self.value.rename_for_config(config);
+ self.ty.rename_for_config(config, &GenericParams::default()); // FIXME: should probably propagate something here
+ }
+
+ fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ self.ty.resolve_declaration_types(resolver);
+ }
+}
+
+impl Constant {
+ pub fn write_declaration<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ associated_to_struct: &Struct,
+ ) {
+ debug_assert!(self.associated_to.is_some());
+ debug_assert!(config.language == Language::Cxx);
+ debug_assert!(!associated_to_struct.is_transparent);
+ debug_assert!(config.structure.associated_constants_in_body);
+ debug_assert!(config.constant.allow_static_const);
+
+ if let Type::Ptr { is_const: true, .. } = self.ty {
+ out.write("static ");
+ } else {
+ out.write("static const ");
+ }
+ self.ty.write(config, out);
+ write!(out, " {};", self.export_name())
+ }
+
+ pub fn write<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ associated_to_struct: Option<&Struct>,
+ ) {
+ if let Some(assoc) = associated_to_struct {
+ if assoc.is_generic() {
+ return; // Not tested / implemented yet, so bail out.
+ }
+ }
+
+ if !self.value.is_valid(out.bindings()) {
+ return;
+ }
+
+ let associated_to_transparent = associated_to_struct.map_or(false, |s| s.is_transparent);
+
+ let in_body = associated_to_struct.is_some()
+ && config.language == Language::Cxx
+ && config.structure.associated_constants_in_body
+ && config.constant.allow_static_const
+ && !associated_to_transparent;
+
+ let condition = self.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ let name = if in_body {
+ Cow::Owned(format!(
+ "{}::{}",
+ associated_to_struct.unwrap().export_name(),
+ self.export_name(),
+ ))
+ } else if self.associated_to.is_none() {
+ Cow::Borrowed(self.export_name())
+ } else {
+ let associated_name = match associated_to_struct {
+ Some(s) => Cow::Borrowed(s.export_name()),
+ None => {
+ let mut name = self.associated_to.as_ref().unwrap().name().to_owned();
+ config.export.rename(&mut name);
+ Cow::Owned(name)
+ }
+ };
+
+ Cow::Owned(format!("{}_{}", associated_name, self.export_name()))
+ };
+
+ let value = match self.value {
+ Literal::Struct {
+ ref fields,
+ ref path,
+ ..
+ } if out.bindings().struct_is_transparent(path) => fields.iter().next().unwrap().1,
+ _ => &self.value,
+ };
+
+ self.documentation.write(config, out);
+
+ let allow_constexpr = config.constant.allow_constexpr && self.value.can_be_constexpr();
+ match config.language {
+ Language::Cxx if config.constant.allow_static_const || allow_constexpr => {
+ if allow_constexpr {
+ out.write("constexpr ")
+ }
+
+ if config.constant.allow_static_const {
+ out.write(if in_body { "inline " } else { "static " });
+ }
+
+ if let Type::Ptr { is_const: true, .. } = self.ty {
+ // Nothing.
+ } else {
+ out.write("const ");
+ }
+
+ self.ty.write(config, out);
+ write!(out, " {} = ", name);
+ value.write(config, out);
+ write!(out, ";");
+ }
+ Language::Cxx | Language::C => {
+ write!(out, "#define {} ", name);
+ value.write(config, out);
+ }
+ Language::Cython => {
+ out.write("const ");
+ self.ty.write(config, out);
+ // For extern Cython declarations the initializer is ignored,
+ // but still useful as documentation, so we write it as a comment.
+ write!(out, " {} # = ", name);
+ value.write(config, out);
+ }
+ }
+
+ condition.write_after(config, out);
+ }
+}
diff --git a/src/bindgen/ir/documentation.rs b/src/bindgen/ir/documentation.rs
new file mode 100644
index 0000000..6822c0e
--- /dev/null
+++ b/src/bindgen/ir/documentation.rs
@@ -0,0 +1,111 @@
+/* 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::io::Write;
+
+use crate::bindgen::config::{Config, DocumentationLength, DocumentationStyle, Language};
+use crate::bindgen::utilities::SynAttributeHelpers;
+use crate::bindgen::writer::{Source, SourceWriter};
+
+#[derive(Debug, Clone)]
+pub struct Documentation {
+ pub doc_comment: Vec<String>,
+}
+
+impl Documentation {
+ pub fn load(attrs: &[syn::Attribute]) -> Self {
+ let doc = attrs
+ .get_comment_lines()
+ .into_iter()
+ .filter(|x| !x.trim_start().starts_with("cbindgen:"))
+ .collect();
+
+ Documentation { doc_comment: doc }
+ }
+
+ pub fn simple(line: &str) -> Self {
+ Documentation {
+ doc_comment: vec![line.to_owned()],
+ }
+ }
+
+ pub fn none() -> Self {
+ Documentation {
+ doc_comment: Vec::new(),
+ }
+ }
+}
+
+impl Source for Documentation {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ if self.doc_comment.is_empty() || !config.documentation {
+ return;
+ }
+
+ let end = match config.documentation_length {
+ DocumentationLength::Short => 1,
+ DocumentationLength::Full => self.doc_comment.len(),
+ };
+
+ // Cython uses Python-style comments, so `documentation_style` is not relevant.
+ if config.language == Language::Cython {
+ for line in &self.doc_comment[..end] {
+ write!(out, "#{}", line);
+ out.new_line();
+ }
+ return;
+ }
+
+ let style = match config.documentation_style {
+ DocumentationStyle::Auto if config.language == Language::C => DocumentationStyle::Doxy,
+ DocumentationStyle::Auto if config.language == Language::Cxx => DocumentationStyle::Cxx,
+ DocumentationStyle::Auto => DocumentationStyle::C, // Fallback if `Language` gets extended.
+ other => other,
+ };
+
+ // Following these documents for style conventions:
+ // https://en.wikibooks.org/wiki/C++_Programming/Code/Style_Conventions/Comments
+ // https://www.cs.cmu.edu/~410/doc/doxygen.html
+ match style {
+ DocumentationStyle::C => {
+ out.write("/*");
+ out.new_line();
+ }
+
+ DocumentationStyle::Doxy => {
+ out.write("/**");
+ out.new_line();
+ }
+
+ _ => (),
+ }
+
+ for line in &self.doc_comment[..end] {
+ match style {
+ DocumentationStyle::C => out.write(""),
+ DocumentationStyle::Doxy => out.write(" *"),
+ DocumentationStyle::C99 => out.write("//"),
+ DocumentationStyle::Cxx => out.write("///"),
+ DocumentationStyle::Auto => unreachable!(), // Auto case should always be covered
+ }
+
+ write!(out, "{}", line);
+ out.new_line();
+ }
+
+ match style {
+ DocumentationStyle::C => {
+ out.write(" */");
+ out.new_line();
+ }
+
+ DocumentationStyle::Doxy => {
+ out.write(" */");
+ out.new_line();
+ }
+
+ _ => (),
+ }
+ }
+}
diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs
new file mode 100644
index 0000000..a456b76
--- /dev/null
+++ b/src/bindgen/ir/enumeration.rs
@@ -0,0 +1,1556 @@
+/* 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::io::Write;
+
+use syn::ext::IdentExt;
+
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{
+ AnnotationSet, AnnotationValue, Cfg, ConditionWrite, DeprecatedNoteKind, Documentation, Field,
+ GenericArgument, GenericParams, GenericPath, Item, ItemContainer, Literal, Path, Repr,
+ ReprStyle, Struct, ToCondition, Type,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::mangle;
+use crate::bindgen::monomorph::Monomorphs;
+use crate::bindgen::rename::{IdentifierType, RenameRule};
+use crate::bindgen::reserved;
+use crate::bindgen::writer::{ListType, Source, SourceWriter};
+
+#[allow(clippy::large_enum_variant)]
+#[derive(Debug, Clone)]
+pub enum VariantBody {
+ Empty(AnnotationSet),
+ Body {
+ /// The variant field / export name.
+ name: String,
+ /// The struct with all the items.
+ body: Struct,
+ /// A separate named struct is not created for this variant,
+ /// an unnamed struct is inlined at the point of use instead.
+ /// This is a reasonable thing to do only for tuple variants with a single field.
+ inline: bool,
+ /// Generated cast methods return the variant's only field instead of the variant itself.
+ /// For backward compatibility casts are inlined in a slightly
+ /// larger set of cases than whole variants.
+ inline_casts: bool,
+ },
+}
+
+impl VariantBody {
+ fn empty() -> Self {
+ Self::Empty(AnnotationSet::new())
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ match *self {
+ Self::Empty(ref anno) => anno,
+ Self::Body { ref body, .. } => &body.annotations,
+ }
+ }
+
+ fn is_empty(&self) -> bool {
+ match *self {
+ Self::Empty(..) => true,
+ Self::Body { .. } => false,
+ }
+ }
+
+ fn specialize(
+ &self,
+ generic_values: &[GenericArgument],
+ mappings: &[(&Path, &GenericArgument)],
+ config: &Config,
+ ) -> Self {
+ match *self {
+ Self::Empty(ref annos) => Self::Empty(annos.clone()),
+ Self::Body {
+ ref name,
+ ref body,
+ inline,
+ inline_casts,
+ } => Self::Body {
+ name: name.clone(),
+ body: body.specialize(generic_values, mappings, config),
+ inline,
+ inline_casts,
+ },
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct EnumVariant {
+ pub name: String,
+ pub export_name: String,
+ pub discriminant: Option<Literal>,
+ pub body: VariantBody,
+ pub cfg: Option<Cfg>,
+ pub documentation: Documentation,
+}
+
+impl EnumVariant {
+ fn load(
+ inline_tag_field: bool,
+ variant: &syn::Variant,
+ generic_params: GenericParams,
+ mod_cfg: Option<&Cfg>,
+ self_path: &Path,
+ enum_annotations: &AnnotationSet,
+ config: &Config,
+ ) -> Result<Self, String> {
+ let discriminant = match variant.discriminant {
+ Some((_, ref expr)) => Some(Literal::load(expr)?),
+ None => None,
+ };
+
+ fn parse_fields(
+ inline_tag_field: bool,
+ fields: &syn::punctuated::Punctuated<syn::Field, syn::token::Comma>,
+ self_path: &Path,
+ inline_name: Option<&str>,
+ ) -> Result<Vec<Field>, String> {
+ let mut res = Vec::new();
+
+ if inline_tag_field {
+ res.push(Field::from_name_and_type(
+ inline_name.map_or_else(|| "tag".to_string(), |name| format!("{}_tag", name)),
+ Type::Path(GenericPath::new(Path::new("Tag"), vec![])),
+ ));
+ }
+
+ for (i, field) in fields.iter().enumerate() {
+ if let Some(mut ty) = Type::load(&field.ty)? {
+ ty.replace_self_with(self_path);
+ res.push(Field {
+ name: inline_name.map_or_else(
+ || match field.ident {
+ Some(ref ident) => ident.unraw().to_string(),
+ None => i.to_string(),
+ },
+ |name| name.to_string(),
+ ),
+ ty,
+ cfg: Cfg::load(&field.attrs),
+ annotations: AnnotationSet::load(&field.attrs)?,
+ documentation: Documentation::load(&field.attrs),
+ });
+ }
+ }
+
+ Ok(res)
+ }
+
+ let variant_cfg = Cfg::append(mod_cfg, Cfg::load(&variant.attrs));
+ let mut annotations = AnnotationSet::load(&variant.attrs)?;
+ if let Some(b) = enum_annotations.bool("derive-ostream") {
+ annotations.add_default("derive-ostream", AnnotationValue::Bool(b));
+ }
+
+ let body_rule = enum_annotations
+ .parse_atom::<RenameRule>("rename-variant-name-fields")
+ .unwrap_or(config.enumeration.rename_variant_name_fields);
+
+ let body = match variant.fields {
+ syn::Fields::Unit => VariantBody::Empty(annotations),
+ syn::Fields::Named(ref fields) => {
+ let path = Path::new(format!("{}_Body", variant.ident));
+ let name = body_rule
+ .apply(
+ &variant.ident.unraw().to_string(),
+ IdentifierType::StructMember,
+ )
+ .into_owned();
+ VariantBody::Body {
+ body: Struct::new(
+ path,
+ generic_params,
+ parse_fields(inline_tag_field, &fields.named, self_path, None)?,
+ inline_tag_field,
+ true,
+ None,
+ false,
+ None,
+ annotations,
+ Documentation::none(),
+ ),
+ name,
+ inline: false,
+ inline_casts: false,
+ }
+ }
+ syn::Fields::Unnamed(ref fields) => {
+ let path = Path::new(format!("{}_Body", variant.ident));
+ let name = body_rule
+ .apply(
+ &variant.ident.unraw().to_string(),
+ IdentifierType::StructMember,
+ )
+ .into_owned();
+ let inline_casts = fields.unnamed.len() == 1;
+ // In C++ types with destructors cannot be put into unnamed structs like the
+ // inlining requires, and it's hard to detect such types.
+ // Besides that for C++ we generate casts/getters that can be used instead of
+ // direct field accesses and also have a benefit of being checked.
+ // As a result we don't currently inline variant definitions in C++ mode at all.
+ let inline = inline_casts && config.language != Language::Cxx;
+ let inline_name = if inline { Some(&*name) } else { None };
+ VariantBody::Body {
+ body: Struct::new(
+ path,
+ generic_params,
+ parse_fields(inline_tag_field, &fields.unnamed, self_path, inline_name)?,
+ inline_tag_field,
+ true,
+ None,
+ false,
+ None,
+ annotations,
+ Documentation::none(),
+ ),
+ name,
+ inline,
+ inline_casts,
+ }
+ }
+ };
+
+ Ok(EnumVariant::new(
+ variant.ident.unraw().to_string(),
+ discriminant,
+ body,
+ variant_cfg,
+ Documentation::load(&variant.attrs),
+ ))
+ }
+
+ pub fn new(
+ name: String,
+ discriminant: Option<Literal>,
+ body: VariantBody,
+ cfg: Option<Cfg>,
+ documentation: Documentation,
+ ) -> Self {
+ let export_name = name.clone();
+ Self {
+ name,
+ export_name,
+ discriminant,
+ body,
+ cfg,
+ documentation,
+ }
+ }
+
+ fn simplify_standard_types(&mut self, config: &Config) {
+ if let VariantBody::Body { ref mut body, .. } = self.body {
+ body.simplify_standard_types(config);
+ }
+ }
+
+ fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ if let VariantBody::Body { ref body, .. } = self.body {
+ body.add_dependencies(library, out);
+ }
+ }
+
+ fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ if let VariantBody::Body { ref mut body, .. } = self.body {
+ body.resolve_declaration_types(resolver);
+ }
+ }
+
+ fn specialize(
+ &self,
+ generic_values: &[GenericArgument],
+ mappings: &[(&Path, &GenericArgument)],
+ config: &Config,
+ ) -> Self {
+ Self::new(
+ mangle::mangle_name(&self.name, generic_values, &config.export.mangle),
+ self.discriminant.clone(),
+ self.body.specialize(generic_values, mappings, config),
+ self.cfg.clone(),
+ self.documentation.clone(),
+ )
+ }
+
+ fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
+ if let VariantBody::Body { ref body, .. } = self.body {
+ body.add_monomorphs(library, out);
+ }
+ }
+
+ fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
+ if let VariantBody::Body { ref mut body, .. } = self.body {
+ body.mangle_paths(monomorphs);
+ }
+ }
+}
+
+impl Source for EnumVariant {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ let condition = self.cfg.to_condition(config);
+ // Cython doesn't support conditional enum variants.
+ if config.language != Language::Cython {
+ condition.write_before(config, out);
+ }
+ self.documentation.write(config, out);
+ write!(out, "{}", self.export_name);
+ if let Some(discriminant) = &self.discriminant {
+ if config.language == Language::Cython {
+ // For extern Cython declarations the enumerator value is ignored,
+ // but still useful as documentation, so we write it as a comment.
+ out.write(" #")
+ }
+ out.write(" = ");
+ discriminant.write(config, out);
+ }
+ out.write(",");
+ if config.language != Language::Cython {
+ condition.write_after(config, out);
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Enum {
+ pub path: Path,
+ pub export_name: String,
+ pub generic_params: GenericParams,
+ pub repr: Repr,
+ pub variants: Vec<EnumVariant>,
+ pub tag: Option<String>,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+}
+
+impl Enum {
+ /// Name of the generated tag enum.
+ fn tag_name(&self) -> &str {
+ self.tag.as_deref().unwrap_or_else(|| self.export_name())
+ }
+
+ /// Enum with data turns into a union of structs with each struct having its own tag field.
+ fn inline_tag_field(repr: &Repr) -> bool {
+ repr.style != ReprStyle::C
+ }
+
+ pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
+ if self.generic_params.len() > 0 {
+ return;
+ }
+
+ for v in &self.variants {
+ v.add_monomorphs(library, out);
+ }
+ }
+
+ fn can_derive_eq(&self) -> bool {
+ if self.tag.is_none() {
+ return false;
+ }
+
+ self.variants.iter().all(|variant| match variant.body {
+ VariantBody::Empty(..) => true,
+ VariantBody::Body { ref body, .. } => body.can_derive_eq(),
+ })
+ }
+
+ pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
+ for variant in &mut self.variants {
+ variant.mangle_paths(monomorphs);
+ }
+ }
+
+ pub fn load(
+ item: &syn::ItemEnum,
+ mod_cfg: Option<&Cfg>,
+ config: &Config,
+ ) -> Result<Enum, String> {
+ let repr = Repr::load(&item.attrs)?;
+ if repr.style == ReprStyle::Rust && repr.ty.is_none() {
+ return Err("Enum is not marked with a valid #[repr(prim)] or #[repr(C)].".to_owned());
+ }
+ // TODO: Implement translation of aligned enums.
+ if repr.align.is_some() {
+ return Err("Enum is marked with #[repr(align(...))] or #[repr(packed)].".to_owned());
+ }
+
+ let path = Path::new(item.ident.unraw().to_string());
+ let generic_params = GenericParams::load(&item.generics)?;
+
+ let mut variants = Vec::new();
+ let mut has_data = false;
+
+ let annotations = AnnotationSet::load(&item.attrs)?;
+
+ for variant in item.variants.iter() {
+ let variant = EnumVariant::load(
+ Self::inline_tag_field(&repr),
+ variant,
+ generic_params.clone(),
+ mod_cfg,
+ &path,
+ &annotations,
+ config,
+ )?;
+ has_data = has_data || !variant.body.is_empty();
+ variants.push(variant);
+ }
+
+ if let Some(names) = annotations.list("enum-trailing-values") {
+ for name in names {
+ variants.push(EnumVariant::new(
+ name,
+ None,
+ VariantBody::empty(),
+ None,
+ Documentation::none(),
+ ));
+ }
+ }
+
+ if config.enumeration.add_sentinel(&annotations) {
+ variants.push(EnumVariant::new(
+ "Sentinel".to_owned(),
+ None,
+ VariantBody::empty(),
+ None,
+ Documentation::simple(" Must be last for serialization purposes"),
+ ));
+ }
+
+ let tag = if has_data {
+ Some("Tag".to_string())
+ } else {
+ None
+ };
+
+ Ok(Enum::new(
+ path,
+ generic_params,
+ repr,
+ variants,
+ tag,
+ Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
+ annotations,
+ Documentation::load(&item.attrs),
+ ))
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ path: Path,
+ generic_params: GenericParams,
+ repr: Repr,
+ variants: Vec<EnumVariant>,
+ tag: Option<String>,
+ cfg: Option<Cfg>,
+ annotations: AnnotationSet,
+ documentation: Documentation,
+ ) -> Self {
+ let export_name = path.name().to_owned();
+ Self {
+ path,
+ export_name,
+ generic_params,
+ repr,
+ variants,
+ tag,
+ cfg,
+ annotations,
+ documentation,
+ }
+ }
+}
+
+impl Item for Enum {
+ fn path(&self) -> &Path {
+ &self.path
+ }
+
+ fn export_name(&self) -> &str {
+ &self.export_name
+ }
+
+ fn cfg(&self) -> Option<&Cfg> {
+ self.cfg.as_ref()
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ &self.annotations
+ }
+
+ fn annotations_mut(&mut self) -> &mut AnnotationSet {
+ &mut self.annotations
+ }
+
+ fn container(&self) -> ItemContainer {
+ ItemContainer::Enum(self.clone())
+ }
+
+ fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
+ if self.tag.is_some() {
+ if self.repr.style == ReprStyle::C {
+ resolver.add_struct(&self.path);
+ } else {
+ resolver.add_union(&self.path);
+ }
+ } else if self.repr.style == ReprStyle::C {
+ resolver.add_enum(&self.path);
+ } else {
+ // This is important to handle conflicting names with opaque items.
+ resolver.add_none(&self.path);
+ }
+ }
+
+ 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.export_name);
+
+ if config.language != Language::Cxx && self.tag.is_some() {
+ // it makes sense to always prefix Tag with type name in C
+ let new_tag = format!("{}_Tag", self.export_name);
+ if self.repr.style == ReprStyle::Rust {
+ for variant in &mut self.variants {
+ if let VariantBody::Body { ref mut body, .. } = variant.body {
+ let path = Path::new(new_tag.clone());
+ let generic_path = GenericPath::new(path, vec![]);
+ body.fields[0].ty = Type::Path(generic_path);
+ }
+ }
+ }
+ self.tag = Some(new_tag);
+ }
+
+ for variant in &mut self.variants {
+ reserved::escape(&mut variant.export_name);
+ if let Some(discriminant) = &mut variant.discriminant {
+ discriminant.rename_for_config(config);
+ }
+ if let VariantBody::Body {
+ ref mut name,
+ ref mut body,
+ ..
+ } = variant.body
+ {
+ body.rename_for_config(config);
+ reserved::escape(name);
+ }
+ }
+
+ if config.enumeration.prefix_with_name
+ || self.annotations.bool("prefix-with-name").unwrap_or(false)
+ {
+ let separator = if config.export.mangle.remove_underscores {
+ ""
+ } else {
+ "_"
+ };
+
+ for variant in &mut self.variants {
+ variant.export_name =
+ format!("{}{}{}", self.export_name, separator, variant.export_name);
+ if let VariantBody::Body { ref mut body, .. } = variant.body {
+ body.export_name =
+ format!("{}{}{}", self.export_name, separator, body.export_name());
+ }
+ }
+ }
+
+ let rules = self
+ .annotations
+ .parse_atom::<RenameRule>("rename-all")
+ .unwrap_or(config.enumeration.rename_variants);
+
+ if let Some(r) = rules.not_none() {
+ self.variants = self
+ .variants
+ .iter()
+ .map(|variant| {
+ EnumVariant::new(
+ r.apply(
+ &variant.export_name,
+ IdentifierType::EnumVariant {
+ prefix: &self.export_name,
+ },
+ )
+ .into_owned(),
+ variant.discriminant.clone(),
+ match variant.body {
+ VariantBody::Empty(..) => variant.body.clone(),
+ VariantBody::Body {
+ ref name,
+ ref body,
+ inline,
+ inline_casts,
+ } => VariantBody::Body {
+ name: r.apply(name, IdentifierType::StructMember).into_owned(),
+ body: body.clone(),
+ inline,
+ inline_casts,
+ },
+ },
+ variant.cfg.clone(),
+ variant.documentation.clone(),
+ )
+ })
+ .collect();
+ }
+ }
+
+ fn instantiate_monomorph(
+ &self,
+ generic_values: &[GenericArgument],
+ library: &Library,
+ out: &mut Monomorphs,
+ ) {
+ let mappings = self.generic_params.call(self.path.name(), generic_values);
+
+ for variant in &self.variants {
+ if let VariantBody::Body { ref body, .. } = variant.body {
+ body.instantiate_monomorph(generic_values, library, out);
+ }
+ }
+
+ let mangled_path = mangle::mangle_path(
+ &self.path,
+ generic_values,
+ &library.get_config().export.mangle,
+ );
+
+ let monomorph = Enum::new(
+ mangled_path,
+ GenericParams::default(),
+ self.repr,
+ self.variants
+ .iter()
+ .map(|v| v.specialize(generic_values, &mappings, library.get_config()))
+ .collect(),
+ self.tag.clone(),
+ self.cfg.clone(),
+ self.annotations.clone(),
+ self.documentation.clone(),
+ );
+
+ out.insert_enum(library, self, monomorph, generic_values.to_owned());
+ }
+
+ fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ for variant in &self.variants {
+ variant.add_dependencies(library, out);
+ }
+ }
+}
+
+impl Source for Enum {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ let size = self.repr.ty.map(|ty| ty.to_primitive().to_repr_c(config));
+ let has_data = self.tag.is_some();
+ let inline_tag_field = Self::inline_tag_field(&self.repr);
+ let tag_name = self.tag_name();
+
+ let condition = self.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ self.documentation.write(config, out);
+ self.generic_params.write(config, out);
+
+ // If the enum has data, we need to emit a struct or union for the data
+ // and enum for the tag. C++ supports nested type definitions, so we open
+ // the struct or union here and define the tag enum inside it (*).
+ if has_data && config.language == Language::Cxx {
+ self.open_struct_or_union(config, out, inline_tag_field);
+ }
+
+ // Emit the tag enum and everything related to it.
+ self.write_tag_enum(config, out, size, has_data, tag_name);
+
+ // If the enum has data, we need to emit structs for the variants and gather them together.
+ if has_data {
+ self.write_variant_defs(config, out);
+ out.new_line();
+ out.new_line();
+
+ // Open the struct or union for the data (**), gathering all the variants with data
+ // together, unless it's C++, then we have already opened that struct/union at (*) and
+ // are currently inside it.
+ if config.language != Language::Cxx {
+ self.open_struct_or_union(config, out, inline_tag_field);
+ }
+
+ // Emit tag field that is separate from all variants.
+ self.write_tag_field(config, out, size, inline_tag_field, tag_name);
+ out.new_line();
+
+ // Open union of all variants with data, only in the non-inline tag scenario.
+ // Cython extern declarations don't manage layouts, layouts are defined entierly by the
+ // corresponding C code. So we can inline the unnamed union into the struct and get the
+ // same observable result. Moreother we have to do it because Cython doesn't support
+ // unnamed unions.
+ if !inline_tag_field && config.language != Language::Cython {
+ out.write("union");
+ out.open_brace();
+ }
+
+ // Emit fields for all variants with data.
+ self.write_variant_fields(config, out, inline_tag_field);
+
+ // Close union of all variants with data, only in the non-inline tag scenario.
+ // See the comment about Cython on `open_brace`.
+ if !inline_tag_field && config.language != Language::Cython {
+ out.close_brace(true);
+ }
+
+ // Emit convenience methods for the struct or enum for the data.
+ self.write_derived_functions_data(config, out, tag_name);
+
+ // Emit the post_body section, if relevant.
+ if let Some(body) = config.export.post_body(&self.path) {
+ out.new_line();
+ out.write_raw_block(body);
+ }
+
+ // Close the struct or union opened either at (*) or at (**).
+ if config.language == Language::C && config.style.generate_typedef() {
+ out.close_brace(false);
+ write!(out, " {};", self.export_name);
+ } else {
+ out.close_brace(true);
+ }
+ }
+
+ condition.write_after(config, out);
+ }
+}
+
+impl Enum {
+ /// Emit the tag enum and convenience methods for it.
+ /// For enums with data this is only a part of the output,
+ /// but for enums without data it's the whole output (modulo doc comments etc.).
+ fn write_tag_enum<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ size: Option<&str>,
+ has_data: bool,
+ tag_name: &str,
+ ) {
+ // Open the tag enum.
+ match config.language {
+ Language::C => {
+ if let Some(prim) = size {
+ // If we need to specify size, then we have no choice but to create a typedef,
+ // so `config.style` is not respected.
+ write!(out, "enum");
+ if let Some(note) = self
+ .annotations
+ .deprecated_note(config, DeprecatedNoteKind::Enum)
+ {
+ write!(out, " {}", note);
+ }
+ write!(out, " {}", tag_name);
+
+ if config.cpp_compatible_c() {
+ out.new_line();
+ out.write("#ifdef __cplusplus");
+ out.new_line();
+ write!(out, " : {}", prim);
+ out.new_line();
+ out.write("#endif // __cplusplus");
+ out.new_line();
+ }
+ } else {
+ if config.style.generate_typedef() {
+ out.write("typedef ");
+ }
+ out.write("enum");
+ if let Some(note) = self
+ .annotations
+ .deprecated_note(config, DeprecatedNoteKind::Enum)
+ {
+ write!(out, " {}", note);
+ }
+ if config.style.generate_tag() {
+ write!(out, " {}", tag_name);
+ }
+ }
+ }
+ Language::Cxx => {
+ if config.enumeration.enum_class(&self.annotations) {
+ out.write("enum class");
+ } else {
+ out.write("enum");
+ }
+
+ if self.annotations.must_use(config) {
+ if let Some(ref anno) = config.enumeration.must_use {
+ write!(out, " {}", anno)
+ }
+ }
+
+ if let Some(note) = self
+ .annotations
+ .deprecated_note(config, DeprecatedNoteKind::Enum)
+ {
+ write!(out, " {}", note);
+ }
+
+ write!(out, " {}", tag_name);
+ if let Some(prim) = size {
+ write!(out, " : {}", prim);
+ }
+ }
+ Language::Cython => {
+ if size.is_some() {
+ // If we need to specify size, then we have no choice but to create a typedef,
+ // so `config.style` is not respected.
+ write!(out, "cdef enum");
+ } else {
+ write!(out, "{}enum {}", config.style.cython_def(), tag_name);
+ }
+ }
+ }
+ out.open_brace();
+
+ // Emit enumerators for the tag enum.
+ for (i, variant) in self.variants.iter().enumerate() {
+ if i != 0 {
+ out.new_line()
+ }
+ variant.write(config, out);
+ }
+
+ // Close the tag enum.
+ if config.language == Language::C && size.is_none() && config.style.generate_typedef() {
+ out.close_brace(false);
+ write!(out, " {};", tag_name);
+ } else {
+ out.close_brace(true);
+ }
+
+ // Emit typedef specifying the tag enum's size if necessary.
+ // In C++ enums can "inherit" from numeric types (`enum E: uint8_t { ... }`),
+ // but in C `typedef uint8_t E` is the only way to give a fixed size to `E`.
+ if let Some(prim) = size {
+ if config.cpp_compatible_c() {
+ out.new_line_if_not_start();
+ out.write("#ifndef __cplusplus");
+ }
+
+ if config.language != Language::Cxx {
+ out.new_line();
+ write!(out, "{} {} {};", config.language.typedef(), prim, tag_name);
+ }
+
+ if config.cpp_compatible_c() {
+ out.new_line_if_not_start();
+ out.write("#endif // __cplusplus");
+ }
+ }
+
+ // Emit convenience methods for the tag enum.
+ self.write_derived_functions_enum(config, out, has_data, tag_name);
+ }
+
+ /// The code here mirrors the beginning of `Struct::write` and `Union::write`.
+ fn open_struct_or_union<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ inline_tag_field: bool,
+ ) {
+ match config.language {
+ Language::C if config.style.generate_typedef() => out.write("typedef "),
+ Language::C | Language::Cxx => {}
+ Language::Cython => out.write(config.style.cython_def()),
+ }
+
+ out.write(if inline_tag_field { "union" } else { "struct" });
+
+ if self.annotations.must_use(config) {
+ if let Some(ref anno) = config.structure.must_use {
+ write!(out, " {}", anno);
+ }
+ }
+
+ if let Some(note) = self
+ .annotations
+ .deprecated_note(config, DeprecatedNoteKind::Struct)
+ {
+ write!(out, " {} ", note);
+ }
+
+ if config.language != Language::C || config.style.generate_tag() {
+ write!(out, " {}", self.export_name());
+ }
+
+ out.open_brace();
+
+ // Emit the pre_body section, if relevant.
+ if let Some(body) = config.export.pre_body(&self.path) {
+ out.write_raw_block(body);
+ out.new_line();
+ }
+ }
+
+ /// Emit struct definitions for variants having data.
+ fn write_variant_defs<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ for variant in &self.variants {
+ if let VariantBody::Body {
+ ref body,
+ inline: false,
+ ..
+ } = variant.body
+ {
+ out.new_line();
+ out.new_line();
+ let condition = variant.cfg.to_condition(config);
+ // Cython doesn't support conditional enum variants.
+ if config.language != Language::Cython {
+ condition.write_before(config, out);
+ }
+ body.write(config, out);
+ if config.language != Language::Cython {
+ condition.write_after(config, out);
+ }
+ }
+ }
+ }
+
+ /// Emit tag field that is separate from all variants.
+ /// For non-inline tag scenario this is *the* tag field, and it does not exist in the variants.
+ /// For the inline tag scenario this is just a convenience and another way
+ /// to refer to the same tag that exist in all the variants.
+ fn write_tag_field<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ size: Option<&str>,
+ inline_tag_field: bool,
+ tag_name: &str,
+ ) {
+ // C++ allows accessing only common initial sequence of union
+ // fields so we have to wrap the tag field into an anonymous struct.
+ let wrap_tag = inline_tag_field && config.language == Language::Cxx;
+
+ if wrap_tag {
+ out.write("struct");
+ out.open_brace();
+ }
+
+ if config.language == Language::C && size.is_none() && !config.style.generate_typedef() {
+ out.write("enum ");
+ }
+
+ write!(out, "{} tag;", tag_name);
+
+ if wrap_tag {
+ out.close_brace(true);
+ }
+ }
+
+ /// Emit fields for all variants with data.
+ fn write_variant_fields<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ inline_tag_field: bool,
+ ) {
+ let mut first = true;
+ for variant in &self.variants {
+ if let VariantBody::Body {
+ name, body, inline, ..
+ } = &variant.body
+ {
+ if !first {
+ out.new_line();
+ }
+ first = false;
+ let condition = variant.cfg.to_condition(config);
+ // Cython doesn't support conditional enum variants.
+ if config.language != Language::Cython {
+ condition.write_before(config, out);
+ }
+ if *inline {
+ // Write definition of an inlined variant with data.
+ // Cython extern declarations don't manage layouts, layouts are defined entierly
+ // by the corresponding C code. So we can inline the unnamed struct and get the
+ // same observable result. Moreother we have to do it because Cython doesn't
+ // support unnamed structs.
+ // For the same reason with Cython we can omit per-variant tags (the first
+ // field) to avoid extra noise, the main `tag` is enough in this case.
+ if config.language != Language::Cython {
+ out.write("struct");
+ out.open_brace();
+ }
+ let start_field =
+ usize::from(inline_tag_field && config.language == Language::Cython);
+ out.write_vertical_source_list(&body.fields[start_field..], ListType::Cap(";"));
+ if config.language != Language::Cython {
+ out.close_brace(true);
+ }
+ } else if config.style.generate_typedef() || config.language == Language::Cython {
+ write!(out, "{} {};", body.export_name(), name);
+ } else {
+ write!(out, "struct {} {};", body.export_name(), name);
+ }
+ if config.language != Language::Cython {
+ condition.write_after(config, out);
+ }
+ }
+ }
+ }
+
+ // Emit convenience methods for enums themselves.
+ fn write_derived_functions_enum<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ has_data: bool,
+ tag_name: &str,
+ ) {
+ if config.language != Language::Cxx {
+ return;
+ }
+
+ // Emit an ostream function if required.
+ if config.enumeration.derive_ostream(&self.annotations) {
+ // For enums without data, this emits the serializer function for the
+ // enum. For enums with data, this emits the serializer function for
+ // the tag enum. In the latter case we need a couple of minor changes
+ // due to the function living inside the top-level struct or enum.
+ let stream = config
+ .function
+ .rename_args
+ .apply("stream", IdentifierType::FunctionArg);
+ let instance = config
+ .function
+ .rename_args
+ .apply("instance", IdentifierType::FunctionArg);
+
+ out.new_line();
+ out.new_line();
+ // For enums without data, we mark the function inline because the
+ // header might get included into multiple compilation units that
+ // get linked together, and not marking it inline would result in
+ // multiply-defined symbol errors. For enums with data we don't have
+ // the same problem, but mark it as a friend function of the
+ // containing union/struct.
+ // Note also that for enums with data, the case labels for switch
+ // statements apparently need to be qualified to the top-level
+ // generated struct or union. This is why the generated case labels
+ // below use the A::B::C format for enums with data, with A being
+ // self.export_name(). Failure to have that qualification results
+ // in a surprising compilation failure for the generated header.
+ write!(
+ out,
+ "{} std::ostream& operator<<(std::ostream& {}, const {}& {})",
+ if has_data { "friend" } else { "inline" },
+ stream,
+ tag_name,
+ instance,
+ );
+
+ out.open_brace();
+ if has_data {
+ // C++ name resolution rules are weird.
+ write!(
+ out,
+ "using {} = {}::{};",
+ tag_name,
+ self.export_name(),
+ tag_name
+ );
+ out.new_line();
+ }
+ write!(out, "switch ({})", instance);
+ out.open_brace();
+ let vec: Vec<_> = self
+ .variants
+ .iter()
+ .map(|x| {
+ format!(
+ "case {}::{}: {} << \"{}\"; break;",
+ tag_name, x.export_name, stream, x.export_name
+ )
+ })
+ .collect();
+ out.write_vertical_source_list(&vec[..], ListType::Join(""));
+ out.close_brace(false);
+ out.new_line();
+
+ write!(out, "return {};", stream);
+ out.close_brace(false);
+
+ if has_data {
+ // For enums with data, this emits the serializer function for
+ // the top-level union or struct.
+ out.new_line();
+ out.new_line();
+ write!(
+ out,
+ "friend std::ostream& operator<<(std::ostream& {}, const {}& {})",
+ stream,
+ self.export_name(),
+ instance,
+ );
+
+ out.open_brace();
+
+ // C++ name resolution rules are weird.
+ write!(
+ out,
+ "using {} = {}::{};",
+ tag_name,
+ self.export_name(),
+ tag_name
+ );
+ out.new_line();
+
+ write!(out, "switch ({}.tag)", instance);
+ out.open_brace();
+ let vec: Vec<_> = self
+ .variants
+ .iter()
+ .map(|x| {
+ let tag_str = format!("\"{}\"", x.export_name);
+ if let VariantBody::Body {
+ ref name, ref body, ..
+ } = x.body
+ {
+ format!(
+ "case {}::{}: {} << {}{}{}.{}; break;",
+ tag_name,
+ x.export_name,
+ stream,
+ if body.has_tag_field { "" } else { &tag_str },
+ if body.has_tag_field { "" } else { " << " },
+ instance,
+ name,
+ )
+ } else {
+ format!(
+ "case {}::{}: {} << {}; break;",
+ tag_name, x.export_name, stream, tag_str,
+ )
+ }
+ })
+ .collect();
+ out.write_vertical_source_list(&vec[..], ListType::Join(""));
+ out.close_brace(false);
+ out.new_line();
+
+ write!(out, "return {};", stream);
+ out.close_brace(false);
+ }
+ }
+ }
+
+ // Emit convenience methods for structs or unions produced for enums with data.
+ fn write_derived_functions_data<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ tag_name: &str,
+ ) {
+ if config.language != Language::Cxx {
+ return;
+ }
+
+ if config.enumeration.derive_helper_methods(&self.annotations) {
+ for variant in &self.variants {
+ out.new_line();
+ out.new_line();
+
+ let condition = variant.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ let arg_renamer = |name: &str| {
+ config
+ .function
+ .rename_args
+ .apply(name, IdentifierType::FunctionArg)
+ .into_owned()
+ };
+
+ macro_rules! write_attrs {
+ ($op:expr) => {{
+ if let Some(Some(attrs)) =
+ variant
+ .body
+ .annotations()
+ .atom(concat!("variant-", $op, "-attributes"))
+ {
+ write!(out, "{} ", attrs);
+ }
+ }};
+ }
+
+ write_attrs!("constructor");
+ write!(out, "static {} {}(", self.export_name, variant.export_name);
+
+ if let VariantBody::Body { ref body, .. } = variant.body {
+ let skip_fields = body.has_tag_field as usize;
+ let vec: Vec<_> = body
+ .fields
+ .iter()
+ .skip(skip_fields)
+ .map(|field| {
+ Field::from_name_and_type(
+ // const-ref args to constructor
+ arg_renamer(&field.name),
+ Type::const_ref_to(&field.ty),
+ )
+ })
+ .collect();
+ out.write_vertical_source_list(&vec[..], ListType::Join(","));
+ }
+
+ write!(out, ")");
+ out.open_brace();
+
+ write!(out, "{} result;", self.export_name);
+
+ if let VariantBody::Body {
+ name: ref variant_name,
+ ref body,
+ ..
+ } = variant.body
+ {
+ let skip_fields = body.has_tag_field as usize;
+ for field in body.fields.iter().skip(skip_fields) {
+ out.new_line();
+ match field.ty {
+ Type::Array(ref ty, ref length) => {
+ // arrays are not assignable in C++ so we
+ // need to manually copy the elements
+ write!(out, "for (int i = 0; i < {}; i++)", length.as_str());
+ out.open_brace();
+ write!(out, "::new (&result.{}.{}[i]) (", variant_name, field.name);
+ ty.write(config, out);
+ write!(out, ")({}[i]);", arg_renamer(&field.name));
+ out.close_brace(false);
+ }
+ ref ty => {
+ write!(out, "::new (&result.{}.{}) (", variant_name, field.name);
+ ty.write(config, out);
+ write!(out, ")({});", arg_renamer(&field.name));
+ }
+ }
+ }
+ }
+
+ out.new_line();
+ write!(out, "result.tag = {}::{};", tag_name, variant.export_name);
+ out.new_line();
+ write!(out, "return result;");
+ out.close_brace(false);
+
+ out.new_line();
+ out.new_line();
+
+ write_attrs!("is");
+ // FIXME: create a config for method case
+ write!(out, "bool Is{}() const", variant.export_name);
+ out.open_brace();
+ write!(out, "return tag == {}::{};", tag_name, variant.export_name);
+ out.close_brace(false);
+
+ let assert_name = match config.enumeration.cast_assert_name {
+ Some(ref n) => &**n,
+ None => "assert",
+ };
+
+ let mut derive_casts = |const_casts: bool| {
+ let (member_name, body, inline_casts) = match variant.body {
+ VariantBody::Body {
+ ref name,
+ ref body,
+ inline_casts,
+ ..
+ } => (name, body, inline_casts),
+ VariantBody::Empty(..) => return,
+ };
+
+ let skip_fields = body.has_tag_field as usize;
+ let field_count = body.fields.len() - skip_fields;
+ if field_count == 0 {
+ return;
+ }
+
+ out.new_line();
+ out.new_line();
+
+ if const_casts {
+ write_attrs!("const-cast");
+ } else {
+ write_attrs!("mut-cast");
+ }
+ if inline_casts {
+ let field = body.fields.last().unwrap();
+ let return_type = field.ty.clone();
+ let return_type = Type::Ptr {
+ ty: Box::new(return_type),
+ is_const: const_casts,
+ is_ref: true,
+ is_nullable: false,
+ };
+ return_type.write(config, out);
+ } else if const_casts {
+ write!(out, "const {}&", body.export_name());
+ } else {
+ write!(out, "{}&", body.export_name());
+ }
+
+ write!(out, " As{}()", variant.export_name);
+ if const_casts {
+ write!(out, " const");
+ }
+ out.open_brace();
+ write!(out, "{}(Is{}());", assert_name, variant.export_name);
+ out.new_line();
+ write!(out, "return {}", member_name);
+ if inline_casts {
+ write!(out, "._0");
+ }
+ write!(out, ";");
+ out.close_brace(false);
+ };
+
+ if config.enumeration.derive_const_casts(&self.annotations) {
+ derive_casts(true)
+ }
+
+ if config.enumeration.derive_mut_casts(&self.annotations) {
+ derive_casts(false)
+ }
+
+ condition.write_after(config, out);
+ }
+ }
+
+ let other = config
+ .function
+ .rename_args
+ .apply("other", IdentifierType::FunctionArg);
+
+ macro_rules! write_attrs {
+ ($op:expr) => {{
+ if let Some(Some(attrs)) = self.annotations.atom(concat!($op, "-attributes")) {
+ write!(out, "{} ", attrs);
+ }
+ }};
+ }
+
+ if self.can_derive_eq() && config.structure.derive_eq(&self.annotations) {
+ out.new_line();
+ out.new_line();
+ write_attrs!("eq");
+ write!(
+ out,
+ "bool operator==(const {}& {}) const",
+ self.export_name, other
+ );
+ out.open_brace();
+ write!(out, "if (tag != {}.tag)", other);
+ out.open_brace();
+ write!(out, "return false;");
+ out.close_brace(false);
+ out.new_line();
+ write!(out, "switch (tag)");
+ out.open_brace();
+ let mut exhaustive = true;
+ for variant in &self.variants {
+ if let VariantBody::Body {
+ name: ref variant_name,
+ ..
+ } = variant.body
+ {
+ let condition = variant.cfg.to_condition(config);
+ condition.write_before(config, out);
+ write!(
+ out,
+ "case {}::{}: return {} == {}.{};",
+ self.tag.as_ref().unwrap(),
+ variant.export_name,
+ variant_name,
+ other,
+ variant_name
+ );
+ condition.write_after(config, out);
+ out.new_line();
+ } else {
+ exhaustive = false;
+ }
+ }
+ if !exhaustive {
+ write!(out, "default: break;");
+ }
+ out.close_brace(false);
+
+ out.new_line();
+ write!(out, "return true;");
+
+ out.close_brace(false);
+
+ if config.structure.derive_neq(&self.annotations) {
+ out.new_line();
+ out.new_line();
+ write_attrs!("neq");
+ write!(
+ out,
+ "bool operator!=(const {}& {}) const",
+ self.export_name, other
+ );
+ out.open_brace();
+ write!(out, "return !(*this == {});", other);
+ out.close_brace(false);
+ }
+ }
+
+ if config
+ .enumeration
+ .private_default_tagged_enum_constructor(&self.annotations)
+ {
+ out.new_line();
+ out.new_line();
+ write!(out, "private:");
+ out.new_line();
+ write!(out, "{}()", self.export_name);
+ out.open_brace();
+ out.close_brace(false);
+ out.new_line();
+ write!(out, "public:");
+ out.new_line();
+ }
+
+ if config
+ .enumeration
+ .derive_tagged_enum_destructor(&self.annotations)
+ {
+ out.new_line();
+ out.new_line();
+ write_attrs!("destructor");
+ write!(out, "~{}()", self.export_name);
+ out.open_brace();
+ write!(out, "switch (tag)");
+ out.open_brace();
+ let mut exhaustive = true;
+ for variant in &self.variants {
+ if let VariantBody::Body {
+ ref name, ref body, ..
+ } = variant.body
+ {
+ let condition = variant.cfg.to_condition(config);
+ condition.write_before(config, out);
+ write!(
+ out,
+ "case {}::{}: {}.~{}(); break;",
+ self.tag.as_ref().unwrap(),
+ variant.export_name,
+ name,
+ body.export_name(),
+ );
+ condition.write_after(config, out);
+ out.new_line();
+ } else {
+ exhaustive = false;
+ }
+ }
+ if !exhaustive {
+ write!(out, "default: break;");
+ }
+ out.close_brace(false);
+ out.close_brace(false);
+ }
+
+ if config
+ .enumeration
+ .derive_tagged_enum_copy_constructor(&self.annotations)
+ {
+ out.new_line();
+ out.new_line();
+ write_attrs!("copy-constructor");
+ write!(
+ out,
+ "{}(const {}& {})",
+ self.export_name, self.export_name, other
+ );
+ out.new_line();
+ write!(out, " : tag({}.tag)", other);
+ out.open_brace();
+ write!(out, "switch (tag)");
+ out.open_brace();
+ let mut exhaustive = true;
+ for variant in &self.variants {
+ if let VariantBody::Body {
+ ref name, ref body, ..
+ } = variant.body
+ {
+ let condition = variant.cfg.to_condition(config);
+ condition.write_before(config, out);
+ write!(
+ out,
+ "case {}::{}: ::new (&{}) ({})({}.{}); break;",
+ self.tag.as_ref().unwrap(),
+ variant.export_name,
+ name,
+ body.export_name(),
+ other,
+ name,
+ );
+ condition.write_after(config, out);
+ out.new_line();
+ } else {
+ exhaustive = false;
+ }
+ }
+ if !exhaustive {
+ write!(out, "default: break;");
+ }
+ out.close_brace(false);
+ out.close_brace(false);
+
+ if config
+ .enumeration
+ .derive_tagged_enum_copy_assignment(&self.annotations)
+ {
+ out.new_line();
+ write_attrs!("copy-assignment");
+ write!(
+ out,
+ "{}& operator=(const {}& {})",
+ self.export_name, self.export_name, other
+ );
+ out.open_brace();
+ write!(out, "if (this != &{})", other);
+ out.open_brace();
+ write!(out, "this->~{}();", self.export_name);
+ out.new_line();
+ write!(out, "new (this) {}({});", self.export_name, other);
+ out.close_brace(false);
+ out.new_line();
+ write!(out, "return *this;");
+ out.close_brace(false);
+ }
+ }
+ }
+
+ pub fn simplify_standard_types(&mut self, config: &Config) {
+ for variant in &mut self.variants {
+ variant.simplify_standard_types(config);
+ }
+ }
+}
diff --git a/src/bindgen/ir/field.rs b/src/bindgen/ir/field.rs
new file mode 100644
index 0000000..6e132bf
--- /dev/null
+++ b/src/bindgen/ir/field.rs
@@ -0,0 +1,80 @@
+use std::io::Write;
+
+use syn::ext::IdentExt;
+
+use crate::bindgen::cdecl;
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::ir::{AnnotationSet, Cfg, ConditionWrite};
+use crate::bindgen::ir::{Documentation, Path, ToCondition, Type};
+use crate::bindgen::writer::{Source, SourceWriter};
+
+#[derive(Debug, Clone)]
+pub struct Field {
+ pub name: String,
+ pub ty: Type,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+}
+
+impl Field {
+ pub fn from_name_and_type(name: String, ty: Type) -> Field {
+ Field {
+ name,
+ ty,
+ cfg: None,
+ annotations: AnnotationSet::new(),
+ documentation: Documentation::none(),
+ }
+ }
+
+ pub fn load(field: &syn::Field, self_path: &Path) -> Result<Option<Field>, String> {
+ Ok(if let Some(mut ty) = Type::load(&field.ty)? {
+ ty.replace_self_with(self_path);
+ Some(Field {
+ name: field
+ .ident
+ .as_ref()
+ .ok_or_else(|| "field is missing identifier".to_string())?
+ .unraw()
+ .to_string(),
+ ty,
+ cfg: Cfg::load(&field.attrs),
+ annotations: AnnotationSet::load(&field.attrs)?,
+ documentation: Documentation::load(&field.attrs),
+ })
+ } else {
+ None
+ })
+ }
+}
+
+impl Source for Field {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ // Cython doesn't support conditional fields.
+ let condition = self.cfg.to_condition(config);
+ if config.language != Language::Cython {
+ condition.write_before(config, out);
+ }
+
+ self.documentation.write(config, out);
+ cdecl::write_field(out, &self.ty, &self.name, config);
+ // Cython extern declarations don't manage layouts, layouts are defined entierly by the
+ // corresponding C code. So we can omit bitfield sizes which are not supported by Cython.
+ if config.language != Language::Cython {
+ if let Some(bitfield) = self.annotations.atom("bitfield") {
+ write!(out, ": {}", bitfield.unwrap_or_default());
+ }
+ }
+
+ if config.language != Language::Cython {
+ condition.write_after(config, out);
+ // FIXME(#634): `write_vertical_source_list` should support
+ // configuring list elements natively. For now we print a newline
+ // here to avoid printing `#endif;` with semicolon.
+ if condition.is_some() {
+ out.new_line();
+ }
+ }
+ }
+}
diff --git a/src/bindgen/ir/function.rs b/src/bindgen/ir/function.rs
new file mode 100644
index 0000000..8c65f2f
--- /dev/null
+++ b/src/bindgen/ir/function.rs
@@ -0,0 +1,390 @@
+/* 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::HashMap;
+use std::io::Write;
+
+use syn::ext::IdentExt;
+
+use crate::bindgen::cdecl;
+use crate::bindgen::config::{Config, Language, Layout};
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{
+ AnnotationSet, Cfg, ConditionWrite, DeprecatedNoteKind, Documentation, GenericPath, Path,
+ ToCondition, Type,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::monomorph::Monomorphs;
+use crate::bindgen::rename::{IdentifierType, RenameRule};
+use crate::bindgen::reserved;
+use crate::bindgen::utilities::IterHelpers;
+use crate::bindgen::writer::{Source, SourceWriter};
+
+#[derive(Debug, Clone)]
+pub struct FunctionArgument {
+ pub name: Option<String>,
+ pub ty: Type,
+ pub array_length: Option<String>,
+}
+
+#[derive(Debug, Clone)]
+pub struct Function {
+ pub path: Path,
+ /// Path to the self-type of the function
+ /// If the function is a method, this will contain the path of the type in the impl block
+ pub self_type_path: Option<Path>,
+ pub ret: Type,
+ pub args: Vec<FunctionArgument>,
+ pub extern_decl: bool,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+ pub never_return: bool,
+}
+
+impl Function {
+ pub fn load(
+ path: Path,
+ self_type_path: Option<&Path>,
+ sig: &syn::Signature,
+ extern_decl: bool,
+ attrs: &[syn::Attribute],
+ mod_cfg: Option<&Cfg>,
+ ) -> Result<Function, String> {
+ let mut args = sig.inputs.iter().try_skip_map(|x| x.as_argument())?;
+
+ let (mut ret, never_return) = Type::load_from_output(&sig.output)?;
+
+ if let Some(self_path) = self_type_path {
+ for arg in &mut args {
+ arg.ty.replace_self_with(self_path);
+ }
+ ret.replace_self_with(self_path);
+ }
+
+ Ok(Function {
+ path,
+ self_type_path: self_type_path.cloned(),
+ ret,
+ args,
+ extern_decl,
+ cfg: Cfg::append(mod_cfg, Cfg::load(attrs)),
+ annotations: AnnotationSet::load(attrs)?,
+ documentation: Documentation::load(attrs),
+ never_return,
+ })
+ }
+
+ pub fn swift_name(&self, config: &Config) -> Option<String> {
+ if config.language == Language::Cython {
+ return None;
+ }
+ // If the symbol name starts with the type name, separate the two components with '.'
+ // so that Swift recognises the association between the method and the type
+ let (ref type_prefix, ref type_name) = match self.self_type_path {
+ Some(ref type_name) => {
+ let type_name = type_name.to_string();
+ if !self.path.name().starts_with(&type_name) {
+ return Some(self.path.to_string());
+ }
+ (format!("{}.", type_name), type_name)
+ }
+ None => ("".to_string(), "".to_string()),
+ };
+
+ let item_name = self
+ .path
+ .name()
+ .trim_start_matches(type_name)
+ .trim_start_matches('_');
+
+ let item_args = {
+ let mut items = Vec::with_capacity(self.args.len());
+ for arg in self.args.iter() {
+ items.push(format!("{}:", arg.name.as_ref()?.as_str()));
+ }
+ items.join("")
+ };
+ Some(format!("{}{}({})", type_prefix, item_name, item_args))
+ }
+
+ pub fn path(&self) -> &Path {
+ &self.path
+ }
+
+ pub fn simplify_standard_types(&mut self, config: &Config) {
+ self.ret.simplify_standard_types(config);
+ for arg in &mut self.args {
+ arg.ty.simplify_standard_types(config);
+ }
+ }
+
+ pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ self.ret.add_dependencies(library, out);
+ for arg in &self.args {
+ arg.ty.add_dependencies(library, out);
+ }
+ }
+
+ pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
+ self.ret.add_monomorphs(library, out);
+ for arg in &self.args {
+ arg.ty.add_monomorphs(library, out);
+ }
+ }
+
+ pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
+ self.ret.mangle_paths(monomorphs);
+ for arg in &mut self.args {
+ arg.ty.mangle_paths(monomorphs);
+ }
+ }
+
+ pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ self.ret.resolve_declaration_types(resolver);
+ for arg in &mut self.args {
+ arg.ty.resolve_declaration_types(resolver);
+ }
+ }
+
+ pub fn rename_for_config(&mut self, config: &Config) {
+ // Rename the types used in arguments
+ let generic_params = Default::default();
+ self.ret.rename_for_config(config, &generic_params);
+
+ // Apply rename rules to argument names
+ let rules = self
+ .annotations
+ .parse_atom::<RenameRule>("rename-all")
+ .unwrap_or(config.function.rename_args);
+
+ if let Some(r) = rules.not_none() {
+ let args = std::mem::take(&mut self.args);
+ self.args = args
+ .into_iter()
+ .map(|arg| {
+ let name = arg
+ .name
+ .map(|n| r.apply(&n, IdentifierType::FunctionArg).into_owned());
+ FunctionArgument {
+ name,
+ ty: arg.ty,
+ array_length: None,
+ }
+ })
+ .collect()
+ }
+
+ // Escape C/C++ reserved keywords used in argument names, and
+ // recursively rename argument types.
+ for arg in &mut self.args {
+ arg.ty.rename_for_config(config, &generic_params);
+ if let Some(ref mut name) = arg.name {
+ reserved::escape(name);
+ }
+ }
+
+ // Save the array length of the pointer arguments which need to use
+ // the C-array notation
+ if let Some(tuples) = self.annotations.list("ptrs-as-arrays") {
+ let mut ptrs_as_arrays: HashMap<String, String> = HashMap::new();
+ for str_tuple in tuples {
+ let parts: Vec<&str> = str_tuple[1..str_tuple.len() - 1]
+ .split(';')
+ .map(|x| x.trim())
+ .collect();
+ if parts.len() != 2 {
+ warn!(
+ "{:?} does not follow the correct syntax, so the annotation is being ignored",
+ parts
+ );
+ continue;
+ }
+ ptrs_as_arrays.insert(parts[0].to_string(), parts[1].to_string());
+ }
+
+ for arg in &mut self.args {
+ match arg.ty {
+ Type::Ptr { .. } => {}
+ _ => continue,
+ }
+ let name = match arg.name {
+ Some(ref name) => name,
+ None => continue,
+ };
+ arg.array_length = ptrs_as_arrays.get(name).cloned();
+ }
+ }
+ }
+}
+
+impl Source for Function {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ fn write_1<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
+ let prefix = config.function.prefix(&func.annotations);
+ let postfix = config.function.postfix(&func.annotations);
+
+ let condition = func.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ func.documentation.write(config, out);
+
+ if func.extern_decl {
+ out.write("extern ");
+ } else {
+ if let Some(ref prefix) = prefix {
+ write!(out, "{} ", prefix);
+ }
+ if func.annotations.must_use(config) {
+ if let Some(ref anno) = config.function.must_use {
+ write!(out, "{} ", anno);
+ }
+ }
+ if let Some(note) = func
+ .annotations
+ .deprecated_note(config, DeprecatedNoteKind::Function)
+ {
+ write!(out, "{} ", note);
+ }
+ }
+ cdecl::write_func(out, func, Layout::Horizontal, config);
+
+ if !func.extern_decl {
+ if let Some(ref postfix) = postfix {
+ write!(out, " {}", postfix);
+ }
+ }
+
+ if let Some(ref swift_name_macro) = config.function.swift_name_macro {
+ if let Some(swift_name) = func.swift_name(config) {
+ write!(out, " {}({})", swift_name_macro, swift_name);
+ }
+ }
+
+ out.write(";");
+
+ condition.write_after(config, out);
+ }
+
+ fn write_2<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
+ let prefix = config.function.prefix(&func.annotations);
+ let postfix = config.function.postfix(&func.annotations);
+
+ let condition = func.cfg.to_condition(config);
+
+ condition.write_before(config, out);
+
+ func.documentation.write(config, out);
+
+ if func.extern_decl {
+ out.write("extern ");
+ } else {
+ if let Some(ref prefix) = prefix {
+ write!(out, "{}", prefix);
+ out.new_line();
+ }
+ if func.annotations.must_use(config) {
+ if let Some(ref anno) = config.function.must_use {
+ write!(out, "{}", anno);
+ out.new_line();
+ }
+ }
+ if let Some(note) = func
+ .annotations
+ .deprecated_note(config, DeprecatedNoteKind::Function)
+ {
+ write!(out, "{}", note);
+ out.new_line();
+ }
+ }
+ cdecl::write_func(out, func, Layout::Vertical, config);
+ if !func.extern_decl {
+ if let Some(ref postfix) = postfix {
+ out.new_line();
+ write!(out, "{}", postfix);
+ }
+ }
+
+ if let Some(ref swift_name_macro) = config.function.swift_name_macro {
+ if let Some(swift_name) = func.swift_name(config) {
+ write!(out, " {}({})", swift_name_macro, swift_name);
+ }
+ }
+
+ out.write(";");
+
+ condition.write_after(config, out);
+ }
+
+ match config.function.args {
+ Layout::Horizontal => write_1(self, config, out),
+ Layout::Vertical => write_2(self, config, out),
+ Layout::Auto => {
+ if !out.try_write(|out| write_1(self, config, out), config.line_length) {
+ write_2(self, config, out)
+ }
+ }
+ }
+ }
+}
+
+trait SynFnArgHelpers {
+ fn as_argument(&self) -> Result<Option<FunctionArgument>, String>;
+}
+
+fn gen_self_type(receiver: &syn::Receiver) -> Type {
+ let self_ty = Type::Path(GenericPath::self_path());
+ if receiver.reference.is_none() {
+ return self_ty;
+ }
+
+ let is_const = receiver.mutability.is_none();
+ Type::Ptr {
+ ty: Box::new(self_ty),
+ is_const,
+ is_nullable: false,
+ is_ref: false,
+ }
+}
+
+impl SynFnArgHelpers for syn::FnArg {
+ fn as_argument(&self) -> Result<Option<FunctionArgument>, String> {
+ match *self {
+ syn::FnArg::Typed(syn::PatType {
+ ref pat, ref ty, ..
+ }) => {
+ let name = match **pat {
+ syn::Pat::Wild(..) => None,
+ syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => {
+ Some(ident.unraw().to_string())
+ }
+ _ => {
+ return Err(format!(
+ "Parameter has an unsupported argument name: {:?}",
+ pat
+ ))
+ }
+ };
+ let ty = match Type::load(ty)? {
+ Some(x) => x,
+ None => return Ok(None),
+ };
+ if let Type::Array(..) = ty {
+ return Err("Array as function arguments are not supported".to_owned());
+ }
+ Ok(Some(FunctionArgument {
+ name,
+ ty,
+ array_length: None,
+ }))
+ }
+ syn::FnArg::Receiver(ref receiver) => Ok(Some(FunctionArgument {
+ name: Some("self".to_string()),
+ ty: gen_self_type(receiver),
+ array_length: None,
+ })),
+ }
+ }
+}
diff --git a/src/bindgen/ir/generic_path.rs b/src/bindgen/ir/generic_path.rs
new file mode 100644
index 0000000..ef14890
--- /dev/null
+++ b/src/bindgen/ir/generic_path.rs
@@ -0,0 +1,303 @@
+use std::io::Write;
+use std::ops::Deref;
+
+use syn::ext::IdentExt;
+
+use crate::bindgen::cdecl;
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::declarationtyperesolver::{DeclarationType, DeclarationTypeResolver};
+use crate::bindgen::ir::{ConstExpr, Path, Type};
+use crate::bindgen::utilities::IterHelpers;
+use crate::bindgen::writer::{Source, SourceWriter};
+
+#[derive(Debug, Clone)]
+pub enum GenericParamType {
+ Type,
+ Const(Type),
+}
+
+#[derive(Debug, Clone)]
+pub struct GenericParam {
+ name: Path,
+ ty: GenericParamType,
+}
+
+impl GenericParam {
+ pub fn new_type_param(name: &str) -> Self {
+ GenericParam {
+ name: Path::new(name),
+ ty: GenericParamType::Type,
+ }
+ }
+
+ pub fn load(param: &syn::GenericParam) -> Result<Option<Self>, String> {
+ match *param {
+ syn::GenericParam::Type(syn::TypeParam { ref ident, .. }) => Ok(Some(GenericParam {
+ name: Path::new(ident.unraw().to_string()),
+ ty: GenericParamType::Type,
+ })),
+
+ syn::GenericParam::Lifetime(_) => Ok(None),
+
+ syn::GenericParam::Const(syn::ConstParam {
+ ref ident, ref ty, ..
+ }) => match Type::load(ty)? {
+ None => {
+ // A type that evaporates, like PhantomData.
+ Err(format!("unsupported const generic type: {:?}", ty))
+ }
+ Some(ty) => Ok(Some(GenericParam {
+ name: Path::new(ident.unraw().to_string()),
+ ty: GenericParamType::Const(ty),
+ })),
+ },
+ }
+ }
+
+ pub fn name(&self) -> &Path {
+ &self.name
+ }
+}
+
+#[derive(Default, Debug, Clone)]
+pub struct GenericParams(pub Vec<GenericParam>);
+
+impl GenericParams {
+ pub fn load(generics: &syn::Generics) -> Result<Self, String> {
+ let mut params = vec![];
+ for param in &generics.params {
+ if let Some(p) = GenericParam::load(param)? {
+ params.push(p);
+ }
+ }
+
+ Ok(GenericParams(params))
+ }
+
+ /// Associate each parameter with an argument.
+ pub fn call<'out>(
+ &'out self,
+ item_name: &str,
+ arguments: &'out [GenericArgument],
+ ) -> Vec<(&'out Path, &'out GenericArgument)> {
+ assert!(self.len() > 0, "{} is not generic", item_name);
+ assert!(
+ self.len() == arguments.len(),
+ "{} has {} params but is being instantiated with {} values",
+ item_name,
+ self.len(),
+ arguments.len(),
+ );
+ self.iter()
+ .map(|param| param.name())
+ .zip(arguments.iter())
+ .collect()
+ }
+
+ fn write_internal<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ with_default: bool,
+ ) {
+ if !self.0.is_empty() && config.language == Language::Cxx {
+ out.write("template<");
+ for (i, item) in self.0.iter().enumerate() {
+ if i != 0 {
+ out.write(", ");
+ }
+ match item.ty {
+ GenericParamType::Type => {
+ write!(out, "typename {}", item.name);
+ if with_default {
+ write!(out, " = void");
+ }
+ }
+ GenericParamType::Const(ref ty) => {
+ cdecl::write_field(out, ty, item.name.name(), config);
+ if with_default {
+ write!(out, " = 0");
+ }
+ }
+ }
+ }
+ out.write(">");
+ out.new_line();
+ }
+ }
+
+ pub fn write_with_default<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ self.write_internal(config, out, true);
+ }
+}
+
+impl Deref for GenericParams {
+ type Target = [GenericParam];
+
+ fn deref(&self) -> &[GenericParam] {
+ &self.0
+ }
+}
+
+impl Source for GenericParams {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ self.write_internal(config, out, false);
+ }
+}
+
+/// A (non-lifetime) argument passed to a generic, either a type or a constant expression.
+///
+/// Note: Both arguments in a type like `Array<T, N>` are represented as
+/// `GenericArgument::Type`s, even if `N` is actually the name of a const. This
+/// is a consequence of `syn::GenericArgument` doing the same thing.
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum GenericArgument {
+ Type(Type),
+ Const(ConstExpr),
+}
+
+impl GenericArgument {
+ pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> GenericArgument {
+ match *self {
+ GenericArgument::Type(ref ty) => {
+ if let Type::Path(ref path) = *ty {
+ if path.is_single_identifier() {
+ // See note on `GenericArgument` above: `ty` may
+ // actually be the name of a const. Check for that now.
+ for &(name, value) in mappings {
+ if *name == path.path {
+ return value.clone();
+ }
+ }
+ }
+ }
+ GenericArgument::Type(ty.specialize(mappings))
+ }
+ GenericArgument::Const(ref expr) => GenericArgument::Const(expr.clone()),
+ }
+ }
+
+ 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),
+ }
+ }
+}
+
+impl Source for GenericArgument {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ match *self {
+ GenericArgument::Type(ref ty) => ty.write(config, out),
+ GenericArgument::Const(ref expr) => expr.write(config, out),
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct GenericPath {
+ path: Path,
+ export_name: String,
+ generics: Vec<GenericArgument>,
+ ctype: Option<DeclarationType>,
+}
+
+impl GenericPath {
+ pub fn new(path: Path, generics: Vec<GenericArgument>) -> Self {
+ let export_name = path.name().to_owned();
+ Self {
+ path,
+ export_name,
+ generics,
+ ctype: None,
+ }
+ }
+
+ pub fn self_path() -> Self {
+ Self::new(Path::new("Self"), vec![])
+ }
+
+ pub fn replace_self_with(&mut self, self_ty: &Path) {
+ if self.path.replace_self_with(self_ty) {
+ self.export_name = self_ty.name().to_owned();
+ }
+ // Caller deals with generics.
+ }
+
+ pub fn path(&self) -> &Path {
+ &self.path
+ }
+
+ pub fn generics(&self) -> &[GenericArgument] {
+ &self.generics
+ }
+
+ pub fn generics_mut(&mut self) -> &mut [GenericArgument] {
+ &mut self.generics
+ }
+
+ pub fn ctype(&self) -> Option<&DeclarationType> {
+ self.ctype.as_ref()
+ }
+
+ pub fn name(&self) -> &str {
+ self.path.name()
+ }
+
+ pub fn export_name(&self) -> &str {
+ &self.export_name
+ }
+
+ pub fn is_single_identifier(&self) -> bool {
+ self.generics.is_empty()
+ }
+
+ pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) {
+ for generic in &mut self.generics {
+ generic.rename_for_config(config, generic_params);
+ }
+ if !generic_params.iter().any(|param| param.name == self.path) {
+ config.export.rename(&mut self.export_name);
+ }
+ }
+
+ pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ self.ctype = resolver.type_for(&self.path);
+ }
+
+ pub fn load(path: &syn::Path) -> Result<Self, String> {
+ assert!(
+ !path.segments.is_empty(),
+ "{:?} doesn't have any segments",
+ path
+ );
+ let last_segment = path.segments.last().unwrap();
+ let name = last_segment.ident.unraw().to_string();
+
+ let path = Path::new(name);
+ let phantom_data_path = Path::new("PhantomData");
+ if path == phantom_data_path {
+ return Ok(Self::new(path, Vec::new()));
+ }
+
+ let generics = match last_segment.arguments {
+ syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
+ ref args,
+ ..
+ }) => args.iter().try_skip_map(|x| match *x {
+ syn::GenericArgument::Type(ref x) => Ok(Type::load(x)?.map(GenericArgument::Type)),
+ syn::GenericArgument::Lifetime(_) => Ok(None),
+ syn::GenericArgument::Const(ref x) => {
+ Ok(Some(GenericArgument::Const(ConstExpr::load(x)?)))
+ }
+ _ => Err(format!("can't handle generic argument {:?}", x)),
+ })?,
+ syn::PathArguments::Parenthesized(_) => {
+ return Err("Path contains parentheses.".to_owned());
+ }
+ _ => Vec::new(),
+ };
+
+ Ok(Self::new(path, generics))
+ }
+}
diff --git a/src/bindgen/ir/global.rs b/src/bindgen/ir/global.rs
new file mode 100644
index 0000000..82a1756
--- /dev/null
+++ b/src/bindgen/ir/global.rs
@@ -0,0 +1,121 @@
+/* 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::io::Write;
+
+use crate::bindgen::cdecl;
+use crate::bindgen::config::Config;
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{AnnotationSet, Cfg, Documentation, Item, ItemContainer, Path, Type};
+use crate::bindgen::library::Library;
+use crate::bindgen::writer::{Source, SourceWriter};
+
+#[derive(Debug, Clone)]
+pub struct Static {
+ pub path: Path,
+ pub export_name: String,
+ pub ty: Type,
+ pub mutable: bool,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+}
+
+impl Static {
+ pub fn load(
+ path: Path,
+ item: &syn::ItemStatic,
+ mod_cfg: Option<&Cfg>,
+ ) -> Result<Static, String> {
+ let ty = Type::load(&item.ty)?;
+
+ if ty.is_none() {
+ return Err("Cannot have a zero sized static definition.".to_owned());
+ }
+
+ Ok(Static::new(
+ path,
+ ty.unwrap(),
+ item.mutability.is_some(),
+ Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
+ AnnotationSet::load(&item.attrs)?,
+ Documentation::load(&item.attrs),
+ ))
+ }
+
+ pub fn new(
+ path: Path,
+ ty: Type,
+ mutable: bool,
+ cfg: Option<Cfg>,
+ annotations: AnnotationSet,
+ documentation: Documentation,
+ ) -> Self {
+ let export_name = path.name().to_owned();
+ Self {
+ path,
+ export_name,
+ ty,
+ mutable,
+ cfg,
+ annotations,
+ documentation,
+ }
+ }
+
+ pub fn simplify_standard_types(&mut self, config: &Config) {
+ self.ty.simplify_standard_types(config);
+ }
+}
+
+impl Item for Static {
+ fn path(&self) -> &Path {
+ &self.path
+ }
+
+ fn export_name(&self) -> &str {
+ &self.export_name
+ }
+
+ fn cfg(&self) -> Option<&Cfg> {
+ self.cfg.as_ref()
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ &self.annotations
+ }
+
+ fn annotations_mut(&mut self) -> &mut AnnotationSet {
+ &mut self.annotations
+ }
+
+ fn container(&self) -> ItemContainer {
+ ItemContainer::Static(self.clone())
+ }
+
+ fn rename_for_config(&mut self, config: &Config) {
+ self.ty.rename_for_config(config, &Default::default());
+ }
+
+ 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);
+ }
+}
+
+impl Source for Static {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ out.write("extern ");
+ if let Type::Ptr { is_const: true, .. } = self.ty {
+ } else if !self.mutable {
+ out.write("const ");
+ }
+ cdecl::write_field(out, &self.ty, &self.export_name, config);
+ out.write(";");
+ }
+}
diff --git a/src/bindgen/ir/item.rs b/src/bindgen/ir/item.rs
new file mode 100644
index 0000000..16d98f5
--- /dev/null
+++ b/src/bindgen/ir/item.rs
@@ -0,0 +1,250 @@
+/* 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 indexmap::IndexMap;
+use std::mem;
+
+use crate::bindgen::config::Config;
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{
+ AnnotationSet, Cfg, Constant, Enum, GenericArgument, OpaqueItem, Path, Static, Struct, Typedef,
+ Union,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::monomorph::Monomorphs;
+
+/// An item is any type of rust item besides a function
+pub trait Item {
+ fn path(&self) -> &Path;
+ fn name(&self) -> &str {
+ self.path().name()
+ }
+ fn export_name(&self) -> &str {
+ self.name()
+ }
+ fn cfg(&self) -> Option<&Cfg>;
+ fn annotations(&self) -> &AnnotationSet;
+ fn annotations_mut(&mut self) -> &mut AnnotationSet;
+
+ 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(
+ &self,
+ _generics: &[GenericArgument],
+ _library: &Library,
+ _out: &mut Monomorphs,
+ ) {
+ unreachable!("Cannot instantiate {} as a generic.", self.name())
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum ItemContainer {
+ Constant(Constant),
+ Static(Static),
+ OpaqueItem(OpaqueItem),
+ Struct(Struct),
+ Union(Union),
+ Enum(Enum),
+ Typedef(Typedef),
+}
+
+impl ItemContainer {
+ pub fn deref(&self) -> &dyn Item {
+ match *self {
+ ItemContainer::Constant(ref x) => x,
+ ItemContainer::Static(ref x) => x,
+ ItemContainer::OpaqueItem(ref x) => x,
+ ItemContainer::Struct(ref x) => x,
+ ItemContainer::Union(ref x) => x,
+ ItemContainer::Enum(ref x) => x,
+ ItemContainer::Typedef(ref x) => x,
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum ItemValue<T: Item> {
+ Cfg(Vec<T>),
+ Single(T),
+}
+
+#[derive(Debug, Clone)]
+pub struct ItemMap<T: Item> {
+ data: IndexMap<Path, ItemValue<T>>,
+}
+
+impl<T: Item> Default for ItemMap<T> {
+ fn default() -> ItemMap<T> {
+ ItemMap {
+ data: Default::default(),
+ }
+ }
+}
+
+impl<T: Item + Clone> ItemMap<T> {
+ pub fn rebuild(&mut self) {
+ let old = mem::take(self);
+ old.for_all_items(|x| {
+ self.try_insert(x.clone());
+ });
+ }
+
+ pub fn try_insert(&mut self, item: T) -> bool {
+ match (item.cfg().is_some(), self.data.get_mut(item.path())) {
+ (true, Some(&mut ItemValue::Cfg(ref mut items))) => {
+ items.push(item);
+ return true;
+ }
+ (false, Some(&mut ItemValue::Cfg(_))) => {
+ return false;
+ }
+ (true, Some(&mut ItemValue::Single(_))) => {
+ return false;
+ }
+ (false, Some(&mut ItemValue::Single(_))) => {
+ return false;
+ }
+ _ => {}
+ }
+
+ let path = item.path().clone();
+ if item.cfg().is_some() {
+ self.data.insert(path, ItemValue::Cfg(vec![item]));
+ } else {
+ self.data.insert(path, ItemValue::Single(item));
+ }
+
+ true
+ }
+
+ pub fn extend_with(&mut self, other: &ItemMap<T>) {
+ other.for_all_items(|x| {
+ self.try_insert(x.clone());
+ });
+ }
+
+ pub fn to_vec(&self) -> Vec<T> {
+ let mut result = Vec::with_capacity(self.data.len());
+ for container in self.data.values() {
+ match *container {
+ ItemValue::Cfg(ref items) => result.extend_from_slice(items),
+ ItemValue::Single(ref item) => {
+ result.push(item.clone());
+ }
+ }
+ }
+ result
+ }
+
+ pub fn get_items(&self, path: &Path) -> Option<Vec<ItemContainer>> {
+ Some(match *self.data.get(path)? {
+ ItemValue::Cfg(ref items) => items.iter().map(|x| x.container()).collect(),
+ ItemValue::Single(ref item) => vec![item.container()],
+ })
+ }
+
+ pub fn filter<F>(&mut self, callback: F)
+ where
+ F: Fn(&T) -> bool,
+ {
+ let data = mem::take(&mut self.data);
+
+ for (name, container) in data {
+ match container {
+ ItemValue::Cfg(items) => {
+ let mut new_items = Vec::new();
+ for item in items {
+ if !callback(&item) {
+ new_items.push(item);
+ }
+ }
+ if !new_items.is_empty() {
+ self.data.insert(name, ItemValue::Cfg(new_items));
+ }
+ }
+ ItemValue::Single(item) => {
+ if !callback(&item) {
+ self.data.insert(name, ItemValue::Single(item));
+ }
+ }
+ }
+ }
+ }
+
+ pub fn for_all_items<F>(&self, mut callback: F)
+ where
+ F: FnMut(&T),
+ {
+ for container in self.data.values() {
+ match *container {
+ ItemValue::Cfg(ref items) => {
+ for item in items {
+ callback(item);
+ }
+ }
+ ItemValue::Single(ref item) => callback(item),
+ }
+ }
+ }
+
+ pub fn for_all_items_mut<F>(&mut self, mut callback: F)
+ where
+ F: FnMut(&mut T),
+ {
+ for container in self.data.values_mut() {
+ match *container {
+ ItemValue::Cfg(ref mut items) => {
+ for item in items {
+ callback(item);
+ }
+ }
+ ItemValue::Single(ref mut item) => callback(item),
+ }
+ }
+ }
+
+ pub fn for_items<F>(&self, path: &Path, mut callback: F)
+ where
+ F: FnMut(&T),
+ {
+ match self.data.get(path) {
+ Some(ItemValue::Cfg(items)) => {
+ for item in items {
+ callback(item);
+ }
+ }
+ Some(ItemValue::Single(item)) => {
+ callback(item);
+ }
+ None => {}
+ }
+ }
+
+ pub fn for_items_mut<F>(&mut self, path: &Path, mut callback: F)
+ where
+ F: FnMut(&mut T),
+ {
+ match self.data.get_mut(path) {
+ Some(&mut ItemValue::Cfg(ref mut items)) => {
+ for item in items {
+ callback(item);
+ }
+ }
+ Some(&mut ItemValue::Single(ref mut item)) => {
+ callback(item);
+ }
+ None => {}
+ }
+ }
+}
diff --git a/src/bindgen/ir/mod.rs b/src/bindgen/ir/mod.rs
new file mode 100644
index 0000000..3566f0d
--- /dev/null
+++ b/src/bindgen/ir/mod.rs
@@ -0,0 +1,39 @@
+/* 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/. */
+
+pub mod annotation;
+pub mod cfg;
+pub mod constant;
+pub mod documentation;
+pub mod enumeration;
+pub mod field;
+pub mod function;
+pub mod generic_path;
+pub mod global;
+pub mod item;
+pub mod opaque;
+pub mod path;
+pub mod repr;
+pub mod structure;
+pub mod ty;
+pub mod typedef;
+pub mod union;
+
+pub use self::annotation::{AnnotationSet, AnnotationValue, DeprecatedNoteKind};
+pub use self::cfg::*;
+pub use self::constant::*;
+pub use self::documentation::Documentation;
+pub use self::enumeration::*;
+pub use self::field::*;
+pub use self::function::*;
+pub use self::generic_path::*;
+pub use self::global::*;
+pub use self::item::*;
+pub use self::opaque::*;
+pub use self::path::*;
+pub use self::repr::*;
+pub use self::structure::*;
+pub use self::ty::*;
+pub use self::typedef::*;
+pub use self::union::*;
diff --git a/src/bindgen/ir/opaque.rs b/src/bindgen/ir/opaque.rs
new file mode 100644
index 0000000..4451d4a
--- /dev/null
+++ b/src/bindgen/ir/opaque.rs
@@ -0,0 +1,176 @@
+/* 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::io::Write;
+
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{
+ AnnotationSet, Cfg, ConditionWrite, Documentation, GenericArgument, GenericParams, Item,
+ ItemContainer, Path, ToCondition,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::mangle;
+use crate::bindgen::monomorph::Monomorphs;
+use crate::bindgen::writer::{Source, SourceWriter};
+
+#[derive(Debug, Clone)]
+pub struct OpaqueItem {
+ pub path: Path,
+ pub export_name: String,
+ pub generic_params: GenericParams,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+}
+
+impl OpaqueItem {
+ pub fn load(
+ path: Path,
+ generics: &syn::Generics,
+ attrs: &[syn::Attribute],
+ mod_cfg: Option<&Cfg>,
+ ) -> Result<OpaqueItem, String> {
+ Ok(Self::new(
+ path,
+ GenericParams::load(generics)?,
+ Cfg::append(mod_cfg, Cfg::load(attrs)),
+ AnnotationSet::load(attrs).unwrap_or_else(|_| AnnotationSet::new()),
+ Documentation::load(attrs),
+ ))
+ }
+
+ pub fn new(
+ path: Path,
+ generic_params: GenericParams,
+ cfg: Option<Cfg>,
+ annotations: AnnotationSet,
+ documentation: Documentation,
+ ) -> OpaqueItem {
+ let export_name = path.name().to_owned();
+ Self {
+ path,
+ export_name,
+ generic_params,
+ cfg,
+ annotations,
+ documentation,
+ }
+ }
+}
+
+impl Item for OpaqueItem {
+ fn path(&self) -> &Path {
+ &self.path
+ }
+
+ fn export_name(&self) -> &str {
+ &self.export_name
+ }
+
+ fn cfg(&self) -> Option<&Cfg> {
+ self.cfg.as_ref()
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ &self.annotations
+ }
+
+ fn annotations_mut(&mut self) -> &mut AnnotationSet {
+ &mut self.annotations
+ }
+
+ fn container(&self) -> ItemContainer {
+ ItemContainer::OpaqueItem(self.clone())
+ }
+
+ fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
+ resolver.add_struct(&self.path);
+ }
+
+ fn rename_for_config(&mut self, config: &Config) {
+ config.export.rename(&mut self.export_name);
+ }
+
+ fn add_dependencies(&self, _: &Library, _: &mut Dependencies) {}
+
+ fn instantiate_monomorph(
+ &self,
+ generic_values: &[GenericArgument],
+ library: &Library,
+ out: &mut Monomorphs,
+ ) {
+ assert!(
+ !self.generic_params.is_empty(),
+ "{} is not generic",
+ self.path
+ );
+
+ // We can be instantiated with less generic params because of default
+ // template parameters, or because of empty types that we remove during
+ // parsing (`()`).
+ assert!(
+ self.generic_params.len() >= generic_values.len(),
+ "{} has {} params but is being instantiated with {} values",
+ self.path,
+ self.generic_params.len(),
+ generic_values.len(),
+ );
+
+ let mangled_path = mangle::mangle_path(
+ &self.path,
+ generic_values,
+ &library.get_config().export.mangle,
+ );
+
+ let monomorph = OpaqueItem::new(
+ mangled_path,
+ GenericParams::default(),
+ self.cfg.clone(),
+ self.annotations.clone(),
+ self.documentation.clone(),
+ );
+
+ out.insert_opaque(self, monomorph, generic_values.to_owned());
+ }
+}
+
+impl Source for OpaqueItem {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ let condition = self.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ self.documentation.write(config, out);
+
+ self.generic_params.write_with_default(config, out);
+
+ match config.language {
+ Language::C if config.style.generate_typedef() => {
+ write!(
+ out,
+ "typedef struct {} {};",
+ self.export_name(),
+ self.export_name()
+ );
+ }
+ Language::C | Language::Cxx => {
+ write!(out, "struct {};", self.export_name());
+ }
+ Language::Cython => {
+ write!(
+ out,
+ "{}struct {}",
+ config.style.cython_def(),
+ self.export_name()
+ );
+ out.open_brace();
+ out.write("pass");
+ out.close_brace(false);
+ }
+ }
+
+ condition.write_after(config, out);
+ }
+}
diff --git a/src/bindgen/ir/path.rs b/src/bindgen/ir/path.rs
new file mode 100644
index 0000000..480c31b
--- /dev/null
+++ b/src/bindgen/ir/path.rs
@@ -0,0 +1,50 @@
+/* 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::cmp::Ordering;
+use std::fmt;
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Path {
+ name: String,
+}
+
+impl Path {
+ pub fn new<T>(name: T) -> Self
+ where
+ String: From<T>,
+ {
+ Self { name: name.into() }
+ }
+
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn replace_self_with(&mut self, path: &Self) -> bool {
+ if self.name() != "Self" {
+ return false;
+ }
+ *self = path.clone();
+ true
+ }
+}
+
+impl PartialOrd for Path {
+ fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Path {
+ fn cmp(&self, other: &Path) -> Ordering {
+ self.name.cmp(&other.name)
+ }
+}
+
+impl fmt::Display for Path {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.name)
+ }
+}
diff --git a/src/bindgen/ir/repr.rs b/src/bindgen/ir/repr.rs
new file mode 100644
index 0000000..c40cd7f
--- /dev/null
+++ b/src/bindgen/ir/repr.rs
@@ -0,0 +1,182 @@
+/* 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 syn::ext::IdentExt;
+
+use crate::bindgen::ir::ty::{IntKind, PrimitiveType};
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
+pub enum ReprStyle {
+ #[default]
+ Rust,
+ C,
+ Transparent,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct ReprType {
+ kind: IntKind,
+ signed: bool,
+}
+
+impl ReprType {
+ pub(crate) fn to_primitive(self) -> PrimitiveType {
+ PrimitiveType::Integer {
+ kind: self.kind,
+ signed: self.signed,
+ zeroable: true,
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum ReprAlign {
+ Packed,
+ Align(u64),
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
+pub struct Repr {
+ pub style: ReprStyle,
+ pub ty: Option<ReprType>,
+ pub align: Option<ReprAlign>,
+}
+
+impl Repr {
+ pub fn load(attrs: &[syn::Attribute]) -> Result<Repr, String> {
+ let ids = attrs
+ .iter()
+ .filter_map(|attr| {
+ if let syn::Meta::List(syn::MetaList { path, nested, .. }) =
+ attr.parse_meta().ok()?
+ {
+ if path.is_ident("repr") {
+ return Some(nested.into_iter().collect::<Vec<_>>());
+ }
+ }
+ None
+ })
+ .flatten()
+ .filter_map(|meta| match meta {
+ syn::NestedMeta::Meta(syn::Meta::Path(path)) => Some((
+ path.segments.first().unwrap().ident.unraw().to_string(),
+ None,
+ )),
+ syn::NestedMeta::Meta(syn::Meta::List(syn::MetaList { path, nested, .. })) => {
+ Some((
+ path.segments.first().unwrap().ident.unraw().to_string(),
+ Some(
+ nested
+ .iter()
+ .filter_map(|meta| match meta {
+ // Only used for #[repr(align(...))].
+ syn::NestedMeta::Lit(syn::Lit::Int(literal)) => {
+ Some(literal.base10_digits().to_string())
+ }
+ // Only single levels of nesting supported at the moment.
+ _ => None,
+ })
+ .collect::<Vec<_>>(),
+ ),
+ ))
+ }
+ _ => None,
+ });
+
+ let mut repr = Repr::default();
+ for id in ids {
+ let (int_kind, signed) = match (id.0.as_ref(), id.1) {
+ ("u8", None) => (IntKind::B8, false),
+ ("u16", None) => (IntKind::B16, false),
+ ("u32", None) => (IntKind::B32, false),
+ ("u64", None) => (IntKind::B64, false),
+ ("usize", None) => (IntKind::Size, false),
+ ("i8", None) => (IntKind::B8, true),
+ ("i16", None) => (IntKind::B16, true),
+ ("i32", None) => (IntKind::B32, true),
+ ("i64", None) => (IntKind::B64, true),
+ ("isize", None) => (IntKind::Size, true),
+ ("C", None) => {
+ repr.style = ReprStyle::C;
+ continue;
+ }
+ ("transparent", None) => {
+ repr.style = ReprStyle::Transparent;
+ continue;
+ }
+ ("packed", args) => {
+ // #[repr(packed(n))] not supported because of some open questions about how
+ // to calculate the native alignment of types. See mozilla/cbindgen#433.
+ if args.is_some() {
+ return Err(
+ "Not-yet-implemented #[repr(packed(...))] encountered.".to_string()
+ );
+ }
+ let align = ReprAlign::Packed;
+ // Only permit a single alignment-setting repr.
+ if let Some(old_align) = repr.align {
+ return Err(format!(
+ "Conflicting #[repr(align(...))] type hints {:?} and {:?}.",
+ old_align, align
+ ));
+ }
+ repr.align = Some(align);
+ continue;
+ }
+ ("align", Some(args)) => {
+ // #[repr(align(...))] only allows a single argument.
+ if args.len() != 1 {
+ return Err(format!(
+ "Unsupported #[repr(align({}))], align must have exactly one argument.",
+ args.join(", ")
+ ));
+ }
+ // Must be a positive integer.
+ let align = match args.first().unwrap().parse::<u64>() {
+ Ok(align) => align,
+ Err(_) => {
+ return Err(format!("Non-numeric #[repr(align({}))].", args.join(", ")))
+ }
+ };
+ // Must be a power of 2.
+ if !align.is_power_of_two() || align == 0 {
+ return Err(format!("Invalid alignment to #[repr(align({}))].", align));
+ }
+ // Only permit a single alignment-setting repr.
+ if let Some(old_align) = repr.align {
+ return Err(format!(
+ "Conflicting #[repr(align(...))] type hints {:?} and {:?}.",
+ old_align,
+ ReprAlign::Align(align)
+ ));
+ }
+ repr.align = Some(ReprAlign::Align(align));
+ continue;
+ }
+ (path, args) => match args {
+ None => return Err(format!("Unsupported #[repr({})].", path)),
+ Some(args) => {
+ return Err(format!(
+ "Unsupported #[repr({}({}))].",
+ path,
+ args.join(", ")
+ ));
+ }
+ },
+ };
+ let ty = ReprType {
+ kind: int_kind,
+ signed,
+ };
+ if let Some(old_ty) = repr.ty {
+ return Err(format!(
+ "Conflicting #[repr(...)] type hints {:?} and {:?}.",
+ old_ty, ty
+ ));
+ }
+ repr.ty = Some(ty);
+ }
+ Ok(repr)
+ }
+}
diff --git a/src/bindgen/ir/structure.rs b/src/bindgen/ir/structure.rs
new file mode 100644
index 0000000..2eefa95
--- /dev/null
+++ b/src/bindgen/ir/structure.rs
@@ -0,0 +1,717 @@
+/* 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::io::Write;
+
+use syn::ext::IdentExt;
+
+use crate::bindgen::config::{Config, Language, LayoutConfig};
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{
+ AnnotationSet, Cfg, ConditionWrite, Constant, DeprecatedNoteKind, Documentation, Field,
+ GenericArgument, GenericParams, Item, ItemContainer, Path, Repr, ReprAlign, ReprStyle,
+ ToCondition, Type, Typedef,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::mangle;
+use crate::bindgen::monomorph::Monomorphs;
+use crate::bindgen::rename::{IdentifierType, RenameRule};
+use crate::bindgen::reserved;
+use crate::bindgen::utilities::IterHelpers;
+use crate::bindgen::writer::{ListType, Source, SourceWriter};
+
+#[derive(Debug, Clone)]
+pub struct Struct {
+ pub path: Path,
+ pub export_name: String,
+ pub generic_params: GenericParams,
+ pub fields: Vec<Field>,
+ /// Whether there's a tag field on the body of this struct. When this is
+ /// true, is_enum_variant_body is also guaranteed to be true.
+ pub has_tag_field: bool,
+ /// Whether this is an enum variant body.
+ pub is_enum_variant_body: bool,
+ pub alignment: Option<ReprAlign>,
+ pub is_transparent: bool,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+ pub associated_constants: Vec<Constant>,
+}
+
+impl Struct {
+ /// Whether this struct can derive operator== / operator!=.
+ pub fn can_derive_eq(&self) -> bool {
+ !self.fields.is_empty() && self.fields.iter().all(|x| x.ty.can_cmp_eq())
+ }
+
+ pub fn add_associated_constant(&mut self, c: Constant) {
+ self.associated_constants.push(c);
+ }
+
+ pub fn load(
+ layout_config: &LayoutConfig,
+ item: &syn::ItemStruct,
+ mod_cfg: Option<&Cfg>,
+ ) -> Result<Self, String> {
+ let repr = Repr::load(&item.attrs)?;
+ let is_transparent = match repr.style {
+ ReprStyle::C => false,
+ ReprStyle::Transparent => true,
+ _ => {
+ return Err("Struct is not marked #[repr(C)] or #[repr(transparent)].".to_owned());
+ }
+ };
+
+ let path = Path::new(item.ident.unraw().to_string());
+
+ // Ensure we can safely represent the struct given the configuration.
+ if let Some(align) = repr.align {
+ layout_config.ensure_safe_to_represent(&align)?;
+ }
+
+ let fields = match item.fields {
+ syn::Fields::Unit => Vec::new(),
+ syn::Fields::Named(ref fields) => fields
+ .named
+ .iter()
+ .try_skip_map(|field| Field::load(field, &path))?,
+ syn::Fields::Unnamed(ref fields) => {
+ let mut out = Vec::new();
+ let mut current = 0;
+ for field in fields.unnamed.iter() {
+ if let Some(mut ty) = Type::load(&field.ty)? {
+ ty.replace_self_with(&path);
+ out.push(Field {
+ name: format!("{}", current),
+ ty,
+ cfg: Cfg::load(&field.attrs),
+ annotations: AnnotationSet::load(&field.attrs)?,
+ documentation: Documentation::load(&field.attrs),
+ });
+ current += 1;
+ }
+ }
+ out
+ }
+ };
+
+ let has_tag_field = false;
+ let is_enum_variant_body = false;
+
+ Ok(Struct::new(
+ path,
+ GenericParams::load(&item.generics)?,
+ fields,
+ has_tag_field,
+ is_enum_variant_body,
+ repr.align,
+ is_transparent,
+ Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
+ AnnotationSet::load(&item.attrs)?,
+ Documentation::load(&item.attrs),
+ ))
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ path: Path,
+ generic_params: GenericParams,
+ fields: Vec<Field>,
+ has_tag_field: bool,
+ is_enum_variant_body: bool,
+ alignment: Option<ReprAlign>,
+ is_transparent: bool,
+ cfg: Option<Cfg>,
+ annotations: AnnotationSet,
+ documentation: Documentation,
+ ) -> Self {
+ let export_name = path.name().to_owned();
+ Self {
+ path,
+ export_name,
+ generic_params,
+ fields,
+ has_tag_field,
+ is_enum_variant_body,
+ alignment,
+ is_transparent,
+ cfg,
+ annotations,
+ documentation,
+ associated_constants: vec![],
+ }
+ }
+
+ pub fn simplify_standard_types(&mut self, config: &Config) {
+ for field in &mut self.fields {
+ field.ty.simplify_standard_types(config);
+ }
+ }
+
+ pub fn is_generic(&self) -> bool {
+ self.generic_params.len() > 0
+ }
+
+ pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
+ // Generic structs can instantiate monomorphs only once they've been
+ // instantiated. See `instantiate_monomorph` for more details.
+ if self.is_generic() {
+ return;
+ }
+
+ for field in &self.fields {
+ field.ty.add_monomorphs(library, out);
+ }
+ }
+
+ pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
+ for field in &mut self.fields {
+ field.ty.mangle_paths(monomorphs);
+ }
+ }
+
+ pub fn specialize(
+ &self,
+ generic_values: &[GenericArgument],
+ mappings: &[(&Path, &GenericArgument)],
+ config: &Config,
+ ) -> Self {
+ let mangled_path = mangle::mangle_path(&self.path, generic_values, &config.export.mangle);
+ Struct::new(
+ mangled_path,
+ GenericParams::default(),
+ self.fields
+ .iter()
+ .map(|field| Field {
+ name: field.name.clone(),
+ ty: field.ty.specialize(mappings),
+ cfg: field.cfg.clone(),
+ annotations: field.annotations.clone(),
+ documentation: field.documentation.clone(),
+ })
+ .collect(),
+ self.has_tag_field,
+ self.is_enum_variant_body,
+ self.alignment,
+ self.is_transparent,
+ self.cfg.clone(),
+ self.annotations.clone(),
+ self.documentation.clone(),
+ )
+ }
+
+ fn emit_bitflags_binop<F: Write>(
+ &self,
+ constexpr_prefix: &str,
+ operator: char,
+ other: &str,
+ out: &mut SourceWriter<F>,
+ ) {
+ let bits = &self.fields[0].name;
+ out.new_line();
+ write!(
+ out,
+ "{}{} operator{}(const {}& {}) const",
+ constexpr_prefix,
+ self.export_name(),
+ operator,
+ self.export_name(),
+ other
+ );
+ out.open_brace();
+ write!(
+ out,
+ "return {} {{ static_cast<decltype({bits})>(this->{bits} {operator} {other}.{bits}) }};",
+ self.export_name()
+ );
+ out.close_brace(false);
+
+ out.new_line();
+ write!(
+ out,
+ "{}& operator{}=(const {}& {})",
+ self.export_name(),
+ operator,
+ self.export_name(),
+ other
+ );
+ out.open_brace();
+ write!(out, "*this = (*this {} {});", operator, other);
+ out.new_line();
+ write!(out, "return *this;");
+ out.close_brace(false);
+ }
+}
+
+impl Item for Struct {
+ fn path(&self) -> &Path {
+ &self.path
+ }
+
+ fn export_name(&self) -> &str {
+ &self.export_name
+ }
+
+ fn cfg(&self) -> Option<&Cfg> {
+ self.cfg.as_ref()
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ &self.annotations
+ }
+
+ fn annotations_mut(&mut self) -> &mut AnnotationSet {
+ &mut self.annotations
+ }
+
+ fn container(&self) -> ItemContainer {
+ ItemContainer::Struct(self.clone())
+ }
+
+ fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
+ if self.is_transparent {
+ resolver.add_none(&self.path);
+ } else {
+ resolver.add_struct(&self.path);
+ }
+ }
+
+ fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ for field in &mut self.fields {
+ field.ty.resolve_declaration_types(resolver);
+ }
+ }
+
+ fn rename_for_config(&mut self, config: &Config) {
+ // Rename the name of the struct
+ if !(self.has_tag_field && config.language == Language::Cxx) {
+ config.export.rename(&mut self.export_name);
+ }
+
+ // Rename the types used in fields
+ {
+ let fields = self.fields.iter_mut().skip(self.has_tag_field as usize);
+ for field in fields {
+ field.ty.rename_for_config(config, &self.generic_params);
+ }
+ }
+
+ // Apply renaming rules to fields in the following order
+ // 1. `cbindgen::field-names` annotation
+ // 2. `cbindgen::rename-all` annotation
+ // 3. config struct rename rule
+ // If the struct is a tuple struct and we have not renamed the
+ // fields, then prefix each of them with an underscore.
+ // If any field is a reserved keyword, then postfix it with an
+ // underscore.
+
+ // Scope for mutable borrow of fields
+ {
+ let names = self.fields.iter_mut().map(|field| &mut field.name);
+
+ let field_rules = self
+ .annotations
+ .parse_atom::<RenameRule>("rename-all")
+ .unwrap_or(config.structure.rename_fields);
+
+ if let Some(o) = self.annotations.list("field-names") {
+ for (dest, src) in names.zip(o) {
+ *dest = src;
+ }
+ } else if let Some(r) = field_rules.not_none() {
+ for name in names {
+ *name = r.apply(name, IdentifierType::StructMember).into_owned();
+ }
+ } else {
+ // If we don't have any rules for a tuple struct, prefix them with
+ // an underscore so it still compiles.
+ for name in names {
+ if name.starts_with(|c: char| c.is_ascii_digit()) {
+ name.insert(0, '_');
+ }
+ }
+ }
+ }
+
+ for field in &mut self.fields {
+ reserved::escape(&mut field.name);
+ }
+
+ for c in self.associated_constants.iter_mut() {
+ c.rename_for_config(config);
+ }
+ }
+
+ fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ let mut fields = self.fields.iter();
+
+ // If there is a tag field, skip it
+ if self.has_tag_field {
+ fields.next();
+ }
+
+ for field in fields {
+ field
+ .ty
+ .add_dependencies_ignoring_generics(&self.generic_params, library, out);
+ }
+
+ for c in &self.associated_constants {
+ c.add_dependencies(library, out);
+ }
+ }
+
+ fn instantiate_monomorph(
+ &self,
+ generic_values: &[GenericArgument],
+ library: &Library,
+ out: &mut Monomorphs,
+ ) {
+ let mappings = self.generic_params.call(self.path.name(), generic_values);
+ let monomorph = self.specialize(generic_values, &mappings, library.get_config());
+ out.insert_struct(library, self, monomorph, generic_values.to_owned());
+ }
+}
+
+impl Source for Struct {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ if self.is_transparent {
+ let typedef = Typedef {
+ path: self.path.clone(),
+ export_name: self.export_name.to_owned(),
+ generic_params: self.generic_params.clone(),
+ aliased: self.fields[0].ty.clone(),
+ cfg: self.cfg.clone(),
+ annotations: self.annotations.clone(),
+ documentation: self.documentation.clone(),
+ };
+ typedef.write(config, out);
+ for constant in &self.associated_constants {
+ out.new_line();
+ constant.write(config, out, Some(self));
+ }
+ return;
+ }
+
+ let condition = self.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ self.documentation.write(config, out);
+
+ if !self.is_enum_variant_body {
+ self.generic_params.write(config, out);
+ }
+
+ // 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 {
+ match config.language {
+ Language::C if config.style.generate_typedef() => out.write("typedef "),
+ Language::C | Language::Cxx => {}
+ Language::Cython => out.write(config.style.cython_def()),
+ }
+
+ // Cython extern declarations don't manage layouts, layouts are defined entierly by the
+ // corresponding C code. So this `packed` is only for documentation, and missing
+ // `aligned(n)` is also not a problem.
+ if config.language == Language::Cython {
+ if let Some(align) = self.alignment {
+ match align {
+ ReprAlign::Packed => out.write("packed "),
+ ReprAlign::Align(_) => {} // Not supported
+ }
+ }
+ }
+
+ out.write("struct");
+
+ if config.language != Language::Cython {
+ if let Some(align) = self.alignment {
+ match align {
+ ReprAlign::Packed => {
+ if let Some(ref anno) = config.layout.packed {
+ write!(out, " {}", anno);
+ }
+ }
+ ReprAlign::Align(n) => {
+ if let Some(ref anno) = config.layout.aligned_n {
+ write!(out, " {}({})", anno, n);
+ }
+ }
+ }
+ }
+ }
+
+ if self.annotations.must_use(config) {
+ if let Some(ref anno) = config.structure.must_use {
+ write!(out, " {}", anno);
+ }
+ }
+ if let Some(note) = self
+ .annotations
+ .deprecated_note(config, DeprecatedNoteKind::Struct)
+ {
+ write!(out, " {}", note);
+ }
+
+ if config.language != Language::C || config.style.generate_tag() {
+ write!(out, " {}", self.export_name());
+ }
+
+ out.open_brace();
+
+ // Emit the pre_body section, if relevant
+ if let Some(body) = config.export.pre_body(&self.path) {
+ out.write_raw_block(body);
+ out.new_line();
+ }
+
+ out.write_vertical_source_list(&self.fields, ListType::Cap(";"));
+ if config.language == Language::Cython && self.fields.is_empty() {
+ out.write("pass");
+ }
+
+ if config.language == Language::Cxx {
+ let mut wrote_start_newline = false;
+
+ if config.structure.derive_constructor(&self.annotations) && !self.fields.is_empty() {
+ if !wrote_start_newline {
+ wrote_start_newline = true;
+ out.new_line();
+ }
+
+ out.new_line();
+
+ let arg_renamer = |name: &str| {
+ config
+ .function
+ .rename_args
+ .apply(name, IdentifierType::FunctionArg)
+ .into_owned()
+ };
+ write!(out, "{}(", self.export_name());
+ let vec: Vec<_> = self
+ .fields
+ .iter()
+ .map(|field| {
+ Field::from_name_and_type(
+ // const-ref args to constructor
+ format!("const& {}", arg_renamer(&field.name)),
+ field.ty.clone(),
+ )
+ })
+ .collect();
+ out.write_vertical_source_list(&vec[..], ListType::Join(","));
+ write!(out, ")");
+ out.new_line();
+ write!(out, " : ");
+ let vec: Vec<_> = self
+ .fields
+ .iter()
+ .map(|field| format!("{}({})", field.name, arg_renamer(&field.name)))
+ .collect();
+ out.write_vertical_source_list(&vec[..], ListType::Join(","));
+ out.new_line();
+ write!(out, "{{}}");
+ out.new_line();
+ }
+
+ let other = config
+ .function
+ .rename_args
+ .apply("other", IdentifierType::FunctionArg);
+
+ if self
+ .annotations
+ .bool("internal-derive-bitflags")
+ .unwrap_or(false)
+ {
+ assert_eq!(self.fields.len(), 1);
+ let bits = &self.fields[0].name;
+ if !wrote_start_newline {
+ wrote_start_newline = true;
+ out.new_line();
+ }
+ let constexpr_prefix = if config.constant.allow_constexpr {
+ "constexpr "
+ } else {
+ ""
+ };
+
+ out.new_line();
+ write!(out, "{}explicit operator bool() const", constexpr_prefix);
+ out.open_brace();
+ write!(out, "return !!{bits};");
+ out.close_brace(false);
+
+ out.new_line();
+ write!(
+ out,
+ "{}{} operator~() const",
+ constexpr_prefix,
+ self.export_name()
+ );
+ out.open_brace();
+ write!(
+ out,
+ "return {} {{ static_cast<decltype({bits})>(~{bits}) }};",
+ self.export_name()
+ );
+ out.close_brace(false);
+ self.emit_bitflags_binop(constexpr_prefix, '|', &other, out);
+ self.emit_bitflags_binop(constexpr_prefix, '&', &other, out);
+ self.emit_bitflags_binop(constexpr_prefix, '^', &other, out);
+ }
+
+ // Generate a serializer function that allows dumping this struct
+ // to an std::ostream. It's defined as a friend function inside the
+ // struct definition, and doesn't need the `inline` keyword even
+ // though it's implemented right in the generated header file.
+ if config.structure.derive_ostream(&self.annotations) {
+ if !wrote_start_newline {
+ wrote_start_newline = true;
+ out.new_line();
+ }
+
+ out.new_line();
+ let stream = config
+ .function
+ .rename_args
+ .apply("stream", IdentifierType::FunctionArg);
+ let instance = config
+ .function
+ .rename_args
+ .apply("instance", IdentifierType::FunctionArg);
+ write!(
+ out,
+ "friend std::ostream& operator<<(std::ostream& {}, const {}& {})",
+ stream,
+ self.export_name(),
+ instance,
+ );
+ out.open_brace();
+ write!(out, "return {} << \"{{ \"", stream);
+ let vec: Vec<_> = self
+ .fields
+ .iter()
+ .map(|x| format!(" << \"{}=\" << {}.{}", x.name, instance, x.name))
+ .collect();
+ out.write_vertical_source_list(&vec[..], ListType::Join(" << \", \""));
+ out.write(" << \" }\";");
+ out.close_brace(false);
+ }
+
+ let skip_fields = self.has_tag_field as usize;
+
+ macro_rules! emit_op {
+ ($op_name:expr, $op:expr, $conjuc:expr) => {{
+ if !wrote_start_newline {
+ #[allow(unused_assignments)]
+ {
+ wrote_start_newline = true;
+ }
+ out.new_line();
+ }
+
+ out.new_line();
+
+ if let Some(Some(attrs)) =
+ self.annotations.atom(concat!($op_name, "-attributes"))
+ {
+ write!(out, "{} ", attrs);
+ }
+
+ write!(
+ out,
+ "bool operator{}(const {}& {}) const",
+ $op,
+ self.export_name(),
+ other
+ );
+ out.open_brace();
+ out.write("return ");
+ let vec: Vec<_> = self
+ .fields
+ .iter()
+ .skip(skip_fields)
+ .map(|field| format!("{} {} {}.{}", field.name, $op, other, field.name))
+ .collect();
+ out.write_vertical_source_list(
+ &vec[..],
+ ListType::Join(&format!(" {}", $conjuc)),
+ );
+ out.write(";");
+ out.close_brace(false);
+ }};
+ }
+
+ if config.structure.derive_eq(&self.annotations) && self.can_derive_eq() {
+ emit_op!("eq", "==", "&&");
+ }
+ if config.structure.derive_neq(&self.annotations) && self.can_derive_eq() {
+ emit_op!("neq", "!=", "||");
+ }
+ if config.structure.derive_lt(&self.annotations)
+ && self.fields.len() == 1
+ && self.fields[0].ty.can_cmp_order()
+ {
+ emit_op!("lt", "<", "&&");
+ }
+ if config.structure.derive_lte(&self.annotations)
+ && self.fields.len() == 1
+ && self.fields[0].ty.can_cmp_order()
+ {
+ emit_op!("lte", "<=", "&&");
+ }
+ if config.structure.derive_gt(&self.annotations)
+ && self.fields.len() == 1
+ && self.fields[0].ty.can_cmp_order()
+ {
+ emit_op!("gt", ">", "&&");
+ }
+ if config.structure.derive_gte(&self.annotations)
+ && self.fields.len() == 1
+ && self.fields[0].ty.can_cmp_order()
+ {
+ emit_op!("gte", ">=", "&&");
+ }
+ }
+
+ // Emit the post_body section, if relevant
+ if let Some(body) = config.export.post_body(&self.path) {
+ out.new_line();
+ out.write_raw_block(body);
+ }
+
+ if config.language == Language::Cxx
+ && config.structure.associated_constants_in_body
+ && config.constant.allow_static_const
+ {
+ for constant in &self.associated_constants {
+ out.new_line();
+ constant.write_declaration(config, out, self);
+ }
+ }
+
+ if config.language == Language::C && config.style.generate_typedef() {
+ out.close_brace(false);
+ write!(out, " {};", self.export_name());
+ } else {
+ out.close_brace(true);
+ }
+
+ for constant in &self.associated_constants {
+ out.new_line();
+ constant.write(config, out, Some(self));
+ }
+
+ condition.write_after(config, out);
+ }
+}
diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs
new file mode 100644
index 0000000..5a31fb6
--- /dev/null
+++ b/src/bindgen/ir/ty.rs
@@ -0,0 +1,1017 @@
+/* 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::borrow::Cow;
+use std::io::Write;
+
+use syn::ext::IdentExt;
+
+use crate::bindgen::cdecl;
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{GenericArgument, GenericParams, GenericPath, Path};
+use crate::bindgen::library::Library;
+use crate::bindgen::monomorph::Monomorphs;
+use crate::bindgen::utilities::IterHelpers;
+use crate::bindgen::writer::{Source, SourceWriter};
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum PrimitiveType {
+ Void,
+ Bool,
+ Char,
+ SChar,
+ UChar,
+ Char32,
+ Float,
+ Double,
+ VaList,
+ PtrDiffT,
+ Integer {
+ zeroable: bool,
+ signed: bool,
+ kind: IntKind,
+ },
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum IntKind {
+ Short,
+ Int,
+ Long,
+ LongLong,
+ SizeT,
+ Size,
+ B8,
+ B16,
+ B32,
+ B64,
+}
+
+impl PrimitiveType {
+ pub fn maybe(path: &str) -> Option<PrimitiveType> {
+ Some(match path {
+ "c_void" => PrimitiveType::Void,
+ "c_char" => PrimitiveType::Char,
+ "c_schar" => PrimitiveType::SChar,
+ "c_uchar" => PrimitiveType::UChar,
+ "c_float" => PrimitiveType::Float,
+ "c_double" => PrimitiveType::Double,
+ "ptrdiff_t" => PrimitiveType::PtrDiffT,
+ "VaList" => PrimitiveType::VaList,
+ "bool" => PrimitiveType::Bool,
+ "char" => PrimitiveType::Char32,
+
+ "f32" => PrimitiveType::Float,
+ "f64" => PrimitiveType::Double,
+
+ _ => {
+ let (kind, signed) = match path {
+ "c_short" => (IntKind::Short, true),
+ "c_int" => (IntKind::Int, true),
+ "c_long" => (IntKind::Long, true),
+ "c_longlong" => (IntKind::LongLong, true),
+ "ssize_t" => (IntKind::SizeT, true),
+ "c_ushort" => (IntKind::Short, false),
+ "c_uint" => (IntKind::Int, false),
+ "c_ulong" => (IntKind::Long, false),
+ "c_ulonglong" => (IntKind::LongLong, false),
+ "size_t" => (IntKind::SizeT, false),
+ "RawFd" => (IntKind::Int, true),
+
+ "isize" | "intptr_t" => (IntKind::Size, true),
+ "usize" | "uintptr_t" => (IntKind::Size, false),
+
+ "u8" | "uint8_t" => (IntKind::B8, false),
+ "u16" | "uint16_t" => (IntKind::B16, false),
+ "u32" | "uint32_t" => (IntKind::B32, false),
+ "u64" | "uint64_t" => (IntKind::B64, false),
+ "i8" | "int8_t" => (IntKind::B8, true),
+ "i16" | "int16_t" => (IntKind::B16, true),
+ "i32" | "int32_t" => (IntKind::B32, true),
+ "i64" | "int64_t" => (IntKind::B64, true),
+ _ => return None,
+ };
+ PrimitiveType::Integer {
+ zeroable: true,
+ signed,
+ kind,
+ }
+ }
+ })
+ }
+
+ pub fn to_repr_rust(&self) -> &'static str {
+ match *self {
+ PrimitiveType::Bool => "bool",
+ PrimitiveType::Void => "c_void",
+ PrimitiveType::Char => "c_char",
+ PrimitiveType::SChar => "c_schar",
+ PrimitiveType::UChar => "c_uchar",
+ PrimitiveType::Char32 => "char",
+ PrimitiveType::Integer {
+ kind,
+ signed,
+ zeroable: _,
+ } => match kind {
+ IntKind::Short => {
+ if signed {
+ "c_short"
+ } else {
+ "c_ushort"
+ }
+ }
+ IntKind::Int => {
+ if signed {
+ "c_int"
+ } else {
+ "c_uint"
+ }
+ }
+ IntKind::Long => {
+ if signed {
+ "c_long"
+ } else {
+ "c_ulong"
+ }
+ }
+ IntKind::LongLong => {
+ if signed {
+ "c_longlong"
+ } else {
+ "c_ulonglong"
+ }
+ }
+ IntKind::SizeT => {
+ if signed {
+ "ssize_t"
+ } else {
+ "size_t"
+ }
+ }
+ IntKind::Size => {
+ if signed {
+ "isize"
+ } else {
+ "usize"
+ }
+ }
+ IntKind::B8 => {
+ if signed {
+ "i8"
+ } else {
+ "u8"
+ }
+ }
+ IntKind::B16 => {
+ if signed {
+ "i16"
+ } else {
+ "u16"
+ }
+ }
+ IntKind::B32 => {
+ if signed {
+ "i32"
+ } else {
+ "u32"
+ }
+ }
+ IntKind::B64 => {
+ if signed {
+ "i64"
+ } else {
+ "u64"
+ }
+ }
+ },
+ PrimitiveType::Float => "f32",
+ PrimitiveType::Double => "f64",
+ PrimitiveType::PtrDiffT => "ptrdiff_t",
+ PrimitiveType::VaList => "va_list",
+ }
+ }
+
+ pub fn to_repr_c(&self, config: &Config) -> &'static str {
+ match *self {
+ PrimitiveType::Void => "void",
+ PrimitiveType::Bool => "bool",
+ PrimitiveType::Char => "char",
+ PrimitiveType::SChar => "signed char",
+ PrimitiveType::UChar => "unsigned char",
+ // NOTE: It'd be nice to use a char32_t, but:
+ //
+ // * uchar.h is not present on mac (see #423).
+ //
+ // * char32_t isn't required to be compatible with Rust's char, as
+ // the C++ spec only requires it to be the same size as
+ // uint_least32_t, which is _not_ guaranteed to be 4-bytes.
+ //
+ PrimitiveType::Char32 => "uint32_t",
+ PrimitiveType::Integer {
+ kind,
+ signed,
+ zeroable: _,
+ } => match kind {
+ IntKind::Short => {
+ if signed {
+ "short"
+ } else {
+ "unsigned short"
+ }
+ }
+ IntKind::Int => {
+ if signed {
+ "int"
+ } else {
+ "unsigned int"
+ }
+ }
+ IntKind::Long => {
+ if signed {
+ "long"
+ } else {
+ "unsigned long"
+ }
+ }
+ IntKind::LongLong => {
+ if signed {
+ "long long"
+ } else {
+ "unsigned long long"
+ }
+ }
+ IntKind::SizeT => {
+ if signed {
+ "ssize_t"
+ } else {
+ "size_t"
+ }
+ }
+ IntKind::Size => {
+ if config.usize_is_size_t {
+ if signed {
+ "ptrdiff_t"
+ } else {
+ "size_t"
+ }
+ } else if signed {
+ "intptr_t"
+ } else {
+ "uintptr_t"
+ }
+ }
+ IntKind::B8 => {
+ if signed {
+ "int8_t"
+ } else {
+ "uint8_t"
+ }
+ }
+ IntKind::B16 => {
+ if signed {
+ "int16_t"
+ } else {
+ "uint16_t"
+ }
+ }
+ IntKind::B32 => {
+ if signed {
+ "int32_t"
+ } else {
+ "uint32_t"
+ }
+ }
+ IntKind::B64 => {
+ if signed {
+ "int64_t"
+ } else {
+ "uint64_t"
+ }
+ }
+ },
+ PrimitiveType::Float => "float",
+ PrimitiveType::Double => "double",
+ PrimitiveType::PtrDiffT => "ptrdiff_t",
+ PrimitiveType::VaList => "va_list",
+ }
+ }
+
+ fn can_cmp_order(&self) -> bool {
+ !matches!(*self, PrimitiveType::Bool)
+ }
+
+ fn can_cmp_eq(&self) -> bool {
+ true
+ }
+}
+
+/// Constant expressions.
+///
+/// Used for the `U` part of `[T; U]` and const generics. We support a very
+/// limited vocabulary here: only identifiers and literals.
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum ConstExpr {
+ Name(String),
+ Value(String),
+}
+
+impl ConstExpr {
+ pub fn as_str(&self) -> &str {
+ match *self {
+ ConstExpr::Name(ref string) | 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 load(expr: &syn::Expr) -> Result<Self, String> {
+ match *expr {
+ syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
+ let val = match *lit {
+ syn::Lit::Bool(syn::LitBool { value, .. }) => value.to_string(),
+ syn::Lit::Int(ref len) => len.base10_digits().to_string(),
+ syn::Lit::Byte(ref byte) => u8::to_string(&byte.value()),
+ syn::Lit::Char(ref ch) => u32::to_string(&ch.value().into()),
+ _ => return Err(format!("can't handle const expression {:?}", lit)),
+ };
+ 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()))
+ }
+ _ => 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);
+ for &(param, value) in mappings {
+ if path == *param {
+ 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());
+ }
+ GenericArgument::Const(ref expr) => {
+ return expr.clone();
+ }
+ _ => {
+ // unsupported argument type - really should be an error
+ }
+ }
+ }
+ }
+ }
+ ConstExpr::Value(_) => {}
+ }
+ self.clone()
+ }
+}
+
+impl Source for ConstExpr {
+ fn write<F: Write>(&self, _config: &Config, out: &mut SourceWriter<F>) {
+ write!(out, "{}", self.as_str());
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum Type {
+ Ptr {
+ ty: Box<Type>,
+ is_const: bool,
+ is_nullable: bool,
+ // FIXME: This is a bit of a hack, this is only to get us to codegen
+ // `T&` / `const T&`, but we should probably pass that down as an option
+ // to code generation or something.
+ is_ref: bool,
+ },
+ Path(GenericPath),
+ Primitive(PrimitiveType),
+ Array(Box<Type>, ConstExpr),
+ FuncPtr {
+ ret: Box<Type>,
+ args: Vec<(Option<String>, Type)>,
+ is_nullable: bool,
+ never_return: bool,
+ },
+}
+
+impl Type {
+ pub fn const_ref_to(ty: &Self) -> Self {
+ Type::Ptr {
+ ty: Box::new(ty.clone()),
+ is_const: true,
+ is_nullable: false,
+ is_ref: true,
+ }
+ }
+
+ pub fn load_from_output(output: &syn::ReturnType) -> Result<(Type, bool), String> {
+ let mut never_return = false;
+ let ty = match output {
+ syn::ReturnType::Default => Type::Primitive(PrimitiveType::Void),
+ syn::ReturnType::Type(_, ref ty) => {
+ if let syn::Type::Never(_) = ty.as_ref() {
+ never_return = true;
+ Type::Primitive(PrimitiveType::Void)
+ } else {
+ Type::load(ty)?.unwrap_or(Type::Primitive(PrimitiveType::Void))
+ }
+ }
+ };
+ Ok((ty, never_return))
+ }
+
+ pub fn load(ty: &syn::Type) -> Result<Option<Type>, String> {
+ let converted = match *ty {
+ syn::Type::Reference(ref reference) => {
+ let converted = Type::load(&reference.elem)?;
+
+ let converted = match converted {
+ Some(converted) => converted,
+ None => Type::Primitive(PrimitiveType::Void),
+ };
+
+ // TODO(emilio): we could make these use is_ref: true.
+ let is_const = reference.mutability.is_none();
+ Type::Ptr {
+ ty: Box::new(converted),
+ is_const,
+ is_nullable: false,
+ is_ref: false,
+ }
+ }
+ syn::Type::Ptr(ref pointer) => {
+ let converted = Type::load(&pointer.elem)?;
+
+ let converted = match converted {
+ Some(converted) => converted,
+ None => Type::Primitive(PrimitiveType::Void),
+ };
+
+ let is_const = pointer.mutability.is_none();
+ Type::Ptr {
+ ty: Box::new(converted),
+ is_const,
+ is_nullable: true,
+ is_ref: false,
+ }
+ }
+ syn::Type::Path(ref path) => {
+ let generic_path = GenericPath::load(&path.path)?;
+
+ if generic_path.name() == "PhantomData" || generic_path.name() == "PhantomPinned" {
+ return Ok(None);
+ }
+
+ if let Some(prim) = PrimitiveType::maybe(generic_path.name()) {
+ if !generic_path.generics().is_empty() {
+ return Err("Primitive has generics.".to_owned());
+ }
+ Type::Primitive(prim)
+ } else {
+ Type::Path(generic_path)
+ }
+ }
+ syn::Type::Array(syn::TypeArray {
+ ref elem, ref len, ..
+ }) => {
+ let converted = Type::load(elem)?;
+
+ let converted = match converted {
+ Some(converted) => converted,
+ None => return Err("Cannot have an array of zero sized types.".to_owned()),
+ };
+
+ let len = ConstExpr::load(len)?;
+ Type::Array(Box::new(converted), len)
+ }
+ syn::Type::BareFn(ref function) => {
+ let mut wildcard_counter = 0;
+ let args = function.inputs.iter().try_skip_map(|x| {
+ Type::load(&x.ty).map(|opt_ty| {
+ opt_ty.map(|ty| {
+ (
+ x.name.as_ref().map(|(ref ident, _)| {
+ if ident == "_" {
+ wildcard_counter += 1;
+ if wildcard_counter == 1 {
+ "_".to_owned()
+ } else {
+ format!("_{}", wildcard_counter - 1)
+ }
+ } else {
+ ident.unraw().to_string()
+ }
+ }),
+ ty,
+ )
+ })
+ })
+ })?;
+ let (ret, never_return) = Type::load_from_output(&function.output)?;
+ Type::FuncPtr {
+ ret: Box::new(ret),
+ args,
+ is_nullable: false,
+ never_return,
+ }
+ }
+ syn::Type::Tuple(ref tuple) => {
+ if tuple.elems.is_empty() {
+ return Ok(None);
+ }
+ return Err("Tuples are not supported types.".to_owned());
+ }
+ syn::Type::Verbatim(ref tokens) if tokens.to_string() == "..." => {
+ Type::Primitive(PrimitiveType::VaList)
+ }
+ _ => return Err(format!("Unsupported type: {:?}", ty)),
+ };
+
+ Ok(Some(converted))
+ }
+
+ pub fn is_ptr(&self) -> bool {
+ matches!(*self, Type::Ptr { .. } | Type::FuncPtr { .. })
+ }
+
+ pub fn is_primitive_or_ptr_primitive(&self) -> bool {
+ match *self {
+ Type::Primitive(..) => true,
+ Type::Ptr { ref ty, .. } => matches!(ty.as_ref(), Type::Primitive(..)),
+ _ => false,
+ }
+ }
+
+ pub fn make_zeroable(&self) -> Option<Self> {
+ let (kind, signed) = match *self {
+ Type::Primitive(PrimitiveType::Integer {
+ zeroable: false,
+ kind,
+ signed,
+ }) => (kind, signed),
+ _ => return None,
+ };
+
+ Some(Type::Primitive(PrimitiveType::Integer {
+ kind,
+ signed,
+ zeroable: true,
+ }))
+ }
+
+ pub fn make_nullable(&self) -> Option<Self> {
+ match *self {
+ Type::Ptr {
+ ref ty,
+ is_const,
+ is_ref,
+ is_nullable: false,
+ } => Some(Type::Ptr {
+ ty: ty.clone(),
+ is_const,
+ is_ref,
+ is_nullable: true,
+ }),
+ Type::FuncPtr {
+ ref ret,
+ ref args,
+ is_nullable: false,
+ never_return,
+ } => Some(Type::FuncPtr {
+ ret: ret.clone(),
+ args: args.clone(),
+ is_nullable: true,
+ never_return,
+ }),
+ _ => None,
+ }
+ }
+
+ fn nonzero_to_primitive(&self) -> Option<Self> {
+ let path = match *self {
+ Type::Path(ref p) => p,
+ _ => return None,
+ };
+
+ if !path.generics().is_empty() {
+ return None;
+ }
+
+ let name = path.name();
+ if !name.starts_with("NonZero") {
+ return None;
+ }
+
+ let (kind, signed) = match path.name() {
+ "NonZeroU8" => (IntKind::B8, false),
+ "NonZeroU16" => (IntKind::B16, false),
+ "NonZeroU32" => (IntKind::B32, false),
+ "NonZeroU64" => (IntKind::B64, false),
+ "NonZeroUSize" => (IntKind::Size, false),
+ "NonZeroI8" => (IntKind::B8, true),
+ "NonZeroI16" => (IntKind::B16, true),
+ "NonZeroI32" => (IntKind::B32, true),
+ "NonZeroI64" => (IntKind::B64, true),
+ "NonZeroISize" => (IntKind::Size, true),
+ _ => return None,
+ };
+
+ Some(Type::Primitive(PrimitiveType::Integer {
+ zeroable: false,
+ signed,
+ kind,
+ }))
+ }
+
+ fn simplified_type(&self, config: &Config) -> Option<Self> {
+ let path = match *self {
+ Type::Path(ref p) => p,
+ _ => return None,
+ };
+
+ if path.generics().is_empty() {
+ return self.nonzero_to_primitive();
+ }
+
+ if path.generics().len() != 1 {
+ return None;
+ }
+
+ let unsimplified_generic = match path.generics()[0] {
+ GenericArgument::Type(ref ty) => ty,
+ GenericArgument::Const(_) => return None,
+ };
+
+ let generic = match unsimplified_generic.simplified_type(config) {
+ Some(generic) => Cow::Owned(generic),
+ None => Cow::Borrowed(unsimplified_generic),
+ };
+ match path.name() {
+ "Option" => {
+ if let Some(nullable) = generic.make_nullable() {
+ return Some(nullable);
+ }
+ if let Some(zeroable) = generic.make_zeroable() {
+ return Some(zeroable);
+ }
+ None
+ }
+ "NonNull" => Some(Type::Ptr {
+ ty: Box::new(generic.into_owned()),
+ is_const: false,
+ is_nullable: false,
+ is_ref: false,
+ }),
+ "Box" if config.language != Language::Cxx => Some(Type::Ptr {
+ ty: Box::new(generic.into_owned()),
+ is_const: false,
+ is_nullable: false,
+ is_ref: false,
+ }),
+ "Cell" => Some(generic.into_owned()),
+ "ManuallyDrop" | "MaybeUninit" | "Pin" if config.language != Language::Cxx => {
+ Some(generic.into_owned())
+ }
+ _ => None,
+ }
+ }
+
+ pub fn simplify_standard_types(&mut self, config: &Config) {
+ self.visit_types(|ty| ty.simplify_standard_types(config));
+ if let Some(ty) = self.simplified_type(config) {
+ *self = ty;
+ }
+ }
+
+ pub fn replace_self_with(&mut self, self_ty: &Path) {
+ if let Type::Path(ref mut generic_path) = *self {
+ generic_path.replace_self_with(self_ty);
+ }
+ self.visit_types(|ty| ty.replace_self_with(self_ty))
+ }
+
+ fn visit_types(&mut self, mut visitor: impl FnMut(&mut Type)) {
+ match *self {
+ Type::Array(ref mut ty, ..) | Type::Ptr { ref mut ty, .. } => visitor(ty),
+ Type::Path(ref mut path) => {
+ for generic in path.generics_mut() {
+ match *generic {
+ GenericArgument::Type(ref mut ty) => visitor(ty),
+ GenericArgument::Const(_) => {}
+ }
+ }
+ }
+ Type::Primitive(..) => {}
+ Type::FuncPtr {
+ ref mut ret,
+ ref mut args,
+ ..
+ } => {
+ visitor(ret);
+ for arg in args {
+ visitor(&mut arg.1)
+ }
+ }
+ }
+ }
+
+ pub fn get_root_path(&self) -> Option<Path> {
+ let mut current = self;
+ loop {
+ match *current {
+ Type::Ptr { ref ty, .. } => current = ty,
+ Type::Path(ref generic) => {
+ return Some(generic.path().clone());
+ }
+ Type::Primitive(..) => {
+ return None;
+ }
+ Type::Array(..) => {
+ return None;
+ }
+ Type::FuncPtr { .. } => {
+ return None;
+ }
+ };
+ }
+ }
+
+ pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> Type {
+ match *self {
+ Type::Ptr {
+ ref ty,
+ is_const,
+ is_nullable,
+ is_ref,
+ } => Type::Ptr {
+ ty: Box::new(ty.specialize(mappings)),
+ is_const,
+ is_nullable,
+ is_ref,
+ },
+ Type::Path(ref generic_path) => {
+ for &(param, value) in mappings {
+ if generic_path.path() == param {
+ if let GenericArgument::Type(ref ty) = *value {
+ return ty.clone();
+ }
+ }
+ }
+
+ let specialized = GenericPath::new(
+ generic_path.path().clone(),
+ generic_path
+ .generics()
+ .iter()
+ .map(|x| x.specialize(mappings))
+ .collect(),
+ );
+ Type::Path(specialized)
+ }
+ Type::Primitive(ref primitive) => Type::Primitive(primitive.clone()),
+ Type::Array(ref ty, ref constant) => Type::Array(
+ Box::new(ty.specialize(mappings)),
+ constant.specialize(mappings),
+ ),
+ Type::FuncPtr {
+ ref ret,
+ ref args,
+ is_nullable,
+ never_return,
+ } => Type::FuncPtr {
+ ret: Box::new(ret.specialize(mappings)),
+ args: args
+ .iter()
+ .cloned()
+ .map(|(name, ty)| (name, ty.specialize(mappings)))
+ .collect(),
+ is_nullable,
+ never_return,
+ },
+ }
+ }
+
+ pub fn add_dependencies_ignoring_generics(
+ &self,
+ generic_params: &GenericParams,
+ library: &Library,
+ out: &mut Dependencies,
+ ) {
+ match *self {
+ Type::Ptr { ref ty, .. } => {
+ ty.add_dependencies_ignoring_generics(generic_params, library, out);
+ }
+ Type::Path(ref generic) => {
+ for generic_value in generic.generics() {
+ if let GenericArgument::Type(ref ty) = *generic_value {
+ ty.add_dependencies_ignoring_generics(generic_params, library, out);
+ }
+ }
+ let path = generic.path();
+ if !generic_params.iter().any(|param| param.name() == path) {
+ if let Some(items) = library.get_items(path) {
+ if !out.items.contains(path) {
+ out.items.insert(path.clone());
+
+ for item in &items {
+ item.deref().add_dependencies(library, out);
+ }
+ for item in items {
+ out.order.push(item);
+ }
+ }
+ } else {
+ warn!(
+ "Can't find {}. This usually means that this type was incompatible or \
+ not found.",
+ path
+ );
+ }
+ }
+ }
+ Type::Primitive(_) => {}
+ Type::Array(ref ty, _) => {
+ ty.add_dependencies_ignoring_generics(generic_params, library, out);
+ }
+ Type::FuncPtr {
+ ref ret, ref args, ..
+ } => {
+ ret.add_dependencies_ignoring_generics(generic_params, library, out);
+ for (_, ref arg) in args {
+ arg.add_dependencies_ignoring_generics(generic_params, library, out);
+ }
+ }
+ }
+ }
+
+ pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ self.add_dependencies_ignoring_generics(&GenericParams::default(), library, out)
+ }
+
+ pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
+ match *self {
+ Type::Ptr { ref ty, .. } => {
+ ty.add_monomorphs(library, out);
+ }
+ Type::Path(ref generic) => {
+ if generic.generics().is_empty() || out.contains(generic) {
+ return;
+ }
+ let path = generic.path();
+ if let Some(items) = library.get_items(path) {
+ for item in items {
+ item.deref()
+ .instantiate_monomorph(generic.generics(), library, out);
+ }
+ }
+ }
+ Type::Primitive(_) => {}
+ Type::Array(ref ty, _) => {
+ ty.add_monomorphs(library, out);
+ }
+ Type::FuncPtr {
+ ref ret, ref args, ..
+ } => {
+ ret.add_monomorphs(library, out);
+ for (_, ref arg) in args {
+ arg.add_monomorphs(library, out);
+ }
+ }
+ }
+ }
+
+ pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) {
+ match *self {
+ Type::Ptr { ref mut ty, .. } => {
+ ty.rename_for_config(config, generic_params);
+ }
+ Type::Path(ref mut ty) => {
+ ty.rename_for_config(config, generic_params);
+ }
+ Type::Primitive(_) => {}
+ Type::Array(ref mut ty, ref mut len) => {
+ ty.rename_for_config(config, generic_params);
+ len.rename_for_config(config);
+ }
+ Type::FuncPtr {
+ ref mut ret,
+ ref mut args,
+ ..
+ } => {
+ ret.rename_for_config(config, generic_params);
+ for (_, arg) in args {
+ arg.rename_for_config(config, generic_params);
+ }
+ }
+ }
+ }
+
+ pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ match *self {
+ Type::Ptr { ref mut ty, .. } => {
+ ty.resolve_declaration_types(resolver);
+ }
+ Type::Path(ref mut generic_path) => {
+ generic_path.resolve_declaration_types(resolver);
+ }
+ Type::Primitive(_) => {}
+ Type::Array(ref mut ty, _) => {
+ ty.resolve_declaration_types(resolver);
+ }
+ Type::FuncPtr {
+ ref mut ret,
+ ref mut args,
+ ..
+ } => {
+ ret.resolve_declaration_types(resolver);
+ for (_, ref mut arg) in args {
+ arg.resolve_declaration_types(resolver);
+ }
+ }
+ }
+ }
+
+ pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
+ match *self {
+ Type::Ptr { ref mut ty, .. } => {
+ ty.mangle_paths(monomorphs);
+ }
+ Type::Path(ref mut generic_path) => {
+ if generic_path.generics().is_empty() {
+ return;
+ }
+
+ if let Some(mangled_path) = monomorphs.mangle_path(generic_path) {
+ *generic_path = GenericPath::new(mangled_path.clone(), vec![]);
+ } else {
+ warn!(
+ "Cannot find a mangling for generic path {:?}. This usually means that a \
+ type referenced by this generic was incompatible or not found.",
+ generic_path
+ );
+ }
+ }
+ Type::Primitive(_) => {}
+ Type::Array(ref mut ty, _) => {
+ ty.mangle_paths(monomorphs);
+ }
+ Type::FuncPtr {
+ ref mut ret,
+ ref mut args,
+ ..
+ } => {
+ ret.mangle_paths(monomorphs);
+ for (_, ref mut arg) in args {
+ arg.mangle_paths(monomorphs);
+ }
+ }
+ }
+ }
+
+ pub fn can_cmp_order(&self) -> bool {
+ match *self {
+ // FIXME: Shouldn't this look at ty.can_cmp_order() as well?
+ Type::Ptr { is_ref, .. } => !is_ref,
+ Type::Path(..) => true,
+ Type::Primitive(ref p) => p.can_cmp_order(),
+ Type::Array(..) => false,
+ Type::FuncPtr { .. } => false,
+ }
+ }
+
+ pub fn can_cmp_eq(&self) -> bool {
+ match *self {
+ Type::Ptr { ref ty, is_ref, .. } => !is_ref || ty.can_cmp_eq(),
+ Type::Path(..) => true,
+ Type::Primitive(ref p) => p.can_cmp_eq(),
+ Type::Array(..) => false,
+ Type::FuncPtr { .. } => true,
+ }
+ }
+}
+
+impl Source for String {
+ fn write<F: Write>(&self, _config: &Config, out: &mut SourceWriter<F>) {
+ write!(out, "{}", self);
+ }
+}
+
+impl Source for Type {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ cdecl::write_type(out, self, config);
+ }
+}
diff --git a/src/bindgen/ir/typedef.rs b/src/bindgen/ir/typedef.rs
new file mode 100644
index 0000000..626732e
--- /dev/null
+++ b/src/bindgen/ir/typedef.rs
@@ -0,0 +1,208 @@
+/* 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::HashMap;
+use std::io::Write;
+
+use syn::ext::IdentExt;
+
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{
+ AnnotationSet, Cfg, ConditionWrite, Documentation, Field, GenericArgument, GenericParams, Item,
+ ItemContainer, Path, ToCondition, Type,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::mangle;
+use crate::bindgen::monomorph::Monomorphs;
+use crate::bindgen::writer::{Source, SourceWriter};
+
+/// A type alias that is represented as a C typedef
+#[derive(Debug, Clone)]
+pub struct Typedef {
+ pub path: Path,
+ pub export_name: String,
+ pub generic_params: GenericParams,
+ pub aliased: Type,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+}
+
+impl Typedef {
+ pub fn load(item: &syn::ItemType, mod_cfg: Option<&Cfg>) -> Result<Typedef, String> {
+ if let Some(x) = Type::load(&item.ty)? {
+ let path = Path::new(item.ident.unraw().to_string());
+ Ok(Typedef::new(
+ path,
+ GenericParams::load(&item.generics)?,
+ x,
+ Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
+ AnnotationSet::load(&item.attrs)?,
+ Documentation::load(&item.attrs),
+ ))
+ } else {
+ Err("Cannot have a typedef of a zero sized type.".to_owned())
+ }
+ }
+
+ pub fn new(
+ path: Path,
+ generic_params: GenericParams,
+ aliased: Type,
+ cfg: Option<Cfg>,
+ annotations: AnnotationSet,
+ documentation: Documentation,
+ ) -> Self {
+ let export_name = path.name().to_owned();
+ Self {
+ path,
+ export_name,
+ generic_params,
+ aliased,
+ cfg,
+ annotations,
+ documentation,
+ }
+ }
+
+ pub fn simplify_standard_types(&mut self, config: &Config) {
+ self.aliased.simplify_standard_types(config);
+ }
+
+ pub fn transfer_annotations(&mut self, out: &mut HashMap<Path, AnnotationSet>) {
+ if self.annotations.is_empty() {
+ return;
+ }
+
+ if let Some(alias_path) = self.aliased.get_root_path() {
+ if out.contains_key(&alias_path) {
+ warn!(
+ "Multiple typedef's with annotations for {}. Ignoring annotations from {}.",
+ alias_path, self.path
+ );
+ return;
+ }
+
+ out.insert(alias_path, self.annotations.clone());
+ self.annotations = AnnotationSet::new();
+ }
+ }
+
+ pub fn is_generic(&self) -> bool {
+ self.generic_params.len() > 0
+ }
+
+ pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
+ // Generic structs can instantiate monomorphs only once they've been
+ // instantiated. See `instantiate_monomorph` for more details.
+ if self.is_generic() {
+ return;
+ }
+
+ self.aliased.add_monomorphs(library, out);
+ }
+
+ pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
+ self.aliased.mangle_paths(monomorphs);
+ }
+}
+
+impl Item for Typedef {
+ fn path(&self) -> &Path {
+ &self.path
+ }
+
+ fn export_name(&self) -> &str {
+ &self.export_name
+ }
+
+ fn cfg(&self) -> Option<&Cfg> {
+ self.cfg.as_ref()
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ &self.annotations
+ }
+
+ fn annotations_mut(&mut self) -> &mut AnnotationSet {
+ &mut self.annotations
+ }
+
+ fn container(&self) -> ItemContainer {
+ ItemContainer::Typedef(self.clone())
+ }
+
+ fn rename_for_config(&mut self, config: &Config) {
+ config.export.rename(&mut self.export_name);
+ self.aliased.rename_for_config(config, &self.generic_params);
+ }
+
+ fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
+ resolver.add_none(&self.path);
+ }
+
+ 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);
+ }
+
+ fn instantiate_monomorph(
+ &self,
+ generic_values: &[GenericArgument],
+ library: &Library,
+ out: &mut Monomorphs,
+ ) {
+ let mappings = self.generic_params.call(self.path.name(), generic_values);
+
+ let mangled_path = mangle::mangle_path(
+ &self.path,
+ generic_values,
+ &library.get_config().export.mangle,
+ );
+
+ let monomorph = Typedef::new(
+ mangled_path,
+ GenericParams::default(),
+ self.aliased.specialize(&mappings),
+ self.cfg.clone(),
+ self.annotations.clone(),
+ self.documentation.clone(),
+ );
+
+ out.insert_typedef(library, self, monomorph, generic_values.to_owned());
+ }
+}
+
+impl Source for Typedef {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ let condition = self.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ self.documentation.write(config, out);
+
+ self.generic_params.write(config, out);
+
+ match config.language {
+ Language::Cxx => {
+ write!(out, "using {} = ", self.export_name());
+ self.aliased.write(config, out);
+ }
+ Language::C | Language::Cython => {
+ write!(out, "{} ", config.language.typedef());
+ Field::from_name_and_type(self.export_name().to_owned(), self.aliased.clone())
+ .write(config, out);
+ }
+ }
+
+ out.write(";");
+
+ condition.write_after(config, out);
+ }
+}
diff --git a/src/bindgen/ir/union.rs b/src/bindgen/ir/union.rs
new file mode 100644
index 0000000..07bd16c
--- /dev/null
+++ b/src/bindgen/ir/union.rs
@@ -0,0 +1,336 @@
+/* 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::io::Write;
+
+use syn::ext::IdentExt;
+
+use crate::bindgen::config::{Config, Language, LayoutConfig};
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{
+ AnnotationSet, Cfg, ConditionWrite, Documentation, Field, GenericArgument, GenericParams, Item,
+ ItemContainer, Path, Repr, ReprAlign, ReprStyle, ToCondition,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::mangle;
+use crate::bindgen::monomorph::Monomorphs;
+use crate::bindgen::rename::{IdentifierType, RenameRule};
+use crate::bindgen::utilities::IterHelpers;
+use crate::bindgen::writer::{ListType, Source, SourceWriter};
+
+#[derive(Debug, Clone)]
+pub struct Union {
+ pub path: Path,
+ pub export_name: String,
+ pub generic_params: GenericParams,
+ pub fields: Vec<Field>,
+ pub tuple_union: bool,
+ pub alignment: Option<ReprAlign>,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+}
+
+impl Union {
+ pub fn load(
+ layout_config: &LayoutConfig,
+ item: &syn::ItemUnion,
+ mod_cfg: Option<&Cfg>,
+ ) -> Result<Union, String> {
+ let repr = Repr::load(&item.attrs)?;
+ if repr.style != ReprStyle::C {
+ return Err("Union is not marked #[repr(C)].".to_owned());
+ }
+
+ // Ensure we can safely represent the union given the configuration.
+ if let Some(align) = repr.align {
+ layout_config.ensure_safe_to_represent(&align)?;
+ }
+
+ let path = Path::new(item.ident.unraw().to_string());
+
+ let (fields, tuple_union) = {
+ let out = item
+ .fields
+ .named
+ .iter()
+ .try_skip_map(|field| Field::load(field, &path))?;
+ (out, false)
+ };
+
+ Ok(Union::new(
+ path,
+ GenericParams::load(&item.generics)?,
+ fields,
+ repr.align,
+ tuple_union,
+ Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
+ AnnotationSet::load(&item.attrs)?,
+ Documentation::load(&item.attrs),
+ ))
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ path: Path,
+ generic_params: GenericParams,
+ fields: Vec<Field>,
+ alignment: Option<ReprAlign>,
+ tuple_union: bool,
+ cfg: Option<Cfg>,
+ annotations: AnnotationSet,
+ documentation: Documentation,
+ ) -> Self {
+ let export_name = path.name().to_owned();
+ Self {
+ path,
+ export_name,
+ generic_params,
+ fields,
+ tuple_union,
+ alignment,
+ cfg,
+ annotations,
+ documentation,
+ }
+ }
+
+ pub fn simplify_standard_types(&mut self, config: &Config) {
+ for field in &mut self.fields {
+ field.ty.simplify_standard_types(config);
+ }
+ }
+
+ pub fn is_generic(&self) -> bool {
+ self.generic_params.len() > 0
+ }
+
+ pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
+ // Generic unions can instantiate monomorphs only once they've been
+ // instantiated. See `instantiate_monomorph` for more details.
+ if self.is_generic() {
+ return;
+ }
+
+ for field in &self.fields {
+ field.ty.add_monomorphs(library, out);
+ }
+ }
+
+ pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
+ for field in &mut self.fields {
+ field.ty.mangle_paths(monomorphs);
+ }
+ }
+}
+
+impl Item for Union {
+ fn path(&self) -> &Path {
+ &self.path
+ }
+
+ fn export_name(&self) -> &str {
+ &self.export_name
+ }
+
+ fn cfg(&self) -> Option<&Cfg> {
+ self.cfg.as_ref()
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ &self.annotations
+ }
+
+ fn annotations_mut(&mut self) -> &mut AnnotationSet {
+ &mut self.annotations
+ }
+
+ fn container(&self) -> ItemContainer {
+ ItemContainer::Union(self.clone())
+ }
+
+ fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
+ resolver.add_union(&self.path);
+ }
+
+ fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ for field in &mut self.fields {
+ field.ty.resolve_declaration_types(resolver);
+ }
+ }
+
+ fn rename_for_config(&mut self, config: &Config) {
+ config.export.rename(&mut self.export_name);
+ for field in &mut self.fields {
+ field.ty.rename_for_config(config, &self.generic_params);
+ }
+
+ let rules = self
+ .annotations
+ .parse_atom::<RenameRule>("rename-all")
+ .unwrap_or(config.structure.rename_fields);
+
+ if let Some(o) = self.annotations.list("field-names") {
+ let mut overriden_fields = Vec::new();
+
+ for (i, field) in self.fields.iter().enumerate() {
+ if i >= o.len() {
+ overriden_fields.push(field.clone());
+ } else {
+ overriden_fields.push(Field {
+ name: o[i].clone(),
+ ty: field.ty.clone(),
+ cfg: field.cfg.clone(),
+ annotations: field.annotations.clone(),
+ documentation: field.documentation.clone(),
+ });
+ }
+ }
+
+ self.fields = overriden_fields;
+ } else if let Some(r) = rules.not_none() {
+ self.fields = self
+ .fields
+ .iter()
+ .map(|field| Field {
+ name: r
+ .apply(&field.name, IdentifierType::StructMember)
+ .into_owned(),
+ ty: field.ty.clone(),
+ cfg: field.cfg.clone(),
+ annotations: field.annotations.clone(),
+ documentation: field.documentation.clone(),
+ })
+ .collect();
+ } else if self.tuple_union {
+ // If we don't have any rules for a tuple union, prefix them with
+ // an underscore so it still compiles
+ for field in &mut self.fields {
+ field.name.insert(0, '_');
+ }
+ }
+ }
+
+ fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ for field in &self.fields {
+ field
+ .ty
+ .add_dependencies_ignoring_generics(&self.generic_params, library, out);
+ }
+ }
+
+ fn instantiate_monomorph(
+ &self,
+ generic_values: &[GenericArgument],
+ library: &Library,
+ out: &mut Monomorphs,
+ ) {
+ let mappings = self.generic_params.call(self.path.name(), generic_values);
+
+ let mangled_path = mangle::mangle_path(
+ &self.path,
+ generic_values,
+ &library.get_config().export.mangle,
+ );
+
+ let monomorph = Union::new(
+ mangled_path,
+ GenericParams::default(),
+ self.fields
+ .iter()
+ .map(|field| Field {
+ name: field.name.clone(),
+ ty: field.ty.specialize(&mappings),
+ cfg: field.cfg.clone(),
+ annotations: field.annotations.clone(),
+ documentation: field.documentation.clone(),
+ })
+ .collect(),
+ self.alignment,
+ self.tuple_union,
+ self.cfg.clone(),
+ self.annotations.clone(),
+ self.documentation.clone(),
+ );
+
+ out.insert_union(library, self, monomorph, generic_values.to_owned());
+ }
+}
+
+impl Source for Union {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ let condition = self.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ self.documentation.write(config, out);
+
+ self.generic_params.write(config, out);
+
+ // 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 {
+ match config.language {
+ Language::C if config.style.generate_typedef() => out.write("typedef "),
+ Language::C | Language::Cxx => {}
+ Language::Cython => out.write(config.style.cython_def()),
+ }
+
+ out.write("union");
+
+ // Cython supports `packed` on structs (see comments there), but not on unions.
+ if config.language != Language::Cython {
+ if let Some(align) = self.alignment {
+ match align {
+ ReprAlign::Packed => {
+ if let Some(ref anno) = config.layout.packed {
+ write!(out, " {}", anno);
+ }
+ }
+ ReprAlign::Align(n) => {
+ if let Some(ref anno) = config.layout.aligned_n {
+ write!(out, " {}({})", anno, n);
+ }
+ }
+ }
+ }
+ }
+
+ if config.language != Language::C || config.style.generate_tag() {
+ write!(out, " {}", self.export_name);
+ }
+
+ out.open_brace();
+
+ // Emit the pre_body section, if relevant
+ if let Some(body) = config.export.pre_body(&self.path) {
+ out.write_raw_block(body);
+ out.new_line();
+ }
+
+ out.write_vertical_source_list(&self.fields, ListType::Cap(";"));
+ if config.language == Language::Cython && self.fields.is_empty() {
+ out.write("pass");
+ }
+
+ // Emit the post_body section, if relevant
+ if let Some(body) = config.export.post_body(&self.path) {
+ out.new_line();
+ out.write_raw_block(body);
+ }
+
+ if config.language == Language::C && config.style.generate_typedef() {
+ out.close_brace(false);
+ write!(out, " {};", self.export_name);
+ } else {
+ out.close_brace(true);
+ }
+
+ condition.write_after(config, out);
+ }
+}
diff --git a/src/bindgen/library.rs b/src/bindgen/library.rs
new file mode 100644
index 0000000..cb4cfbd
--- /dev/null
+++ b/src/bindgen/library.rs
@@ -0,0 +1,446 @@
+/* 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::HashMap;
+use std::path::PathBuf;
+
+use crate::bindgen::bindings::Bindings;
+use crate::bindgen::config::{Config, Language, SortKey};
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::error::Error;
+use crate::bindgen::ir::{Constant, Enum, Function, Item, ItemContainer, ItemMap};
+use crate::bindgen::ir::{OpaqueItem, Path, Static, Struct, Typedef, Union};
+use crate::bindgen::monomorph::Monomorphs;
+use crate::bindgen::ItemType;
+
+#[derive(Debug, Clone)]
+pub struct Library {
+ config: Config,
+ constants: ItemMap<Constant>,
+ globals: ItemMap<Static>,
+ enums: ItemMap<Enum>,
+ structs: ItemMap<Struct>,
+ unions: ItemMap<Union>,
+ opaque_items: ItemMap<OpaqueItem>,
+ typedefs: ItemMap<Typedef>,
+ functions: Vec<Function>,
+ source_files: Vec<PathBuf>,
+}
+
+impl Library {
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ config: Config,
+ constants: ItemMap<Constant>,
+ globals: ItemMap<Static>,
+ enums: ItemMap<Enum>,
+ structs: ItemMap<Struct>,
+ unions: ItemMap<Union>,
+ opaque_items: ItemMap<OpaqueItem>,
+ typedefs: ItemMap<Typedef>,
+ functions: Vec<Function>,
+ source_files: Vec<PathBuf>,
+ ) -> Library {
+ Library {
+ config,
+ constants,
+ globals,
+ enums,
+ structs,
+ unions,
+ opaque_items,
+ typedefs,
+ functions,
+ source_files,
+ }
+ }
+
+ pub fn generate(mut self) -> Result<Bindings, Error> {
+ self.transfer_annotations();
+ self.simplify_standard_types();
+
+ match self.config.function.sort_by.unwrap_or(self.config.sort_by) {
+ SortKey::Name => self.functions.sort_by(|x, y| x.path.cmp(&y.path)),
+ SortKey::None => { /* keep input order */ }
+ }
+
+ if self.config.language != Language::Cxx {
+ self.instantiate_monomorphs();
+ }
+ self.remove_excluded();
+ if self.config.language == Language::C {
+ self.resolve_declaration_types();
+ }
+
+ self.rename_items();
+
+ let mut dependencies = Dependencies::new();
+
+ for function in &self.functions {
+ function.add_dependencies(&self, &mut dependencies);
+ }
+ self.globals.for_all_items(|global| {
+ global.add_dependencies(&self, &mut dependencies);
+ });
+ self.constants.for_all_items(|constant| {
+ constant.add_dependencies(&self, &mut dependencies);
+ });
+ for name in &self.config.export.include {
+ let path = Path::new(name.clone());
+ if let Some(items) = self.get_items(&path) {
+ if dependencies.items.insert(path) {
+ for item in &items {
+ item.deref().add_dependencies(&self, &mut dependencies);
+ }
+ for item in items {
+ dependencies.order.push(item);
+ }
+ }
+ }
+ }
+
+ dependencies.sort();
+
+ let items = dependencies.order;
+ let constants = if self.config.export.should_generate(ItemType::Constants) {
+ let mut constants = self.constants.to_vec();
+ match self.config.constant.sort_by.unwrap_or(self.config.sort_by) {
+ SortKey::Name => constants.sort_by(|x, y| x.path.cmp(&y.path)),
+ SortKey::None => { /* keep input order */ }
+ }
+ constants
+ } else {
+ vec![]
+ };
+
+ let globals = if self.config.export.should_generate(ItemType::Globals) {
+ let mut globals = self.globals.to_vec();
+ match self.config.constant.sort_by.unwrap_or(self.config.sort_by) {
+ SortKey::Name => globals.sort_by(|x, y| x.path.cmp(&y.path)),
+ SortKey::None => { /* keep input order */ }
+ }
+ globals
+ } else {
+ vec![]
+ };
+ let functions = if self.config.export.should_generate(ItemType::Functions) {
+ self.functions
+ } else {
+ vec![]
+ };
+
+ Ok(Bindings::new(
+ self.config,
+ self.structs,
+ self.typedefs,
+ constants,
+ globals,
+ items,
+ functions,
+ self.source_files,
+ false,
+ ))
+ }
+
+ pub fn get_items(&self, p: &Path) -> Option<Vec<ItemContainer>> {
+ macro_rules! find {
+ ($field:ident, $kind:ident) => {
+ if self.config.export.should_generate(ItemType::$kind) {
+ if let Some(x) = self.$field.get_items(p) {
+ return Some(x);
+ }
+ }
+ };
+ }
+
+ find!(enums, Enums);
+ find!(structs, Structs);
+ find!(unions, Unions);
+ find!(opaque_items, OpaqueItems);
+ find!(typedefs, Typedefs);
+
+ None
+ }
+
+ pub fn get_config(&self) -> &Config {
+ &self.config
+ }
+
+ fn remove_excluded(&mut self) {
+ let config = &self.config;
+ // FIXME: interpret `config.export.exclude` as `Path`s.
+ self.functions
+ .retain(|x| !config.export.exclude.iter().any(|y| y == x.path().name()));
+ self.enums
+ .filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
+ self.structs
+ .filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
+ self.unions
+ .filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
+ self.opaque_items
+ .filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
+ self.typedefs
+ .filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
+ self.globals
+ .filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
+ self.constants
+ .filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
+ }
+
+ fn transfer_annotations(&mut self) {
+ let mut annotations = HashMap::new();
+
+ self.typedefs.for_all_items_mut(|x| {
+ x.transfer_annotations(&mut annotations);
+ });
+
+ for (alias_path, annotations) in annotations {
+ // TODO
+ let mut transferred = false;
+
+ self.enums.for_items_mut(&alias_path, |x| {
+ if x.annotations().is_empty() {
+ *x.annotations_mut() = annotations.clone();
+ transferred = true;
+ } else {
+ warn!(
+ "Can't transfer annotations from typedef to alias ({}) \
+ that already has annotations.",
+ alias_path
+ );
+ }
+ });
+ if transferred {
+ continue;
+ }
+ self.structs.for_items_mut(&alias_path, |x| {
+ if x.annotations().is_empty() {
+ *x.annotations_mut() = annotations.clone();
+ transferred = true;
+ } else {
+ warn!(
+ "Can't transfer annotations from typedef to alias ({}) \
+ that already has annotations.",
+ alias_path
+ );
+ }
+ });
+ if transferred {
+ continue;
+ }
+ self.unions.for_items_mut(&alias_path, |x| {
+ if x.annotations().is_empty() {
+ *x.annotations_mut() = annotations.clone();
+ transferred = true;
+ } else {
+ warn!(
+ "Can't transfer annotations from typedef to alias ({}) \
+ that already has annotations.",
+ alias_path
+ );
+ }
+ });
+ if transferred {
+ continue;
+ }
+ self.opaque_items.for_items_mut(&alias_path, |x| {
+ if x.annotations().is_empty() {
+ *x.annotations_mut() = annotations.clone();
+ transferred = true;
+ } else {
+ warn!(
+ "Can't transfer annotations from typedef to alias ({}) \
+ that already has annotations.",
+ alias_path
+ );
+ }
+ });
+ if transferred {
+ continue;
+ }
+ self.typedefs.for_items_mut(&alias_path, |x| {
+ if x.annotations().is_empty() {
+ *x.annotations_mut() = annotations.clone();
+ transferred = true;
+ } else {
+ warn!(
+ "Can't transfer annotations from typedef to alias ({}) \
+ that already has annotations.",
+ alias_path
+ );
+ }
+ });
+ if transferred {
+ continue;
+ }
+ }
+ }
+
+ fn rename_items(&mut self) {
+ let config = &self.config;
+
+ self.globals
+ .for_all_items_mut(|x| x.rename_for_config(config));
+ self.globals.rebuild();
+
+ self.constants
+ .for_all_items_mut(|x| x.rename_for_config(config));
+ self.constants.rebuild();
+
+ self.structs
+ .for_all_items_mut(|x| x.rename_for_config(config));
+ self.structs.rebuild();
+
+ self.unions
+ .for_all_items_mut(|x| x.rename_for_config(config));
+ self.unions.rebuild();
+
+ self.enums
+ .for_all_items_mut(|x| x.rename_for_config(config));
+ self.enums.rebuild();
+
+ self.opaque_items
+ .for_all_items_mut(|x| x.rename_for_config(config));
+ self.opaque_items.rebuild();
+
+ self.typedefs
+ .for_all_items_mut(|x| x.rename_for_config(config));
+ self.typedefs.rebuild();
+
+ for item in &mut self.functions {
+ item.rename_for_config(&self.config);
+ }
+ }
+
+ fn resolve_declaration_types(&mut self) {
+ if !self.config.style.generate_tag() {
+ return;
+ }
+
+ let mut resolver = DeclarationTypeResolver::default();
+
+ self.structs.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.typedefs.for_all_items(|x| {
+ x.collect_declaration_types(&mut resolver);
+ });
+
+ // NOTE: Intentionally last, so that in case there's an opaque type
+ // which is conflicting with a non-opaque one, the later wins.
+ self.opaque_items.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_standard_types(&mut self) {
+ let config = &self.config;
+
+ self.structs.for_all_items_mut(|x| {
+ x.simplify_standard_types(config);
+ });
+ self.enums.for_all_items_mut(|x| {
+ x.simplify_standard_types(config);
+ });
+ self.unions.for_all_items_mut(|x| {
+ x.simplify_standard_types(config);
+ });
+ self.globals.for_all_items_mut(|x| {
+ x.simplify_standard_types(config);
+ });
+ self.typedefs.for_all_items_mut(|x| {
+ x.simplify_standard_types(config);
+ });
+ for x in &mut self.functions {
+ x.simplify_standard_types(config);
+ }
+ }
+
+ fn instantiate_monomorphs(&mut self) {
+ // Collect a list of monomorphs
+ let mut monomorphs = Monomorphs::default();
+
+ self.structs.for_all_items(|x| {
+ x.add_monomorphs(self, &mut monomorphs);
+ });
+ self.unions.for_all_items(|x| {
+ x.add_monomorphs(self, &mut monomorphs);
+ });
+ self.enums.for_all_items(|x| {
+ x.add_monomorphs(self, &mut monomorphs);
+ });
+ self.typedefs.for_all_items(|x| {
+ x.add_monomorphs(self, &mut monomorphs);
+ });
+ for x in &self.functions {
+ x.add_monomorphs(self, &mut monomorphs);
+ }
+
+ // Insert the monomorphs into self
+ for monomorph in monomorphs.drain_structs() {
+ self.structs.try_insert(monomorph);
+ }
+ for monomorph in monomorphs.drain_unions() {
+ self.unions.try_insert(monomorph);
+ }
+ for monomorph in monomorphs.drain_opaques() {
+ self.opaque_items.try_insert(monomorph);
+ }
+ for monomorph in monomorphs.drain_typedefs() {
+ self.typedefs.try_insert(monomorph);
+ }
+ for monomorph in monomorphs.drain_enums() {
+ self.enums.try_insert(monomorph);
+ }
+
+ // Remove structs and opaque items that are generic
+ self.opaque_items.filter(|x| x.generic_params.len() > 0);
+ self.structs.filter(|x| x.generic_params.len() > 0);
+ self.unions.filter(|x| x.generic_params.len() > 0);
+ self.enums.filter(|x| x.generic_params.len() > 0);
+ self.typedefs.filter(|x| x.generic_params.len() > 0);
+
+ // Mangle the paths that remain
+ self.unions
+ .for_all_items_mut(|x| x.mangle_paths(&monomorphs));
+ self.structs
+ .for_all_items_mut(|x| x.mangle_paths(&monomorphs));
+ self.enums
+ .for_all_items_mut(|x| x.mangle_paths(&monomorphs));
+ self.typedefs
+ .for_all_items_mut(|x| x.mangle_paths(&monomorphs));
+ for x in &mut self.functions {
+ x.mangle_paths(&monomorphs);
+ }
+ }
+}
diff --git a/src/bindgen/mangle.rs b/src/bindgen/mangle.rs
new file mode 100644
index 0000000..c8769f8
--- /dev/null
+++ b/src/bindgen/mangle.rs
@@ -0,0 +1,348 @@
+/* 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 crate::bindgen::config::MangleConfig;
+use crate::bindgen::ir::{ConstExpr, GenericArgument, GenericPath, Path, Type};
+use crate::bindgen::rename::IdentifierType;
+
+pub fn mangle_path(path: &Path, generic_values: &[GenericArgument], config: &MangleConfig) -> Path {
+ Path::new(mangle_name(path.name(), generic_values, config))
+}
+
+pub fn mangle_name(
+ name: &str,
+ generic_values: &[GenericArgument],
+ config: &MangleConfig,
+) -> String {
+ Mangler::new(name, generic_values, /* last = */ true, config).mangle()
+}
+
+enum Separator {
+ OpeningAngleBracket = 1,
+ Comma,
+ ClosingAngleBracket,
+ BeginMutPtr,
+ BeginConstPtr,
+ BeginFn,
+ BetweenFnArg,
+ EndFn,
+}
+
+struct Mangler<'a> {
+ input: &'a str,
+ generic_values: &'a [GenericArgument],
+ output: String,
+ last: bool,
+ config: &'a MangleConfig,
+}
+
+impl<'a> Mangler<'a> {
+ fn new(
+ input: &'a str,
+ generic_values: &'a [GenericArgument],
+ last: bool,
+ config: &'a MangleConfig,
+ ) -> Self {
+ Self {
+ input,
+ generic_values,
+ output: String::new(),
+ last,
+ config,
+ }
+ }
+
+ fn mangle(mut self) -> String {
+ self.mangle_internal();
+ self.output
+ }
+
+ fn push(&mut self, id: Separator) {
+ let count = id as usize;
+ let separator = if self.config.remove_underscores {
+ ""
+ } else {
+ "_"
+ };
+ self.output.extend(std::iter::repeat(separator).take(count));
+ }
+
+ fn append_mangled_argument(&mut self, arg: &GenericArgument, last: bool) {
+ match *arg {
+ GenericArgument::Type(ref ty) => self.append_mangled_type(ty, last),
+ GenericArgument::Const(ConstExpr::Name(ref name)) => {
+ // This must behave the same as a GenericArgument::Type,
+ // because const arguments are commonly represented as Types;
+ // see the comment on `enum GenericArgument`.
+ let fake_ty = Type::Path(GenericPath::new(Path::new(name), vec![]));
+ self.append_mangled_type(&fake_ty, last);
+ }
+ GenericArgument::Const(ConstExpr::Value(ref val)) => self.output.push_str(val),
+ }
+ }
+
+ fn append_mangled_type(&mut self, ty: &Type, last: bool) {
+ match *ty {
+ Type::Path(ref generic) => {
+ let sub_path =
+ Mangler::new(generic.export_name(), generic.generics(), last, self.config)
+ .mangle();
+
+ self.output.push_str(
+ &self
+ .config
+ .rename_types
+ .apply(&sub_path, IdentifierType::Type),
+ );
+ }
+ Type::Primitive(ref primitive) => {
+ self.output.push_str(
+ &self
+ .config
+ .rename_types
+ .apply(primitive.to_repr_rust(), IdentifierType::Type),
+ );
+ }
+ Type::Ptr {
+ ref ty, is_const, ..
+ } => {
+ self.push(if is_const {
+ Separator::BeginConstPtr
+ } else {
+ Separator::BeginMutPtr
+ });
+ self.append_mangled_type(ty, last);
+ }
+ Type::FuncPtr {
+ ref ret, ref args, ..
+ } => {
+ self.push(Separator::BeginFn);
+ self.append_mangled_type(ret, args.is_empty());
+ for (i, arg) in args.iter().enumerate() {
+ self.push(Separator::BetweenFnArg);
+ let last = last && i == args.len() - 1;
+ self.append_mangled_type(&arg.1, last);
+ }
+ if !self.last {
+ self.push(Separator::EndFn);
+ }
+ }
+ Type::Array(..) => {
+ unimplemented!(
+ "Unable to mangle generic parameter {:?} for '{}'",
+ ty,
+ self.input
+ );
+ }
+ }
+ }
+
+ fn mangle_internal(&mut self) {
+ debug_assert!(self.output.is_empty());
+ self.output = self.input.to_owned();
+ if self.generic_values.is_empty() {
+ return;
+ }
+
+ self.push(Separator::OpeningAngleBracket);
+ for (i, arg) in self.generic_values.iter().enumerate() {
+ if i != 0 {
+ self.push(Separator::Comma);
+ }
+ let last = self.last && i == self.generic_values.len() - 1;
+ self.append_mangled_argument(arg, last);
+ }
+
+ // Skip writing the trailing '>' mangling when possible
+ if !self.last {
+ self.push(Separator::ClosingAngleBracket)
+ }
+ }
+}
+
+#[test]
+fn generics() {
+ use crate::bindgen::ir::{GenericPath, PrimitiveType};
+ use crate::bindgen::rename::RenameRule::{self, PascalCase};
+
+ fn float() -> GenericArgument {
+ GenericArgument::Type(Type::Primitive(PrimitiveType::Float))
+ }
+
+ fn c_char() -> GenericArgument {
+ GenericArgument::Type(Type::Primitive(PrimitiveType::Char))
+ }
+
+ fn path(path: &str) -> GenericArgument {
+ generic_path(path, &[])
+ }
+
+ fn generic_path(path: &str, arguments: &[GenericArgument]) -> GenericArgument {
+ let path = Path::new(path);
+ let generic_path = GenericPath::new(path, arguments.to_owned());
+ GenericArgument::Type(Type::Path(generic_path))
+ }
+
+ // Foo<f32> => Foo_f32
+ assert_eq!(
+ mangle_path(&Path::new("Foo"), &[float()], &MangleConfig::default()),
+ Path::new("Foo_f32")
+ );
+
+ // Foo<Bar<f32>> => Foo_Bar_f32
+ assert_eq!(
+ mangle_path(
+ &Path::new("Foo"),
+ &[generic_path("Bar", &[float()])],
+ &MangleConfig::default(),
+ ),
+ Path::new("Foo_Bar_f32")
+ );
+
+ // Foo<Bar> => Foo_Bar
+ assert_eq!(
+ mangle_path(&Path::new("Foo"), &[path("Bar")], &MangleConfig::default()),
+ Path::new("Foo_Bar")
+ );
+
+ // Foo<Bar> => FooBar
+ assert_eq!(
+ mangle_path(
+ &Path::new("Foo"),
+ &[path("Bar")],
+ &MangleConfig {
+ remove_underscores: true,
+ rename_types: RenameRule::None,
+ }
+ ),
+ Path::new("FooBar")
+ );
+
+ // Foo<Bar<f32>> => FooBarF32
+ assert_eq!(
+ mangle_path(
+ &Path::new("Foo"),
+ &[generic_path("Bar", &[float()])],
+ &MangleConfig {
+ remove_underscores: true,
+ rename_types: PascalCase,
+ },
+ ),
+ Path::new("FooBarF32")
+ );
+
+ // Foo<Bar<c_char>> => FooBarCChar
+ assert_eq!(
+ mangle_path(
+ &Path::new("Foo"),
+ &[generic_path("Bar", &[c_char()])],
+ &MangleConfig {
+ remove_underscores: true,
+ rename_types: PascalCase,
+ },
+ ),
+ Path::new("FooBarCChar")
+ );
+
+ // Foo<Bar<T>> => Foo_Bar_T
+ assert_eq!(
+ mangle_path(
+ &Path::new("Foo"),
+ &[generic_path("Bar", &[path("T")])],
+ &MangleConfig::default(),
+ ),
+ Path::new("Foo_Bar_T")
+ );
+
+ // Foo<Bar<T>, E> => Foo_Bar_T_____E
+ assert_eq!(
+ mangle_path(
+ &Path::new("Foo"),
+ &[generic_path("Bar", &[path("T")]), path("E")],
+ &MangleConfig::default(),
+ ),
+ Path::new("Foo_Bar_T_____E")
+ );
+
+ // Foo<Bar<T>, Bar<E>> => Foo_Bar_T_____Bar_E
+ assert_eq!(
+ mangle_path(
+ &Path::new("Foo"),
+ &[
+ generic_path("Bar", &[path("T")]),
+ generic_path("Bar", &[path("E")]),
+ ],
+ &MangleConfig::default(),
+ ),
+ Path::new("Foo_Bar_T_____Bar_E")
+ );
+
+ // Foo<Bar<T>, E> => FooBarTE
+ assert_eq!(
+ mangle_path(
+ &Path::new("Foo"),
+ &[generic_path("Bar", &[path("T")]), path("E")],
+ &MangleConfig {
+ remove_underscores: true,
+ rename_types: PascalCase,
+ },
+ ),
+ Path::new("FooBarTE")
+ );
+
+ // Foo<Bar<T>, Bar<E>> => FooBarTBarE
+ assert_eq!(
+ mangle_path(
+ &Path::new("Foo"),
+ &[
+ generic_path("Bar", &[path("T")]),
+ generic_path("Bar", &[path("E")]),
+ ],
+ &MangleConfig {
+ remove_underscores: true,
+ rename_types: PascalCase,
+ },
+ ),
+ Path::new("FooBarTBarE")
+ );
+
+ assert_eq!(
+ mangle_path(
+ &Path::new("Top"),
+ &[GenericArgument::Const(ConstExpr::Value("40".to_string()))],
+ &MangleConfig::default(),
+ ),
+ Path::new("Top_40")
+ );
+
+ assert_eq!(
+ mangle_path(
+ &Path::new("Top"),
+ &[GenericArgument::Const(ConstExpr::Name("N".to_string()))],
+ &MangleConfig::default(),
+ ),
+ Path::new("Top_N")
+ );
+
+ assert_eq!(
+ mangle_path(
+ &Path::new("Top"),
+ &[generic_path("N", &[])],
+ &MangleConfig::default(),
+ ),
+ Path::new("Top_N")
+ );
+
+ assert_eq!(
+ mangle_path(
+ &Path::new("Foo"),
+ &[
+ float(),
+ GenericArgument::Const(ConstExpr::Value("40".to_string()))
+ ],
+ &MangleConfig::default(),
+ ),
+ Path::new("Foo_f32__40")
+ );
+}
diff --git a/src/bindgen/mod.rs b/src/bindgen/mod.rs
new file mode 100644
index 0000000..d0789da
--- /dev/null
+++ b/src/bindgen/mod.rs
@@ -0,0 +1,65 @@
+/* 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/. */
+
+/// A helper macro for deriving deserialize for an enum to be used in toml-rs.
+/// This macro works be relying on an existing FromStr implementation for the
+/// desired type.
+macro_rules! deserialize_enum_str {
+ ($name:ident) => {
+ impl<'de> ::serde::Deserialize<'de> for $name {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: ::serde::Deserializer<'de>,
+ {
+ struct Visitor;
+ impl<'de> ::serde::de::Visitor<'de> for Visitor {
+ type Value = $name;
+
+ fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ f.write_str("$name")
+ }
+
+ fn visit_str<E>(self, v: &str) -> Result<$name, E>
+ where
+ E: ::serde::de::Error,
+ {
+ match v.parse::<$name>() {
+ Ok(v) => Ok(v),
+ Err(m) => Err(E::custom(m)),
+ }
+ }
+ }
+ deserializer.deserialize_str(Visitor)
+ }
+ }
+ };
+}
+
+mod bindings;
+mod bitflags;
+mod builder;
+mod cargo;
+mod cdecl;
+mod config;
+mod declarationtyperesolver;
+mod dependencies;
+mod error;
+mod ir;
+mod library;
+mod mangle;
+mod monomorph;
+mod parser;
+mod rename;
+mod reserved;
+mod utilities;
+mod writer;
+
+#[allow(unused)]
+pub(crate) use self::cargo::*;
+
+pub use self::bindings::Bindings;
+pub use self::builder::Builder;
+pub use self::config::Profile; // disambiguate with cargo::Profile
+pub use self::config::*;
+pub use self::error::Error;
diff --git a/src/bindgen/monomorph.rs b/src/bindgen/monomorph.rs
new file mode 100644
index 0000000..db6dce6
--- /dev/null
+++ b/src/bindgen/monomorph.rs
@@ -0,0 +1,147 @@
+/* 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::HashMap;
+use std::mem;
+
+use crate::bindgen::ir::{
+ Enum, GenericArgument, GenericPath, OpaqueItem, Path, Struct, Typedef, Union,
+};
+use crate::bindgen::library::Library;
+
+#[derive(Default, Clone, Debug)]
+pub struct Monomorphs {
+ replacements: HashMap<GenericPath, Path>,
+ opaques: Vec<OpaqueItem>,
+ structs: Vec<Struct>,
+ unions: Vec<Union>,
+ typedefs: Vec<Typedef>,
+ enums: Vec<Enum>,
+}
+
+impl Monomorphs {
+ pub fn contains(&self, path: &GenericPath) -> bool {
+ self.replacements.contains_key(path)
+ }
+
+ pub fn insert_struct(
+ &mut self,
+ library: &Library,
+ generic: &Struct,
+ monomorph: Struct,
+ arguments: Vec<GenericArgument>,
+ ) {
+ let replacement_path = GenericPath::new(generic.path.clone(), arguments);
+
+ debug_assert!(generic.generic_params.len() > 0);
+ debug_assert!(!self.contains(&replacement_path));
+
+ self.replacements
+ .insert(replacement_path, monomorph.path.clone());
+
+ monomorph.add_monomorphs(library, self);
+
+ self.structs.push(monomorph);
+ }
+
+ pub fn insert_enum(
+ &mut self,
+ library: &Library,
+ generic: &Enum,
+ monomorph: Enum,
+ arguments: Vec<GenericArgument>,
+ ) {
+ let replacement_path = GenericPath::new(generic.path.clone(), arguments);
+
+ debug_assert!(generic.generic_params.len() > 0);
+ debug_assert!(!self.contains(&replacement_path));
+
+ self.replacements
+ .insert(replacement_path, monomorph.path.clone());
+
+ monomorph.add_monomorphs(library, self);
+
+ self.enums.push(monomorph);
+ }
+
+ pub fn insert_union(
+ &mut self,
+ library: &Library,
+ generic: &Union,
+ monomorph: Union,
+ arguments: Vec<GenericArgument>,
+ ) {
+ let replacement_path = GenericPath::new(generic.path.clone(), arguments);
+
+ debug_assert!(generic.generic_params.len() > 0);
+ debug_assert!(!self.contains(&replacement_path));
+
+ self.replacements
+ .insert(replacement_path, monomorph.path.clone());
+
+ monomorph.add_monomorphs(library, self);
+
+ self.unions.push(monomorph);
+ }
+
+ pub fn insert_opaque(
+ &mut self,
+ generic: &OpaqueItem,
+ monomorph: OpaqueItem,
+ arguments: Vec<GenericArgument>,
+ ) {
+ let replacement_path = GenericPath::new(generic.path.clone(), arguments);
+
+ debug_assert!(generic.generic_params.len() > 0);
+ debug_assert!(!self.contains(&replacement_path));
+
+ self.replacements
+ .insert(replacement_path, monomorph.path.clone());
+ self.opaques.push(monomorph);
+ }
+
+ pub fn insert_typedef(
+ &mut self,
+ library: &Library,
+ generic: &Typedef,
+ monomorph: Typedef,
+ arguments: Vec<GenericArgument>,
+ ) {
+ let replacement_path = GenericPath::new(generic.path.clone(), arguments);
+
+ debug_assert!(generic.generic_params.len() > 0);
+ debug_assert!(!self.contains(&replacement_path));
+
+ self.replacements
+ .insert(replacement_path, monomorph.path.clone());
+
+ monomorph.add_monomorphs(library, self);
+
+ self.typedefs.push(monomorph);
+ }
+
+ pub fn mangle_path(&self, path: &GenericPath) -> Option<&Path> {
+ self.replacements.get(path)
+ }
+
+ pub fn drain_opaques(&mut self) -> Vec<OpaqueItem> {
+ mem::take(&mut self.opaques)
+ }
+
+ pub fn drain_structs(&mut self) -> Vec<Struct> {
+ mem::take(&mut self.structs)
+ }
+
+ pub fn drain_unions(&mut self) -> Vec<Union> {
+ mem::take(&mut self.unions)
+ }
+
+ pub fn drain_typedefs(&mut self) -> Vec<Typedef> {
+ mem::take(&mut self.typedefs)
+ }
+
+ pub fn drain_enums(&mut self) -> Vec<Enum> {
+ mem::take(&mut self.enums)
+ }
+}
diff --git a/src/bindgen/parser.rs b/src/bindgen/parser.rs
new file mode 100644
index 0000000..b54c7fb
--- /dev/null
+++ b/src/bindgen/parser.rs
@@ -0,0 +1,1011 @@
+/* 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::hash_map::Entry;
+use std::collections::{HashMap, HashSet};
+use std::fs::File;
+use std::io::Read;
+use std::path::{Path as FilePath, PathBuf as FilePathBuf};
+
+use syn::ext::IdentExt;
+
+use crate::bindgen::bitflags;
+use crate::bindgen::cargo::{Cargo, PackageRef};
+use crate::bindgen::config::{Config, ParseConfig};
+use crate::bindgen::error::Error;
+use crate::bindgen::ir::{
+ AnnotationSet, AnnotationValue, Cfg, Constant, Documentation, Enum, Function, GenericParam,
+ GenericParams, ItemMap, OpaqueItem, Path, Static, Struct, Type, Typedef, Union,
+};
+use crate::bindgen::utilities::{SynAbiHelpers, SynAttributeHelpers, SynItemHelpers};
+
+const STD_CRATES: &[&str] = &[
+ "std",
+ "std_unicode",
+ "alloc",
+ "collections",
+ "core",
+ "proc_macro",
+];
+
+type ParseResult = Result<Parse, Error>;
+
+/// Parses a single rust source file, not following `mod` or `extern crate`.
+pub fn parse_src(src_file: &FilePath, config: &Config) -> ParseResult {
+ let mod_name = src_file.file_stem().unwrap().to_str().unwrap();
+ let mut config = config.clone();
+ config.parse = ParseConfig {
+ parse_deps: true,
+ ..ParseConfig::default()
+ };
+
+ let mut context = Parser {
+ binding_crate_name: mod_name.to_owned(),
+ config: &config,
+ lib: None,
+ parsed_crates: HashSet::new(),
+ cache_src: HashMap::new(),
+ cache_expanded_crate: HashMap::new(),
+ cfg_stack: Vec::new(),
+ out: Parse::new(),
+ };
+
+ let pkg_ref = PackageRef {
+ name: mod_name.to_owned(),
+ version: None,
+ };
+
+ context.parse_mod(&pkg_ref, src_file, 0)?;
+ context.out.source_files = context.cache_src.keys().map(|k| k.to_owned()).collect();
+ Ok(context.out)
+}
+
+/// Recursively parses a rust library starting at the root crate's directory.
+///
+/// Inside a crate, `mod` and `extern crate` declarations are followed
+/// and parsed. To find an external crate, the parser uses the `cargo metadata`
+/// command to find the location of dependencies.
+pub(crate) fn parse_lib(lib: Cargo, config: &Config) -> ParseResult {
+ let mut context = Parser {
+ binding_crate_name: lib.binding_crate_name().to_owned(),
+ config,
+ lib: Some(lib),
+ parsed_crates: HashSet::new(),
+ cache_src: HashMap::new(),
+ cache_expanded_crate: HashMap::new(),
+ cfg_stack: Vec::new(),
+ out: Parse::new(),
+ };
+
+ let binding_crate = context.lib.as_ref().unwrap().binding_crate_ref();
+ context.parse_crate(&binding_crate)?;
+ context.out.source_files = context.cache_src.keys().map(|k| k.to_owned()).collect();
+ Ok(context.out)
+}
+
+#[derive(Debug, Clone)]
+struct Parser<'a> {
+ binding_crate_name: String,
+ lib: Option<Cargo>,
+ config: &'a Config,
+
+ parsed_crates: HashSet<String>,
+ cache_src: HashMap<FilePathBuf, Vec<syn::Item>>,
+ cache_expanded_crate: HashMap<String, Vec<syn::Item>>,
+
+ cfg_stack: Vec<Cfg>,
+
+ out: Parse,
+}
+
+impl<'a> Parser<'a> {
+ fn should_parse_dependency(&self, pkg_name: &str) -> bool {
+ if self.parsed_crates.contains(pkg_name) {
+ return false;
+ }
+
+ if !self.config.parse.parse_deps {
+ return false;
+ }
+
+ // Skip any whitelist or blacklist for expand
+ if self
+ .config
+ .parse
+ .expand
+ .crates
+ .iter()
+ .any(|name| name == pkg_name)
+ {
+ return true;
+ }
+
+ // If we have a whitelist, check it
+ if let Some(ref include) = self.config.parse.include {
+ if !include.iter().any(|name| name == pkg_name) {
+ debug!("Excluding crate {}", pkg_name);
+ return false;
+ }
+ }
+
+ // Check the blacklist
+ !STD_CRATES.contains(&pkg_name)
+ && !self
+ .config
+ .parse
+ .exclude
+ .iter()
+ .any(|name| name == pkg_name)
+ }
+
+ fn parse_crate(&mut self, pkg: &PackageRef) -> Result<(), Error> {
+ assert!(self.lib.is_some());
+ debug!("Parsing crate {}", pkg.name);
+ self.parsed_crates.insert(pkg.name.clone());
+
+ // Check if we should use cargo expand for this crate
+ if self.config.parse.expand.crates.contains(&pkg.name) {
+ self.parse_expand_crate(pkg)?;
+ } else {
+ // Parse the crate before the dependencies otherwise the same-named idents we
+ // want to generate bindings for would be replaced by the ones provided
+ // by the first dependency containing it.
+ let crate_src = self.lib.as_ref().unwrap().find_crate_src(pkg);
+
+ match crate_src {
+ Some(crate_src) => self.parse_mod(pkg, crate_src.as_path(), 0)?,
+ None => {
+ // This should be an error, but is common enough to just elicit a warning
+ warn!(
+ "Parsing crate `{}`: can't find lib.rs with `cargo metadata`. \
+ The crate may be available only on a particular platform, \
+ so consider setting `fetch_all_dependencies` in your cbindgen configuration.",
+ pkg.name
+ );
+ }
+ }
+ }
+
+ for (dep_pkg, cfg) in self.lib.as_ref().unwrap().dependencies(pkg) {
+ if !self.should_parse_dependency(&dep_pkg.name) {
+ continue;
+ }
+
+ if let Some(ref cfg) = cfg {
+ self.cfg_stack.push(cfg.clone());
+ }
+
+ self.parse_crate(&dep_pkg)?;
+
+ if cfg.is_some() {
+ self.cfg_stack.pop();
+ }
+ }
+
+ Ok(())
+ }
+
+ fn parse_expand_crate(&mut self, pkg: &PackageRef) -> Result<(), Error> {
+ assert!(self.lib.is_some());
+
+ let mod_items = {
+ if !self.cache_expanded_crate.contains_key(&pkg.name) {
+ let s = self
+ .lib
+ .as_ref()
+ .unwrap()
+ .expand_crate(
+ pkg,
+ self.config.parse.expand.all_features,
+ self.config.parse.expand.default_features,
+ &self.config.parse.expand.features,
+ self.config.parse.expand.profile,
+ )
+ .map_err(|x| Error::CargoExpand(pkg.name.clone(), x))?;
+ let i = syn::parse_file(&s).map_err(|x| Error::ParseSyntaxError {
+ crate_name: pkg.name.clone(),
+ src_path: "".to_owned(),
+ error: x,
+ })?;
+ self.cache_expanded_crate.insert(pkg.name.clone(), i.items);
+ }
+
+ self.cache_expanded_crate.get(&pkg.name).unwrap().clone()
+ };
+
+ self.process_mod(
+ pkg, None, None, &mod_items, 0, /* is_mod_rs = */ true,
+ /* is_inline = */ false,
+ )
+ }
+
+ fn parse_mod(
+ &mut self,
+ pkg: &PackageRef,
+ mod_path: &FilePath,
+ depth: usize,
+ ) -> Result<(), Error> {
+ let mod_items = match self.cache_src.entry(mod_path.to_path_buf()) {
+ Entry::Vacant(vacant_entry) => {
+ let mut s = String::new();
+ let mut f = File::open(mod_path).map_err(|_| Error::ParseCannotOpenFile {
+ crate_name: pkg.name.clone(),
+ src_path: mod_path.to_str().unwrap().to_owned(),
+ })?;
+
+ f.read_to_string(&mut s)
+ .map_err(|_| Error::ParseCannotOpenFile {
+ crate_name: pkg.name.clone(),
+ src_path: mod_path.to_str().unwrap().to_owned(),
+ })?;
+
+ let i = syn::parse_file(&s).map_err(|x| Error::ParseSyntaxError {
+ crate_name: pkg.name.clone(),
+ src_path: mod_path.to_string_lossy().into(),
+ error: x,
+ })?;
+
+ vacant_entry.insert(i.items).clone()
+ }
+ Entry::Occupied(occupied_entry) => occupied_entry.get().clone(),
+ };
+
+ // Compute module directory according to Rust 2018 rules
+ let submod_dir_2018;
+
+ let mod_dir = mod_path.parent().unwrap();
+
+ let is_mod_rs = depth == 0 || mod_path.ends_with("mod.rs");
+ let submod_dir = if is_mod_rs {
+ mod_dir
+ } else {
+ submod_dir_2018 = mod_path
+ .parent()
+ .unwrap()
+ .join(mod_path.file_stem().unwrap());
+ &submod_dir_2018
+ };
+
+ self.process_mod(
+ pkg,
+ Some(mod_dir),
+ Some(submod_dir),
+ &mod_items,
+ depth,
+ /* is_inline = */ false,
+ is_mod_rs,
+ )
+ }
+
+ /// `mod_dir` is the path to the current directory of the module. It may be
+ /// `None` for pre-expanded modules.
+ ///
+ /// `submod_dir` is the path to search submodules in by default, which might
+ /// be different for rust 2018 for example.
+ #[allow(clippy::too_many_arguments)]
+ fn process_mod(
+ &mut self,
+ pkg: &PackageRef,
+ mod_dir: Option<&FilePath>,
+ submod_dir: Option<&FilePath>,
+ items: &[syn::Item],
+ depth: usize,
+ is_inline: bool,
+ is_in_mod_rs: bool,
+ ) -> Result<(), Error> {
+ debug_assert_eq!(mod_dir.is_some(), submod_dir.is_some());
+ // We process the items first then the nested modules.
+ let nested_modules = self.out.load_syn_crate_mod(
+ self.config,
+ &self.binding_crate_name,
+ &pkg.name,
+ Cfg::join(&self.cfg_stack).as_ref(),
+ items,
+ );
+
+ for item in nested_modules {
+ let next_mod_name = item.ident.unraw().to_string();
+ let cfg = Cfg::load(&item.attrs);
+ if let Some(ref cfg) = cfg {
+ self.cfg_stack.push(cfg.clone());
+ }
+
+ if let Some((_, ref inline_items)) = item.content {
+ // TODO(emilio): This should use #[path] attribute if present,
+ // rather than next_mod_name.
+ let next_submod_dir = submod_dir.map(|dir| dir.join(&next_mod_name));
+ let next_mod_dir = mod_dir.map(|dir| dir.join(&next_mod_name));
+ self.process_mod(
+ pkg,
+ next_mod_dir.as_deref(),
+ next_submod_dir.as_deref(),
+ inline_items,
+ depth,
+ /* is_inline = */ true,
+ is_in_mod_rs,
+ )?;
+ } else if let Some(mod_dir) = mod_dir {
+ let submod_dir = submod_dir.unwrap();
+ let next_mod_path1 = submod_dir.join(next_mod_name.clone() + ".rs");
+ let next_mod_path2 = submod_dir.join(next_mod_name.clone()).join("mod.rs");
+
+ if next_mod_path1.exists() {
+ self.parse_mod(pkg, next_mod_path1.as_path(), depth + 1)?;
+ } else if next_mod_path2.exists() {
+ self.parse_mod(pkg, next_mod_path2.as_path(), depth + 1)?;
+ } else {
+ // Last chance to find a module path
+ let mut path_attr_found = false;
+ for attr in &item.attrs {
+ if let Ok(syn::Meta::NameValue(syn::MetaNameValue { path, lit, .. })) =
+ attr.parse_meta()
+ {
+ match lit {
+ syn::Lit::Str(ref path_lit) if path.is_ident("path") => {
+ path_attr_found = true;
+ // https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute
+ //
+ // For path attributes on modules not inside inline module blocks, the file path
+ // is relative to the directory the source file is located.
+ //
+ // For path attributes inside inline module blocks, the relative location of the
+ // file path depends on the kind of source file the path attribute is located
+ // in. "mod-rs" source files are root modules (such as lib.rs or main.rs) and
+ // modules with files named mod.rs. "non-mod-rs" source files are all other
+ // module files.
+ //
+ // Paths for path attributes inside inline module blocks in a mod-rs file are
+ // relative to the directory of the mod-rs file including the inline module
+ // components as directories. For non-mod-rs files, it is the same except the
+ // path starts with a directory with the name of the non-mod-rs module.
+ //
+ let base = if is_inline && !is_in_mod_rs {
+ submod_dir
+ } else {
+ mod_dir
+ };
+ self.parse_mod(pkg, &base.join(path_lit.value()), depth + 1)?;
+ break;
+ }
+ _ => (),
+ }
+ }
+ }
+
+ // This should be an error, but it's common enough to
+ // just elicit a warning
+ if !path_attr_found {
+ warn!(
+ "Parsing crate `{}`: can't find mod {}`.",
+ pkg.name, next_mod_name
+ );
+ }
+ }
+ } else {
+ warn!(
+ "Parsing expanded crate `{}`: can't find mod {}`.",
+ pkg.name, next_mod_name
+ );
+ }
+
+ if cfg.is_some() {
+ self.cfg_stack.pop();
+ }
+ }
+
+ Ok(())
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Parse {
+ pub constants: ItemMap<Constant>,
+ pub globals: ItemMap<Static>,
+ pub enums: ItemMap<Enum>,
+ pub structs: ItemMap<Struct>,
+ pub unions: ItemMap<Union>,
+ pub opaque_items: ItemMap<OpaqueItem>,
+ pub typedefs: ItemMap<Typedef>,
+ pub functions: Vec<Function>,
+ pub source_files: Vec<FilePathBuf>,
+}
+
+impl Parse {
+ pub fn new() -> Parse {
+ Parse {
+ constants: ItemMap::default(),
+ globals: ItemMap::default(),
+ enums: ItemMap::default(),
+ structs: ItemMap::default(),
+ unions: ItemMap::default(),
+ opaque_items: ItemMap::default(),
+ typedefs: ItemMap::default(),
+ functions: Vec::new(),
+ source_files: Vec::new(),
+ }
+ }
+
+ pub fn add_std_types(&mut self) {
+ let mut add_opaque = |path: &str, generic_params: Vec<&str>| {
+ let path = Path::new(path);
+ let generic_params: Vec<_> = generic_params
+ .into_iter()
+ .map(GenericParam::new_type_param)
+ .collect();
+ self.opaque_items.try_insert(OpaqueItem::new(
+ path,
+ GenericParams(generic_params),
+ None,
+ AnnotationSet::new(),
+ Documentation::none(),
+ ))
+ };
+
+ add_opaque("String", vec![]);
+ add_opaque("Box", vec!["T"]);
+ add_opaque("RefCell", vec!["T"]);
+ add_opaque("Rc", vec!["T"]);
+ add_opaque("Arc", vec!["T"]);
+ add_opaque("Result", vec!["T", "E"]);
+ add_opaque("Option", vec!["T"]);
+ add_opaque("NonNull", vec!["T"]);
+ add_opaque("Vec", vec!["T"]);
+ add_opaque("HashMap", vec!["K", "V", "Hasher"]);
+ add_opaque("BTreeMap", vec!["K", "V"]);
+ add_opaque("HashSet", vec!["T"]);
+ add_opaque("BTreeSet", vec!["T"]);
+ add_opaque("LinkedList", vec!["T"]);
+ add_opaque("VecDeque", vec!["T"]);
+ add_opaque("ManuallyDrop", vec!["T"]);
+ add_opaque("MaybeUninit", vec!["T"]);
+ }
+
+ pub fn extend_with(&mut self, other: &Parse) {
+ self.constants.extend_with(&other.constants);
+ self.globals.extend_with(&other.globals);
+ self.enums.extend_with(&other.enums);
+ self.structs.extend_with(&other.structs);
+ self.unions.extend_with(&other.unions);
+ self.opaque_items.extend_with(&other.opaque_items);
+ self.typedefs.extend_with(&other.typedefs);
+ self.functions.extend_from_slice(&other.functions);
+ self.source_files.extend_from_slice(&other.source_files);
+ }
+
+ fn load_syn_crate_mod<'a>(
+ &mut self,
+ config: &Config,
+ binding_crate_name: &str,
+ crate_name: &str,
+ mod_cfg: Option<&Cfg>,
+ items: &'a [syn::Item],
+ ) -> Vec<&'a syn::ItemMod> {
+ let mut impls_with_assoc_consts = Vec::new();
+ let mut nested_modules = Vec::new();
+
+ for item in items {
+ if item.should_skip_parsing() {
+ continue;
+ }
+ match item {
+ syn::Item::ForeignMod(ref item) => {
+ self.load_syn_foreign_mod(
+ config,
+ binding_crate_name,
+ crate_name,
+ mod_cfg,
+ item,
+ );
+ }
+ syn::Item::Fn(ref item) => {
+ self.load_syn_fn(config, binding_crate_name, crate_name, mod_cfg, item);
+ }
+ syn::Item::Const(ref item) => {
+ self.load_syn_const(config, binding_crate_name, crate_name, mod_cfg, item);
+ }
+ syn::Item::Static(ref item) => {
+ self.load_syn_static(config, binding_crate_name, crate_name, mod_cfg, item);
+ }
+ syn::Item::Struct(ref item) => {
+ self.load_syn_struct(config, crate_name, mod_cfg, item);
+ }
+ syn::Item::Union(ref item) => {
+ self.load_syn_union(config, crate_name, mod_cfg, item);
+ }
+ syn::Item::Enum(ref item) => {
+ self.load_syn_enum(config, crate_name, mod_cfg, item);
+ }
+ syn::Item::Type(ref item) => {
+ self.load_syn_ty(crate_name, mod_cfg, item);
+ }
+ syn::Item::Impl(ref item_impl) => {
+ let has_assoc_const = item_impl
+ .items
+ .iter()
+ .any(|item| matches!(item, syn::ImplItem::Const(_)));
+ if has_assoc_const {
+ impls_with_assoc_consts.push(item_impl);
+ }
+
+ if let syn::Type::Path(ref path) = *item_impl.self_ty {
+ if let Some(type_name) = path.path.get_ident() {
+ for method in item_impl.items.iter().filter_map(|item| match item {
+ syn::ImplItem::Method(method) => Some(method),
+ _ => None,
+ }) {
+ self.load_syn_method(
+ config,
+ binding_crate_name,
+ crate_name,
+ mod_cfg,
+ &Path::new(type_name.unraw().to_string()),
+ method,
+ )
+ }
+ }
+ }
+ }
+ syn::Item::Macro(ref item) => {
+ self.load_builtin_macro(config, crate_name, mod_cfg, item);
+ }
+ syn::Item::Mod(ref item) => {
+ nested_modules.push(item);
+ }
+ _ => {}
+ }
+ }
+
+ for item_impl in impls_with_assoc_consts {
+ self.load_syn_assoc_consts_from_impl(crate_name, mod_cfg, item_impl)
+ }
+
+ nested_modules
+ }
+
+ fn load_syn_assoc_consts_from_impl(
+ &mut self,
+ crate_name: &str,
+ mod_cfg: Option<&Cfg>,
+ item_impl: &syn::ItemImpl,
+ ) {
+ let associated_constants = item_impl.items.iter().filter_map(|item| match item {
+ syn::ImplItem::Const(ref associated_constant) => Some(associated_constant),
+ _ => None,
+ });
+ self.load_syn_assoc_consts(
+ crate_name,
+ mod_cfg,
+ &item_impl.self_ty,
+ associated_constants,
+ );
+ }
+
+ /// Enters a `extern "C" { }` declaration and loads function declarations.
+ fn load_syn_foreign_mod(
+ &mut self,
+ config: &Config,
+ binding_crate_name: &str,
+ crate_name: &str,
+ mod_cfg: Option<&Cfg>,
+ item: &syn::ItemForeignMod,
+ ) {
+ if !item.abi.is_c() {
+ info!("Skip {} - (extern block must be extern C).", crate_name);
+ return;
+ }
+
+ for foreign_item in &item.items {
+ if let syn::ForeignItem::Fn(ref function) = *foreign_item {
+ if !config
+ .parse
+ .should_generate_top_level_item(crate_name, binding_crate_name)
+ {
+ info!(
+ "Skip {}::{} - (fn's outside of the binding crate are not used).",
+ crate_name, &function.sig.ident
+ );
+ return;
+ }
+ let path = Path::new(function.sig.ident.unraw().to_string());
+ match Function::load(path, None, &function.sig, true, &function.attrs, mod_cfg) {
+ Ok(func) => {
+ info!("Take {}::{}.", crate_name, &function.sig.ident);
+
+ self.functions.push(func);
+ }
+ Err(msg) => {
+ error!(
+ "Cannot use fn {}::{} ({}).",
+ crate_name, &function.sig.ident, msg
+ );
+ }
+ }
+ }
+ }
+ }
+
+ /// Loads a `fn` declaration inside an `impl` block, if the type is a simple identifier
+ fn load_syn_method(
+ &mut self,
+ config: &Config,
+ binding_crate_name: &str,
+ crate_name: &str,
+ mod_cfg: Option<&Cfg>,
+ self_type: &Path,
+ item: &syn::ImplItemMethod,
+ ) {
+ self.load_fn_declaration(
+ config,
+ binding_crate_name,
+ crate_name,
+ mod_cfg,
+ item,
+ Some(self_type),
+ &item.sig,
+ &item.attrs,
+ )
+ }
+
+ /// Loads a `fn` declaration
+ fn load_syn_fn(
+ &mut self,
+ config: &Config,
+ binding_crate_name: &str,
+ crate_name: &str,
+ mod_cfg: Option<&Cfg>,
+ item: &syn::ItemFn,
+ ) {
+ self.load_fn_declaration(
+ config,
+ binding_crate_name,
+ crate_name,
+ mod_cfg,
+ item,
+ None,
+ &item.sig,
+ &item.attrs,
+ );
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ fn load_fn_declaration(
+ &mut self,
+ config: &Config,
+ binding_crate_name: &str,
+ crate_name: &str,
+ mod_cfg: Option<&Cfg>,
+ named_symbol: &dyn SynItemHelpers,
+ self_type: Option<&Path>,
+ sig: &syn::Signature,
+ attrs: &[syn::Attribute],
+ ) {
+ if !config
+ .parse
+ .should_generate_top_level_item(crate_name, binding_crate_name)
+ {
+ info!(
+ "Skip {}::{} - (fn's outside of the binding crate are not used).",
+ crate_name, &sig.ident
+ );
+ return;
+ }
+
+ let loggable_item_name = || {
+ let mut items = Vec::with_capacity(3);
+ items.push(crate_name.to_owned());
+ if let Some(ref self_type) = self_type {
+ items.push(self_type.to_string());
+ }
+ items.push(sig.ident.unraw().to_string());
+ items.join("::")
+ };
+
+ let is_extern_c = sig.abi.is_omitted() || sig.abi.is_c();
+ let exported_name = named_symbol.exported_name();
+
+ match (is_extern_c, exported_name) {
+ (true, Some(exported_name)) => {
+ let path = Path::new(exported_name);
+ match Function::load(path, self_type, sig, false, attrs, mod_cfg) {
+ Ok(func) => {
+ info!("Take {}.", loggable_item_name());
+ self.functions.push(func);
+ }
+ Err(msg) => {
+ error!("Cannot use fn {} ({}).", loggable_item_name(), msg);
+ }
+ }
+ }
+ (true, None) => {
+ warn!(
+ "Skipping {} - (not `no_mangle`, and has no `export_name` attribute)",
+ loggable_item_name()
+ );
+ }
+ (false, Some(_exported_name)) => {
+ warn!("Skipping {} - (not `extern \"C\"`", loggable_item_name());
+ }
+ (false, None) => {}
+ }
+ }
+
+ /// Loads associated `const` declarations
+ fn load_syn_assoc_consts<'a, I>(
+ &mut self,
+ crate_name: &str,
+ mod_cfg: Option<&Cfg>,
+ impl_ty: &syn::Type,
+ items: I,
+ ) where
+ I: IntoIterator<Item = &'a syn::ImplItemConst>,
+ {
+ let ty = match Type::load(impl_ty) {
+ Ok(ty) => ty,
+ Err(e) => {
+ warn!("Skipping associated constants for {:?}: {:?}", impl_ty, e);
+ return;
+ }
+ };
+
+ let ty = match ty {
+ Some(ty) => ty,
+ None => return,
+ };
+
+ let impl_path = match ty.get_root_path() {
+ Some(p) => p,
+ None => {
+ warn!(
+ "Couldn't find path for {:?}, skipping associated constants",
+ ty
+ );
+ return;
+ }
+ };
+
+ for item in items.into_iter() {
+ if let syn::Visibility::Public(_) = item.vis {
+ } else {
+ warn!("Skip {}::{} - (not `pub`).", crate_name, &item.ident);
+ return;
+ }
+
+ let path = Path::new(item.ident.unraw().to_string());
+ match Constant::load(
+ path,
+ mod_cfg,
+ &item.ty,
+ &item.expr,
+ &item.attrs,
+ Some(impl_path.clone()),
+ ) {
+ Ok(constant) => {
+ info!("Take {}::{}::{}.", crate_name, impl_path, &item.ident);
+ let mut any = false;
+ self.structs.for_items_mut(&impl_path, |item| {
+ any = true;
+ item.add_associated_constant(constant.clone());
+ });
+ // Handle associated constants to other item types that are
+ // not structs like enums or such as regular constants.
+ if !any && !self.constants.try_insert(constant) {
+ error!(
+ "Conflicting name for constant {}::{}::{}.",
+ crate_name, impl_path, &item.ident,
+ );
+ }
+ }
+ Err(msg) => {
+ warn!("Skip {}::{} - ({})", crate_name, &item.ident, msg);
+ }
+ }
+ }
+ }
+
+ /// Loads a `const` declaration
+ fn load_syn_const(
+ &mut self,
+ config: &Config,
+ binding_crate_name: &str,
+ crate_name: &str,
+ mod_cfg: Option<&Cfg>,
+ item: &syn::ItemConst,
+ ) {
+ if !config
+ .parse
+ .should_generate_top_level_item(crate_name, binding_crate_name)
+ {
+ info!(
+ "Skip {}::{} - (const's outside of the binding crate are not used).",
+ crate_name, &item.ident
+ );
+ return;
+ }
+
+ if let syn::Visibility::Public(_) = item.vis {
+ } else {
+ warn!("Skip {}::{} - (not `pub`).", crate_name, &item.ident);
+ return;
+ }
+
+ let path = Path::new(item.ident.unraw().to_string());
+ match Constant::load(path, mod_cfg, &item.ty, &item.expr, &item.attrs, None) {
+ Ok(constant) => {
+ info!("Take {}::{}.", crate_name, &item.ident);
+
+ let full_name = constant.path.clone();
+ if !self.constants.try_insert(constant) {
+ error!("Conflicting name for constant {}", full_name);
+ }
+ }
+ Err(msg) => {
+ warn!("Skip {}::{} - ({})", crate_name, &item.ident, msg);
+ }
+ }
+ }
+
+ /// Loads a `static` declaration
+ fn load_syn_static(
+ &mut self,
+ config: &Config,
+ binding_crate_name: &str,
+ crate_name: &str,
+ mod_cfg: Option<&Cfg>,
+ item: &syn::ItemStatic,
+ ) {
+ if !config
+ .parse
+ .should_generate_top_level_item(crate_name, binding_crate_name)
+ {
+ info!(
+ "Skip {}::{} - (static's outside of the binding crate are not used).",
+ crate_name, &item.ident
+ );
+ return;
+ }
+
+ if let Some(exported_name) = item.exported_name() {
+ let path = Path::new(exported_name);
+ match Static::load(path, item, mod_cfg) {
+ Ok(constant) => {
+ info!("Take {}::{}.", crate_name, &item.ident);
+ self.globals.try_insert(constant);
+ }
+ Err(msg) => {
+ warn!("Skip {}::{} - ({})", crate_name, &item.ident, msg);
+ }
+ }
+ } else {
+ warn!("Skip {}::{} - (not `no_mangle`).", crate_name, &item.ident);
+ }
+ }
+
+ /// Loads a `struct` declaration
+ fn load_syn_struct(
+ &mut self,
+ config: &Config,
+ crate_name: &str,
+ mod_cfg: Option<&Cfg>,
+ item: &syn::ItemStruct,
+ ) {
+ match Struct::load(&config.layout, item, mod_cfg) {
+ Ok(st) => {
+ info!("Take {}::{}.", crate_name, &item.ident);
+ self.structs.try_insert(st);
+ }
+ Err(msg) => {
+ info!("Take {}::{} - opaque ({}).", crate_name, &item.ident, msg);
+ let path = Path::new(item.ident.unraw().to_string());
+ self.opaque_items.try_insert(
+ OpaqueItem::load(path, &item.generics, &item.attrs, mod_cfg).unwrap(),
+ );
+ }
+ }
+ }
+
+ /// Loads a `union` declaration
+ fn load_syn_union(
+ &mut self,
+ config: &Config,
+ crate_name: &str,
+ mod_cfg: Option<&Cfg>,
+ item: &syn::ItemUnion,
+ ) {
+ match Union::load(&config.layout, item, mod_cfg) {
+ Ok(st) => {
+ info!("Take {}::{}.", crate_name, &item.ident);
+
+ self.unions.try_insert(st);
+ }
+ Err(msg) => {
+ info!("Take {}::{} - opaque ({}).", crate_name, &item.ident, msg);
+ let path = Path::new(item.ident.unraw().to_string());
+ self.opaque_items.try_insert(
+ OpaqueItem::load(path, &item.generics, &item.attrs, mod_cfg).unwrap(),
+ );
+ }
+ }
+ }
+
+ /// Loads a `enum` declaration
+ fn load_syn_enum(
+ &mut self,
+ config: &Config,
+ crate_name: &str,
+ mod_cfg: Option<&Cfg>,
+ item: &syn::ItemEnum,
+ ) {
+ match Enum::load(item, mod_cfg, config) {
+ Ok(en) => {
+ info!("Take {}::{}.", crate_name, &item.ident);
+ self.enums.try_insert(en);
+ }
+ Err(msg) => {
+ info!("Take {}::{} - opaque ({}).", crate_name, &item.ident, msg);
+ let path = Path::new(item.ident.unraw().to_string());
+ self.opaque_items.try_insert(
+ OpaqueItem::load(path, &item.generics, &item.attrs, mod_cfg).unwrap(),
+ );
+ }
+ }
+ }
+
+ /// Loads a `type` declaration
+ fn load_syn_ty(&mut self, crate_name: &str, mod_cfg: Option<&Cfg>, item: &syn::ItemType) {
+ match Typedef::load(item, mod_cfg) {
+ Ok(st) => {
+ info!("Take {}::{}.", crate_name, &item.ident);
+
+ self.typedefs.try_insert(st);
+ }
+ Err(msg) => {
+ info!("Take {}::{} - opaque ({}).", crate_name, &item.ident, msg);
+ let path = Path::new(item.ident.unraw().to_string());
+ self.opaque_items.try_insert(
+ OpaqueItem::load(path, &item.generics, &item.attrs, mod_cfg).unwrap(),
+ );
+ }
+ }
+ }
+
+ fn load_builtin_macro(
+ &mut self,
+ config: &Config,
+ crate_name: &str,
+ mod_cfg: Option<&Cfg>,
+ item: &syn::ItemMacro,
+ ) {
+ let name = match item.mac.path.segments.last() {
+ Some(n) => n.ident.unraw().to_string(),
+ None => return,
+ };
+
+ if name != "bitflags" || !config.macro_expansion.bitflags {
+ return;
+ }
+
+ let bitflags = match bitflags::parse(item.mac.tokens.clone()) {
+ Ok(bf) => bf,
+ Err(e) => {
+ warn!("Failed to parse bitflags invocation: {:?}", e);
+ return;
+ }
+ };
+
+ let (struct_, impl_) = bitflags.expand();
+ if let Some(struct_) = struct_ {
+ self.load_syn_struct(config, crate_name, mod_cfg, &struct_);
+ }
+ if let syn::Type::Path(ref path) = *impl_.self_ty {
+ if let Some(type_name) = path.path.get_ident() {
+ self.structs
+ .for_items_mut(&Path::new(type_name.unraw().to_string()), |item| {
+ item.annotations
+ .add_default("internal-derive-bitflags", AnnotationValue::Bool(true));
+ });
+ }
+ }
+ self.load_syn_assoc_consts_from_impl(crate_name, mod_cfg, &impl_)
+ }
+}
diff --git a/src/bindgen/rename.rs b/src/bindgen/rename.rs
new file mode 100644
index 0000000..f594939
--- /dev/null
+++ b/src/bindgen/rename.rs
@@ -0,0 +1,140 @@
+/* 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::borrow::Cow;
+use std::str::FromStr;
+
+/// The type of identifier to be renamed.
+#[derive(Debug, Clone, Copy)]
+pub enum IdentifierType<'a> {
+ StructMember,
+ EnumVariant { prefix: &'a str },
+ FunctionArg,
+ Type,
+ Enum,
+}
+
+impl<'a> IdentifierType<'a> {
+ fn to_str(self) -> &'static str {
+ match self {
+ IdentifierType::StructMember => "m",
+ IdentifierType::EnumVariant { .. } => "",
+ IdentifierType::FunctionArg => "a",
+ IdentifierType::Type => "",
+ IdentifierType::Enum => "",
+ }
+ }
+}
+
+/// A rule to apply to an identifier when generating bindings.
+#[derive(Debug, Clone, Copy, Default)]
+pub enum RenameRule {
+ /// Do not apply any renaming. The default.
+ #[default]
+ None,
+ /// Converts the identifier to PascalCase and adds a context dependent prefix
+ GeckoCase,
+ /// Converts the identifier to lower case.
+ LowerCase,
+ /// Converts the identifier to upper case.
+ UpperCase,
+ /// Converts the identifier to PascalCase.
+ PascalCase,
+ /// Converts the identifier to camelCase.
+ CamelCase,
+ /// Converts the identifier to snake_case.
+ SnakeCase,
+ /// Converts the identifier to SCREAMING_SNAKE_CASE.
+ ScreamingSnakeCase,
+ /// Converts the identifier to SCREAMING_SNAKE_CASE and prefixes enum variants
+ /// with the enum name.
+ QualifiedScreamingSnakeCase,
+}
+
+impl RenameRule {
+ pub(crate) fn not_none(self) -> Option<Self> {
+ match self {
+ RenameRule::None => None,
+ other => Some(other),
+ }
+ }
+
+ /// Applies the rename rule to a string
+ pub fn apply<'a>(self, text: &'a str, context: IdentifierType) -> Cow<'a, str> {
+ use heck::*;
+
+ if text.is_empty() {
+ return Cow::Borrowed(text);
+ }
+
+ Cow::Owned(match self {
+ RenameRule::None => return Cow::Borrowed(text),
+ RenameRule::GeckoCase => context.to_str().to_owned() + &text.to_upper_camel_case(),
+ RenameRule::LowerCase => text.to_lowercase(),
+ RenameRule::UpperCase => text.to_uppercase(),
+ RenameRule::PascalCase => text.to_pascal_case(),
+ RenameRule::CamelCase => text.to_lower_camel_case(),
+ RenameRule::SnakeCase => text.to_snake_case(),
+ RenameRule::ScreamingSnakeCase => text.to_shouty_snake_case(),
+ RenameRule::QualifiedScreamingSnakeCase => {
+ let mut result = String::new();
+
+ if let IdentifierType::EnumVariant { prefix } = context {
+ result.push_str(
+ &RenameRule::ScreamingSnakeCase.apply(prefix, IdentifierType::Enum),
+ );
+ result.push('_');
+ }
+
+ result.push_str(&RenameRule::ScreamingSnakeCase.apply(text, context));
+ result
+ }
+ })
+ }
+}
+
+impl FromStr for RenameRule {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<RenameRule, Self::Err> {
+ match s {
+ "none" => Ok(RenameRule::None),
+ "None" => Ok(RenameRule::None),
+
+ "mGeckoCase" => Ok(RenameRule::GeckoCase),
+ "GeckoCase" => Ok(RenameRule::GeckoCase),
+ "gecko_case" => Ok(RenameRule::GeckoCase),
+
+ "lowercase" => Ok(RenameRule::LowerCase),
+ "LowerCase" => Ok(RenameRule::LowerCase),
+ "lower_case" => Ok(RenameRule::LowerCase),
+
+ "UPPERCASE" => Ok(RenameRule::UpperCase),
+ "UpperCase" => Ok(RenameRule::UpperCase),
+ "upper_case" => Ok(RenameRule::UpperCase),
+
+ "PascalCase" => Ok(RenameRule::PascalCase),
+ "pascal_case" => Ok(RenameRule::PascalCase),
+
+ "camelCase" => Ok(RenameRule::CamelCase),
+ "CamelCase" => Ok(RenameRule::CamelCase),
+ "camel_case" => Ok(RenameRule::CamelCase),
+
+ "snake_case" => Ok(RenameRule::SnakeCase),
+ "SnakeCase" => Ok(RenameRule::SnakeCase),
+
+ "SCREAMING_SNAKE_CASE" => Ok(RenameRule::ScreamingSnakeCase),
+ "ScreamingSnakeCase" => Ok(RenameRule::ScreamingSnakeCase),
+ "screaming_snake_case" => Ok(RenameRule::ScreamingSnakeCase),
+
+ "QUALIFIED_SCREAMING_SNAKE_CASE" => Ok(RenameRule::QualifiedScreamingSnakeCase),
+ "QualifiedScreamingSnakeCase" => Ok(RenameRule::QualifiedScreamingSnakeCase),
+ "qualified_screaming_snake_case" => Ok(RenameRule::QualifiedScreamingSnakeCase),
+
+ _ => Err(format!("Unrecognized RenameRule: '{}'.", s)),
+ }
+ }
+}
+
+deserialize_enum_str!(RenameRule);
diff --git a/src/bindgen/reserved.rs b/src/bindgen/reserved.rs
new file mode 100644
index 0000000..4983f99
--- /dev/null
+++ b/src/bindgen/reserved.rs
@@ -0,0 +1,91 @@
+/* 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/. */
+
+/// Taken from `https://en.cppreference.com/w/cpp/keyword`
+/// Some experimental keywords were filtered out and the resulting list was
+/// sorted using a rust program.
+const RESERVED_KEYWORDS: &[&str] = &[
+ "alignas",
+ "alignof",
+ "auto",
+ "bool",
+ "break",
+ "case",
+ "catch",
+ "char",
+ "char16_t",
+ "char32_t",
+ "char8_t",
+ "class",
+ "const",
+ "const_cast",
+ "consteval",
+ "constexpr",
+ "continue",
+ "decltype",
+ "default",
+ "delete",
+ "do",
+ "double",
+ "dynamic_cast",
+ "else",
+ "enum",
+ "explicit",
+ "export",
+ "extern",
+ "false",
+ "float",
+ "for",
+ "friend",
+ "goto",
+ "if",
+ "inline",
+ "int",
+ "long",
+ "mutable",
+ "namespace",
+ "new",
+ "noexcept",
+ "nullptr",
+ "operator",
+ "private",
+ "protected",
+ "public",
+ "register",
+ "reinterpret_cast",
+ "return",
+ "short",
+ "signed",
+ "sizeof",
+ "static",
+ "static_assert",
+ "static_cast",
+ "struct",
+ "switch",
+ "template",
+ "this",
+ "thread_local",
+ "throw",
+ "true",
+ "try",
+ "typedef",
+ "typename",
+ "union",
+ "unsigned",
+ "using",
+ "virtual",
+ "void",
+ "volatile",
+ "wchar_t",
+ "while",
+];
+
+pub fn escape(rust_identifier: &mut String) {
+ if RESERVED_KEYWORDS
+ .binary_search(&rust_identifier.as_ref())
+ .is_ok()
+ {
+ rust_identifier.push('_');
+ }
+}
diff --git a/src/bindgen/utilities.rs b/src/bindgen/utilities.rs
new file mode 100644
index 0000000..0e314b9
--- /dev/null
+++ b/src/bindgen/utilities.rs
@@ -0,0 +1,348 @@
+/* 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/. */
+
+#![allow(clippy::redundant_closure_call)]
+
+use syn::ext::IdentExt;
+
+pub trait IterHelpers: Iterator {
+ fn try_skip_map<F, T, E>(&mut self, f: F) -> Result<Vec<T>, E>
+ where
+ F: FnMut(&Self::Item) -> Result<Option<T>, E>;
+}
+
+impl<I> IterHelpers for I
+where
+ I: Iterator,
+{
+ fn try_skip_map<F, T, E>(&mut self, mut f: F) -> Result<Vec<T>, E>
+ where
+ F: FnMut(&Self::Item) -> Result<Option<T>, E>,
+ {
+ let mut out = Vec::new();
+ for item in self {
+ if let Some(x) = f(&item)? {
+ out.push(x);
+ }
+ }
+ Ok(out)
+ }
+}
+
+pub trait SynItemHelpers: SynAttributeHelpers {
+ fn exported_name(&self) -> Option<String>;
+}
+
+impl SynItemHelpers for syn::ItemFn {
+ fn exported_name(&self) -> Option<String> {
+ self.attrs
+ .attr_name_value_lookup("export_name")
+ .or_else(|| {
+ if self.is_no_mangle() {
+ Some(self.sig.ident.unraw().to_string())
+ } else {
+ None
+ }
+ })
+ }
+}
+
+impl SynItemHelpers for syn::ImplItemMethod {
+ fn exported_name(&self) -> Option<String> {
+ self.attrs
+ .attr_name_value_lookup("export_name")
+ .or_else(|| {
+ if self.is_no_mangle() {
+ Some(self.sig.ident.unraw().to_string())
+ } else {
+ None
+ }
+ })
+ }
+}
+
+impl SynItemHelpers for syn::ItemStatic {
+ fn exported_name(&self) -> Option<String> {
+ self.attrs
+ .attr_name_value_lookup("export_name")
+ .or_else(|| {
+ if self.is_no_mangle() {
+ Some(self.ident.unraw().to_string())
+ } else {
+ None
+ }
+ })
+ }
+}
+
+/// Returns whether this attribute causes us to skip at item. This basically
+/// checks for `#[cfg(test)]`, `#[test]`, `/// cbindgen::ignore` and
+/// variations thereof.
+fn is_skip_item_attr(attr: &syn::Meta) -> bool {
+ match *attr {
+ syn::Meta::Path(ref path) => {
+ // TODO(emilio): It'd be great if rustc allowed us to use a syntax
+ // like `#[cbindgen::ignore]` or such.
+ path.is_ident("test")
+ }
+ syn::Meta::List(ref list) => {
+ if !list.path.is_ident("cfg") {
+ return false;
+ }
+ list.nested.iter().any(|nested| match *nested {
+ syn::NestedMeta::Meta(ref meta) => is_skip_item_attr(meta),
+ syn::NestedMeta::Lit(..) => false,
+ })
+ }
+ syn::Meta::NameValue(ref name_value) => {
+ if name_value.path.is_ident("doc") {
+ if let syn::Lit::Str(ref content) = name_value.lit {
+ // FIXME(emilio): Maybe should use the general annotation
+ // mechanism, but it seems overkill for this.
+ if content.value().trim() == "cbindgen:ignore" {
+ return true;
+ }
+ }
+ }
+ false
+ }
+ }
+}
+
+pub trait SynAttributeHelpers {
+ /// Returns the list of attributes for an item.
+ fn attrs(&self) -> &[syn::Attribute];
+
+ /// Searches for attributes like `#[test]`.
+ /// Example:
+ /// - `item.has_attr_word("test")` => `#[test]`
+ fn has_attr_word(&self, name: &str) -> bool {
+ self.attrs()
+ .iter()
+ .filter_map(|x| x.parse_meta().ok())
+ .any(|attr| {
+ if let syn::Meta::Path(ref path) = attr {
+ path.is_ident(name)
+ } else {
+ false
+ }
+ })
+ }
+
+ fn find_deprecated_note(&self) -> Option<String> {
+ let attrs = self.attrs();
+ // #[deprecated = ""]
+ if let Some(note) = attrs.attr_name_value_lookup("deprecated") {
+ return Some(note);
+ }
+
+ // #[deprecated]
+ if attrs.has_attr_word("deprecated") {
+ return Some(String::new());
+ }
+
+ // #[deprecated(note = "")]
+ let attr = attrs.iter().find(|attr| {
+ if let Ok(syn::Meta::List(list)) = attr.parse_meta() {
+ list.path.is_ident("deprecated")
+ } else {
+ false
+ }
+ })?;
+
+ let args: syn::punctuated::Punctuated<syn::MetaNameValue, Token![,]> =
+ match attr.parse_args_with(syn::punctuated::Punctuated::parse_terminated) {
+ Ok(args) => args,
+ Err(_) => {
+ warn!("couldn't parse deprecated attribute");
+ return None;
+ }
+ };
+
+ let arg = args.iter().find(|arg| arg.path.is_ident("note"))?;
+ if let syn::Lit::Str(ref lit) = arg.lit {
+ Some(lit.value())
+ } else {
+ warn!("deprecated attribute must be a string");
+ None
+ }
+ }
+
+ fn is_no_mangle(&self) -> bool {
+ self.has_attr_word("no_mangle")
+ }
+
+ /// Sees whether we should skip parsing a given item.
+ fn should_skip_parsing(&self) -> bool {
+ for attr in self.attrs() {
+ let meta = match attr.parse_meta() {
+ Ok(attr) => attr,
+ Err(..) => return false,
+ };
+ if is_skip_item_attr(&meta) {
+ return true;
+ }
+ }
+
+ false
+ }
+
+ fn attr_name_value_lookup(&self, name: &str) -> Option<String> {
+ self.attrs()
+ .iter()
+ .filter_map(|attr| {
+ let attr = attr.parse_meta().ok()?;
+ if let syn::Meta::NameValue(syn::MetaNameValue {
+ path,
+ lit: syn::Lit::Str(lit),
+ ..
+ }) = attr
+ {
+ if path.is_ident(name) {
+ return Some(lit.value());
+ }
+ }
+ None
+ })
+ .next()
+ }
+
+ fn get_comment_lines(&self) -> Vec<String> {
+ let mut comment = Vec::new();
+
+ for attr in self.attrs() {
+ if attr.style == syn::AttrStyle::Outer {
+ if let Ok(syn::Meta::NameValue(syn::MetaNameValue {
+ path,
+ lit: syn::Lit::Str(content),
+ ..
+ })) = attr.parse_meta()
+ {
+ if path.is_ident("doc") {
+ comment.extend(split_doc_attr(&content.value()));
+ }
+ }
+ }
+ }
+
+ comment
+ }
+}
+
+macro_rules! syn_item_match_helper {
+ ($s:ident => has_attrs: |$i:ident| $a:block, otherwise: || $b:block) => {
+ match *$s {
+ syn::Item::Const(ref $i) => $a,
+ syn::Item::Enum(ref $i) => $a,
+ syn::Item::ExternCrate(ref $i) => $a,
+ syn::Item::Fn(ref $i) => $a,
+ syn::Item::ForeignMod(ref $i) => $a,
+ syn::Item::Impl(ref $i) => $a,
+ syn::Item::Macro(ref $i) => $a,
+ syn::Item::Macro2(ref $i) => $a,
+ syn::Item::Mod(ref $i) => $a,
+ syn::Item::Static(ref $i) => $a,
+ syn::Item::Struct(ref $i) => $a,
+ syn::Item::Trait(ref $i) => $a,
+ syn::Item::Type(ref $i) => $a,
+ syn::Item::Union(ref $i) => $a,
+ syn::Item::Use(ref $i) => $a,
+ syn::Item::TraitAlias(ref $i) => $a,
+ syn::Item::Verbatim(_) => $b,
+ _ => panic!("Unhandled syn::Item: {:?}", $s),
+ }
+ };
+}
+
+impl SynAttributeHelpers for syn::Item {
+ fn attrs(&self) -> &[syn::Attribute] {
+ syn_item_match_helper!(self =>
+ has_attrs: |item| { &item.attrs },
+ otherwise: || { &[] }
+ )
+ }
+}
+
+macro_rules! impl_syn_item_helper {
+ ($t:ty) => {
+ impl SynAttributeHelpers for $t {
+ fn attrs(&self) -> &[syn::Attribute] {
+ &self.attrs
+ }
+ }
+ };
+}
+
+impl_syn_item_helper!(syn::ItemExternCrate);
+impl_syn_item_helper!(syn::ItemUse);
+impl_syn_item_helper!(syn::ItemStatic);
+impl_syn_item_helper!(syn::ItemConst);
+impl_syn_item_helper!(syn::ItemFn);
+impl_syn_item_helper!(syn::ImplItemMethod);
+impl_syn_item_helper!(syn::ItemMod);
+impl_syn_item_helper!(syn::ItemForeignMod);
+impl_syn_item_helper!(syn::ItemType);
+impl_syn_item_helper!(syn::ItemStruct);
+impl_syn_item_helper!(syn::ItemEnum);
+impl_syn_item_helper!(syn::ItemUnion);
+impl_syn_item_helper!(syn::ItemTrait);
+impl_syn_item_helper!(syn::ItemImpl);
+impl_syn_item_helper!(syn::ItemMacro);
+impl_syn_item_helper!(syn::ItemMacro2);
+impl_syn_item_helper!(syn::ItemTraitAlias);
+
+/// Helper function for accessing Abi information
+pub trait SynAbiHelpers {
+ fn is_c(&self) -> bool;
+ fn is_omitted(&self) -> bool;
+}
+
+impl SynAbiHelpers for Option<syn::Abi> {
+ fn is_c(&self) -> bool {
+ if let Some(ref abi) = *self {
+ if let Some(ref lit_string) = abi.name {
+ return matches!(lit_string.value().as_str(), "C" | "C-unwind");
+ }
+ }
+ false
+ }
+ fn is_omitted(&self) -> bool {
+ if let Some(ref abi) = *self {
+ abi.name.is_none()
+ } else {
+ false
+ }
+ }
+}
+
+impl SynAbiHelpers for syn::Abi {
+ fn is_c(&self) -> bool {
+ if let Some(ref lit_string) = self.name {
+ matches!(lit_string.value().as_str(), "C" | "C-unwind")
+ } else {
+ false
+ }
+ }
+ fn is_omitted(&self) -> bool {
+ self.name.is_none()
+ }
+}
+
+impl SynAttributeHelpers for [syn::Attribute] {
+ fn attrs(&self) -> &[syn::Attribute] {
+ self
+ }
+}
+
+fn split_doc_attr(input: &str) -> Vec<String> {
+ input
+ // Convert two newline (indicate "new paragraph") into two line break.
+ .replace("\n\n", " \n \n")
+ // Convert newline after two spaces (indicate "line break") into line break.
+ .split(" \n")
+ // Convert single newline (indicate hard-wrapped) into space.
+ .map(|s| s.replace('\n', " "))
+ .map(|s| s.trim_end().to_string())
+ .collect()
+}
diff --git a/src/bindgen/writer.rs b/src/bindgen/writer.rs
new file mode 100644
index 0000000..eed6917
--- /dev/null
+++ b/src/bindgen/writer.rs
@@ -0,0 +1,258 @@
+/* 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::cmp;
+use std::io;
+use std::io::Write;
+
+use crate::bindgen::config::{Braces, Config, Language};
+use crate::bindgen::Bindings;
+
+/// A type of way to format a list.
+pub enum ListType<'a> {
+ /// Join each adjacent item with a str.
+ Join(&'a str),
+ /// End each item with a str.
+ Cap(&'a str),
+}
+
+/// A utility wrapper to write unbuffered data and correctly adjust positions.
+struct InnerWriter<'a, 'b: 'a, F: 'a + Write>(&'a mut SourceWriter<'b, F>);
+
+impl<'a, 'b, F: Write> Write for InnerWriter<'a, 'b, F> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let writer = &mut self.0;
+
+ if !writer.line_started {
+ for _ in 0..writer.spaces() {
+ write!(writer.out, " ").unwrap();
+ }
+ writer.line_started = true;
+ writer.line_length += writer.spaces();
+ }
+
+ let written = writer.out.write(buf)?;
+ writer.line_length += written;
+ writer.max_line_length = cmp::max(writer.max_line_length, writer.line_length);
+ Ok(written)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.0.out.flush()
+ }
+}
+
+/// A utility writer for generating code easier.
+pub struct SourceWriter<'a, F: Write> {
+ out: F,
+ bindings: &'a Bindings,
+ spaces: Vec<usize>,
+ line_started: bool,
+ line_length: usize,
+ line_number: usize,
+ max_line_length: usize,
+}
+
+pub type MeasureWriter<'a> = SourceWriter<'a, &'a mut Vec<u8>>;
+
+impl<'a, F: Write> SourceWriter<'a, F> {
+ pub fn new(out: F, bindings: &'a Bindings) -> Self {
+ SourceWriter {
+ out,
+ bindings,
+ spaces: vec![0],
+ line_started: false,
+ line_length: 0,
+ line_number: 1,
+ max_line_length: 0,
+ }
+ }
+
+ pub fn bindings(&self) -> &Bindings {
+ self.bindings
+ }
+
+ /// Takes a function that writes source and returns the maximum line length
+ /// written.
+ pub fn try_write<T>(&mut self, func: T, max_line_length: usize) -> bool
+ where
+ T: Fn(&mut MeasureWriter),
+ {
+ if self.line_length > max_line_length {
+ return false;
+ }
+
+ let mut buffer = Vec::new();
+ let line_length = {
+ let mut measurer = SourceWriter {
+ out: &mut buffer,
+ bindings: self.bindings,
+ spaces: self.spaces.clone(),
+ line_started: self.line_started,
+ line_length: self.line_length,
+ line_number: self.line_number,
+ max_line_length: self.line_length,
+ };
+
+ func(&mut measurer);
+
+ measurer.max_line_length
+ };
+
+ if line_length > max_line_length {
+ return false;
+ }
+ // We don't want the extra alignment, it's already accounted for by the
+ // measurer.
+ self.line_started = true;
+ InnerWriter(self).write_all(&buffer).unwrap();
+ true
+ }
+
+ fn spaces(&self) -> usize {
+ *self.spaces.last().unwrap()
+ }
+
+ pub fn push_set_spaces(&mut self, spaces: usize) {
+ self.spaces.push(spaces);
+ }
+
+ pub fn pop_set_spaces(&mut self) {
+ self.pop_tab()
+ }
+
+ pub fn line_length_for_align(&self) -> usize {
+ if self.line_started {
+ self.line_length
+ } else {
+ self.line_length + self.spaces()
+ }
+ }
+
+ pub fn push_tab(&mut self) {
+ let spaces = self.spaces() - (self.spaces() % self.bindings.config.tab_width)
+ + self.bindings.config.tab_width;
+ self.spaces.push(spaces);
+ }
+
+ pub fn pop_tab(&mut self) {
+ assert!(!self.spaces.is_empty());
+ self.spaces.pop();
+ }
+
+ pub fn new_line(&mut self) {
+ self.out
+ .write_all(self.bindings.config.line_endings.as_str().as_bytes())
+ .unwrap();
+ self.line_started = false;
+ self.line_length = 0;
+ self.line_number += 1;
+ }
+
+ pub fn new_line_if_not_start(&mut self) {
+ if self.line_number != 1 {
+ self.new_line();
+ }
+ }
+
+ pub fn open_brace(&mut self) {
+ match self.bindings.config.language {
+ Language::Cxx | Language::C => match self.bindings.config.braces {
+ Braces::SameLine => {
+ self.write(" {");
+ self.push_tab();
+ self.new_line();
+ }
+ Braces::NextLine => {
+ self.new_line();
+ self.write("{");
+ self.push_tab();
+ self.new_line();
+ }
+ },
+ Language::Cython => {
+ self.write(":");
+ self.new_line();
+ self.push_tab();
+ }
+ }
+ }
+
+ pub fn close_brace(&mut self, semicolon: bool) {
+ self.pop_tab();
+ match self.bindings.config.language {
+ Language::Cxx | Language::C => {
+ self.new_line();
+ if semicolon {
+ self.write("};");
+ } else {
+ self.write("}");
+ }
+ }
+ Language::Cython => {}
+ }
+ }
+
+ pub fn write(&mut self, text: &'static str) {
+ write!(self, "{}", text);
+ }
+
+ pub fn write_raw_block(&mut self, block: &str) {
+ self.line_started = true;
+ write!(self, "{}", block);
+ }
+
+ pub fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) {
+ InnerWriter(self).write_fmt(fmt).unwrap();
+ }
+
+ pub fn write_horizontal_source_list<S: Source>(
+ &mut self,
+ items: &[S],
+ list_type: ListType<'_>,
+ ) {
+ for (i, item) in items.iter().enumerate() {
+ item.write(&self.bindings.config, self);
+
+ match list_type {
+ ListType::Join(text) => {
+ if i != items.len() - 1 {
+ write!(self, "{}", text);
+ }
+ }
+ ListType::Cap(text) => {
+ write!(self, "{}", text);
+ }
+ }
+ }
+ }
+
+ pub fn write_vertical_source_list<S: Source>(&mut self, items: &[S], list_type: ListType<'_>) {
+ let align_length = self.line_length_for_align();
+ self.push_set_spaces(align_length);
+ for (i, item) in items.iter().enumerate() {
+ item.write(&self.bindings.config, self);
+
+ match list_type {
+ ListType::Join(text) => {
+ if i != items.len() - 1 {
+ write!(self, "{}", text);
+ }
+ }
+ ListType::Cap(text) => {
+ write!(self, "{}", text);
+ }
+ }
+
+ if i != items.len() - 1 {
+ self.new_line();
+ }
+ }
+ self.pop_tab();
+ }
+}
+
+pub trait Source {
+ fn write<F: Write>(&self, config: &Config, _: &mut SourceWriter<F>);
+}