diff --git a/.github/workflows/cbindgen.yml b/.github/workflows/cbindgen.yml index b36c2511..c9c86bb5 100644 --- a/.github/workflows/cbindgen.yml +++ b/.github/workflows/cbindgen.yml @@ -60,6 +60,11 @@ jobs: python -m pip install --upgrade pip wheel pip install Cython==3.0.2 + - name: Install Zig + uses: goto-bus-stop/setup-zig@v2 + with: + version: master + - name: Build run: | cargo +stable build --verbose diff --git a/docs.md b/docs.md index c8ce9f4f..9515f3fc 100644 --- a/docs.md +++ b/docs.md @@ -37,8 +37,8 @@ cbindgen --config cbindgen.toml --crate my_rust_library --output my_header.h ``` This produces a header file for C++. For C, add the `--lang c` switch. \ -`cbindgen` also supports generation of [Cython](https://cython.org) bindings, -use `--lang cython` for that. +`cbindgen` also supports generation of [Cython](https://cython.org) and [Zig](https://ziglang.org) bindings, +use `--lang cython` or `--lang zig` for that. See `cbindgen --help` for more options. @@ -419,7 +419,7 @@ Note that many options defined here only apply for one of C or C++. Usually it's ```toml # The language to output bindings in # -# possible values: "C", "C++", "Cython" +# possible values: "C", "C++", "Cython", "Zig" # # default: "C++" language = "C" diff --git a/src/bindgen/bindings.rs b/src/bindgen/bindings.rs index 029cfc66..57650d50 100644 --- a/src/bindgen/bindings.rs +++ b/src/bindgen/bindings.rs @@ -16,7 +16,7 @@ use crate::bindgen::ir::{ Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Type, Typedef, }; use crate::bindgen::language_backend::{ - CLikeLanguageBackend, CythonLanguageBackend, LanguageBackend, + CLikeLanguageBackend, CythonLanguageBackend, ZigLanguageBackend, LanguageBackend, }; use crate::bindgen::writer::SourceWriter; @@ -210,6 +210,9 @@ impl Bindings { Language::Cython => { self.write_with_backend(file, &mut CythonLanguageBackend::new(&self.config)) } + Language::Zig => { + self.write_with_backend(file, &mut ZigLanguageBackend::new(&self.config)) + } } } diff --git a/src/bindgen/cdecl.rs b/src/bindgen/cdecl.rs index 3ba543ac..1bcf5731 100644 --- a/src/bindgen/cdecl.rs +++ b/src/bindgen/cdecl.rs @@ -115,7 +115,9 @@ impl CDecl { "error generating cdecl for {:?}", t ); - "const".clone_into(&mut self.type_qualifers); + if config.language != Language::Zig { + "const".clone_into(&mut self.type_qualifers); + } } assert!( @@ -139,7 +141,9 @@ impl CDecl { "error generating cdecl for {:?}", t ); - "const".clone_into(&mut self.type_qualifers); + if config.language != Language::Zig { + "const".clone_into(&mut self.type_qualifers); + } } assert!( @@ -147,7 +151,12 @@ impl CDecl { "error generating cdecl for {:?}", t ); - self.type_name = p.to_repr_c(config).to_string(); + + if config.language == Language::Zig { + self.type_name = p.to_repr_zig().to_string(); + } else { + self.type_name = p.to_repr_c(config).to_string(); + } } Type::Ptr { ref ty, @@ -210,7 +219,9 @@ impl CDecl { } } - write!(out, "{}", self.type_name); + if config.language != Language::Zig { + write!(out, "{}", self.type_name); + } if !self.type_generic_args.is_empty() { out.write("<"); @@ -228,11 +239,16 @@ impl CDecl { // When we have an identifier, put a space between the type and the declarators if ident.is_some() { - out.write(" "); + if config.language == Language::Zig { + out.write(""); + } else { + out.write(" "); + } } // Write the left part of declarators before the identifier let mut iter_rev = self.declarators.iter().rev().peekable(); + let mut is_functors = false; #[allow(clippy::while_let_on_iterator)] while let Some(declarator) = iter_rev.next() { @@ -244,9 +260,26 @@ impl CDecl { is_nullable, is_ref, } => { - out.write(if is_ref { "&" } else { "*" }); + if config.language != Language::Zig { + out.write(if is_ref { "&" } else { "*" }); + } else { + if !self.type_qualifers.is_empty() { + write!(out, "{}", self.type_qualifers); + } else { + if ident.is_none() && config.language == Language::Zig { + out.write("_"); + } + if config.language != Language::Zig { + out.write("_"); + } + } + } if is_const { - out.write("const "); + if config.language == Language::Zig { + write!(out, "{} ", config.style.zig_def()); + } else { + out.write("const "); + } } if !is_nullable && !is_ref && config.language != Language::Cython { if let Some(attr) = &config.pointer.non_null_attribute { @@ -260,16 +293,28 @@ impl CDecl { } } CDeclarator::Func { .. } => { - if next_is_pointer { + if next_is_pointer && config.language != Language::Zig { out.write("("); } + if !next_is_pointer && config.language == Language::Zig { + out.write("extern fn "); + } + is_functors = true; } } } // Write the identifier if let Some(ident) = ident { - write!(out, "{}", ident); + if config.language == Language::Zig && self.declarators.is_empty() { + if ident.is_empty() { + write!(out, "{}", self.type_name); + } else { + write!(out, "{}: {}", ident, self.type_name); + } + } else { + write!(out, "{}", ident); + } } // Write the right part of declarators after the identifier @@ -281,12 +326,34 @@ impl CDecl { match *declarator { CDeclarator::Ptr { .. } => { last_was_pointer = true; + + if config.language == Language::Zig { + if self.type_name.contains("u8") || self.type_name.contains("const u8") { + write!(out, ": ?[*:0]const {}", self.type_name); + } else if is_functors { + out.write(": ?fn"); + } else { + write!(out, ": ?*{}", self.type_name); + } + } } CDeclarator::Array(ref constant) => { if last_was_pointer { out.write(")"); } - write!(out, "[{}]", constant); + if config.language == Language::Zig { + if constant.is_empty() { + write!(out, "{}: [*]{}", self.type_qualifers, self.type_name); + } else { + write!( + out, + "{}: [{}]{}", + self.type_qualifers, constant, self.type_name + ); + } + } else { + write!(out, "[{}]", constant); + } last_was_pointer = false; } @@ -295,9 +362,10 @@ impl CDecl { ref layout, never_return, } => { - if last_was_pointer { + if last_was_pointer && config.language != Language::Zig { out.write(")"); } + is_functors = false; out.write("("); if args.is_empty() && config.language == Language::C { @@ -364,6 +432,14 @@ impl CDecl { } } + if config.language == Language::Zig { + if !last_was_pointer && self.type_name.contains("anyopaque") { + out.write(" callconv(.C) void") + } else { + write!(out, " {}", self.type_name); + } + } + last_was_pointer = true; } } diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs index 31316503..0003db58 100644 --- a/src/bindgen/config.rs +++ b/src/bindgen/config.rs @@ -23,6 +23,7 @@ pub enum Language { Cxx, C, Cython, + Zig, } impl FromStr for Language { @@ -42,6 +43,8 @@ impl FromStr for Language { "C" => Ok(Language::C), "cython" => Ok(Language::Cython), "Cython" => Ok(Language::Cython), + "zig" => Ok(Language::Zig), + "Zig" => Ok(Language::Zig), _ => Err(format!("Unrecognized Language: '{}'.", s)), } } @@ -54,6 +57,7 @@ impl Language { match self { Language::Cxx | Language::C => "typedef", Language::Cython => "ctypedef", + Language::Zig => "pub const", } } } @@ -167,6 +171,7 @@ pub enum DocumentationStyle { Doxy, Cxx, Auto, + Zig, } impl FromStr for DocumentationStyle { @@ -180,6 +185,7 @@ impl FromStr for DocumentationStyle { "c++" => Ok(DocumentationStyle::Cxx), "doxy" => Ok(DocumentationStyle::Doxy), "auto" => Ok(DocumentationStyle::Auto), + "zig" => Ok(DocumentationStyle::Zig), _ => Err(format!("Unrecognized documentation style: '{}'.", s)), } } @@ -240,6 +246,14 @@ impl Style { "ctypedef " } } + + pub fn zig_def(self) -> &'static str { + if self.generate_tag() { + "pub const " + } else { + "pub extern" + } + } } impl FromStr for Style { @@ -711,6 +725,8 @@ pub struct ConstantConfig { pub allow_static_const: bool, /// Whether a generated constant should be constexpr in C++ mode. pub allow_constexpr: bool, + /// Whether a generated compile-time should be comptime in Zig mode. + pub allow_comptime: bool, /// Sort key for constants pub sort_by: Option, } @@ -720,6 +736,7 @@ impl Default for ConstantConfig { ConstantConfig { allow_static_const: true, allow_constexpr: true, + allow_comptime: true, sort_by: None, } } @@ -894,6 +911,15 @@ pub struct CythonConfig { pub cimports: BTreeMap>, } +#[derive(Debug, Clone, Default, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct ZigConfig { + pub header: Option, + pub cimports: BTreeMap>, +} + /// A collection of settings to customize the generated bindings. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "snake_case")] @@ -1018,6 +1044,8 @@ pub struct Config { pub only_target_dependencies: bool, /// Configuration options specific to Cython. pub cython: CythonConfig, + /// Configuration options specific to Zig. + pub zig: ZigConfig, #[doc(hidden)] #[serde(skip)] /// Internal field for tracking from which file the config was loaded. @@ -1070,6 +1098,7 @@ impl Default for Config { pointer: PtrConfig::default(), only_target_dependencies: false, cython: CythonConfig::default(), + zig: ZigConfig::default(), config_path: None, } } @@ -1081,7 +1110,7 @@ impl Config { } pub(crate) fn include_guard(&self) -> Option<&str> { - if self.language == Language::Cython { + if self.language == Language::Cython || self.language == Language::Zig { None } else { self.include_guard.as_deref() @@ -1089,7 +1118,7 @@ impl Config { } pub(crate) fn includes(&self) -> &[String] { - if self.language == Language::Cython { + if self.language == Language::Cython || self.language == Language::Zig { &[] } else { &self.includes @@ -1097,7 +1126,7 @@ impl Config { } pub(crate) fn sys_includes(&self) -> &[String] { - if self.language == Language::Cython { + if self.language == Language::Cython || self.language == Language::Zig { &[] } else { &self.sys_includes diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs index 2f1d3bff..4d114022 100644 --- a/src/bindgen/ir/constant.rs +++ b/src/bindgen/ir/constant.rs @@ -721,6 +721,12 @@ impl Constant { write!(out, " {} # = ", name); language_backend.write_literal(out, value); } + Language::Zig => { + out.write(config.style.zig_def()); + write!(out, "{} = ", name); + language_backend.write_literal(out, value); + out.write(";"); + } } condition.write_after(config, out); diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs index 2e633a7d..d1aa765b 100644 --- a/src/bindgen/ir/enumeration.rs +++ b/src/bindgen/ir/enumeration.rs @@ -729,6 +729,9 @@ impl Enum { write!(out, "{}enum {}", config.style.cython_def(), tag_name); } } + Language::Zig => { + write!(out, "{}{} = enum", config.style.zig_def(), tag_name); + } } out.open_brace(); @@ -757,7 +760,7 @@ impl Enum { out.write("#ifndef __cplusplus"); } - if config.language != Language::Cxx { + if config.language != Language::Cxx && config.language != Language::Zig { out.new_line(); write!(out, "{} {} {};", config.language.typedef(), prim, tag_name); } @@ -783,6 +786,14 @@ impl Enum { Language::C if config.style.generate_typedef() => out.write("typedef "), Language::C | Language::Cxx => {} Language::Cython => out.write(config.style.cython_def()), + Language::Zig => { + write!( + out, + "{}{} = extern ", + config.style.zig_def(), + self.export_name() + ); + } } out.write(if inline_tag_field { "union" } else { "struct" }); @@ -800,9 +811,9 @@ impl Enum { write!(out, " {} ", note); } - if config.language != Language::C || config.style.generate_tag() { - write!(out, " {}", self.export_name()); - } + // if config.language != Language::C || config.language != Language::Zig || config.style.generate_tag() { + // write!(out, " {}", self.export_name()); + // } out.open_brace(); @@ -867,7 +878,11 @@ impl Enum { out.write("enum "); } - write!(out, "{} tag;", tag_name); + if config.language != Language::Zig { + write!(out, "{} tag;", tag_name); + } else { + write!(out, "tag: {},", tag_name); + } if wrap_tag { out.close_brace(true); @@ -910,7 +925,7 @@ impl Enum { // support unnamed structs. // For the same reason with Cython we can omit per-variant tags (the first // field) to avoid extra noise, the main `tag` is enough in this case. - if config.language != Language::Cython { + if config.language != Language::Cython && config.language != Language::Zig { out.write("struct"); out.open_brace(); } @@ -919,16 +934,24 @@ impl Enum { out.write_vertical_source_list( language_backend, &body.fields[start_field..], - ListType::Cap(";"), + ListType::Cap(if config.language != Language::Zig { + ";" + } else { + "," + }), &write_field, ); - if config.language != Language::Cython { + if config.language != Language::Cython && config.language != Language::Zig { out.close_brace(true); } + } else if config.language == Language::Zig { + write!(out, "{}: {},", name, body.export_name()); } else if config.style.generate_typedef() || config.language == Language::Cython { write!(out, "{} {};", body.export_name(), name); } else { - write!(out, "struct {} {};", body.export_name(), name); + if config.language != Language::Zig { + write!(out, "struct {} {};", body.export_name(), name); + } } if config.language != Language::Cython { condition.write_after(config, out); diff --git a/src/bindgen/ir/field.rs b/src/bindgen/ir/field.rs index 73019eee..f1a3dbda 100644 --- a/src/bindgen/ir/field.rs +++ b/src/bindgen/ir/field.rs @@ -42,4 +42,4 @@ impl Field { None }) } -} +} \ No newline at end of file diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs index 4a41d4cf..e1ac7c3c 100644 --- a/src/bindgen/ir/ty.rs +++ b/src/bindgen/ir/ty.rs @@ -171,6 +171,106 @@ impl PrimitiveType { } } + pub fn to_repr_zig(&self) -> &'static str { + match *self { + PrimitiveType::Bool => "bool", + PrimitiveType::Void => "anyopaque", + PrimitiveType::Char => "u8", + PrimitiveType::SChar => "i8", + PrimitiveType::UChar => "u8", + PrimitiveType::Char32 => "u32", + PrimitiveType::Integer { + kind, + signed, + zeroable: _, + } => match kind { + IntKind::Short => { + if signed { + "c_short" + } else { + "c_ushort" + } + } + IntKind::Int => { + if signed { + "c_int" + } else { + "c_uint" + } + } + IntKind::Long => { + if signed { + "c_long" + } else { + "c_ulong" + } + } + IntKind::LongLong => { + if signed { + "c_longlong" + } else { + "c_ulonglong" + } + } + IntKind::SizeT => { + if signed { + "ssize_t" + } else { + "size_t" + } + } + IntKind::Size => { + if signed { + "isize" + } else { + "usize" + } + } + IntKind::B8 => { + if signed { + "i8" + } else { + "u8" + } + } + IntKind::B16 => { + if signed { + "i16" + } else { + "u16" + } + } + IntKind::B32 => { + if signed { + "i32" + } else { + "u32" + } + } + IntKind::B64 => { + if signed { + "i64" + } else { + "u64" + } + } + }, + PrimitiveType::Float => "f32", + PrimitiveType::Double => "f64", + PrimitiveType::PtrDiffT => "ptrdiff_t", + PrimitiveType::VaList => { + #[cfg(target_os = "windows")] + { + "va_list" + } + #[cfg(not(target_os = "windows"))] + { + "..." + } + } + } + } + pub fn to_repr_c(&self, config: &Config) -> &'static str { match *self { PrimitiveType::Void => "void", diff --git a/src/bindgen/language_backend/clike.rs b/src/bindgen/language_backend/clike.rs index bf5a39fb..bac7e821 100644 --- a/src/bindgen/language_backend/clike.rs +++ b/src/bindgen/language_backend/clike.rs @@ -815,6 +815,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { DocumentationStyle::Doxy => out.write(" *"), DocumentationStyle::C99 => out.write("//"), DocumentationStyle::Cxx => out.write("///"), + DocumentationStyle::Zig => out.write("///"), DocumentationStyle::Auto => unreachable!(), // Auto case should always be covered } diff --git a/src/bindgen/language_backend/mod.rs b/src/bindgen/language_backend/mod.rs index 065bade2..9c338522 100644 --- a/src/bindgen/language_backend/mod.rs +++ b/src/bindgen/language_backend/mod.rs @@ -10,9 +10,11 @@ use std::io::Write; mod clike; mod cython; +mod zig; pub use clike::CLikeLanguageBackend; pub use cython::CythonLanguageBackend; +pub use zig::ZigLanguageBackend; pub trait LanguageBackend: Sized { fn open_namespaces(&mut self, out: &mut SourceWriter); diff --git a/src/bindgen/language_backend/zig.rs b/src/bindgen/language_backend/zig.rs new file mode 100644 index 00000000..7538a261 --- /dev/null +++ b/src/bindgen/language_backend/zig.rs @@ -0,0 +1,555 @@ +use crate::bindgen::ir::{ + to_known_assoc_constant, ConditionWrite, DeprecatedNoteKind, Documentation, Enum, EnumVariant, + Field, GenericParams, 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::{cdecl, Bindings, Config, Language}; +use crate::bindgen::{DocumentationLength, DocumentationStyle}; +use std::io::Write; + +pub struct ZigLanguageBackend<'a> { + config: &'a Config, +} + +impl<'a> ZigLanguageBackend<'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, _: bool) { + out.new_line(); + } +} + +impl LanguageBackend for ZigLanguageBackend<'_> { + 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.after_includes.is_none() + { + return; + } + + out.new_line_if_not_start(); + + if !self.config.no_includes { + match self.config.language { + Language::Zig => { + out.write("const std = @import(\"std\");"); + 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, _: &mut SourceWriter) { + // 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); + + // 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 (**). + out.close_brace(false); + write!(out, ";"); + } + + 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); + } + + write!(out, "const {} = extern struct", s.export_name); + + 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); + } + + 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); + + // 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); + } + } + + out.close_brace(false); + write!(out, ";"); + + 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); + + write!(out, "const {} = extern union", u.export_name); + + 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); + } + } + } + } + // todo!("See union tag"); + // 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); + } + + out.close_brace(false); + write!(out, ";"); + + 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); + + // like 'void*' in ziglang (optional null or ptr) + write!(out, "const {} = ?*anyopaque;", o.export_name()); + 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); + + 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(";"); + + condition.write_after(self.config, out); + } + + 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 { + out.write("const "); + } + cdecl::write_field(self, out, &s.ty, &s.export_name, self.config); + out.write(";"); + } + + 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 => DocumentationStyle::Zig, // 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::Zig => { + 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::Zig => out.write("///"), + DocumentationStyle::Auto => unreachable!(), // Auto case should always be covered + } + + write!(out, "{}", line); + out.new_line(); + } + + match style { + DocumentationStyle::Zig => { + out.write(" ///"); + out.new_line(); + } + + _ => (), + } + } + + fn write_literal(&mut self, out: &mut SourceWriter, l: &Literal) { + match l { + Literal::Expr(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 = "_"; + 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, + } => { + 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, ", "); + } + is_first_field = false; + write!(out, ".{} = ", ordered_key); + 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.language == Language::Zig { + // if let Some(ref using_namespaces) = b.config.using_namespaces { + // for namespace in using_namespaces { + // out.new_line(); + // write!(out, "usingnamespace {};", namespace); + // } + // 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 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/writer.rs b/src/bindgen/writer.rs index f36757d2..8f7f3bf2 100644 --- a/src/bindgen/writer.rs +++ b/src/bindgen/writer.rs @@ -159,7 +159,7 @@ impl<'a, F: Write> SourceWriter<'a, F> { pub fn open_brace(&mut self) { match self.bindings.config.language { - Language::Cxx | Language::C => match self.bindings.config.braces { + Language::Cxx | Language::C | Language::Zig => match self.bindings.config.braces { Braces::SameLine => { self.write(" {"); self.push_tab(); @@ -183,7 +183,7 @@ impl<'a, F: Write> SourceWriter<'a, F> { pub fn close_brace(&mut self, semicolon: bool) { self.pop_tab(); match self.bindings.config.language { - Language::Cxx | Language::C => { + Language::Cxx | Language::C | Language::Zig => { self.new_line(); if semicolon { self.write("};"); diff --git a/src/main.rs b/src/main.rs index a6a1852c..b9074368 100644 --- a/src/main.rs +++ b/src/main.rs @@ -165,7 +165,7 @@ fn main() { .long("lang") .value_name("LANGUAGE") .help("Specify the language to output bindings in") - .value_parser(["c++", "C++", "c", "C", "cython", "Cython"]), + .value_parser(["c++", "C++", "c", "C", "cython", "Cython", "zig", "Zig"]), ) .arg( Arg::new("package-version") diff --git a/tests/expectations/alias.zig b/tests/expectations/alias.zig new file mode 100644 index 00000000..ec8586f1 --- /dev/null +++ b/tests/expectations/alias.zig @@ -0,0 +1,31 @@ +const std = @import("std"); + +pub const Status = enum(c_int) { + Ok = 0, + Err = -1, +}; + +pub const Dep = extern struct { + a: i32, + b: f32, +}; + +pub const Foo_i32 = extern struct { + a: i32, + b: i32, + c: Dep, +}; + +pub const IntFoo = Foo_i32; + +pub const Foo_f64 = extern struct { + a: f64, + b: f64, + c: Dep, +}; + +pub const DoubleFoo = Foo_f64; +pub const Unit = i32; +pub const SpecialStatus = Status; + +extern fn root(x: IntFoo, y: DoubleFoo, z: Unit, w: SpecialStatus) anyopaque; diff --git a/tests/expectations/annotation.zig b/tests/expectations/annotation.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/annotation.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/array.zig b/tests/expectations/array.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/array.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/asserted_cast.zig b/tests/expectations/asserted_cast.zig new file mode 100644 index 00000000..f3db0c9d --- /dev/null +++ b/tests/expectations/asserted_cast.zig @@ -0,0 +1 @@ +const std = @import("std"); \ No newline at end of file diff --git a/tests/expectations/assoc_const_conflict.zig b/tests/expectations/assoc_const_conflict.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/assoc_const_conflict.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/bitfield.zig b/tests/expectations/bitfield.zig new file mode 100644 index 00000000..77334802 --- /dev/null +++ b/tests/expectations/bitfield.zig @@ -0,0 +1,8 @@ +const std = @import("std"); + +pub const HasBitfields = packed struct { + foo: u8, + bar: u56, +}; + +extern fn root(?*const HasBitfields) anyopaque; diff --git a/tests/expectations/bitflags.zig b/tests/expectations/bitflags.zig new file mode 100644 index 00000000..5b567634 --- /dev/null +++ b/tests/expectations/bitflags.zig @@ -0,0 +1,35 @@ +const std = @import("std"); + +pub const AlignFlags = extern struct { + bits: u8, +}; + +pub const DebugFlags = extern struct { + bits: u32, +}; + +extern fn root(flags: AlignFlags, bigger_flags: DebugFlags) void; + +pub const AlignFlags_AUTO = std.mem.zeroInit(AlignFlags, .{ + .bits = std.zig.c_translation.cast(u8, @as(c_int, 0)), +}); + +pub const AlignFlags_NORMAL = std.mem.zeroInit(AlignFlags, .{ + .bits = std.zig.c_translation.cast(u8, @as(c_int, 1)), +}); + +pub const AlignFlags_START = std.mem.zeroInit(AlignFlags, .{ + .bits = std.zig.c_translation.cast(u8, @as(c_int, 1) << @as(c_int, 1)), +}); + +pub const AlignFlags_END = std.mem.zeroInit(AlignFlags, .{ + .bits = std.zig.c_translation.cast(u8, @as(c_int, 1) << @as(c_int, 2)), +}); + +pub const AlignFlags_FLEX_START = std.mem.zeroInit(AlignFlags, .{ + .bits = std.zig.c_translation.cast(u8, @as(c_int, 1) << @as(c_int, 3)), +}); + +pub const DebugFlags_BIGGEST_ALLOWED = std.mem.zeroInit(DebugFlags, .{ + .bits = std.zig.c_translation.cast(u32, @as(c_int, 1) << @as(c_int, 31)), +}); diff --git a/tests/expectations/body.zig b/tests/expectations/body.zig new file mode 100644 index 00000000..0dd0a2cc --- /dev/null +++ b/tests/expectations/body.zig @@ -0,0 +1,75 @@ +const std = @import("std"); + +pub const MyCLikeEnum = extern union(c_uint) { + Foo1, + Bar1, + Baz1, +}; + +pub const MyCLikeEnum_Prepended = extern union(c_uint) { + Foo1_Prepended, + Bar1_Prepended, + Baz1_Prepended, +}; + +pub const MyFancyStruct = extern struct { + i: i32, +}; + +pub const MyFancyEnum_Tag = extern union(c_uint) { + Foo, + Bar, + Baz, +}; + +const struct_unnamed_2 = extern struct { + bar: i32, +}; +const struct_unnamed_3 = extern struct { + baz: i32, +}; +const union_unnamed_1 = extern union { + unnamed_0: struct_unnamed_2, + unnamed_1: struct_unnamed_3, +}; +pub const MyFancyEnum = extern struct { + tag: MyFancyEnum_Tag, + unnamed_0: union_unnamed_1, +}; +pub const MyUnion = extern union { + f: f32, + u: u32, + extra_member: i32, +}; +pub const MyFancyStruct_Prepended = extern struct { + i: i32, +}; + +pub const MyFancyEnum_Prepended_Tag = extern union(c_uint) { + Foo_Prepended, + Bar_Prepended, + Baz_Prepended, +}; + +const struct_unnamed_5 = extern struct { + bar_prepended: i32, +}; +const struct_unnamed_6 = extern struct { + baz_prepended: i32, +}; +const union_unnamed_4 = extern union { + unnamed_0: struct_unnamed_5, + unnamed_1: struct_unnamed_6, +}; +pub const MyFancyEnum_Prepended = extern struct { + tag: MyFancyEnum_Prepended_Tag, + unnamed_0: union_unnamed_4, +}; + +pub const MyUnion_Prepended = extern union { + extra_member: i32, + f: f32, + u: u32, +}; + +extern fn root(s: MyFancyStruct, e: MyFancyEnum, c: MyCLikeEnum, u: MyUnion, sp: MyFancyStruct_Prepended, ep: MyFancyEnum_Prepended, cp: MyCLikeEnum_Prepended, up: MyUnion_Prepended) anyopaque; diff --git a/tests/expectations/box.zig b/tests/expectations/box.zig new file mode 100644 index 00000000..90f4f06c --- /dev/null +++ b/tests/expectations/box.zig @@ -0,0 +1,11 @@ +const std = @import("std"); + +pub const struct_NotReprC_____i32 = opaque {}; +pub const NotReprC_____i32 = struct_NotReprC_____i32; +pub const Foo = NotReprC_____i32; +pub const MyStruct = extern struct { + number: [*c]i32, +}; +extern fn root(a: ?*const Foo, with_box: [*c]const MyStruct) anyopaque; +extern fn drop_box(x: [*c]i32) anyopaque; +extern fn drop_box_opt(x: [*c]i32) anyopaque; diff --git a/tests/expectations/cdecl.zig b/tests/expectations/cdecl.zig new file mode 100644 index 00000000..14fe62d6 --- /dev/null +++ b/tests/expectations/cdecl.zig @@ -0,0 +1,22 @@ +const std = @import("std"); + +pub const A = ?fn () callconv(.C) void; +pub const B = ?fn () callconv(.C) void; +pub const C = ?fn (i32, i32) callconv(.C) bool; +pub const D = ?fn (i32) callconv(.C) ?fn (f32) callconv(.C) bool; +pub const E = ?fn () callconv(.C) [*c]const [16]i32; +pub const F = [*c]const i32; +pub const G = [*c]const [*c]const i32; +pub const H = [*c]const [*c]i32; +pub const I = [*c]const [16]i32; +pub const J = [*c]?fn (f32) callconv(.C) f64; +pub const K = [16]i32; +pub const L = [16][*c]const i32; +pub const M = [16]?fn (i32, i32) callconv(.C) bool; +pub const N = [16]?fn (i32, i32) callconv(.C) void; + +pub const P = ?fn (i32, bool, bool, i32) callconv(.C) void; + +extern fn O() ?fn () callconv(.C) void; + +extern fn root(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: [*c]i32, l: [*c][*c]const i32, m: [*c]?fn (i32, i32) callconv(.C) bool, n: [*c]?fn (i32, i32) callconv(.C) void, p: P) void; diff --git a/tests/expectations/cell.zig b/tests/expectations/cell.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/cell.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/cfg.zig b/tests/expectations/cfg.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/cfg.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/char.zig b/tests/expectations/char.zig new file mode 100644 index 00000000..cddd27b9 --- /dev/null +++ b/tests/expectations/char.zig @@ -0,0 +1,6 @@ +const std = @import("std"); + +pub const Foo = extern struct { + a: u32, +}; +extern fn root(a: Foo) anyopaque; diff --git a/tests/expectations/constant.zig b/tests/expectations/constant.zig new file mode 100644 index 00000000..a1851153 --- /dev/null +++ b/tests/expectations/constant.zig @@ -0,0 +1,9 @@ +const std = @import("std"); + +pub const FOO: u32 = 10; + +pub const Foo = extern struct { + x: [FOO]i32, +}; + +extern fn root(x: Foo) anyopaque; diff --git a/tests/expectations/constant_big.zig b/tests/expectations/constant_big.zig new file mode 100644 index 00000000..b656ec5d --- /dev/null +++ b/tests/expectations/constant_big.zig @@ -0,0 +1,9 @@ +const std = @import("std"); + +const UNSIGNED_NEEDS_ULL_SUFFIX: c_ulonglong = 9223372036854775808; + +const UNSIGNED_DOESNT_NEED_ULL_SUFFIX: c_ulonglong = 8070450532247928832; + +const SIGNED_NEEDS_ULL_SUFFIX: c_longlong = -9223372036854775808; + +const SIGNED_DOESNT_NEED_ULL_SUFFIX : c_longlong = -9223372036854775807; \ No newline at end of file diff --git a/tests/expectations/cython_options.zig b/tests/expectations/cython_options.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/cython_options.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/dep_v2.zig b/tests/expectations/dep_v2.zig new file mode 100644 index 00000000..e458840f --- /dev/null +++ b/tests/expectations/dep_v2.zig @@ -0,0 +1,8 @@ +const std = @import("std"); + +pub const dep_struct = extern struct { + x: u32, + y: f64, +}; + +extern fn get_x(dep_struct: ?*const dep_struct) c_int; diff --git a/tests/expectations/derive_eq.zig b/tests/expectations/derive_eq.zig new file mode 100644 index 00000000..9e398dd9 --- /dev/null +++ b/tests/expectations/derive_eq.zig @@ -0,0 +1,39 @@ +const std = @import("std"); + +pub const Foo = extern struct { + a: bool, + b: i32, +}; + +pub const Bar_Tag = enum { + Baz, + Bazz, + FooNamed, + FooParen, +}; + +pub const Bazz_Body = extern struct { + tag: Bar_Tag, + named: Foo, +}; + +pub const FooNamed_Body = extern struct { + tag: Bar_Tag, + different: i32, + fields: u32, +}; + +pub const FooParen_Body = extern struct { + tag: Bar_Tag, + _0: i32, + _1: Foo, +}; + +pub const Bar = extern union { + tag: Bar_Tag, + bazz: Bazz_Body, + foo_named: FooNamed_Body, + foo_paren: FooParen_Body, +}; + +extern fn root(bar: Bar) Foo; diff --git a/tests/expectations/display_list.zig b/tests/expectations/display_list.zig new file mode 100644 index 00000000..a9812802 --- /dev/null +++ b/tests/expectations/display_list.zig @@ -0,0 +1,38 @@ +const std = @import("std"); + +pub const Rect = extern struct { + x: f32, + y: f32, + w: f32, + h: f32, +}; +pub const Color = extern struct { + r: u8, + g: u8, + b: u8, + a: u8, +}; + +pub const DisplayItem_Tag = enum(c_int) { + Fill, + Image, + ClearScreen, +}; + +pub const Fill_Body = extern struct { + tag: DisplayItem_Tag, + _0: Rect, + _1: Color, +}; +pub const Image_Body = extern struct { + tag: DisplayItem_Tag, + id: u32, + bounds: Rect, +}; +pub const DisplayItem = extern union { + tag: DisplayItem_Tag, + fill: Fill_Body, + image: Image_Body, +}; + +extern fn push_item(item: DisplayItem) bool; diff --git a/tests/expectations/doclength_short.zig b/tests/expectations/doclength_short.zig new file mode 100644 index 00000000..e5f799e7 --- /dev/null +++ b/tests/expectations/doclength_short.zig @@ -0,0 +1,14 @@ +const std = @import("std"); + +// The root of all evil. +// +// But at least it contains some more documentation as someone would expect +// from a simple test case like this. Though, this shouldn't appear in the +// output. +extern fn root() anyopaque; + +// A little above the root, and a lot more visible, with a run-on sentence +// to test going over the first line. +// +// Still not here, though. +extern fn trunk() anyopaque; diff --git a/tests/expectations/docstyle.c99.zig b/tests/expectations/docstyle.c99.zig new file mode 100644 index 00000000..ca42cf93 --- /dev/null +++ b/tests/expectations/docstyle.c99.zig @@ -0,0 +1,4 @@ +const std = @import("std"); + +// The root of all evil. +extern fn root() anyopaque; diff --git a/tests/expectations/docstyle_auto.zig b/tests/expectations/docstyle_auto.zig new file mode 100644 index 00000000..ca42cf93 --- /dev/null +++ b/tests/expectations/docstyle_auto.zig @@ -0,0 +1,4 @@ +const std = @import("std"); + +// The root of all evil. +extern fn root() anyopaque; diff --git a/tests/expectations/documentation.zig b/tests/expectations/documentation.zig new file mode 100644 index 00000000..67ef1234 --- /dev/null +++ b/tests/expectations/documentation.zig @@ -0,0 +1,21 @@ +const std = @import("std"); + +// The root of all evil. +// +// But at least it contains some more documentation as someone would expect +// from a simple test case like this. +// +// # Hint +// +// Always ensure that everything is properly documented, even if you feel lazy. +// **Sometimes** it is also helpful to include some markdown formatting. +// +// //////////////////////////////////////////////////////////////////////////// +// +// Attention: +// +// Rust is going to trim all leading `/` symbols. If you want to use them as a +// marker you need to add at least a single whitespace inbetween the tripple +// slash doc-comment marker and the rest. +// +extern fn root() anyopaque; diff --git a/tests/expectations/enum.zig b/tests/expectations/enum.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/enum.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/enum_self.zig b/tests/expectations/enum_self.zig new file mode 100644 index 00000000..5d441e60 --- /dev/null +++ b/tests/expectations/enum_self.zig @@ -0,0 +1,21 @@ +const std = @import("std"); + +pub const Foo_Bar = extern struct { + something: ?*i32, +}; + +pub const Bar_Tag = enum { + Min, + Max, + Other, +}; + +pub const Bar = extern union { + tag: Bar_Tag, + min_tag: Bar_Tag, + min: Foo_Bar, + max_tag: Bar_Tag, + max: Foo_Bar, +}; + +pub extern fn root(b: Bar) anyopaque; diff --git a/tests/expectations/euclid.zig b/tests/expectations/euclid.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/euclid.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/exclude_generic_monomorph.zig b/tests/expectations/exclude_generic_monomorph.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/exclude_generic_monomorph.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/expand.zig b/tests/expectations/expand.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/expand.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/export_name.zig b/tests/expectations/export_name.zig new file mode 100644 index 00000000..fbb98c22 --- /dev/null +++ b/tests/expectations/export_name.zig @@ -0,0 +1,3 @@ +const std = @import("std"); + +extern fn do_the_thing_with_export_name() anyopaque; diff --git a/tests/expectations/extern.zig b/tests/expectations/extern.zig new file mode 100644 index 00000000..07da3683 --- /dev/null +++ b/tests/expectations/extern.zig @@ -0,0 +1,10 @@ +const std = @import("std"); + +pub const Normal = extern struct { + x: i32, + y: f32, +}; + +extern fn foo() i32; + +extern fn bar(a: Normal) anyopaque; diff --git a/tests/expectations/extern_2.zig b/tests/expectations/extern_2.zig new file mode 100644 index 00000000..017e415e --- /dev/null +++ b/tests/expectations/extern_2.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +extern fn first() anyopaque; + +extern fn second() anyopaque; diff --git a/tests/expectations/external_workspace_child.zig b/tests/expectations/external_workspace_child.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/external_workspace_child.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/fns.tag.zig b/tests/expectations/fns.tag.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/fns.tag.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/fns.zig b/tests/expectations/fns.zig new file mode 100644 index 00000000..c06f8fde --- /dev/null +++ b/tests/expectations/fns.zig @@ -0,0 +1,13 @@ +const std = @import("std"); + +pub const Fns = extern struct { + _noArgs: ?fn() anyopaque, + _anonymousArg: ?fn() anyopaque, + _returnsNumber: ?fn() i32, + _namedArgs: ?fn(first: i32, snd: i16) i8, + _namedArgsWildcards: ?fn(_: i32, named: i16, _1: i64) i8, +}; + +extern fn root(_fns: Fns) anyopaque; + +extern fn no_return() anyopaque; diff --git a/tests/expectations/forward_declaration.zig b/tests/expectations/forward_declaration.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/forward_declaration.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/function_args.zig b/tests/expectations/function_args.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/function_args.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/function_noreturn.zig b/tests/expectations/function_noreturn.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/function_noreturn.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/function_ptr.zig b/tests/expectations/function_ptr.zig new file mode 100644 index 00000000..b2d3a733 --- /dev/null +++ b/tests/expectations/function_ptr.zig @@ -0,0 +1,11 @@ +const std = @import("std"); + +pub const MyCallback = : ?fn(a: usize, b: usize) anyopaque; + +pub const MyOtherCallback = : ?fn(a: usize, + lot: usize, + of: usize, + args: usize, + and_then_some: usize) anyopaque; + +pub extern fn my_function(a: MyCallback, b: MyOtherCallback) callconv(.C) void; diff --git a/tests/expectations/generic_pointer.zig b/tests/expectations/generic_pointer.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/generic_pointer.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/global_attr.zig b/tests/expectations/global_attr.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/global_attr.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/ignore.zig b/tests/expectations/ignore.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/ignore.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/include.zig b/tests/expectations/include.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/include.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/include_guard.zig b/tests/expectations/include_guard.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/include_guard.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/include_item.zig b/tests/expectations/include_item.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/include_item.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/infinite_recursion_typedef_monomorph.zig b/tests/expectations/infinite_recursion_typedef_monomorph.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/infinite_recursion_typedef_monomorph.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/inner_mod.zig b/tests/expectations/inner_mod.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/inner_mod.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/item_types.zig b/tests/expectations/item_types.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/item_types.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/layout.zig b/tests/expectations/layout.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/layout.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/layout_aligned_opaque.zig b/tests/expectations/layout_aligned_opaque.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/layout_aligned_opaque.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/layout_packed_opaque.zig b/tests/expectations/layout_packed_opaque.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/layout_packed_opaque.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/lifetime_arg.zig b/tests/expectations/lifetime_arg.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/lifetime_arg.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/linestyle_cr.zig b/tests/expectations/linestyle_cr.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/linestyle_cr.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/linestyle_if.zig b/tests/expectations/linestyle_if.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/linestyle_if.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/literal_target.zig b/tests/expectations/literal_target.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/literal_target.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/mangle.zig b/tests/expectations/mangle.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/mangle.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/manuallydrop.zig b/tests/expectations/manuallydrop.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/manuallydrop.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/maybeuninit.zig b/tests/expectations/maybeuninit.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/maybeuninit.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/mod_2015.zig b/tests/expectations/mod_2015.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/mod_2015.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/mod_2018.zig b/tests/expectations/mod_2018.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/mod_2018.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/monomorph_2.zig b/tests/expectations/monomorph_2.zig new file mode 100644 index 00000000..587190cc --- /dev/null +++ b/tests/expectations/monomorph_2.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +const A = opaque {}; + +const B = opaque {}; + +pub const List_A = extern struct { + _members: ?*A, + count: usize, +}; + +pub const List_B = extern struct { + _members: ?*B, + count: usize, +}; + +extern fn foo(a: List_A) anyopaque; + +extern fn bar(b: List_B) anyopaque; diff --git a/tests/expectations/must_use.zig b/tests/expectations/must_use.zig new file mode 100644 index 00000000..1e954ddf --- /dev/null +++ b/tests/expectations/must_use.zig @@ -0,0 +1,10 @@ +const std = @import("std"); + +pub const MaybeOwnedPtr_i32_Tag = enum { + Owned_i32, + None_i32, +}; + +pub const MaybeOwnedPtr_i32 = extern struct { + tag: MaybeOwnedPtr_i32_Tag, +}; \ No newline at end of file diff --git a/tests/expectations/nested_import.zig b/tests/expectations/nested_import.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/nested_import.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/no_includes.zig b/tests/expectations/no_includes.zig new file mode 100644 index 00000000..69bb11ba --- /dev/null +++ b/tests/expectations/no_includes.zig @@ -0,0 +1 @@ +extern fn root() anyopaque; diff --git a/tests/expectations/nonnull.zig b/tests/expectations/nonnull.zig new file mode 100644 index 00000000..ca190d57 --- /dev/null +++ b/tests/expectations/nonnull.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +const Opaque = opaque {}; + +pub const Foo_u64 = extern struct { + _a: ?*f32, + _b: ?*u64, + _c: ?*Opaque, + __d: ?*u64, + __e: ?*f32, + __f: ?*Opaque, + _g: ?*u64, + _h: ?*i32, + __i: ?*i32, +}; + +extern fn root(_arg: ?*i32, _foo: ?*Foo_u64, __d: ?*Opaque) anyopaque; diff --git a/tests/expectations/nonzero.zig b/tests/expectations/nonzero.zig new file mode 100644 index 00000000..dee33e26 --- /dev/null +++ b/tests/expectations/nonzero.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +const Option_i64 = opaque {}; + +pub const NonZeroTest = extern struct { + a: u8, + b: u16, + c: u32, + d: u64, + e: i8, + f: i16, + g: i32, + h: i64, + i: i64, + j: ?*const Option_i64, +}; + +extern fn root(_test: NonZeroTest, a: u8, b: u16, c: u32, d: u64, e: i8, f: i16, g: i32, h: i64, i: i64, j: ?*const Option_i64) anyopaque; diff --git a/tests/expectations/opaque.zig b/tests/expectations/opaque.zig new file mode 100644 index 00000000..7aa8c07f --- /dev/null +++ b/tests/expectations/opaque.zig @@ -0,0 +1,14 @@ +const std = @import("std"); + +const HashMap_i32__i32__BuildHasherDefault_DefaultHasher = opaque {}; + +const Result_Foo = opaque {}; + +/// Fast hash map used internally. +pub const FastHashMap_i32__i32 = HashMap_i32__i32__BuildHasherDefault_DefaultHasher; + +pub const Foo = FastHashMap_i32__i32; + +pub const Bar = Result_Foo; + +pub extern fn root(a: ?*Foo, b: ?*Bar) anyopaque; diff --git a/tests/expectations/pin.zig b/tests/expectations/pin.zig new file mode 100644 index 00000000..161d81bf --- /dev/null +++ b/tests/expectations/pin.zig @@ -0,0 +1,8 @@ +const std = @import("std"); + +pub const PinTest = extern struct { + _pinned_box: ?*i32, + _pinned_ref: ?*i32, +}; + +pub extern fn root(_s: ?*i32, p: PinTest) anyopaque; diff --git a/tests/expectations/pragma_once.zig b/tests/expectations/pragma_once.zig new file mode 100644 index 00000000..d4c2e15c --- /dev/null +++ b/tests/expectations/pragma_once.zig @@ -0,0 +1,3 @@ +const std = @import("std"); + +pub extern fn root() anyopaque; diff --git a/tests/expectations/prefix.zig b/tests/expectations/prefix.zig new file mode 100644 index 00000000..bc19218c --- /dev/null +++ b/tests/expectations/prefix.zig @@ -0,0 +1,23 @@ +const std = @import("std"); + +pub const LEN = 22; + +pub const X = (22 << 22); + +pub const Y = (X + X); + +pub const NamedLenArray = [LEN]i32; + +pub const ValuedLenArray = [22]i32; + +pub const AbsoluteFontWeight_Tag = enum { + Weight, + Normal, + Bold, +}; + +pub const AbsoluteFontWeight = extern union { + tag: AbsoluteFontWeight_Tag, + weight_tag: AbsoluteFontWeight_Tag, + weight: f32, +}; diff --git a/tests/expectations/prefixed_struct_literal_deep.zig b/tests/expectations/prefixed_struct_literal_deep.zig new file mode 100644 index 00000000..8bdf6399 --- /dev/null +++ b/tests/expectations/prefixed_struct_literal_deep.zig @@ -0,0 +1,20 @@ +const std = @import("std"); + +pub const PREFIXBar = extern struct { + a: i32, +}; +pub const PREFIXFoo = extern struct { + a: i32, + b: u32, + bar: PREFIXBar, +}; + +pub extern fn root(x: PREFIXFoo) anyopaque; + +pub const PREFIXVAL = @import("std").mem.zeroInit(PREFIXFoo, .{ + .a = @as(c_int, 42), + .b = @as(c_int, 1337), + .bar = @import("std").mem.zeroInit(PREFIXBar, .{ + .a = @as(c_int, 323), + }), +}); diff --git a/tests/expectations/ptrs_as_arrays.zig b/tests/expectations/ptrs_as_arrays.zig new file mode 100644 index 00000000..af351be1 --- /dev/null +++ b/tests/expectations/ptrs_as_arrays.zig @@ -0,0 +1,11 @@ +const std = @import("std"); + +extern fn ptr_as_array(n: u32, arg: [3]u32, _v: ?*u64) anyopaque; + +extern fn ptr_as_array1(n: u32, arg: [3]u32, v: [4]u64) anyopaque; + +extern fn ptr_as_array2(n: u32, arg: [*]u32, v: [*]u64) anyopaque; + +extern fn ptr_as_array_wrong_syntax(_arg: ?*u32, _v: ?*u32, _: ?*u32) anyopaque; + +extern fn ptr_as_array_unnamed(_: ?*u32, _: ?*u32) anyopaque; diff --git a/tests/expectations/raw_ident.zig b/tests/expectations/raw_ident.zig new file mode 100644 index 00000000..33408979 --- /dev/null +++ b/tests/expectations/raw_ident.zig @@ -0,0 +1,14 @@ +const std = @import("std"); + +pub const Enum = enum(c_int) { + a, + b, +}; + +pub const Struct = extern struct { + field: Enum, +}; + +pub const STATIC = Enum; + +extern fn (arg: Struct) anyopaque; diff --git a/tests/expectations/raw_lines.zig b/tests/expectations/raw_lines.zig new file mode 100644 index 00000000..643bb551 --- /dev/null +++ b/tests/expectations/raw_lines.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +pub const VERSION = 1; + +extern fn root() anyopaque; diff --git a/tests/expectations/rename.zig b/tests/expectations/rename.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/rename.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/rename_crate.zig b/tests/expectations/rename_crate.zig new file mode 100644 index 00000000..ba040f38 --- /dev/null +++ b/tests/expectations/rename_crate.zig @@ -0,0 +1,20 @@ +const std = @import("std"); + +pub const Foo = extern struct { + x: i32, +}; +pub const RenamedTy = extern struct { + y: u64, +}; +pub const NoExternTy = extern struct { + field: u8, +}; +pub const ContainsNoExternTy = extern struct { + field: NoExternTy, +}; + +pub extern fn root(a: Foo) anyopaque; + +pub extern fn renamed_func(a: RenamedTy) anyopaque; + +pub extern fn no_extern_func(a: ContainsNoExternTy) anyopaque; diff --git a/tests/expectations/renaming_overrides_prefixing.zig b/tests/expectations/renaming_overrides_prefixing.zig new file mode 100644 index 00000000..72dace5f --- /dev/null +++ b/tests/expectations/renaming_overrides_prefixing.zig @@ -0,0 +1,12 @@ +const std = @import("std"); + +const A = opaque { + +}; + +pub const B = extern struct { + x: i32, + y: f32, +}; + +pub extern fn root( a: ?*A, b: B) anyopaque; diff --git a/tests/expectations/reserved.zig b/tests/expectations/reserved.zig new file mode 100644 index 00000000..a85f06f1 --- /dev/null +++ b/tests/expectations/reserved.zig @@ -0,0 +1,34 @@ +const std = @import("std"); + +pub const A = extern struct { + namespace_: i32, + float_: f32, +}; + +pub const B = extern struct { + namespace_: i32, + float_: f32, +}; + +pub const C_Tag = enum { + D, +}; + +pub const D_Body = extern struct { + namespace_: i32, + float_: f32, +}; + +pub const C = extern struct { + tag: C_Tag, +}; + +pub const E_Tag = enum { + Double, + Float, +}; + +pub const E = extern struct { + tag: E_Tag, + float_: f32, +}; \ No newline at end of file diff --git a/tests/expectations/sentinel.zig b/tests/expectations/sentinel.zig new file mode 100644 index 00000000..409ea793 --- /dev/null +++ b/tests/expectations/sentinel.zig @@ -0,0 +1,37 @@ +const std = @import("std"); + +pub const A = enum { + A1, + A2, + A3, +}; + +pub const B = enum { + B1, + B2, + B3, +}; + +pub const C_Tag = enum { + C1, + C2, + C3, +}; + +pub const C1_Body = extern struct { + tag: C_Tag, + a: u32, +}; + +pub const C2_Body = extern struct { + tag: C_Tag, + b: u32, +}; + +pub const C = extern union { + tag: C_Tag, + c1: C1_Body, + c2: C2_Body, +}; + +pub extern fn root(a: A, b: B, c: C) anyopaque; diff --git a/tests/expectations/simplify_option_ptr.zig b/tests/expectations/simplify_option_ptr.zig new file mode 100644 index 00000000..1f9e5c82 --- /dev/null +++ b/tests/expectations/simplify_option_ptr.zig @@ -0,0 +1,20 @@ +const std = @import("std"); + +pub const Opaque = opaque {}; +pub const Option_____Opaque = opaque {}; + +pub const Foo = extern struct { + x: ?*const Opaque, + y: ?*Opaque, + z: ?*const fn () anyopaque, + zz: [*]?*const fn () anyopaque, +}; + +pub const Bar = extern union { + x: ?*const Opaque, + y: ?*Opaque, + z: ?*const fn () anyopaque, + zz: [*]?*const fn () anyopaque, +}; + +pub extern fn root(a: ?*const Opaque, b: ?*Opaque, c: Foo, d: Bar, e: ?*Option_____Opaque, f: ?*const fn (?*const Opaque) anyopaque) anyopaque; diff --git a/tests/expectations/size_types.zig b/tests/expectations/size_types.zig new file mode 100644 index 00000000..e0cd2872 --- /dev/null +++ b/tests/expectations/size_types.zig @@ -0,0 +1,15 @@ +const std = @import("std"); + +pub const IE = enum { + IV, +}; + +pub const UE = enum { + UV, +}; + +pub const Usize = usize; + +pub const Isize = isize; + +pub extern fn root(Usize, Isize, UE, IE) anyopaque; diff --git a/tests/expectations/static.zig b/tests/expectations/static.zig new file mode 100644 index 00000000..1b2ebccc --- /dev/null +++ b/tests/expectations/static.zig @@ -0,0 +1,13 @@ +const std = @import("std"); + +const Bar = opaque {}; + +pub const Foo = extern struct {}; + +pub extern const NUMBER: i32; + +pub extern const FOO: Foo; + +pub extern const BAR: Bar; + +pub extern fn root() anyopaque; diff --git a/tests/expectations/std_lib.zig b/tests/expectations/std_lib.zig new file mode 100644 index 00000000..5af07d1f --- /dev/null +++ b/tests/expectations/std_lib.zig @@ -0,0 +1,9 @@ +const std = @import("std"); + +const Option_i32 = opaque {}; + +const Result_i32__String = opaque {}; + +const Vec_String = opaque {}; + +pub extern fn root(a: ?*Vec_String, b: ?*Option_i32, c: ?*Result_i32__String) anyopaque; diff --git a/tests/expectations/struct.zig b/tests/expectations/struct.zig new file mode 100644 index 00000000..b14ced84 --- /dev/null +++ b/tests/expectations/struct.zig @@ -0,0 +1,31 @@ +const std = @import("std"); + +const Opaque = opaque { + +}; + +pub const Normal = extern struct { + x: i32, + y: f32, +}; + +pub const NormalWithZST = extern struct { + x: i32, + y: f32, +}; + +pub const TupleRenamed = extern struct { + m0: i32, + m1: f32, +}; + +pub const TupleNamed = extern struct { + x: i32, + y: f32, +}; + +pub extern fn root( a: ?*Opaque, + b: Normal, + c: NormalWithZST, + d: TupleRenamed, + e: TupleNamed) anyopaque; diff --git a/tests/expectations/struct_literal.zig b/tests/expectations/struct_literal.zig new file mode 100644 index 00000000..2cb5b030 --- /dev/null +++ b/tests/expectations/struct_literal.zig @@ -0,0 +1,24 @@ +pub const Bar = opaque {}; +pub const Foo = extern struct { + a: i32, + b: u32, +}; + +pub const Foo_FOO = @import("std").mem.zeroInit(Foo, .{ + .a = @as(c_int, 42), + .b = @as(c_int, 47), +}); +pub const Foo_FOO2 = @import("std").mem.zeroInit(Foo, .{ + .a = @as(c_int, 42), + .b = @as(c_int, 47), +}); +pub const Foo_FOO3 = @import("std").mem.zeroInit(Foo, .{ + .a = @as(c_int, 42), + .b = @as(c_int, 47), +}); +pub const BAR = @import("std").mem.zeroInit(Foo, .{ + .a = @as(c_int, 42), + .b = @as(c_int, 1337), +}); + +pub extern fn root(x: Foo, bar: Bar) void; diff --git a/tests/expectations/struct_self.zig b/tests/expectations/struct_self.zig new file mode 100644 index 00000000..e9e179f3 --- /dev/null +++ b/tests/expectations/struct_self.zig @@ -0,0 +1,12 @@ +const std = @import("std"); + +pub const Foo_Bar = extern struct { + something: ?*i32, +}; + +pub const Bar = extern struct { + something: i32, + subexpressions: Foo_Bar, +}; + +pub extern fn root(b: Bar) anyopaque; diff --git a/tests/expectations/style_crash.zig b/tests/expectations/style_crash.zig new file mode 100644 index 00000000..95a0b682 --- /dev/null +++ b/tests/expectations/style_crash.zig @@ -0,0 +1 @@ +const std = @import("std"); diff --git a/tests/expectations/swift_name.zig b/tests/expectations/swift_name.zig new file mode 100644 index 00000000..24351d4f --- /dev/null +++ b/tests/expectations/swift_name.zig @@ -0,0 +1,49 @@ +const std = @import("std"); + +pub const Opaque = opaque {}; + +pub const SelfTypeTestStruct = extern struct { + times: u8, +}; + +pub const PointerToOpaque = extern struct { + ptr: ?*Opaque, +}; + +pub extern fn rust_print_hello_world() anyopaque; + +pub extern fn SelfTypeTestStruct_should_exist_ref(self: [*]const SelfTypeTestStruct) anyopaque; + +pub extern fn SelfTypeTestStruct_should_exist_ref_mut(self: [*]SelfTypeTestStruct) anyopaque; + +pub extern fn SelfTypeTestStruct_should_not_exist_box(self: [*]SelfTypeTestStruct) anyopaque; + +pub extern fn SelfTypeTestStruct_should_not_exist_return_box() [*]SelfTypeTestStruct; + +pub extern fn SelfTypeTestStruct_should_exist_annotated_self(self: SelfTypeTestStruct) anyopaque; + +pub extern fn SelfTypeTestStruct_should_exist_annotated_mut_self(self: SelfTypeTestStruct) anyopaque; + +pub extern fn SelfTypeTestStruct_should_exist_annotated_by_name(self: SelfTypeTestStruct) anyopaque; + +pub extern fn SelfTypeTestStruct_should_exist_annotated_mut_by_name(self: SelfTypeTestStruct) anyopaque; + +pub extern fn SelfTypeTestStruct_should_exist_unannotated(self: SelfTypeTestStruct) anyopaque; + +pub extern fn SelfTypeTestStruct_should_exist_mut_unannotated(self: SelfTypeTestStruct) anyopaque; + +pub extern fn free_function_should_exist_ref(test_struct: [*]const SelfTypeTestStruct) anyopaque; + +pub extern fn free_function_should_exist_ref_mut(test_struct: [*]SelfTypeTestStruct) anyopaque; + +pub extern fn unnamed_argument([*]SelfTypeTestStruct) anyopaque; + +pub extern fn free_function_should_not_exist_box(boxed: [*]SelfTypeTestStruct) anyopaque; + +pub extern fn free_function_should_exist_annotated_by_name(test_struct: SelfTypeTestStruct) anyopaque; + +pub extern fn free_function_should_exist_annotated_mut_by_name(test_struct: SelfTypeTestStruct) anyopaque; + +pub extern fn PointerToOpaque_create(times: u8) PointerToOpaque; + +pub extern fn PointerToOpaque_sayHello(self: PointerToOpaque) anyopaque; diff --git a/tests/expectations/transform_op.zig b/tests/expectations/transform_op.zig new file mode 100644 index 00000000..c99bd318 --- /dev/null +++ b/tests/expectations/transform_op.zig @@ -0,0 +1,93 @@ +const std = @import("std"); + +pub const Point_i32 = extern struct { + x: i32, + y: i32, +}; + +pub const Point_f32 = extern struct { + x: f32, + y: f32, +}; + +pub const Foo_i32_Tag = enum { + Foo_i32, + Bar_i32, + Baz_i32, + Bazz_i32, +}; + +pub const Foo_Body_i32 = extern struct { + tag: Foo_i32_Tag, + x: i32, + y: Point_i32, + z: Point_f32, +}; + +pub const Foo_i32 = extern union { + tag: Foo_i32_Tag, + foo: Foo_Body_i32, +}; + +pub const Bar_i32_Tag = enum { + Bar1_i32, + Bar2_i32, + Bar3_i32, + Bar4_i32, +}; + +pub const Bar1_Body_i32 = extern struct { + x: i32, + y: Point_i32, + z: Point_f32, + u: ?fn () i32, +}; + +pub const Bar_i32 = extern struct { + tag: Bar_i32_Tag, +}; + +pub const Point_u32 = extern struct { + x: u32, + y: u32, +}; + +pub const Bar_u32_Tag = enum { + Bar1_u32, + Bar2_u32, + Bar3_u32, + Bar4_u32, +}; + +pub const Bar1_Body_u32 = extern struct { + x: i32, + y: Point_u32, + z: Point_f32, + u: ?fn () i32, +}; + +pub const Bar_u32 = extern struct { + tag: Bar_u32_Tag, +}; + +pub const Baz_Tag = enum { + Baz1, + Baz2, + Baz3, +}; + +pub const Baz = extern union { + tag: Baz_Tag, +}; + +pub const Taz_Tag = enum { + Taz1, + Taz2, + Taz3, +}; + +pub const Taz = extern struct { + tag: Taz_Tag, +}; + +pub extern fn foo(foo: ?*Foo_i32, bar: ?*Bar_i32, baz: ?*Baz, taz: ?*Taz) anyopaque; diff --git a/tests/expectations/transparent.zig b/tests/expectations/transparent.zig new file mode 100644 index 00000000..252ed751 --- /dev/null +++ b/tests/expectations/transparent.zig @@ -0,0 +1,25 @@ +const std = @import("std"); + +const DummyStruct = opaque {}; + +const EnumWithAssociatedConstantInImpl = opaque {}; + +pub const TransparentComplexWrappingStructTuple = DummyStruct; + +pub const TransparentPrimitiveWrappingStructTuple = u32; + +pub const TransparentComplexWrappingStructure = DummyStruct; + +pub const TransparentPrimitiveWrappingStructure = u32; + +pub const TransparentComplexWrapper_i32 = DummyStruct; + +pub const TransparentPrimitiveWrapper_i32 = u32; + +pub const TransparentPrimitiveWithAssociatedConstants = u32; +pub const TransparentPrimitiveWithAssociatedConstants_ZERO = 0; +pub const TransparentPrimitiveWithAssociatedConstants_ONE = 1; + +pub const EnumWithAssociatedConstantInImpl_TEN = 10; + +extern fn root(a: TransparentComplexWrappingStructTuple, b: TransparentPrimitiveWrappingStructTuple, c: TransparentComplexWrappingStructure, d: TransparentPrimitiveWrappingStructure, e: TransparentComplexWrapper_i32, f: TransparentPrimitiveWrapper_i32, g: TransparentPrimitiveWithAssociatedConstants, h: EnumWithAssociatedConstantInImpl) anyopaque; diff --git a/tests/expectations/typedef.zig b/tests/expectations/typedef.zig new file mode 100644 index 00000000..69ab5804 --- /dev/null +++ b/tests/expectations/typedef.zig @@ -0,0 +1,10 @@ +const std = @import("std"); + +pub const Foo_i32__i32 = extern struct { + x: i32, + y: i32, +}; + +pub const IntFoo_i32 = Foo_i32__i32; + +extern fn root(a: IntFoo_i32) anyopaque; diff --git a/tests/expectations/union.zig b/tests/expectations/union.zig new file mode 100644 index 00000000..4f251c11 --- /dev/null +++ b/tests/expectations/union.zig @@ -0,0 +1,15 @@ +const std = @import("std"); + +const Opaque = opaque {}; + +pub const Normal = extern union { + x: i32, + y: f32, +}; + +pub const NormalWithZST = extern union { + x: i32, + y: f32, +}; + +pub extern fn root(a: ?*Opaque, b: Normal, c: NormalWithZST) anyopaque; diff --git a/tests/expectations/union_self.zig b/tests/expectations/union_self.zig new file mode 100644 index 00000000..8b85870b --- /dev/null +++ b/tests/expectations/union_self.zig @@ -0,0 +1,12 @@ +const std = @import("std"); + +pub const Foo_Bar = extern struct { + something: ?*i32, +}; + +pub const Bar = extern union { + something: i32, + subexpressions: Foo_Bar, +}; + +pub extern fn root(b: Bar) anyopaque; diff --git a/tests/expectations/using_namespaces.zig b/tests/expectations/using_namespaces.zig new file mode 100644 index 00000000..d4c2e15c --- /dev/null +++ b/tests/expectations/using_namespaces.zig @@ -0,0 +1,3 @@ +const std = @import("std"); + +pub extern fn root() anyopaque; diff --git a/tests/expectations/va_list.zig b/tests/expectations/va_list.zig new file mode 100644 index 00000000..9e7a71bf --- /dev/null +++ b/tests/expectations/va_list.zig @@ -0,0 +1,3 @@ +const std = @import("std"); + +pub extern fn va_list_test(ap: ...) i32; diff --git a/tests/expectations/workspace.zig b/tests/expectations/workspace.zig new file mode 100644 index 00000000..72f50096 --- /dev/null +++ b/tests/expectations/workspace.zig @@ -0,0 +1,7 @@ +const std = @import("std"); + +pub const ExtType = extern struct { + data: u32, +}; + +pub extern fn consume_ext(_ext: ExtType) anyopaque; diff --git a/tests/expectations/zst.zig b/tests/expectations/zst.zig new file mode 100644 index 00000000..191e35d9 --- /dev/null +++ b/tests/expectations/zst.zig @@ -0,0 +1,8 @@ +const std = @import("std"); + +pub const TraitObject = extern struct { + data: ?*anyopaque, + vtable: ?*anyopaque, +}; + +extern fn root(ptr: ?*const anyopaque, t: TraitObject) ?*anyopaque; diff --git a/tests/tests.rs b/tests/tests.rs index eebabefb..df37ac8f 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -59,6 +59,9 @@ fn run_cbindgen( Language::Cython => { command.arg("--lang").arg("cython"); } + Language::Zig => { + command.arg("--lang").arg("zig"); + } } if package_version { @@ -123,6 +126,7 @@ fn compile( Language::Cxx => env::var("CXX").unwrap_or_else(|_| "g++".to_owned()), Language::C => env::var("CC").unwrap_or_else(|_| "gcc".to_owned()), Language::Cython => env::var("CYTHON").unwrap_or_else(|_| "cython".to_owned()), + Language::Zig => env::var("ZIG").unwrap_or_else(|_| "zig".to_owned()), }; let file_name = cbindgen_output @@ -185,6 +189,13 @@ fn compile( command.arg("-o").arg(&object); command.arg(cbindgen_output); } + Language::Zig => { + command.arg("build-obj"); + command.arg("-O").arg("ReleaseSmall"); + command.arg("-fsingle-threaded"); + command.arg("--name").arg(&object); + command.arg("-femit-bin=").arg(cbindgen_output); + } } println!("Running: {:?}", command); @@ -230,6 +241,7 @@ fn run_compile_test( // is extension-sensitive and won't work on them, so we use implementation files (`.pyx`) // in the test suite. Language::Cython => ".pyx", + Language::Zig => ".zig", }; let skip_warning_as_error = name.rfind(SKIP_WARNING_AS_ERROR_SUFFIX).is_some();