From ef4a519ea1d3676a7ea8fdcbee17adda0b22df08 Mon Sep 17 00:00:00 2001 From: Thibaut Lorrain Date: Thu, 29 Jun 2023 17:19:56 +0200 Subject: [PATCH 1/3] introduce the concept of language backend --- src/bindgen/bindings.rs | 381 +------- src/bindgen/cdecl.rs | 61 +- src/bindgen/ir/constant.rs | 126 +-- src/bindgen/ir/documentation.rs | 77 -- src/bindgen/ir/enumeration.rs | 209 ++--- src/bindgen/ir/field.rs | 39 +- src/bindgen/ir/function.rs | 120 +-- src/bindgen/ir/generic_path.rs | 32 +- src/bindgen/ir/global.rs | 17 - src/bindgen/ir/opaque.rs | 46 +- src/bindgen/ir/structure.rs | 349 +------- src/bindgen/ir/ty.rs | 21 - src/bindgen/ir/typedef.rs | 45 +- src/bindgen/ir/union.rs | 85 +- src/bindgen/language_backend/clike.rs | 1141 ++++++++++++++++++++++++ src/bindgen/language_backend/cython.rs | 569 ++++++++++++ src/bindgen/language_backend/mod.rs | 141 +++ src/bindgen/mod.rs | 1 + src/bindgen/writer.rs | 33 +- 19 files changed, 2058 insertions(+), 1435 deletions(-) create mode 100644 src/bindgen/language_backend/clike.rs create mode 100644 src/bindgen/language_backend/cython.rs create mode 100644 src/bindgen/language_backend/mod.rs diff --git a/src/bindgen/bindings.rs b/src/bindgen/bindings.rs index 80793db4b..029cfc66b 100644 --- a/src/bindgen/bindings.rs +++ b/src/bindgen/bindings.rs @@ -13,9 +13,12 @@ use std::rc::Rc; use crate::bindgen::config::{Config, Language}; use crate::bindgen::ir::{ - Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Typedef, + Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Type, Typedef, }; -use crate::bindgen::writer::{Source, SourceWriter}; +use crate::bindgen::language_backend::{ + CLikeLanguageBackend, CythonLanguageBackend, LanguageBackend, +}; +use crate::bindgen::writer::SourceWriter; /// A bindings header that can be written. pub struct Bindings { @@ -25,21 +28,15 @@ pub struct Bindings { struct_map: ItemMap, typedef_map: ItemMap, struct_fileds_memo: RefCell>>>, - globals: Vec, - constants: Vec, - items: Vec, - functions: Vec, + pub globals: Vec, + pub constants: Vec, + pub items: Vec, + pub functions: Vec, source_files: Vec, /// Bindings are generated by a recursive call to cbindgen /// and shouldn't do anything when written anywhere. noop: bool, - package_version: String, -} - -#[derive(PartialEq, Eq)] -enum NamespaceOperation { - Open, - Close, + pub package_version: String, } impl Bindings { @@ -80,8 +77,6 @@ impl Bindings { /// 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; @@ -207,360 +202,28 @@ impl Bindings { } } - pub fn write_headers(&self, out: &mut SourceWriter) { - if self.noop { - return; - } - - if self.config.package_version { - out.new_line_if_not_start(); - match self.config.language { - Language::C | Language::Cxx => { - write!(out, "/* Package version: {} */", self.package_version); - } - Language::Cython => { - write!(out, "''' Package version: {} '''", self.package_version); - } - } - - out.new_line(); - } - - 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(); - match self.config.language { - Language::C | Language::Cxx => { - write!( - out, - "/* Generated with cbindgen:{} */", - crate::bindgen::config::VERSION - ); - } - Language::Cython => { - 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 "); - out.new_line(); - out.write("#include "); - out.new_line(); - if self.config.usize_is_size_t { - out.write("#include "); - out.new_line(); - } - out.write("#include "); - out.new_line(); - out.write("#include "); - out.new_line(); - } - Language::Cxx => { - out.write("#include "); - out.new_line(); - if self.config.usize_is_size_t { - out.write("#include "); - out.new_line(); - } - out.write("#include "); - out.new_line(); - out.write("#include "); - out.new_line(); - out.write("#include "); - out.new_line(); - out.write("#include "); - 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 "); - 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); - } + pub fn write(&self, file: F) { + match self.config.language { + Language::Cxx | Language::C => { + self.write_with_backend(file, &mut CLikeLanguageBackend::new(&self.config)) } - } - - 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(); + Language::Cython => { + self.write_with_backend(file, &mut CythonLanguageBackend::new(&self.config)) } } - - if let Some(ref line) = self.config.after_includes { - write!(out, "{}", line); - out.new_line(); - } } - pub fn write(&self, file: F) { + fn write_with_backend( + &self, + file: F, + language_backend: &mut LB, + ) { 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(&self, op: NamespaceOperation, out: &mut SourceWriter) { - 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(&self, out: &mut SourceWriter) { - self.open_close_namespaces(NamespaceOperation::Open, out); - } - - pub(crate) fn close_namespaces(&self, out: &mut SourceWriter) { - self.open_close_namespaces(NamespaceOperation::Close, out); + language_backend.write_bindings(&mut out, self); } } diff --git a/src/bindgen/cdecl.rs b/src/bindgen/cdecl.rs index a9c5a0fcf..c4bef9346 100644 --- a/src/bindgen/cdecl.rs +++ b/src/bindgen/cdecl.rs @@ -7,6 +7,7 @@ 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::language_backend::LanguageBackend; use crate::bindgen::writer::{ListType, SourceWriter}; use crate::bindgen::{Config, Language}; @@ -191,7 +192,13 @@ impl CDecl { } } - fn write(&self, out: &mut SourceWriter, ident: Option<&str>, config: &Config) { + fn write( + &self, + language_backend: &mut LB, + out: &mut SourceWriter, + ident: Option<&str>, + config: &Config, + ) { // Write the type-specifier and type-qualifier first if !self.type_qualifers.is_empty() { write!(out, "{} ", self.type_qualifers); @@ -207,7 +214,15 @@ impl CDecl { if !self.type_generic_args.is_empty() { out.write("<"); - out.write_horizontal_source_list(&self.type_generic_args, ListType::Join(", ")); + out.write_horizontal_source_list( + language_backend, + &self.type_generic_args, + ListType::Join(", "), + |language_backend, out, g| match *g { + GenericArgument::Type(ref ty) => language_backend.write_type(out, ty), + GenericArgument::Const(ref expr) => write!(out, "{}", expr.as_str()), + }, + ); out.write(">"); } @@ -289,7 +304,8 @@ impl CDecl { out.write("void"); } - fn write_vertical( + fn write_vertical( + language_backend: &mut LB, out: &mut SourceWriter, config: &Config, args: &[(Option, CDecl)], @@ -305,12 +321,13 @@ impl CDecl { // Convert &Option to Option<&str> let arg_ident = arg_ident.as_ref().map(|x| x.as_ref()); - arg_ty.write(out, arg_ident, config); + arg_ty.write(language_backend, out, arg_ident, config); } out.pop_tab(); } - fn write_horizontal( + fn write_horizontal( + language_backend: &mut LB, out: &mut SourceWriter, config: &Config, args: &[(Option, CDecl)], @@ -323,19 +340,19 @@ impl CDecl { // Convert &Option to Option<&str> let arg_ident = arg_ident.as_ref().map(|x| x.as_ref()); - arg_ty.write(out, arg_ident, config); + arg_ty.write(language_backend, out, arg_ident, config); } } match layout { - Layout::Vertical => write_vertical(out, config, args), - Layout::Horizontal => write_horizontal(out, config, args), + Layout::Vertical => write_vertical(language_backend, out, config, args), + Layout::Horizontal => write_horizontal(language_backend, out, config, args), Layout::Auto => { if !out.try_write( - |out| write_horizontal(out, config, args), + |out| write_horizontal(language_backend, out, config, args), config.line_length, ) { - write_vertical(out, config, args) + write_vertical(language_backend, out, config, args) } } } @@ -354,19 +371,31 @@ impl CDecl { } } -pub fn write_func( +pub fn write_func( + language_backend: &mut LB, out: &mut SourceWriter, f: &Function, layout: Layout, config: &Config, ) { - CDecl::from_func(f, layout, config).write(out, Some(f.path().name()), config); + CDecl::from_func(f, layout, config).write(language_backend, out, Some(f.path().name()), config); } -pub fn write_field(out: &mut SourceWriter, t: &Type, ident: &str, config: &Config) { - CDecl::from_type(t, config).write(out, Some(ident), config); +pub fn write_field( + language_backend: &mut LB, + out: &mut SourceWriter, + t: &Type, + ident: &str, + config: &Config, +) { + CDecl::from_type(t, config).write(language_backend, out, Some(ident), config); } -pub fn write_type(out: &mut SourceWriter, t: &Type, config: &Config) { - CDecl::from_type(t, config).write(out, None, config); +pub fn write_type( + language_backend: &mut LB, + out: &mut SourceWriter, + t: &Type, + config: &Config, +) { + CDecl::from_type(t, config).write(language_backend, out, None, config); } diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs index 51e59b2d9..d884b6e9d 100644 --- a/src/bindgen/ir/constant.rs +++ b/src/bindgen/ir/constant.rs @@ -16,8 +16,9 @@ use crate::bindgen::ir::{ AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path, Struct, ToCondition, Type, }; +use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::library::Library; -use crate::bindgen::writer::{Source, SourceWriter}; +use crate::bindgen::writer::SourceWriter; use crate::bindgen::Bindings; fn member_to_ident(member: &syn::Member) -> String { @@ -28,7 +29,7 @@ fn member_to_ident(member: &syn::Member) -> String { } // TODO: Maybe add support to more std associated constants. -fn to_known_assoc_constant(associated_to: &Path, name: &str) -> Option { +pub(crate) fn to_known_assoc_constant(associated_to: &Path, name: &str) -> Option { use crate::bindgen::ir::{IntKind, PrimitiveType}; if name != "MAX" && name != "MIN" { @@ -472,107 +473,6 @@ impl Literal { _ => Err(format!("Unsupported expression. {:?}", *expr)), } } - - pub(crate) fn write(&self, config: &Config, out: &mut SourceWriter) { - 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)] @@ -692,9 +592,10 @@ impl Item for Constant { } impl Constant { - pub fn write_declaration( + pub fn write_declaration( &self, config: &Config, + language_backend: &mut LB, out: &mut SourceWriter, associated_to_struct: &Struct, ) { @@ -709,13 +610,14 @@ impl Constant { } else { out.write("static const "); } - self.ty.write(config, out); + language_backend.write_type(out, &self.ty); write!(out, " {};", self.export_name()) } - pub fn write( + pub fn write( &self, config: &Config, + language_backend: &mut LB, out: &mut SourceWriter, associated_to_struct: Option<&Struct>, ) { @@ -770,7 +672,7 @@ impl Constant { _ => &self.value, }; - self.documentation.write(config, out); + language_backend.write_documentation(out, &self.documentation); let allow_constexpr = config.constant.allow_constexpr && self.value.can_be_constexpr(); match config.language { @@ -789,22 +691,22 @@ impl Constant { out.write("const "); } - self.ty.write(config, out); + language_backend.write_type(out, &self.ty); write!(out, " {} = ", name); - value.write(config, out); + language_backend.write_literal(out, value); write!(out, ";"); } Language::Cxx | Language::C => { write!(out, "#define {} ", name); - value.write(config, out); + language_backend.write_literal(out, value); } Language::Cython => { out.write("const "); - self.ty.write(config, out); + language_backend.write_type(out, &self.ty); // For extern Cython declarations the initializer is ignored, // but still useful as documentation, so we write it as a comment. write!(out, " {} # = ", name); - value.write(config, out); + language_backend.write_literal(out, value); } } diff --git a/src/bindgen/ir/documentation.rs b/src/bindgen/ir/documentation.rs index 6822c0eaf..e6dba9794 100644 --- a/src/bindgen/ir/documentation.rs +++ b/src/bindgen/ir/documentation.rs @@ -2,11 +2,7 @@ * 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 { @@ -36,76 +32,3 @@ impl Documentation { } } } - -impl Source for Documentation { - fn write(&self, config: &Config, out: &mut SourceWriter) { - 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 index 11139a0fb..d98cae4ff 100644 --- a/src/bindgen/ir/enumeration.rs +++ b/src/bindgen/ir/enumeration.rs @@ -14,12 +14,13 @@ use crate::bindgen::ir::{ GenericArgument, GenericParams, GenericPath, Item, ItemContainer, Literal, Path, Repr, ReprStyle, Struct, ToCondition, Type, }; +use crate::bindgen::language_backend::LanguageBackend; 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}; +use crate::bindgen::writer::{ListType, SourceWriter}; #[allow(clippy::large_enum_variant)] #[derive(Debug, Clone)] @@ -46,7 +47,7 @@ impl VariantBody { Self::Empty(AnnotationSet::new()) } - fn annotations(&self) -> &AnnotationSet { + pub fn annotations(&self) -> &AnnotationSet { match *self { Self::Empty(ref anno) => anno, Self::Body { ref body, .. } => &body.annotations, @@ -292,40 +293,6 @@ impl EnumVariant { } } -impl Source for EnumVariant { - fn write(&self, config: &Config, out: &mut SourceWriter) { - 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(note) = self - .body - .annotations() - .deprecated_note(config, DeprecatedNoteKind::EnumVariant) - { - write!(out, " {}", note); - } - - 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, @@ -341,12 +308,12 @@ pub struct Enum { impl Enum { /// Name of the generated tag enum. - fn tag_name(&self) -> &str { + pub(crate) 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 { + pub(crate) fn inline_tag_field(repr: &Repr) -> bool { repr.style != ReprStyle::C } @@ -663,99 +630,23 @@ impl Item for Enum { } } -impl Source for Enum { - fn write(&self, config: &Config, out: &mut SourceWriter) { - 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( + pub(crate) fn write_tag_enum< + F: Write, + LB: LanguageBackend, + WV: Fn(&mut LB, &mut SourceWriter, &EnumVariant), + >( &self, config: &Config, + language_backend: &mut LB, out: &mut SourceWriter, size: Option<&str>, - has_data: bool, - tag_name: &str, + write_variant: WV, ) { + let tag_name = self.tag_name(); // Open the tag enum. match config.language { Language::C => { @@ -838,7 +729,7 @@ impl Enum { if i != 0 { out.new_line() } - variant.write(config, out); + write_variant(language_backend, out, variant); } // Close the tag enum. @@ -870,11 +761,11 @@ impl Enum { } // Emit convenience methods for the tag enum. - self.write_derived_functions_enum(config, out, has_data, tag_name); + self.write_derived_functions_enum(config, language_backend, out); } /// The code here mirrors the beginning of `Struct::write` and `Union::write`. - fn open_struct_or_union( + pub(crate) fn open_struct_or_union( &self, config: &Config, out: &mut SourceWriter, @@ -915,7 +806,12 @@ impl Enum { } /// Emit struct definitions for variants having data. - fn write_variant_defs(&self, config: &Config, out: &mut SourceWriter) { + pub(crate) fn write_variant_defs( + &self, + config: &Config, + language_backend: &mut LB, // TODO probably need only one of Config/LanguageBackend + out: &mut SourceWriter, + ) { for variant in &self.variants { if let VariantBody::Body { ref body, @@ -930,7 +826,7 @@ impl Enum { if config.language != Language::Cython { condition.write_before(config, out); } - body.write(config, out); + language_backend.write_struct(out, body); if config.language != Language::Cython { condition.write_after(config, out); } @@ -942,7 +838,7 @@ impl Enum { /// 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( + pub(crate) fn write_tag_field( &self, config: &Config, out: &mut SourceWriter, @@ -971,11 +867,17 @@ impl Enum { } /// Emit fields for all variants with data. - fn write_variant_fields( + pub(crate) fn write_variant_fields< + F: Write, + LB: LanguageBackend, + WF: Fn(&mut LB, &mut SourceWriter, &Field), + >( &self, config: &Config, + language_backend: &mut LB, out: &mut SourceWriter, inline_tag_field: bool, + write_field: WF, ) { let mut first = true; for variant in &self.variants { @@ -1006,7 +908,12 @@ impl Enum { } let start_field = usize::from(inline_tag_field && config.language == Language::Cython); - out.write_vertical_source_list(&body.fields[start_field..], ListType::Cap(";")); + out.write_vertical_source_list( + language_backend, + &body.fields[start_field..], + ListType::Cap(";"), + &write_field, + ); if config.language != Language::Cython { out.close_brace(true); } @@ -1023,13 +930,14 @@ impl Enum { } // Emit convenience methods for enums themselves. - fn write_derived_functions_enum( + fn write_derived_functions_enum( &self, config: &Config, + language_backend: &mut LB, out: &mut SourceWriter, - has_data: bool, - tag_name: &str, ) { + let has_data = self.tag.is_some(); + let tag_name = self.tag_name(); if config.language != Language::Cxx { return; } @@ -1096,7 +1004,12 @@ impl Enum { ) }) .collect(); - out.write_vertical_source_list(&vec[..], ListType::Join("")); + out.write_vertical_source_list( + language_backend, + &vec[..], + ListType::Join(""), + |_, out, s| write!(out, "{}", s), + ); out.close_brace(false); out.new_line(); @@ -1157,7 +1070,12 @@ impl Enum { } }) .collect(); - out.write_vertical_source_list(&vec[..], ListType::Join("")); + out.write_vertical_source_list( + language_backend, + &vec[..], + ListType::Join(""), + |_, out, s| write!(out, "{}", s), + ); out.close_brace(false); out.new_line(); @@ -1168,11 +1086,17 @@ impl Enum { } // Emit convenience methods for structs or unions produced for enums with data. - fn write_derived_functions_data( + pub(crate) fn write_derived_functions_data< + F: Write, + LB: LanguageBackend, + WF: Fn(&mut LB, &mut SourceWriter, &Field), + >( &self, config: &Config, + language_backend: &mut LB, out: &mut SourceWriter, tag_name: &str, + write_field: WF, ) { if config.language != Language::Cxx { return; @@ -1224,7 +1148,12 @@ impl Enum { ) }) .collect(); - out.write_vertical_source_list(&vec[..], ListType::Join(",")); + out.write_vertical_source_list( + language_backend, + &vec[..], + ListType::Join(","), + &write_field, + ); } write!(out, ")"); @@ -1248,13 +1177,13 @@ impl Enum { 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); + language_backend.write_type(out, ty); 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); + language_backend.write_type(out, ty); write!(out, ")({});", arg_renamer(&field.name)); } } @@ -1316,7 +1245,7 @@ impl Enum { is_ref: true, is_nullable: false, }; - return_type.write(config, out); + language_backend.write_type(out, &return_type); } else if const_casts { write!(out, "const {}&", body.export_name()); } else { diff --git a/src/bindgen/ir/field.rs b/src/bindgen/ir/field.rs index 6e132bfaf..73019eee0 100644 --- a/src/bindgen/ir/field.rs +++ b/src/bindgen/ir/field.rs @@ -1,12 +1,7 @@ -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}; +use crate::bindgen::ir::{AnnotationSet, Cfg}; +use crate::bindgen::ir::{Documentation, Path, Type}; #[derive(Debug, Clone)] pub struct Field { @@ -48,33 +43,3 @@ impl Field { }) } } - -impl Source for Field { - fn write(&self, config: &Config, out: &mut SourceWriter) { - // 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 index 8c65f2f9e..579c1cb9f 100644 --- a/src/bindgen/ir/function.rs +++ b/src/bindgen/ir/function.rs @@ -3,24 +3,18 @@ * 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::config::{Config, Language}; 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::ir::{AnnotationSet, Cfg, Documentation, GenericPath, Path, 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 { @@ -220,116 +214,6 @@ impl Function { } } -impl Source for Function { - fn write(&self, config: &Config, out: &mut SourceWriter) { - fn write_1(func: &Function, config: &Config, out: &mut SourceWriter) { - 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(func: &Function, config: &Config, out: &mut SourceWriter) { - 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, String>; } diff --git a/src/bindgen/ir/generic_path.rs b/src/bindgen/ir/generic_path.rs index ef14890ab..60bd3f944 100644 --- a/src/bindgen/ir/generic_path.rs +++ b/src/bindgen/ir/generic_path.rs @@ -7,8 +7,9 @@ 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::language_backend::LanguageBackend; use crate::bindgen::utilities::IterHelpers; -use crate::bindgen::writer::{Source, SourceWriter}; +use crate::bindgen::writer::SourceWriter; #[derive(Debug, Clone)] pub enum GenericParamType { @@ -94,8 +95,9 @@ impl GenericParams { .collect() } - fn write_internal( + pub(crate) fn write_internal( &self, + language_backend: &mut LB, config: &Config, out: &mut SourceWriter, with_default: bool, @@ -114,7 +116,7 @@ impl GenericParams { } } GenericParamType::Const(ref ty) => { - cdecl::write_field(out, ty, item.name.name(), config); + cdecl::write_field(language_backend, out, ty, item.name.name(), config); if with_default { write!(out, " = 0"); } @@ -126,8 +128,13 @@ impl GenericParams { } } - pub fn write_with_default(&self, config: &Config, out: &mut SourceWriter) { - self.write_internal(config, out, true); + pub fn write_with_default( + &self, + language_backend: &mut LB, + config: &Config, + out: &mut SourceWriter, + ) { + self.write_internal(language_backend, config, out, true); } } @@ -139,12 +146,6 @@ impl Deref for GenericParams { } } -impl Source for GenericParams { - fn write(&self, config: &Config, out: &mut SourceWriter) { - 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` are represented as @@ -185,15 +186,6 @@ impl GenericArgument { } } -impl Source for GenericArgument { - fn write(&self, config: &Config, out: &mut SourceWriter) { - 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, diff --git a/src/bindgen/ir/global.rs b/src/bindgen/ir/global.rs index d476947d7..0d42bbc4f 100644 --- a/src/bindgen/ir/global.rs +++ b/src/bindgen/ir/global.rs @@ -2,15 +2,11 @@ * 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 { @@ -107,16 +103,3 @@ impl Item for Static { self.ty.add_dependencies(library, out); } } - -impl Source for Static { - fn write(&self, config: &Config, out: &mut SourceWriter) { - self.documentation.write(config, out); - 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/opaque.rs b/src/bindgen/ir/opaque.rs index 4451d4a16..b14a1c3ba 100644 --- a/src/bindgen/ir/opaque.rs +++ b/src/bindgen/ir/opaque.rs @@ -2,19 +2,15 @@ * 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::config::Config; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ - AnnotationSet, Cfg, ConditionWrite, Documentation, GenericArgument, GenericParams, Item, - ItemContainer, Path, ToCondition, + AnnotationSet, Cfg, Documentation, GenericArgument, GenericParams, Item, ItemContainer, Path, }; 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 { @@ -136,41 +132,3 @@ impl Item for OpaqueItem { out.insert_opaque(self, monomorph, generic_values.to_owned()); } } - -impl Source for OpaqueItem { - fn write(&self, config: &Config, out: &mut SourceWriter) { - 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/structure.rs b/src/bindgen/ir/structure.rs index 2eefa953d..1f1995b73 100644 --- a/src/bindgen/ir/structure.rs +++ b/src/bindgen/ir/structure.rs @@ -10,9 +10,8 @@ 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, + AnnotationSet, Cfg, Constant, Documentation, Field, GenericArgument, GenericParams, Item, + ItemContainer, Path, Repr, ReprAlign, ReprStyle, Type, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; @@ -20,7 +19,7 @@ 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}; +use crate::bindgen::writer::SourceWriter; #[derive(Debug, Clone)] pub struct Struct { @@ -203,7 +202,7 @@ impl Struct { ) } - fn emit_bitflags_binop( + pub(crate) fn emit_bitflags_binop( &self, constexpr_prefix: &str, operator: char, @@ -375,343 +374,3 @@ impl Item for Struct { out.insert_struct(library, self, monomorph, generic_values.to_owned()); } } - -impl Source for Struct { - fn write(&self, config: &Config, out: &mut SourceWriter) { - 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(~{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 index 5a31fb646..a3d846674 100644 --- a/src/bindgen/ir/ty.rs +++ b/src/bindgen/ir/ty.rs @@ -3,11 +3,9 @@ * 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; @@ -15,7 +13,6 @@ 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 { @@ -380,12 +377,6 @@ impl ConstExpr { } } -impl Source for ConstExpr { - fn write(&self, _config: &Config, out: &mut SourceWriter) { - write!(out, "{}", self.as_str()); - } -} - #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum Type { Ptr { @@ -1003,15 +994,3 @@ impl Type { } } } - -impl Source for String { - fn write(&self, _config: &Config, out: &mut SourceWriter) { - write!(out, "{}", self); - } -} - -impl Source for Type { - fn write(&self, config: &Config, out: &mut SourceWriter) { - cdecl::write_type(out, self, config); - } -} diff --git a/src/bindgen/ir/typedef.rs b/src/bindgen/ir/typedef.rs index 626732e2a..dbfdc5ba8 100644 --- a/src/bindgen/ir/typedef.rs +++ b/src/bindgen/ir/typedef.rs @@ -3,21 +3,19 @@ * 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::config::Config; 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, + AnnotationSet, Cfg, Documentation, GenericArgument, GenericParams, Item, ItemContainer, Path, + 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)] @@ -135,11 +133,6 @@ impl Item for Typedef { 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); } @@ -148,6 +141,11 @@ impl Item for Typedef { self.aliased.resolve_declaration_types(resolver); } + 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 add_dependencies(&self, library: &Library, out: &mut Dependencies) { self.aliased .add_dependencies_ignoring_generics(&self.generic_params, library, out); @@ -179,30 +177,3 @@ impl Item for Typedef { out.insert_typedef(library, self, monomorph, generic_values.to_owned()); } } - -impl Source for Typedef { - fn write(&self, config: &Config, out: &mut SourceWriter) { - 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 index 07bd16c58..32db72648 100644 --- a/src/bindgen/ir/union.rs +++ b/src/bindgen/ir/union.rs @@ -2,23 +2,20 @@ * 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::config::{Config, 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, + AnnotationSet, Cfg, Documentation, Field, GenericArgument, GenericParams, Item, ItemContainer, + Path, Repr, ReprAlign, ReprStyle, }; 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 { @@ -258,79 +255,3 @@ impl Item for Union { out.insert_union(library, self, monomorph, generic_values.to_owned()); } } - -impl Source for Union { - fn write(&self, config: &Config, out: &mut SourceWriter) { - 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/language_backend/clike.rs b/src/bindgen/language_backend/clike.rs new file mode 100644 index 000000000..ff23d7c35 --- /dev/null +++ b/src/bindgen/language_backend/clike.rs @@ -0,0 +1,1141 @@ +use crate::bindgen::ir::{ + to_known_assoc_constant, ConditionWrite, DeprecatedNoteKind, Documentation, Enum, EnumVariant, + Field, Function, GenericParams, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, + ToCondition, Type, Typedef, Union, +}; +use crate::bindgen::language_backend::LanguageBackend; +use crate::bindgen::rename::IdentifierType; +use crate::bindgen::writer::{ListType, SourceWriter}; +use crate::bindgen::{cdecl, Bindings, Config, Language, Layout}; +use crate::bindgen::{DocumentationLength, DocumentationStyle}; +use std::io::Write; + +pub struct CLikeLanguageBackend<'a> { + config: &'a Config, +} + +impl<'a> CLikeLanguageBackend<'a> { + pub fn new(config: &'a Config) -> Self { + Self { config } + } + + fn write_enum_variant(&mut self, out: &mut SourceWriter, u: &EnumVariant) { + let condition = u.cfg.to_condition(self.config); + + condition.write_before(self.config, out); + + self.write_documentation(out, &u.documentation); + write!(out, "{}", u.export_name); + if let Some(note) = u + .body + .annotations() + .deprecated_note(self.config, DeprecatedNoteKind::EnumVariant) + { + write!(out, " {}", note); + } + if let Some(discriminant) = &u.discriminant { + out.write(" = "); + + self.write_literal(out, discriminant); + } + out.write(","); + condition.write_after(self.config, out); + } + + fn write_field(&mut self, out: &mut SourceWriter, f: &Field) { + let condition = f.cfg.to_condition(self.config); + condition.write_before(self.config, out); + + self.write_documentation(out, &f.documentation); + cdecl::write_field(self, out, &f.ty, &f.name, self.config); + + if let Some(bitfield) = f.annotations.atom("bitfield") { + write!(out, ": {}", bitfield.unwrap_or_default()); + } + + condition.write_after(self.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(); + } + } + + fn write_generic_param(&mut self, out: &mut SourceWriter, g: &GenericParams) { + g.write_internal(self, self.config, out, false); + } + + fn open_close_namespaces(&mut self, out: &mut SourceWriter, open: bool) { + let mut namespaces = + if self.config.language != Language::Cxx && !self.config.cpp_compatible_c() { + vec![] + } else { + 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 + }; + + if namespaces.is_empty() { + return; + } + + if !open { + 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(); + if open { + write!(out, "namespace {} {{", namespace) + } else { + write!(out, "}} // namespace {}", namespace) + } + } + + out.new_line(); + if self.config.cpp_compatible_c() { + out.write("#endif // __cplusplus"); + out.new_line(); + } + } +} + +impl LanguageBackend for CLikeLanguageBackend<'_> { + fn write_headers(&self, out: &mut SourceWriter, package_version: &str) { + if self.config.package_version { + write!(out, "/* Package version: {} */", package_version); + out.new_line(); + } + 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 { + 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.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 "); + out.new_line(); + out.write("#include "); + out.new_line(); + if self.config.usize_is_size_t { + out.write("#include "); + out.new_line(); + } + out.write("#include "); + out.new_line(); + out.write("#include "); + out.new_line(); + } + Language::Cxx => { + out.write("#include "); + out.new_line(); + if self.config.usize_is_size_t { + out.write("#include "); + out.new_line(); + } + out.write("#include "); + out.new_line(); + out.write("#include "); + out.new_line(); + out.write("#include "); + out.new_line(); + out.write("#include "); + 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 "); + out.new_line(); + } + } + _ => {} + } + } + + 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 let Some(ref line) = self.config.after_includes { + write!(out, "{}", line); + out.new_line(); + } + } + + fn open_namespaces(&mut self, out: &mut SourceWriter) { + self.open_close_namespaces(out, true); + } + + fn close_namespaces(&mut self, out: &mut SourceWriter) { + self.open_close_namespaces(out, false) + } + + fn write_footers(&mut self, out: &mut SourceWriter) { + 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(); + } + } + + fn write_enum(&mut self, out: &mut SourceWriter, e: &Enum) { + let size = e.repr.ty.map(|ty| ty.to_primitive().to_repr_c(self.config)); + let has_data = e.tag.is_some(); + let inline_tag_field = Enum::inline_tag_field(&e.repr); + let tag_name = e.tag_name(); + + let condition = e.cfg.to_condition(self.config); + condition.write_before(self.config, out); + + self.write_documentation(out, &e.documentation); + self.write_generic_param(out, &e.generic_params); + + // 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 && self.config.language == Language::Cxx { + e.open_struct_or_union(self.config, out, inline_tag_field); + } + + // Emit the tag enum and everything related to it. + e.write_tag_enum(self.config, self, out, size, Self::write_enum_variant); + + // If the enum has data, we need to emit structs for the variants and gather them together. + if has_data { + e.write_variant_defs(self.config, self, 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 self.config.language != Language::Cxx { + e.open_struct_or_union(self.config, out, inline_tag_field); + } + + // Emit tag field that is separate from all variants. + e.write_tag_field(self.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. + if !inline_tag_field { + out.write("union"); + out.open_brace(); + } + + // Emit fields for all variants with data. + e.write_variant_fields(self.config, self, out, inline_tag_field, Self::write_field); + + // Close union of all variants with data, only in the non-inline tag scenario. + if !inline_tag_field { + out.close_brace(true); + } + + // Emit convenience methods for the struct or enum for the data. + e.write_derived_functions_data(self.config, self, out, tag_name, Self::write_field); + + // Emit the post_body section, if relevant. + if let Some(body) = self.config.export.post_body(&e.path) { + out.new_line(); + out.write_raw_block(body); + } + + // Close the struct or union opened either at (*) or at (**). + if self.config.language == Language::C && self.config.style.generate_typedef() { + out.close_brace(false); + write!(out, " {};", e.export_name); + } else { + out.close_brace(true); + } + } + + condition.write_after(self.config, out); + } + + fn write_struct(&mut self, out: &mut SourceWriter, s: &Struct) { + if s.is_transparent { + let typedef = Typedef { + path: s.path.clone(), + export_name: s.export_name.to_owned(), + generic_params: s.generic_params.clone(), + aliased: s.fields[0].ty.clone(), + cfg: s.cfg.clone(), + annotations: s.annotations.clone(), + documentation: s.documentation.clone(), + }; + self.write_type_def(out, &typedef); + for constant in &s.associated_constants { + out.new_line(); + constant.write(self.config, self, out, Some(s)); + } + return; + } + + let condition = s.cfg.to_condition(self.config); + condition.write_before(self.config, out); + + self.write_documentation(out, &s.documentation); + + if !s.is_enum_variant_body { + self.write_generic_param(out, &s.generic_params); + } + + // 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 self.config.language { + Language::C if self.config.style.generate_typedef() => out.write("typedef "), + Language::C | Language::Cxx => {} + _ => unreachable!(), + } + + out.write("struct"); + + if let Some(align) = s.alignment { + match align { + ReprAlign::Packed => { + if let Some(ref anno) = self.config.layout.packed { + write!(out, " {}", anno); + } + } + ReprAlign::Align(n) => { + if let Some(ref anno) = self.config.layout.aligned_n { + write!(out, " {}({})", anno, n); + } + } + } + } + + if s.annotations.must_use(self.config) { + if let Some(ref anno) = self.config.structure.must_use { + write!(out, " {}", anno); + } + } + + if let Some(note) = s + .annotations + .deprecated_note(self.config, DeprecatedNoteKind::Struct) + { + write!(out, " {}", note); + } + + if self.config.language != Language::C || self.config.style.generate_tag() { + write!(out, " {}", s.export_name()); + } + + out.open_brace(); + + // Emit the pre_body section, if relevant + if let Some(body) = self.config.export.pre_body(&s.path) { + out.write_raw_block(body); + out.new_line(); + } + + out.write_vertical_source_list(self, &s.fields, ListType::Cap(";"), Self::write_field); + + if self.config.language == Language::Cxx { + let mut wrote_start_newline = false; + + if self.config.structure.derive_constructor(&s.annotations) && !s.fields.is_empty() { + if !wrote_start_newline { + wrote_start_newline = true; + out.new_line(); + } + + out.new_line(); + + let renamed_fields: Vec<_> = s + .fields + .iter() + .map(|field| { + self.config + .function + .rename_args + .apply(&field.name, IdentifierType::FunctionArg) + .into_owned() + }) + .collect(); + write!(out, "{}(", s.export_name()); + let vec: Vec<_> = s + .fields + .iter() + .zip(&renamed_fields) + .map(|(field, renamed)| { + Field::from_name_and_type( + // const-ref args to constructor + format!("const& {}", renamed), + field.ty.clone(), + ) + }) + .collect(); + out.write_vertical_source_list( + self, + &vec[..], + ListType::Join(","), + Self::write_field, + ); + write!(out, ")"); + out.new_line(); + write!(out, " : "); + let vec: Vec<_> = s + .fields + .iter() + .zip(&renamed_fields) + .map(|(field, renamed)| format!("{}({})", field.name, renamed)) + .collect(); + out.write_vertical_source_list(self, &vec[..], ListType::Join(","), |_, out, s| { + write!(out, "{}", s) + }); + out.new_line(); + write!(out, "{{}}"); + out.new_line(); + } + + let other = self + .config + .function + .rename_args + .apply("other", IdentifierType::FunctionArg); + + if s.annotations + .bool("internal-derive-bitflags") + .unwrap_or(false) + { + assert_eq!(s.fields.len(), 1); + let bits = &s.fields[0].name; + if !wrote_start_newline { + wrote_start_newline = true; + out.new_line(); + } + let constexpr_prefix = if self.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, + s.export_name() + ); + out.open_brace(); + write!( + out, + "return {} {{ static_cast(~{bits}) }};", + s.export_name() + ); + out.close_brace(false); + s.emit_bitflags_binop(constexpr_prefix, '|', &other, out); + s.emit_bitflags_binop(constexpr_prefix, '&', &other, out); + s.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 self.config.structure.derive_ostream(&s.annotations) { + if !wrote_start_newline { + wrote_start_newline = true; + out.new_line(); + } + + out.new_line(); + let stream = self + .config + .function + .rename_args + .apply("stream", IdentifierType::FunctionArg); + let instance = self + .config + .function + .rename_args + .apply("instance", IdentifierType::FunctionArg); + write!( + out, + "friend std::ostream& operator<<(std::ostream& {}, const {}& {})", + stream, + s.export_name(), + instance, + ); + out.open_brace(); + write!(out, "return {} << \"{{ \"", stream); + let vec: Vec<_> = s + .fields + .iter() + .map(|x| format!(" << \"{}=\" << {}.{}", x.name, instance, x.name)) + .collect(); + out.write_vertical_source_list( + self, + &vec[..], + ListType::Join(" << \", \""), + |_, out, s| write!(out, "{}", s), + ); + out.write(" << \" }\";"); + out.close_brace(false); + } + + let skip_fields = s.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)) = s.annotations.atom(concat!($op_name, "-attributes")) + { + write!(out, "{} ", attrs); + } + + write!( + out, + "bool operator{}(const {}& {}) const", + $op, + s.export_name(), + other + ); + out.open_brace(); + out.write("return "); + let vec: Vec<_> = s + .fields + .iter() + .skip(skip_fields) + .map(|field| format!("{} {} {}.{}", field.name, $op, other, field.name)) + .collect(); + out.write_vertical_source_list( + self, + &vec[..], + ListType::Join(&format!(" {}", $conjuc)), + |_, out, s| write!(out, "{}", s), + ); + out.write(";"); + out.close_brace(false); + }}; + } + + if self.config.structure.derive_eq(&s.annotations) && s.can_derive_eq() { + emit_op!("eq", "==", "&&"); + } + if self.config.structure.derive_neq(&s.annotations) && s.can_derive_eq() { + emit_op!("neq", "!=", "||"); + } + if self.config.structure.derive_lt(&s.annotations) + && s.fields.len() == 1 + && s.fields[0].ty.can_cmp_order() + { + emit_op!("lt", "<", "&&"); + } + if self.config.structure.derive_lte(&s.annotations) + && s.fields.len() == 1 + && s.fields[0].ty.can_cmp_order() + { + emit_op!("lte", "<=", "&&"); + } + if self.config.structure.derive_gt(&s.annotations) + && s.fields.len() == 1 + && s.fields[0].ty.can_cmp_order() + { + emit_op!("gt", ">", "&&"); + } + if self.config.structure.derive_gte(&s.annotations) + && s.fields.len() == 1 + && s.fields[0].ty.can_cmp_order() + { + emit_op!("gte", ">=", "&&"); + } + } + + // Emit the post_body section, if relevant + if let Some(body) = self.config.export.post_body(&s.path) { + out.new_line(); + out.write_raw_block(body); + } + + if self.config.language == Language::Cxx + && self.config.structure.associated_constants_in_body + && self.config.constant.allow_static_const + { + for constant in &s.associated_constants { + out.new_line(); + constant.write_declaration(self.config, self, out, s); + } + } + + if self.config.language == Language::C && self.config.style.generate_typedef() { + out.close_brace(false); + write!(out, " {};", s.export_name()); + } else { + out.close_brace(true); + } + + for constant in &s.associated_constants { + out.new_line(); + constant.write(self.config, self, out, Some(s)); + } + + condition.write_after(self.config, out); + } + + fn write_union(&mut self, out: &mut SourceWriter, u: &Union) { + let condition = u.cfg.to_condition(self.config); + condition.write_before(self.config, out); + + self.write_documentation(out, &u.documentation); + + self.write_generic_param(out, &u.generic_params); + + // 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 self.config.language { + Language::C if self.config.style.generate_typedef() => out.write("typedef "), + Language::C | Language::Cxx => {} + _ => unreachable!(), + } + + out.write("union"); + + if let Some(align) = u.alignment { + match align { + ReprAlign::Packed => { + if let Some(ref anno) = self.config.layout.packed { + write!(out, " {}", anno); + } + } + ReprAlign::Align(n) => { + if let Some(ref anno) = self.config.layout.aligned_n { + write!(out, " {}({})", anno, n); + } + } + } + } + + if self.config.language != Language::C || self.config.style.generate_tag() { + write!(out, " {}", u.export_name); + } + + out.open_brace(); + + // Emit the pre_body section, if relevant + if let Some(body) = self.config.export.pre_body(&u.path) { + out.write_raw_block(body); + out.new_line(); + } + + out.write_vertical_source_list(self, &u.fields, ListType::Cap(";"), Self::write_field); + + // Emit the post_body section, if relevant + if let Some(body) = self.config.export.post_body(&u.path) { + out.new_line(); + out.write_raw_block(body); + } + + if self.config.language == Language::C && self.config.style.generate_typedef() { + out.close_brace(false); + write!(out, " {};", u.export_name); + } else { + out.close_brace(true); + } + + condition.write_after(self.config, out); + } + + fn write_opaque_item(&mut self, out: &mut SourceWriter, o: &OpaqueItem) { + let condition = o.cfg.to_condition(self.config); + condition.write_before(self.config, out); + + self.write_documentation(out, &o.documentation); + + o.generic_params.write_with_default(self, self.config, out); + + match self.config.language { + Language::C if self.config.style.generate_typedef() => { + write!( + out, + "typedef struct {} {};", + o.export_name(), + o.export_name() + ); + } + Language::C | Language::Cxx => { + write!(out, "struct {};", o.export_name()); + } + _ => unreachable!(), + } + + condition.write_after(self.config, out); + } + + fn write_type_def(&mut self, out: &mut SourceWriter, t: &Typedef) { + let condition = t.cfg.to_condition(self.config); + condition.write_before(self.config, out); + + self.write_documentation(out, &t.documentation); + + self.write_generic_param(out, &t.generic_params); + + match self.config.language { + Language::Cxx => { + write!(out, "using {} = ", t.export_name()); + self.write_type(out, &t.aliased); + } + Language::C => { + write!(out, "{} ", self.config.language.typedef()); + self.write_field( + out, + &Field::from_name_and_type(t.export_name().to_owned(), t.aliased.clone()), + ) + } + _ => unreachable!(), + } + + out.write(";"); + + condition.write_after(self.config, out); + } + + fn write_static(&mut self, out: &mut SourceWriter, s: &Static) { + out.write("extern "); + if let Type::Ptr { is_const: true, .. } = s.ty { + } else if !s.mutable { + out.write("const "); + } + cdecl::write_field(self, out, &s.ty, &s.export_name, self.config); + out.write(";"); + } + + fn write_function(&mut self, out: &mut SourceWriter, f: &Function) { + fn write_1( + func: &Function, + language_backend: &mut CLikeLanguageBackend, + out: &mut SourceWriter, + ) { + let prefix = language_backend.config.function.prefix(&func.annotations); + let postfix = language_backend.config.function.postfix(&func.annotations); + + let condition = func.cfg.to_condition(language_backend.config); + condition.write_before(language_backend.config, out); + + language_backend.write_documentation(out, &func.documentation); + + if func.extern_decl { + out.write("extern "); + } else { + if let Some(ref prefix) = prefix { + write!(out, "{} ", prefix); + } + if func.annotations.must_use(language_backend.config) { + if let Some(ref anno) = language_backend.config.function.must_use { + write!(out, "{} ", anno); + } + } + if let Some(note) = func + .annotations + .deprecated_note(language_backend.config, DeprecatedNoteKind::Function) + { + write!(out, "{} ", note); + } + } + cdecl::write_func( + language_backend, + out, + func, + Layout::Horizontal, + language_backend.config, + ); + + if !func.extern_decl { + if let Some(ref postfix) = postfix { + write!(out, " {}", postfix); + } + } + + if let Some(ref swift_name_macro) = language_backend.config.function.swift_name_macro { + if let Some(swift_name) = func.swift_name(language_backend.config) { + write!(out, " {}({})", swift_name_macro, swift_name); + } + } + + out.write(";"); + + condition.write_after(language_backend.config, out); + } + + fn write_2( + func: &Function, + language_backend: &mut CLikeLanguageBackend, + out: &mut SourceWriter, + ) { + let prefix = language_backend.config.function.prefix(&func.annotations); + let postfix = language_backend.config.function.postfix(&func.annotations); + + let condition = func.cfg.to_condition(language_backend.config); + + condition.write_before(language_backend.config, out); + + language_backend.write_documentation(out, &func.documentation); + + 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(language_backend.config) { + if let Some(ref anno) = language_backend.config.function.must_use { + write!(out, "{}", anno); + out.new_line(); + } + } + if let Some(note) = func + .annotations + .deprecated_note(language_backend.config, DeprecatedNoteKind::Function) + { + write!(out, "{}", note); + out.new_line(); + } + } + cdecl::write_func( + language_backend, + out, + func, + Layout::Vertical, + language_backend.config, + ); + if !func.extern_decl { + if let Some(ref postfix) = postfix { + out.new_line(); + write!(out, "{}", postfix); + } + } + + if let Some(ref swift_name_macro) = language_backend.config.function.swift_name_macro { + if let Some(swift_name) = func.swift_name(language_backend.config) { + write!(out, " {}({})", swift_name_macro, swift_name); + } + } + + out.write(";"); + + condition.write_after(language_backend.config, out); + } + + match self.config.function.args { + Layout::Horizontal => write_1(f, self, out), + Layout::Vertical => write_2(f, self, out), + Layout::Auto => { + let max_line_length = self.config.line_length; + if !out.try_write(|out| write_1(f, self, out), max_line_length) { + write_2(f, self, out) + } + } + } + } + + fn write_type(&mut self, out: &mut SourceWriter, t: &Type) { + cdecl::write_type(self, out, t, self.config); + } + + fn write_documentation(&mut self, out: &mut SourceWriter, d: &Documentation) { + if d.doc_comment.is_empty() || !self.config.documentation { + return; + } + + let end = match self.config.documentation_length { + DocumentationLength::Short => 1, + DocumentationLength::Full => d.doc_comment.len(), + }; + + let style = match self.config.documentation_style { + DocumentationStyle::Auto if self.config.language == Language::C => { + DocumentationStyle::Doxy + } + DocumentationStyle::Auto if self.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 &d.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(); + } + + _ => (), + } + } + + fn write_literal(&mut self, out: &mut SourceWriter, l: &Literal) { + match l { + Literal::Expr(v) => match (&**v, self.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 self.config.language { + Language::Cython | Language::C => "_", + Language::Cxx => { + if self.config.structure.associated_constants_in_body { + "::" + } else { + "_" + } + } + }; + write!(out, "{}{}", export_name, path_separator) + } + write!(out, "{}", name) + } + Literal::FieldAccess { + ref base, + ref field, + } => { + write!(out, "("); + self.write_literal(out, base); + write!(out, ").{}", field); + } + Literal::PostfixUnaryOp { op, ref value } => { + write!(out, "{}", op); + self.write_literal(out, value); + } + Literal::BinOp { + ref left, + op, + ref right, + } => { + write!(out, "("); + self.write_literal(out, left); + write!(out, " {} ", op); + self.write_literal(out, right); + write!(out, ")"); + } + Literal::Cast { ref ty, ref value } => { + out.write("("); + self.write_type(out, ty); + out.write(")"); + self.write_literal(out, value); + } + Literal::Struct { + export_name, + fields, + path, + } => { + match self.config.language { + Language::C => write!(out, "({})", export_name), + Language::Cxx => write!(out, "{}", export_name), + _ => unreachable!(), + } + + 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 self.config.language { + Language::Cxx => write!(out, "/* .{} = */ ", ordered_key), + Language::C => write!(out, ".{} = ", ordered_key), + _ => unreachable!(), + } + self.write_literal(out, lit); + } + } + write!(out, " }}"); + } + } + } + + fn write_globals(&mut self, out: &mut SourceWriter, b: &Bindings) { + // Override default method to open various blocs containing both globals and functions + // these blocks are closed in [`write_functions`] that is also overridden + if !b.functions.is_empty() || !b.globals.is_empty() { + if b.config.cpp_compatible_c() { + out.new_line_if_not_start(); + out.write("#ifdef __cplusplus"); + } + + if b.config.language == Language::Cxx { + if let Some(ref using_namespaces) = b.config.using_namespaces { + for namespace in using_namespaces { + out.new_line(); + write!(out, "using namespace {};", namespace); + } + out.new_line(); + } + } + + if b.config.language == Language::Cxx || b.config.cpp_compatible_c() { + out.new_line(); + out.write("extern \"C\" {"); + out.new_line(); + } + + if b.config.cpp_compatible_c() { + out.write("#endif // __cplusplus"); + out.new_line(); + } + + self.write_globals_default(out, b); + } + } + + fn write_functions(&mut self, out: &mut SourceWriter, b: &Bindings) { + // Override default method to close various blocs containing both globals and functions + // these blocks are opened in [`write_globals`] that is also overridden + if !b.functions.is_empty() || !b.globals.is_empty() { + self.write_functions_default(out, b); + + if b.config.cpp_compatible_c() { + out.new_line(); + out.write("#ifdef __cplusplus"); + } + + if b.config.language == Language::Cxx || b.config.cpp_compatible_c() { + out.new_line(); + out.write("} // extern \"C\""); + out.new_line(); + } + + if b.config.cpp_compatible_c() { + out.write("#endif // __cplusplus"); + out.new_line(); + } + } + } +} diff --git a/src/bindgen/language_backend/cython.rs b/src/bindgen/language_backend/cython.rs new file mode 100644 index 000000000..56392c1c2 --- /dev/null +++ b/src/bindgen/language_backend/cython.rs @@ -0,0 +1,569 @@ +use crate::bindgen::ir::{ + to_known_assoc_constant, ConditionWrite, DeprecatedNoteKind, Documentation, Enum, EnumVariant, + Field, Function, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, ToCondition, Type, + Typedef, Union, +}; +use crate::bindgen::language_backend::LanguageBackend; +use crate::bindgen::writer::{ListType, SourceWriter}; +use crate::bindgen::DocumentationLength; +use crate::bindgen::Layout; +use crate::bindgen::{cdecl, Bindings, Config}; +use std::io::Write; + +pub struct CythonLanguageBackend<'a> { + config: &'a Config, +} + +impl<'a> CythonLanguageBackend<'a> { + pub fn new(config: &'a Config) -> Self { + Self { config } + } + + fn write_enum_variant(&mut self, out: &mut SourceWriter, u: &EnumVariant) { + self.write_documentation(out, &u.documentation); + write!(out, "{}", u.export_name); + if let Some(discriminant) = &u.discriminant { + // 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(" = "); + + self.write_literal(out, discriminant); + } + out.write(","); + } + + fn write_field(&mut self, out: &mut SourceWriter, f: &Field) { + // Cython doesn't support conditional fields. + // let condition = f.cfg.to_condition(self.config); + + self.write_documentation(out, &f.documentation); + cdecl::write_field(self, out, &f.ty, &f.name, self.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 let Some(bitfield) = f.annotations.atom("bitfield") { + // + // } + } +} + +impl LanguageBackend for CythonLanguageBackend<'_> { + fn write_headers(&self, out: &mut SourceWriter, package_version: &str) { + if self.config.package_version { + write!(out, "''' Package version: {} '''", package_version); + out.new_line(); + } + if let Some(ref f) = self.config.header { + out.new_line_if_not_start(); + write!(out, "{}", f); + 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.after_includes.is_none() + { + return; + } + + out.new_line_if_not_start(); + + if !&self.config.no_includes { + 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 (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(); + } + } + + fn open_namespaces(&mut self, out: &mut SourceWriter) { + out.new_line(); + let header = &self.config.cython.header.as_deref().unwrap_or("*"); + write!(out, "cdef extern from {}", header); + out.open_brace(); + } + + fn close_namespaces(&mut self, out: &mut SourceWriter) { + out.close_brace(false); + } + + fn write_footers(&mut self, _out: &mut SourceWriter) {} + + fn write_enum(&mut self, out: &mut SourceWriter, e: &Enum) { + let size = e.repr.ty.map(|ty| ty.to_primitive().to_repr_c(self.config)); + let has_data = e.tag.is_some(); + let inline_tag_field = Enum::inline_tag_field(&e.repr); + let tag_name = e.tag_name(); + + let condition = e.cfg.to_condition(self.config); + condition.write_before(self.config, out); + + self.write_documentation(out, &e.documentation); + + // Emit the tag enum and everything related to it. + e.write_tag_enum(self.config, self, out, size, Self::write_enum_variant); + + // If the enum has data, we need to emit structs for the variants and gather them together. + if has_data { + e.write_variant_defs(self.config, self, out); + out.new_line(); + out.new_line(); + + e.open_struct_or_union(self.config, out, inline_tag_field); + + // Emit tag field that is separate from all variants. + e.write_tag_field(self.config, out, size, inline_tag_field, tag_name); + out.new_line(); + + // Emit fields for all variants with data. + e.write_variant_fields(self.config, self, out, inline_tag_field, Self::write_field); + + // Emit the post_body section, if relevant. + if let Some(body) = &self.config.export.post_body(&e.path) { + out.new_line(); + out.write_raw_block(body); + } + + out.close_brace(true); + } + + condition.write_after(self.config, out); + } + + fn write_struct(&mut self, out: &mut SourceWriter, s: &Struct) { + if s.is_transparent { + let typedef = Typedef { + path: s.path.clone(), + export_name: s.export_name.to_owned(), + generic_params: s.generic_params.clone(), + aliased: s.fields[0].ty.clone(), + cfg: s.cfg.clone(), + annotations: s.annotations.clone(), + documentation: s.documentation.clone(), + }; + self.write_type_def(out, &typedef); + for constant in &s.associated_constants { + out.new_line(); + constant.write(self.config, self, out, Some(s)); + } + return; + } + + let condition = s.cfg.to_condition(self.config); + condition.write_before(self.config, out); + + self.write_documentation(out, &s.documentation); + + out.write(self.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 let Some(align) = s.alignment { + match align { + ReprAlign::Packed => out.write("packed "), + ReprAlign::Align(_) => {} // Not supported + } + } + + out.write("struct"); + + if s.annotations.must_use(self.config) { + if let Some(ref anno) = &self.config.structure.must_use { + write!(out, " {}", anno); + } + } + + if let Some(note) = s + .annotations + .deprecated_note(self.config, DeprecatedNoteKind::Struct) + { + write!(out, " {}", note); + } + + write!(out, " {}", s.export_name()); + + out.open_brace(); + + // Emit the pre_body section, if relevant + if let Some(body) = &self.config.export.pre_body(&s.path) { + out.write_raw_block(body); + out.new_line(); + } + + out.write_vertical_source_list(self, &s.fields, ListType::Cap(";"), Self::write_field); + if s.fields.is_empty() { + out.write("pass"); + } + + // Emit the post_body section, if relevant + if let Some(body) = &self.config.export.post_body(&s.path) { + out.new_line(); + out.write_raw_block(body); + } + out.close_brace(true); + + for constant in &s.associated_constants { + out.new_line(); + constant.write(self.config, self, out, Some(s)); + } + + condition.write_after(self.config, out); + } + + fn write_union(&mut self, out: &mut SourceWriter, u: &Union) { + let condition = u.cfg.to_condition(self.config); + condition.write_before(self.config, out); + + self.write_documentation(out, &u.documentation); + + out.write(self.config.style.cython_def()); + + out.write("union"); + + write!(out, " {}", u.export_name); + + out.open_brace(); + + // Emit the pre_body section, if relevant + if let Some(body) = &self.config.export.pre_body(&u.path) { + out.write_raw_block(body); + out.new_line(); + } + + out.write_vertical_source_list(self, &u.fields, ListType::Cap(";"), Self::write_field); + if u.fields.is_empty() { + out.write("pass"); + } + + // Emit the post_body section, if relevant + if let Some(body) = &self.config.export.post_body(&u.path) { + out.new_line(); + out.write_raw_block(body); + } + + out.close_brace(true); + + condition.write_after(self.config, out); + } + + fn write_opaque_item(&mut self, out: &mut SourceWriter, o: &OpaqueItem) { + let condition = o.cfg.to_condition(self.config); + condition.write_before(self.config, out); + + self.write_documentation(out, &o.documentation); + + o.generic_params.write_with_default(self, self.config, out); + + write!( + out, + "{}struct {}", + &self.config.style.cython_def(), + o.export_name() + ); + out.open_brace(); + out.write("pass"); + out.close_brace(false); + + condition.write_after(self.config, out); + } + + fn write_type_def(&mut self, out: &mut SourceWriter, t: &Typedef) { + let condition = t.cfg.to_condition(self.config); + condition.write_before(self.config, out); + + self.write_documentation(out, &t.documentation); + + write!(out, "{} ", &self.config.language.typedef()); + + self.write_field( + out, + &Field::from_name_and_type(t.export_name().to_owned(), t.aliased.clone()), + ); + + out.write(";"); + + condition.write_after(self.config, out); + } + + fn write_static(&mut self, out: &mut SourceWriter, s: &Static) { + out.write("extern "); + if let Type::Ptr { is_const: true, .. } = s.ty { + } else if !s.mutable { + out.write("const "); + } + cdecl::write_field(self, out, &s.ty, &s.export_name, self.config); + out.write(";"); + } + + fn write_function(&mut self, out: &mut SourceWriter, f: &Function) { + fn write_1( + func: &Function, + language_backend: &mut CythonLanguageBackend, + out: &mut SourceWriter, + ) { + let prefix = language_backend.config.function.prefix(&func.annotations); + let postfix = language_backend.config.function.postfix(&func.annotations); + + let condition = func.cfg.to_condition(language_backend.config); + condition.write_before(language_backend.config, out); + + language_backend.write_documentation(out, &func.documentation); + + if func.extern_decl { + out.write("extern "); + } else { + if let Some(ref prefix) = prefix { + write!(out, "{} ", prefix); + } + if func.annotations.must_use(language_backend.config) { + if let Some(ref anno) = language_backend.config.function.must_use { + write!(out, "{} ", anno); + } + } + if let Some(note) = func + .annotations + .deprecated_note(language_backend.config, DeprecatedNoteKind::Function) + { + write!(out, "{} ", note); + } + } + cdecl::write_func( + language_backend, + out, + func, + Layout::Horizontal, + language_backend.config, + ); + + if !func.extern_decl { + if let Some(ref postfix) = postfix { + write!(out, " {}", postfix); + } + } + + if let Some(ref swift_name_macro) = language_backend.config.function.swift_name_macro { + if let Some(swift_name) = func.swift_name(language_backend.config) { + write!(out, " {}({})", swift_name_macro, swift_name); + } + } + + out.write(";"); + + condition.write_after(language_backend.config, out); + } + + fn write_2( + func: &Function, + language_backend: &mut CythonLanguageBackend, + out: &mut SourceWriter, + ) { + let prefix = language_backend.config.function.prefix(&func.annotations); + let postfix = language_backend.config.function.postfix(&func.annotations); + + let condition = func.cfg.to_condition(language_backend.config); + + condition.write_before(language_backend.config, out); + + language_backend.write_documentation(out, &func.documentation); + + 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(language_backend.config) { + if let Some(ref anno) = language_backend.config.function.must_use { + write!(out, "{}", anno); + out.new_line(); + } + } + if let Some(note) = func + .annotations + .deprecated_note(language_backend.config, DeprecatedNoteKind::Function) + { + write!(out, "{} ", note); + } + } + cdecl::write_func( + language_backend, + out, + func, + Layout::Vertical, + language_backend.config, + ); + if !func.extern_decl { + if let Some(ref postfix) = postfix { + out.new_line(); + write!(out, "{}", postfix); + } + } + + if let Some(ref swift_name_macro) = language_backend.config.function.swift_name_macro { + if let Some(swift_name) = func.swift_name(language_backend.config) { + write!(out, " {}({})", swift_name_macro, swift_name); + } + } + + out.write(";"); + + condition.write_after(language_backend.config, out); + } + + match &self.config.function.args { + Layout::Horizontal => write_1(f, self, out), + Layout::Vertical => write_2(f, self, out), + Layout::Auto => { + let max_line_length = self.config.line_length; + if !out.try_write(|out| write_1(f, self, out), max_line_length) { + write_2(f, self, out) + } + } + } + } + + fn write_type(&mut self, out: &mut SourceWriter, t: &Type) { + cdecl::write_type(self, out, t, self.config); + } + + fn write_documentation(&mut self, out: &mut SourceWriter, d: &Documentation) { + if d.doc_comment.is_empty() || !&self.config.documentation { + return; + } + + let end = match &self.config.documentation_length { + DocumentationLength::Short => 1, + DocumentationLength::Full => d.doc_comment.len(), + }; + + // Cython uses Python-style comments, so `documentation_style` is not relevant. + for line in &d.doc_comment[..end] { + write!(out, "#{}", line); + out.new_line(); + } + } + + fn write_literal(&mut self, out: &mut SourceWriter, l: &Literal) { + match l { + Literal::Expr(v) => match &**v { + "true" => write!(out, "True"), + "false" => 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); + } + write!(out, "{}_", export_name) + } + write!(out, "{}", name) + } + Literal::FieldAccess { + ref base, + ref field, + } => { + write!(out, "("); + self.write_literal(out, base); + write!(out, ").{}", field); + } + Literal::PostfixUnaryOp { op, ref value } => { + write!(out, "{}", op); + self.write_literal(out, value); + } + Literal::BinOp { + ref left, + op, + ref right, + } => { + write!(out, "("); + self.write_literal(out, left); + write!(out, " {} ", op); + self.write_literal(out, right); + write!(out, ")"); + } + Literal::Cast { ref ty, ref value } => { + out.write("<"); + self.write_type(out, ty); + out.write(">"); + self.write_literal(out, value); + } + Literal::Struct { + export_name, + fields, + path, + } => { + 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; + } + self.write_literal(out, lit); + } + } + write!(out, " }}"); + } + } + } + + fn write_functions(&mut self, out: &mut SourceWriter, b: &Bindings) { + self.write_functions_default(out, b); + + if b.globals.is_empty() + && b.constants.is_empty() + && b.items.is_empty() + && b.functions.is_empty() + { + out.write("pass"); + } + } +} diff --git a/src/bindgen/language_backend/mod.rs b/src/bindgen/language_backend/mod.rs new file mode 100644 index 000000000..ffd7f1236 --- /dev/null +++ b/src/bindgen/language_backend/mod.rs @@ -0,0 +1,141 @@ +use crate::bindgen::ir::{ + Documentation, Enum, Function, ItemContainer, Literal, OpaqueItem, Static, Struct, Type, + Typedef, Union, +}; +use crate::bindgen::writer::SourceWriter; +use crate::bindgen::Bindings; + +use std::io::Write; + +mod clike; +mod cython; + +pub use clike::CLikeLanguageBackend; +pub use cython::CythonLanguageBackend; + +pub trait LanguageBackend { + fn open_namespaces(&mut self, out: &mut SourceWriter); + fn close_namespaces(&mut self, out: &mut SourceWriter); + fn write_headers(&self, out: &mut SourceWriter, package_version: &str); + fn write_footers(&mut self, out: &mut SourceWriter); + fn write_enum(&mut self, out: &mut SourceWriter, e: &Enum); + fn write_struct(&mut self, out: &mut SourceWriter, s: &Struct); + fn write_union(&mut self, out: &mut SourceWriter, u: &Union); + fn write_opaque_item(&mut self, out: &mut SourceWriter, o: &OpaqueItem); + fn write_type_def(&mut self, out: &mut SourceWriter, t: &Typedef); + fn write_static(&mut self, out: &mut SourceWriter, s: &Static); + fn write_function(&mut self, out: &mut SourceWriter, f: &Function); + fn write_type(&mut self, out: &mut SourceWriter, t: &Type); + fn write_documentation(&mut self, out: &mut SourceWriter, d: &Documentation); + fn write_literal(&mut self, out: &mut SourceWriter, l: &Literal); + + fn write_bindings(&mut self, out: &mut SourceWriter, b: &Bindings) + where + Self: Sized, + { + self.write_headers(out, &b.package_version); + + self.open_namespaces(out); + + self.write_primitive_constants(out, b); + + self.write_items(out, b); + + self.write_non_primitive_constants(out, b); + + self.write_globals(out, b); + + self.write_functions(out, b); + + self.close_namespaces(out); + + self.write_footers(out); + + self.write_trailer(out, b); + } + + fn write_primitive_constants(&mut self, out: &mut SourceWriter, b: &Bindings) + where + Self: Sized, + { + for constant in &b.constants { + if constant.uses_only_primitive_types() { + out.new_line_if_not_start(); + constant.write(&b.config, self, out, None); + out.new_line(); + } + } + } + + fn write_items(&mut self, out: &mut SourceWriter, b: &Bindings) { + for item in &b.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) => self.write_enum(out, x), + ItemContainer::Struct(ref x) => self.write_struct(out, x), + ItemContainer::Union(ref x) => self.write_union(out, x), + ItemContainer::OpaqueItem(ref x) => self.write_opaque_item(out, x), + ItemContainer::Typedef(ref x) => self.write_type_def(out, x), + } + out.new_line(); + } + } + + fn write_non_primitive_constants(&mut self, out: &mut SourceWriter, b: &Bindings) + where + Self: Sized, + { + for constant in &b.constants { + if !constant.uses_only_primitive_types() { + out.new_line_if_not_start(); + constant.write(&b.config, self, out, None); + out.new_line(); + } + } + } + + fn write_globals(&mut self, out: &mut SourceWriter, b: &Bindings) { + self.write_globals_default(out, b) + } + + fn write_globals_default(&mut self, out: &mut SourceWriter, b: &Bindings) { + for global in &b.globals { + out.new_line_if_not_start(); + self.write_static(out, global); + out.new_line(); + } + } + + fn write_functions(&mut self, out: &mut SourceWriter, b: &Bindings) { + self.write_functions_default(out, b) + } + + fn write_functions_default(&mut self, out: &mut SourceWriter, b: &Bindings) { + for function in &b.functions { + out.new_line_if_not_start(); + self.write_function(out, function); + out.new_line(); + } + } + + fn write_trailer(&mut self, out: &mut SourceWriter, b: &Bindings) { + if let Some(ref f) = b.config.trailer { + out.new_line_if_not_start(); + write!(out, "{}", f); + if !f.ends_with('\n') { + out.new_line(); + } + } + } +} diff --git a/src/bindgen/mod.rs b/src/bindgen/mod.rs index d0789da2e..5d7882118 100644 --- a/src/bindgen/mod.rs +++ b/src/bindgen/mod.rs @@ -46,6 +46,7 @@ mod declarationtyperesolver; mod dependencies; mod error; mod ir; +mod language_backend; mod library; mod mangle; mod monomorph; diff --git a/src/bindgen/writer.rs b/src/bindgen/writer.rs index eed691723..f36757d26 100644 --- a/src/bindgen/writer.rs +++ b/src/bindgen/writer.rs @@ -6,7 +6,8 @@ use std::cmp; use std::io; use std::io::Write; -use crate::bindgen::config::{Braces, Config, Language}; +use crate::bindgen::config::{Braces, Language}; +use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::Bindings; /// A type of way to format a list. @@ -77,7 +78,7 @@ impl<'a, F: Write> SourceWriter<'a, F> { /// written. pub fn try_write(&mut self, func: T, max_line_length: usize) -> bool where - T: Fn(&mut MeasureWriter), + T: FnOnce(&mut MeasureWriter), { if self.line_length > max_line_length { return false; @@ -207,13 +208,19 @@ impl<'a, F: Write> SourceWriter<'a, F> { InnerWriter(self).write_fmt(fmt).unwrap(); } - pub fn write_horizontal_source_list( + pub fn write_horizontal_source_list< + LB: LanguageBackend, + S, + WF: Fn(&mut LB, &mut SourceWriter, &S), + >( &mut self, + language_backend: &mut LB, items: &[S], list_type: ListType<'_>, + writer: WF, ) { for (i, item) in items.iter().enumerate() { - item.write(&self.bindings.config, self); + writer(language_backend, self, item); match list_type { ListType::Join(text) => { @@ -228,11 +235,21 @@ impl<'a, F: Write> SourceWriter<'a, F> { } } - pub fn write_vertical_source_list(&mut self, items: &[S], list_type: ListType<'_>) { + pub fn write_vertical_source_list< + LB: LanguageBackend, + S, + WF: Fn(&mut LB, &mut SourceWriter, &S), + >( + &mut self, + language_backend: &mut LB, + items: &[S], + list_type: ListType<'_>, + writer: WF, + ) { 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); + writer(language_backend, self, item); match list_type { ListType::Join(text) => { @@ -252,7 +269,3 @@ impl<'a, F: Write> SourceWriter<'a, F> { self.pop_tab(); } } - -pub trait Source { - fn write(&self, config: &Config, _: &mut SourceWriter); -} From a0a08ac7ba3787e7ec19a21a8b37c85b37f0ff38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sun, 14 Apr 2024 18:32:08 +0200 Subject: [PATCH 2/3] Remove code duplication for function writing, and other minor clean-ups. --- src/bindgen/cdecl.rs | 2 +- src/bindgen/config.rs | 2 +- src/bindgen/language_backend/clike.rs | 138 +------------------------ src/bindgen/language_backend/cython.rs | 133 +----------------------- src/bindgen/language_backend/mod.rs | 123 +++++++++++++++++----- 5 files changed, 105 insertions(+), 293 deletions(-) diff --git a/src/bindgen/cdecl.rs b/src/bindgen/cdecl.rs index c4bef9346..fc96042cc 100644 --- a/src/bindgen/cdecl.rs +++ b/src/bindgen/cdecl.rs @@ -184,7 +184,7 @@ impl CDecl { }); self.declarators.push(CDeclarator::Func { args, - layout: config.function.args.clone(), + layout: config.function.args, never_return: *never_return, }); self.build_type(ret, false, config); diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs index dc5fa4635..c28c2aa67 100644 --- a/src/bindgen/config.rs +++ b/src/bindgen/config.rs @@ -134,7 +134,7 @@ impl FromStr for Braces { deserialize_enum_str!(Braces); /// A type of layout to use when generating long lines of code. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Layout { Horizontal, Vertical, diff --git a/src/bindgen/language_backend/clike.rs b/src/bindgen/language_backend/clike.rs index ff23d7c35..a2e3e5515 100644 --- a/src/bindgen/language_backend/clike.rs +++ b/src/bindgen/language_backend/clike.rs @@ -1,12 +1,12 @@ use crate::bindgen::ir::{ to_known_assoc_constant, ConditionWrite, DeprecatedNoteKind, Documentation, Enum, EnumVariant, - Field, Function, GenericParams, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, - ToCondition, Type, Typedef, Union, + Field, GenericParams, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, ToCondition, Type, + Typedef, Union, }; use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::rename::IdentifierType; use crate::bindgen::writer::{ListType, SourceWriter}; -use crate::bindgen::{cdecl, Bindings, Config, Language, Layout}; +use crate::bindgen::{cdecl, Bindings, Config, Language}; use crate::bindgen::{DocumentationLength, DocumentationStyle}; use std::io::Write; @@ -780,6 +780,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { } fn write_static(&mut self, out: &mut SourceWriter, s: &Static) { + self.write_documentation(out, &s.documentation); out.write("extern "); if let Type::Ptr { is_const: true, .. } = s.ty { } else if !s.mutable { @@ -789,135 +790,6 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { out.write(";"); } - fn write_function(&mut self, out: &mut SourceWriter, f: &Function) { - fn write_1( - func: &Function, - language_backend: &mut CLikeLanguageBackend, - out: &mut SourceWriter, - ) { - let prefix = language_backend.config.function.prefix(&func.annotations); - let postfix = language_backend.config.function.postfix(&func.annotations); - - let condition = func.cfg.to_condition(language_backend.config); - condition.write_before(language_backend.config, out); - - language_backend.write_documentation(out, &func.documentation); - - if func.extern_decl { - out.write("extern "); - } else { - if let Some(ref prefix) = prefix { - write!(out, "{} ", prefix); - } - if func.annotations.must_use(language_backend.config) { - if let Some(ref anno) = language_backend.config.function.must_use { - write!(out, "{} ", anno); - } - } - if let Some(note) = func - .annotations - .deprecated_note(language_backend.config, DeprecatedNoteKind::Function) - { - write!(out, "{} ", note); - } - } - cdecl::write_func( - language_backend, - out, - func, - Layout::Horizontal, - language_backend.config, - ); - - if !func.extern_decl { - if let Some(ref postfix) = postfix { - write!(out, " {}", postfix); - } - } - - if let Some(ref swift_name_macro) = language_backend.config.function.swift_name_macro { - if let Some(swift_name) = func.swift_name(language_backend.config) { - write!(out, " {}({})", swift_name_macro, swift_name); - } - } - - out.write(";"); - - condition.write_after(language_backend.config, out); - } - - fn write_2( - func: &Function, - language_backend: &mut CLikeLanguageBackend, - out: &mut SourceWriter, - ) { - let prefix = language_backend.config.function.prefix(&func.annotations); - let postfix = language_backend.config.function.postfix(&func.annotations); - - let condition = func.cfg.to_condition(language_backend.config); - - condition.write_before(language_backend.config, out); - - language_backend.write_documentation(out, &func.documentation); - - 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(language_backend.config) { - if let Some(ref anno) = language_backend.config.function.must_use { - write!(out, "{}", anno); - out.new_line(); - } - } - if let Some(note) = func - .annotations - .deprecated_note(language_backend.config, DeprecatedNoteKind::Function) - { - write!(out, "{}", note); - out.new_line(); - } - } - cdecl::write_func( - language_backend, - out, - func, - Layout::Vertical, - language_backend.config, - ); - if !func.extern_decl { - if let Some(ref postfix) = postfix { - out.new_line(); - write!(out, "{}", postfix); - } - } - - if let Some(ref swift_name_macro) = language_backend.config.function.swift_name_macro { - if let Some(swift_name) = func.swift_name(language_backend.config) { - write!(out, " {}({})", swift_name_macro, swift_name); - } - } - - out.write(";"); - - condition.write_after(language_backend.config, out); - } - - match self.config.function.args { - Layout::Horizontal => write_1(f, self, out), - Layout::Vertical => write_2(f, self, out), - Layout::Auto => { - let max_line_length = self.config.line_length; - if !out.try_write(|out| write_1(f, self, out), max_line_length) { - write_2(f, self, out) - } - } - } - } - fn write_type(&mut self, out: &mut SourceWriter, t: &Type) { cdecl::write_type(self, out, t, self.config); } @@ -1116,7 +988,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { } fn write_functions(&mut self, out: &mut SourceWriter, b: &Bindings) { - // Override default method to close various blocs containing both globals and functions + // Override default method to close various blocks containing both globals and functions // these blocks are opened in [`write_globals`] that is also overridden if !b.functions.is_empty() || !b.globals.is_empty() { self.write_functions_default(out, b); diff --git a/src/bindgen/language_backend/cython.rs b/src/bindgen/language_backend/cython.rs index 56392c1c2..eaa666920 100644 --- a/src/bindgen/language_backend/cython.rs +++ b/src/bindgen/language_backend/cython.rs @@ -1,12 +1,10 @@ use crate::bindgen::ir::{ to_known_assoc_constant, ConditionWrite, DeprecatedNoteKind, Documentation, Enum, EnumVariant, - Field, Function, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, ToCondition, Type, - Typedef, Union, + Field, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, ToCondition, Type, Typedef, Union, }; use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::writer::{ListType, SourceWriter}; use crate::bindgen::DocumentationLength; -use crate::bindgen::Layout; use crate::bindgen::{cdecl, Bindings, Config}; use std::io::Write; @@ -323,6 +321,7 @@ impl LanguageBackend for CythonLanguageBackend<'_> { } fn write_static(&mut self, out: &mut SourceWriter, s: &Static) { + self.write_documentation(out, &s.documentation); out.write("extern "); if let Type::Ptr { is_const: true, .. } = s.ty { } else if !s.mutable { @@ -332,134 +331,6 @@ impl LanguageBackend for CythonLanguageBackend<'_> { out.write(";"); } - fn write_function(&mut self, out: &mut SourceWriter, f: &Function) { - fn write_1( - func: &Function, - language_backend: &mut CythonLanguageBackend, - out: &mut SourceWriter, - ) { - let prefix = language_backend.config.function.prefix(&func.annotations); - let postfix = language_backend.config.function.postfix(&func.annotations); - - let condition = func.cfg.to_condition(language_backend.config); - condition.write_before(language_backend.config, out); - - language_backend.write_documentation(out, &func.documentation); - - if func.extern_decl { - out.write("extern "); - } else { - if let Some(ref prefix) = prefix { - write!(out, "{} ", prefix); - } - if func.annotations.must_use(language_backend.config) { - if let Some(ref anno) = language_backend.config.function.must_use { - write!(out, "{} ", anno); - } - } - if let Some(note) = func - .annotations - .deprecated_note(language_backend.config, DeprecatedNoteKind::Function) - { - write!(out, "{} ", note); - } - } - cdecl::write_func( - language_backend, - out, - func, - Layout::Horizontal, - language_backend.config, - ); - - if !func.extern_decl { - if let Some(ref postfix) = postfix { - write!(out, " {}", postfix); - } - } - - if let Some(ref swift_name_macro) = language_backend.config.function.swift_name_macro { - if let Some(swift_name) = func.swift_name(language_backend.config) { - write!(out, " {}({})", swift_name_macro, swift_name); - } - } - - out.write(";"); - - condition.write_after(language_backend.config, out); - } - - fn write_2( - func: &Function, - language_backend: &mut CythonLanguageBackend, - out: &mut SourceWriter, - ) { - let prefix = language_backend.config.function.prefix(&func.annotations); - let postfix = language_backend.config.function.postfix(&func.annotations); - - let condition = func.cfg.to_condition(language_backend.config); - - condition.write_before(language_backend.config, out); - - language_backend.write_documentation(out, &func.documentation); - - 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(language_backend.config) { - if let Some(ref anno) = language_backend.config.function.must_use { - write!(out, "{}", anno); - out.new_line(); - } - } - if let Some(note) = func - .annotations - .deprecated_note(language_backend.config, DeprecatedNoteKind::Function) - { - write!(out, "{} ", note); - } - } - cdecl::write_func( - language_backend, - out, - func, - Layout::Vertical, - language_backend.config, - ); - if !func.extern_decl { - if let Some(ref postfix) = postfix { - out.new_line(); - write!(out, "{}", postfix); - } - } - - if let Some(ref swift_name_macro) = language_backend.config.function.swift_name_macro { - if let Some(swift_name) = func.swift_name(language_backend.config) { - write!(out, " {}({})", swift_name_macro, swift_name); - } - } - - out.write(";"); - - condition.write_after(language_backend.config, out); - } - - match &self.config.function.args { - Layout::Horizontal => write_1(f, self, out), - Layout::Vertical => write_2(f, self, out), - Layout::Auto => { - let max_line_length = self.config.line_length; - if !out.try_write(|out| write_1(f, self, out), max_line_length) { - write_2(f, self, out) - } - } - } - } - fn write_type(&mut self, out: &mut SourceWriter, t: &Type) { cdecl::write_type(self, out, t, self.config); } diff --git a/src/bindgen/language_backend/mod.rs b/src/bindgen/language_backend/mod.rs index ffd7f1236..adb7d8507 100644 --- a/src/bindgen/language_backend/mod.rs +++ b/src/bindgen/language_backend/mod.rs @@ -1,9 +1,10 @@ use crate::bindgen::ir::{ - Documentation, Enum, Function, ItemContainer, Literal, OpaqueItem, Static, Struct, Type, - Typedef, Union, + cfg::ConditionWrite, DeprecatedNoteKind, Documentation, Enum, Function, ItemContainer, Literal, + OpaqueItem, Static, Struct, ToCondition, Type, Typedef, Union, }; use crate::bindgen::writer::SourceWriter; -use crate::bindgen::Bindings; +use crate::bindgen::{cdecl, Bindings, Layout}; +use crate::Config; use std::io::Write; @@ -13,7 +14,7 @@ mod cython; pub use clike::CLikeLanguageBackend; pub use cython::CythonLanguageBackend; -pub trait LanguageBackend { +pub trait LanguageBackend: Sized { fn open_namespaces(&mut self, out: &mut SourceWriter); fn close_namespaces(&mut self, out: &mut SourceWriter); fn write_headers(&self, out: &mut SourceWriter, package_version: &str); @@ -24,40 +25,111 @@ pub trait LanguageBackend { fn write_opaque_item(&mut self, out: &mut SourceWriter, o: &OpaqueItem); fn write_type_def(&mut self, out: &mut SourceWriter, t: &Typedef); fn write_static(&mut self, out: &mut SourceWriter, s: &Static); - fn write_function(&mut self, out: &mut SourceWriter, f: &Function); + + fn write_function( + &mut self, + config: &Config, + out: &mut SourceWriter, + f: &Function, + ) { + match config.function.args { + Layout::Horizontal => { + self.write_function_with_layout(config, out, f, Layout::Horizontal) + } + Layout::Vertical => self.write_function_with_layout(config, out, f, Layout::Vertical), + Layout::Auto => { + let max_line_length = config.line_length; + if !out.try_write( + |out| self.write_function_with_layout(config, out, f, Layout::Horizontal), + max_line_length, + ) { + self.write_function_with_layout(config, out, f, Layout::Vertical); + } + } + } + } + + fn write_function_with_layout( + &mut self, + config: &Config, + out: &mut SourceWriter, + func: &Function, + layout: Layout, + ) { + 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); + + self.write_documentation(out, &func.documentation); + + fn write_space(layout: Layout, out: &mut SourceWriter) { + if layout == Layout::Vertical { + out.new_line(); + } else { + out.write(" ") + } + } + if func.extern_decl { + out.write("extern "); + } else { + if let Some(ref prefix) = prefix { + write!(out, "{}", prefix); + write_space(layout, out); + } + if func.annotations.must_use(config) { + if let Some(ref anno) = config.function.must_use { + write!(out, "{}", anno); + write_space(layout, out); + } + } + if let Some(note) = func + .annotations + .deprecated_note(config, DeprecatedNoteKind::Function) + { + write!(out, "{}", note); + write_space(layout, out); + } + } + cdecl::write_func(self, out, func, layout, config); + + if !func.extern_decl { + if let Some(ref postfix) = postfix { + write_space(layout, out); + write!(out, "{}", postfix); + } + } + + if let Some(ref swift_name_macro) = config.function.swift_name_macro { + if let Some(swift_name) = func.swift_name(config) { + // XXX Should this account for `layout`? + write!(out, " {}({})", swift_name_macro, swift_name); + } + } + + out.write(";"); + condition.write_after(config, out); + } + fn write_type(&mut self, out: &mut SourceWriter, t: &Type); fn write_documentation(&mut self, out: &mut SourceWriter, d: &Documentation); fn write_literal(&mut self, out: &mut SourceWriter, l: &Literal); - fn write_bindings(&mut self, out: &mut SourceWriter, b: &Bindings) - where - Self: Sized, - { + fn write_bindings(&mut self, out: &mut SourceWriter, b: &Bindings) { self.write_headers(out, &b.package_version); - self.open_namespaces(out); - self.write_primitive_constants(out, b); - self.write_items(out, b); - self.write_non_primitive_constants(out, b); - self.write_globals(out, b); - self.write_functions(out, b); - self.close_namespaces(out); - self.write_footers(out); - self.write_trailer(out, b); } - fn write_primitive_constants(&mut self, out: &mut SourceWriter, b: &Bindings) - where - Self: Sized, - { + fn write_primitive_constants(&mut self, out: &mut SourceWriter, b: &Bindings) { for constant in &b.constants { if constant.uses_only_primitive_types() { out.new_line_if_not_start(); @@ -92,10 +164,7 @@ pub trait LanguageBackend { } } - fn write_non_primitive_constants(&mut self, out: &mut SourceWriter, b: &Bindings) - where - Self: Sized, - { + fn write_non_primitive_constants(&mut self, out: &mut SourceWriter, b: &Bindings) { for constant in &b.constants { if !constant.uses_only_primitive_types() { out.new_line_if_not_start(); @@ -124,7 +193,7 @@ pub trait LanguageBackend { fn write_functions_default(&mut self, out: &mut SourceWriter, b: &Bindings) { for function in &b.functions { out.new_line_if_not_start(); - self.write_function(out, function); + self.write_function(&b.config, out, function); out.new_line(); } } From 7af32324394762f3b8faace8df6f2703cec0ee7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sun, 14 Apr 2024 18:59:16 +0200 Subject: [PATCH 3/3] bindgen: More language-backend clean-up. --- src/bindgen/language_backend/clike.rs | 554 ++++++++++++------------- src/bindgen/language_backend/cython.rs | 8 +- 2 files changed, 270 insertions(+), 292 deletions(-) diff --git a/src/bindgen/language_backend/clike.rs b/src/bindgen/language_backend/clike.rs index a2e3e5515..c33d6079c 100644 --- a/src/bindgen/language_backend/clike.rs +++ b/src/bindgen/language_backend/clike.rs @@ -111,6 +111,231 @@ impl<'a> CLikeLanguageBackend<'a> { out.new_line(); } } + + fn generate_typedef(&self) -> bool { + self.config.language == Language::C && self.config.style.generate_typedef() + } + + fn write_derived_cpp_ops(&mut self, out: &mut SourceWriter, s: &Struct) { + let mut wrote_start_newline = false; + + if self.config.structure.derive_constructor(&s.annotations) && !s.fields.is_empty() { + if !wrote_start_newline { + wrote_start_newline = true; + out.new_line(); + } + + out.new_line(); + + let renamed_fields: Vec<_> = s + .fields + .iter() + .map(|field| { + self.config + .function + .rename_args + .apply(&field.name, IdentifierType::FunctionArg) + .into_owned() + }) + .collect(); + write!(out, "{}(", s.export_name()); + let vec: Vec<_> = s + .fields + .iter() + .zip(&renamed_fields) + .map(|(field, renamed)| { + Field::from_name_and_type( + // const-ref args to constructor + format!("const& {}", renamed), + field.ty.clone(), + ) + }) + .collect(); + out.write_vertical_source_list(self, &vec[..], ListType::Join(","), Self::write_field); + write!(out, ")"); + out.new_line(); + write!(out, " : "); + let vec: Vec<_> = s + .fields + .iter() + .zip(&renamed_fields) + .map(|(field, renamed)| format!("{}({})", field.name, renamed)) + .collect(); + out.write_vertical_source_list(self, &vec[..], ListType::Join(","), |_, out, s| { + write!(out, "{}", s) + }); + out.new_line(); + write!(out, "{{}}"); + out.new_line(); + } + + let other = self + .config + .function + .rename_args + .apply("other", IdentifierType::FunctionArg); + + if s.annotations + .bool("internal-derive-bitflags") + .unwrap_or(false) + { + assert_eq!(s.fields.len(), 1); + let bits = &s.fields[0].name; + if !wrote_start_newline { + wrote_start_newline = true; + out.new_line(); + } + let constexpr_prefix = if self.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, + s.export_name() + ); + out.open_brace(); + write!( + out, + "return {} {{ static_cast(~{bits}) }};", + s.export_name() + ); + out.close_brace(false); + s.emit_bitflags_binop(constexpr_prefix, '|', &other, out); + s.emit_bitflags_binop(constexpr_prefix, '&', &other, out); + s.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 self.config.structure.derive_ostream(&s.annotations) { + if !wrote_start_newline { + wrote_start_newline = true; + out.new_line(); + } + + out.new_line(); + let stream = self + .config + .function + .rename_args + .apply("stream", IdentifierType::FunctionArg); + let instance = self + .config + .function + .rename_args + .apply("instance", IdentifierType::FunctionArg); + write!( + out, + "friend std::ostream& operator<<(std::ostream& {}, const {}& {})", + stream, + s.export_name(), + instance, + ); + out.open_brace(); + write!(out, "return {} << \"{{ \"", stream); + let vec: Vec<_> = s + .fields + .iter() + .map(|x| format!(" << \"{}=\" << {}.{}", x.name, instance, x.name)) + .collect(); + out.write_vertical_source_list( + self, + &vec[..], + ListType::Join(" << \", \""), + |_, out, s| write!(out, "{}", s), + ); + out.write(" << \" }\";"); + out.close_brace(false); + } + + let skip_fields = s.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)) = s.annotations.atom(concat!($op_name, "-attributes")) { + write!(out, "{} ", attrs); + } + + write!( + out, + "bool operator{}(const {}& {}) const", + $op, + s.export_name(), + other + ); + out.open_brace(); + out.write("return "); + let vec: Vec<_> = s + .fields + .iter() + .skip(skip_fields) + .map(|field| format!("{} {} {}.{}", field.name, $op, other, field.name)) + .collect(); + out.write_vertical_source_list( + self, + &vec[..], + ListType::Join(&format!(" {}", $conjuc)), + |_, out, s| write!(out, "{}", s), + ); + out.write(";"); + out.close_brace(false); + }}; + } + + if self.config.structure.derive_eq(&s.annotations) && s.can_derive_eq() { + emit_op!("eq", "==", "&&"); + } + if self.config.structure.derive_neq(&s.annotations) && s.can_derive_eq() { + emit_op!("neq", "!=", "||"); + } + if self.config.structure.derive_lt(&s.annotations) + && s.fields.len() == 1 + && s.fields[0].ty.can_cmp_order() + { + emit_op!("lt", "<", "&&"); + } + if self.config.structure.derive_lte(&s.annotations) + && s.fields.len() == 1 + && s.fields[0].ty.can_cmp_order() + { + emit_op!("lte", "<=", "&&"); + } + if self.config.structure.derive_gt(&s.annotations) + && s.fields.len() == 1 + && s.fields[0].ty.can_cmp_order() + { + emit_op!("gt", ">", "&&"); + } + if self.config.structure.derive_gte(&s.annotations) + && s.fields.len() == 1 + && s.fields[0].ty.can_cmp_order() + { + emit_op!("gte", ">=", "&&"); + } + } } impl LanguageBackend for CLikeLanguageBackend<'_> { @@ -303,7 +528,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { } // Close the struct or union opened either at (*) or at (**). - if self.config.language == Language::C && self.config.style.generate_typedef() { + if self.generate_typedef() { out.close_brace(false); write!(out, " {};", e.export_name); } else { @@ -349,10 +574,8 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { // typedef struct { // C with Both as style: // typedef struct Name { - match self.config.language { - Language::C if self.config.style.generate_typedef() => out.write("typedef "), - Language::C | Language::Cxx => {} - _ => unreachable!(), + if self.generate_typedef() { + out.write("typedef "); } out.write("struct"); @@ -400,230 +623,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { out.write_vertical_source_list(self, &s.fields, ListType::Cap(";"), Self::write_field); if self.config.language == Language::Cxx { - let mut wrote_start_newline = false; - - if self.config.structure.derive_constructor(&s.annotations) && !s.fields.is_empty() { - if !wrote_start_newline { - wrote_start_newline = true; - out.new_line(); - } - - out.new_line(); - - let renamed_fields: Vec<_> = s - .fields - .iter() - .map(|field| { - self.config - .function - .rename_args - .apply(&field.name, IdentifierType::FunctionArg) - .into_owned() - }) - .collect(); - write!(out, "{}(", s.export_name()); - let vec: Vec<_> = s - .fields - .iter() - .zip(&renamed_fields) - .map(|(field, renamed)| { - Field::from_name_and_type( - // const-ref args to constructor - format!("const& {}", renamed), - field.ty.clone(), - ) - }) - .collect(); - out.write_vertical_source_list( - self, - &vec[..], - ListType::Join(","), - Self::write_field, - ); - write!(out, ")"); - out.new_line(); - write!(out, " : "); - let vec: Vec<_> = s - .fields - .iter() - .zip(&renamed_fields) - .map(|(field, renamed)| format!("{}({})", field.name, renamed)) - .collect(); - out.write_vertical_source_list(self, &vec[..], ListType::Join(","), |_, out, s| { - write!(out, "{}", s) - }); - out.new_line(); - write!(out, "{{}}"); - out.new_line(); - } - - let other = self - .config - .function - .rename_args - .apply("other", IdentifierType::FunctionArg); - - if s.annotations - .bool("internal-derive-bitflags") - .unwrap_or(false) - { - assert_eq!(s.fields.len(), 1); - let bits = &s.fields[0].name; - if !wrote_start_newline { - wrote_start_newline = true; - out.new_line(); - } - let constexpr_prefix = if self.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, - s.export_name() - ); - out.open_brace(); - write!( - out, - "return {} {{ static_cast(~{bits}) }};", - s.export_name() - ); - out.close_brace(false); - s.emit_bitflags_binop(constexpr_prefix, '|', &other, out); - s.emit_bitflags_binop(constexpr_prefix, '&', &other, out); - s.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 self.config.structure.derive_ostream(&s.annotations) { - if !wrote_start_newline { - wrote_start_newline = true; - out.new_line(); - } - - out.new_line(); - let stream = self - .config - .function - .rename_args - .apply("stream", IdentifierType::FunctionArg); - let instance = self - .config - .function - .rename_args - .apply("instance", IdentifierType::FunctionArg); - write!( - out, - "friend std::ostream& operator<<(std::ostream& {}, const {}& {})", - stream, - s.export_name(), - instance, - ); - out.open_brace(); - write!(out, "return {} << \"{{ \"", stream); - let vec: Vec<_> = s - .fields - .iter() - .map(|x| format!(" << \"{}=\" << {}.{}", x.name, instance, x.name)) - .collect(); - out.write_vertical_source_list( - self, - &vec[..], - ListType::Join(" << \", \""), - |_, out, s| write!(out, "{}", s), - ); - out.write(" << \" }\";"); - out.close_brace(false); - } - - let skip_fields = s.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)) = s.annotations.atom(concat!($op_name, "-attributes")) - { - write!(out, "{} ", attrs); - } - - write!( - out, - "bool operator{}(const {}& {}) const", - $op, - s.export_name(), - other - ); - out.open_brace(); - out.write("return "); - let vec: Vec<_> = s - .fields - .iter() - .skip(skip_fields) - .map(|field| format!("{} {} {}.{}", field.name, $op, other, field.name)) - .collect(); - out.write_vertical_source_list( - self, - &vec[..], - ListType::Join(&format!(" {}", $conjuc)), - |_, out, s| write!(out, "{}", s), - ); - out.write(";"); - out.close_brace(false); - }}; - } - - if self.config.structure.derive_eq(&s.annotations) && s.can_derive_eq() { - emit_op!("eq", "==", "&&"); - } - if self.config.structure.derive_neq(&s.annotations) && s.can_derive_eq() { - emit_op!("neq", "!=", "||"); - } - if self.config.structure.derive_lt(&s.annotations) - && s.fields.len() == 1 - && s.fields[0].ty.can_cmp_order() - { - emit_op!("lt", "<", "&&"); - } - if self.config.structure.derive_lte(&s.annotations) - && s.fields.len() == 1 - && s.fields[0].ty.can_cmp_order() - { - emit_op!("lte", "<=", "&&"); - } - if self.config.structure.derive_gt(&s.annotations) - && s.fields.len() == 1 - && s.fields[0].ty.can_cmp_order() - { - emit_op!("gt", ">", "&&"); - } - if self.config.structure.derive_gte(&s.annotations) - && s.fields.len() == 1 - && s.fields[0].ty.can_cmp_order() - { - emit_op!("gte", ">=", "&&"); - } + self.write_derived_cpp_ops(out, s); } // Emit the post_body section, if relevant @@ -642,7 +642,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { } } - if self.config.language == Language::C && self.config.style.generate_typedef() { + if self.generate_typedef() { out.close_brace(false); write!(out, " {};", s.export_name()); } else { @@ -672,10 +672,8 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { // typedef union { // C with Both as style: // typedef union Name { - match self.config.language { - Language::C if self.config.style.generate_typedef() => out.write("typedef "), - Language::C | Language::Cxx => {} - _ => unreachable!(), + if self.generate_typedef() { + out.write("typedef "); } out.write("union"); @@ -715,7 +713,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { out.write_raw_block(body); } - if self.config.language == Language::C && self.config.style.generate_typedef() { + if self.generate_typedef() { out.close_brace(false); write!(out, " {};", u.export_name); } else { @@ -733,19 +731,15 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { o.generic_params.write_with_default(self, self.config, out); - match self.config.language { - Language::C if self.config.style.generate_typedef() => { - write!( - out, - "typedef struct {} {};", - o.export_name(), - o.export_name() - ); - } - Language::C | Language::Cxx => { - write!(out, "struct {};", o.export_name()); - } - _ => unreachable!(), + if self.generate_typedef() { + write!( + out, + "typedef struct {} {};", + o.export_name(), + o.export_name() + ); + } else { + write!(out, "struct {};", o.export_name()); } condition.write_after(self.config, out); @@ -759,19 +753,15 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { self.write_generic_param(out, &t.generic_params); - match self.config.language { - Language::Cxx => { - write!(out, "using {} = ", t.export_name()); - self.write_type(out, &t.aliased); - } - Language::C => { - write!(out, "{} ", self.config.language.typedef()); - self.write_field( - out, - &Field::from_name_and_type(t.export_name().to_owned(), t.aliased.clone()), - ) - } - _ => unreachable!(), + if self.config.language == Language::Cxx { + write!(out, "using {} = ", t.export_name()); + self.write_type(out, &t.aliased); + } else { + write!(out, "{} ", self.config.language.typedef()); + self.write_field( + out, + &Field::from_name_and_type(t.export_name().to_owned(), t.aliased.clone()), + ); } out.write(";"); @@ -862,11 +852,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { fn write_literal(&mut self, out: &mut SourceWriter, l: &Literal) { match l { - Literal::Expr(v) => match (&**v, self.config.language) { - ("true", Language::Cython) => write!(out, "True"), - ("false", Language::Cython) => write!(out, "False"), - (v, _) => write!(out, "{}", v), - }, + Literal::Expr(v) => write!(out, "{}", v), Literal::Path { ref associated_to, ref name, @@ -875,15 +861,12 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { if let Some(known) = to_known_assoc_constant(path, name) { return write!(out, "{}", known); } - let path_separator = match self.config.language { - Language::Cython | Language::C => "_", - Language::Cxx => { - if self.config.structure.associated_constants_in_body { - "::" - } else { - "_" - } - } + let path_separator = if self.config.language == Language::C { + "_" + } else if self.config.structure.associated_constants_in_body { + "::" + } else { + "_" }; write!(out, "{}{}", export_name, path_separator) } @@ -923,10 +906,10 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { fields, path, } => { - match self.config.language { - Language::C => write!(out, "({})", export_name), - Language::Cxx => write!(out, "{}", export_name), - _ => unreachable!(), + if self.config.language == Language::C { + write!(out, "({})", export_name); + } else { + write!(out, "{}", export_name); } write!(out, "{{ "); @@ -937,13 +920,14 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { if let Some(lit) = fields.get(ordered_key) { if !is_first_field { write!(out, ", "); - } else { - is_first_field = false; } - match self.config.language { - Language::Cxx => write!(out, "/* .{} = */ ", ordered_key), - Language::C => write!(out, ".{} = ", ordered_key), - _ => unreachable!(), + is_first_field = false; + if self.config.language == Language::Cxx { + // TODO: Some C++ versions (c++20?) now support designated + // initializers, consider generating them. + write!(out, "/* .{} = */ ", ordered_key); + } else { + write!(out, ".{} = ", ordered_key); } self.write_literal(out, lit); } diff --git a/src/bindgen/language_backend/cython.rs b/src/bindgen/language_backend/cython.rs index eaa666920..d768c596d 100644 --- a/src/bindgen/language_backend/cython.rs +++ b/src/bindgen/language_backend/cython.rs @@ -23,10 +23,7 @@ impl<'a> CythonLanguageBackend<'a> { if let Some(discriminant) = &u.discriminant { // 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(" = "); - + out.write(" # = "); self.write_literal(out, discriminant); } out.write(","); @@ -41,9 +38,6 @@ impl<'a> CythonLanguageBackend<'a> { // 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 let Some(bitfield) = f.annotations.atom("bitfield") { - // - // } } }