diff --git a/.github/workflows/cbindgen.yml b/.github/workflows/cbindgen.yml index f9d3672ca..5da23a8b1 100644 --- a/.github/workflows/cbindgen.yml +++ b/.github/workflows/cbindgen.yml @@ -60,6 +60,17 @@ jobs: python -m pip install --upgrade pip wheel pip install Cython==3.0.2 + - name: Install dotnet + id: dotnet + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '7.0.302' # dotnet 7.0.302 is used by Unity 2021.3.39f1, 2022.3.33f1, 2023.2.20f1 and 6000.0.5f1 + + - name: Get dotnet version + run: | + DOTNET_VERSION=${{ steps.dotnet.outputs.dotnet-version }} + echo "DOTNET_VERSION=${DOTNET_VERSION}" >> $GITHUB_ENV + - name: Build run: | cargo +stable build --verbose diff --git a/Cargo.lock b/Cargo.lock index 7f4f0f5c1..a2b4b7d84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,6 +80,7 @@ dependencies = [ "syn", "tempfile", "toml", + "which", ] [[package]] @@ -140,6 +141,12 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "equivalent" version = "1.0.1" @@ -174,6 +181,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys", +] + [[package]] name = "indexmap" version = "2.2.6" @@ -471,6 +487,18 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index a545477b3..e990d03af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ features = ["clone-impls", "extra-traits", "fold", "full", "parsing", "printing" [dev-dependencies] serial_test = { version = "2.0.0", default-features = false } pretty_assertions = "1.4.0" +which = "4" [features] default = ["clap"] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 271800cb2..a316e8565 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,3 @@ [toolchain] -channel = "nightly" \ No newline at end of file +channel = "nightly" +components = ["rustfmt", "clippy"] \ No newline at end of file diff --git a/src/bindgen/bindings.rs b/src/bindgen/bindings.rs index 029cfc66b..d0c16e19d 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, CSharpLanguageBackend, CythonLanguageBackend, LanguageBackend, }; use crate::bindgen::writer::SourceWriter; @@ -37,6 +37,7 @@ pub struct Bindings { /// and shouldn't do anything when written anywhere. noop: bool, pub package_version: String, + binding_crate_lib_name: String, } impl Bindings { @@ -52,6 +53,7 @@ impl Bindings { source_files: Vec, noop: bool, package_version: String, + binding_crate_lib_name: String, ) -> Bindings { Bindings { config, @@ -65,6 +67,7 @@ impl Bindings { source_files, noop, package_version, + binding_crate_lib_name, } } @@ -210,6 +213,9 @@ impl Bindings { Language::Cython => { self.write_with_backend(file, &mut CythonLanguageBackend::new(&self.config)) } + Language::CSharp => { + self.write_with_backend(file, &mut CSharpLanguageBackend::new(&self.config)) + } } } diff --git a/src/bindgen/builder.rs b/src/bindgen/builder.rs index d47919b98..f85e88e68 100644 --- a/src/bindgen/builder.rs +++ b/src/bindgen/builder.rs @@ -362,6 +362,7 @@ impl Builder { Default::default(), true, String::new(), + Default::default(), )); } @@ -375,6 +376,8 @@ impl Builder { result.extend_with(&parser::parse_src(x, &self.config)?); } + let binding_crate_lib_name; + if let Some((lib_dir, binding_lib_name)) = self.lib.clone() { let lockfile = self.lockfile.as_deref(); @@ -388,9 +391,14 @@ impl Builder { /* existing_metadata = */ None, )?; + binding_crate_lib_name = cargo.binding_crate_lib_name().to_string(); + result.extend_with(&parser::parse_lib(cargo, &self.config)?); } else if let Some(cargo) = self.lib_cargo.clone() { + binding_crate_lib_name = cargo.binding_crate_lib_name().to_string(); result.extend_with(&parser::parse_lib(cargo, &self.config)?); + } else { + binding_crate_lib_name = String::new() } result.source_files.extend_from_slice(self.srcs.as_slice()); @@ -407,6 +415,7 @@ impl Builder { result.functions, result.source_files, result.package_version, + binding_crate_lib_name, ) .generate() } diff --git a/src/bindgen/cargo/cargo.rs b/src/bindgen/cargo/cargo.rs index d639a82d8..406e9f315 100644 --- a/src/bindgen/cargo/cargo.rs +++ b/src/bindgen/cargo/cargo.rs @@ -25,6 +25,7 @@ fn parse_dep_string(dep_string: &str) -> (&str, Option<&str>) { pub(crate) struct Cargo { manifest_path: PathBuf, binding_crate_name: String, + binding_crate_lib_name: String, lock: Option, metadata: Metadata, clean: bool, @@ -63,25 +64,34 @@ impl Cargo { None }; + let manifest = cargo_toml::manifest(&toml_path) + .map_err(|x| Error::CargoToml(toml_path.to_str().unwrap().to_owned(), x))?; + // Use the specified binding crate name or infer it from the manifest let binding_crate_name = match binding_crate_name { Some(s) => s.to_owned(), - None => { - let manifest = cargo_toml::manifest(&toml_path) - .map_err(|x| Error::CargoToml(toml_path.to_str().unwrap().to_owned(), x))?; - manifest.package.name - } + None => manifest.package.name, }; + let binding_crate_lib_name = manifest + .lib + .and_then(|lib| lib.name) + .unwrap_or_else(|| binding_crate_name.clone()); + Ok(Cargo { manifest_path: toml_path, binding_crate_name, + binding_crate_lib_name, lock, metadata, clean, }) } + pub(crate) fn binding_crate_lib_name(&self) -> &str { + &self.binding_crate_lib_name + } + pub(crate) fn binding_crate_name(&self) -> &str { &self.binding_crate_name } diff --git a/src/bindgen/cargo/cargo_toml.rs b/src/bindgen/cargo/cargo_toml.rs index 998176e0d..4312e0766 100644 --- a/src/bindgen/cargo/cargo_toml.rs +++ b/src/bindgen/cargo/cargo_toml.rs @@ -50,6 +50,7 @@ impl error::Error for Error { #[derive(Clone, Deserialize, Debug)] pub struct Manifest { pub package: Package, + pub lib: Option, } #[derive(Clone, Deserialize, Debug)] @@ -57,6 +58,11 @@ pub struct Package { pub name: String, } +#[derive(Clone, Deserialize, Debug)] +pub struct Lib { + pub name: Option, +} + /// Parse the Cargo.toml for a given path pub fn manifest(manifest_path: &Path) -> Result { let mut s = String::new(); diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs index 31316503d..d76a6437e 100644 --- a/src/bindgen/config.rs +++ b/src/bindgen/config.rs @@ -23,6 +23,7 @@ pub enum Language { Cxx, C, Cython, + CSharp, } impl FromStr for Language { @@ -42,6 +43,12 @@ impl FromStr for Language { "C" => Ok(Language::C), "cython" => Ok(Language::Cython), "Cython" => Ok(Language::Cython), + "csharp" => Ok(Language::CSharp), + "Csharp" => Ok(Language::CSharp), + "CSharp" => Ok(Language::CSharp), + "CSHARP" => Ok(Language::CSharp), + "c#" => Ok(Language::CSharp), + "C#" => Ok(Language::CSharp), _ => Err(format!("Unrecognized Language: '{}'.", s)), } } @@ -54,6 +61,7 @@ impl Language { match self { Language::Cxx | Language::C => "typedef", Language::Cython => "ctypedef", + Language::CSharp => "using", } } } @@ -894,6 +902,22 @@ pub struct CythonConfig { pub cimports: BTreeMap>, } +/// Settings specific to C# bindings. +#[derive(Debug, Clone, Default, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct CSharpConfig { + /// Package to use + pub package: Option, + + /// Name of the generated interface + pub interface_name: Option, + + /// Extra definition to include inside the generated interface + pub extra_defs: Option, +} + /// A collection of settings to customize the generated bindings. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "snake_case")] @@ -1016,6 +1040,8 @@ pub struct Config { /// via the CLI, when ran from a build script cargo sets this variable /// appropriately). pub only_target_dependencies: bool, + /// Configuration options specific to C#. + pub csharp: CSharpConfig, /// Configuration options specific to Cython. pub cython: CythonConfig, #[doc(hidden)] @@ -1069,6 +1095,7 @@ impl Default for Config { documentation_length: DocumentationLength::Full, pointer: PtrConfig::default(), only_target_dependencies: false, + csharp: CSharpConfig::default(), cython: CythonConfig::default(), config_path: None, } diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs index 2f1d3bffd..d29621254 100644 --- a/src/bindgen/ir/constant.rs +++ b/src/bindgen/ir/constant.rs @@ -721,6 +721,22 @@ impl Constant { write!(out, " {} # = ", name); language_backend.write_literal(out, value); } + Language::CSharp => { + // if java_writable_literal(&self.ty, value) { + // out.write("public static final "); + // language_backend.write_type(out, &self.ty); + // write!(out, " {} = ", self.export_name); + // language_backend.write_literal(out, &wrap_java_value(value, &self.ty)); + // out.write(";") + // } else { + // write!( + // out, + // "/* Unsupported literal for constant {} */", + // self.export_name + // ) + // } + out.new_line(); + } } condition.write_after(config, out); diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs index 2e633a7df..b76cc4fe4 100644 --- a/src/bindgen/ir/enumeration.rs +++ b/src/bindgen/ir/enumeration.rs @@ -14,7 +14,7 @@ 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::language_backend::{self, LanguageBackend}; use crate::bindgen::library::Library; use crate::bindgen::mangle; use crate::bindgen::monomorph::Monomorphs; @@ -198,7 +198,12 @@ impl EnumVariant { // Besides that for C++ we generate casts/getters that can be used instead of // direct field accesses and also have a benefit of being checked. // As a result we don't currently inline variant definitions in C++ mode at all. - let inline = inline_casts && config.language != Language::Cxx; + // + // In C# we also don't inline variant definitions because C# doesn't support unnamed + // structs and we don't want to generate extra noise. + let inline = inline_casts + && config.language != Language::Cxx + && config.language != Language::CSharp; let inline_name = if inline { Some(&*name) } else { None }; VariantBody::Body { body: Struct::new( @@ -503,7 +508,10 @@ impl Item for Enum { fn rename_for_config(&mut self, config: &Config) { config.export.rename(&mut self.export_name); - if config.language != Language::Cxx && self.tag.is_some() { + if config.language != Language::Cxx + && config.language != Language::CSharp + && self.tag.is_some() + { // it makes sense to always prefix Tag with type name in C let new_tag = format!("{}_Tag", self.export_name); if self.repr.style == ReprStyle::Rust { @@ -729,6 +737,27 @@ impl Enum { write!(out, "{}enum {}", config.style.cython_def(), tag_name); } } + Language::CSharp => { + out.write("public enum"); + + if self.annotations.must_use(config) { + if let Some(ref anno) = config.enumeration.must_use { + write!(out, " {}", anno) + } + } + + if let Some(note) = self + .annotations + .deprecated_note(config, DeprecatedNoteKind::Enum) + { + write!(out, " {}", note); + } + + write!(out, " {}", tag_name); + if let Some(prim) = size { + write!(out, " : {}", prim); + } + } } out.open_brace(); @@ -745,7 +774,7 @@ impl Enum { out.close_brace(false); write!(out, " {};", tag_name); } else { - out.close_brace(true); + out.close_brace(config.language != Language::CSharp); } // Emit typedef specifying the tag enum's size if necessary. @@ -757,7 +786,7 @@ impl Enum { out.write("#ifndef __cplusplus"); } - if config.language != Language::Cxx { + if config.language != Language::Cxx && config.language != Language::CSharp { out.new_line(); write!(out, "{} {} {};", config.language.typedef(), prim, tag_name); } @@ -783,6 +812,7 @@ 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::CSharp => unreachable!("C# doesn't use this method"), } out.write(if inline_tag_field { "union" } else { "struct" }); @@ -867,6 +897,15 @@ impl Enum { out.write("enum "); } + if config.language == Language::CSharp && inline_tag_field { + out.write("[FieldOffset(0)]"); + out.new_line(); + } + + if config.language == Language::CSharp { + out.write("public "); + } + write!(out, "{} tag;", tag_name); if wrap_tag { @@ -925,6 +964,10 @@ impl Enum { if config.language != Language::Cython { out.close_brace(true); } + } else if config.language == Language::CSharp { + out.write("[FieldOffset(0)]"); + out.new_line(); + write!(out, "public {} {};", body.export_name(), name); } else if config.style.generate_typedef() || config.language == Language::Cython { write!(out, "{} {};", body.export_name(), name); } else { diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs index 4a41d4cf3..43cb7b550 100644 --- a/src/bindgen/ir/ty.rs +++ b/src/bindgen/ir/ty.rs @@ -222,6 +222,48 @@ impl PrimitiveType { } } + pub fn to_repr_csharp(&self, config: &Config) -> &'static str { + match *self { + PrimitiveType::Void => "void", + // NOTE: `[MarshalAs(UnmanagedType.U1)]` should be used for `bool` in C#. + PrimitiveType::Bool => "bool", + PrimitiveType::Char => "byte", + PrimitiveType::SChar => "sbyte", + PrimitiveType::UChar => "byte", + PrimitiveType::Char32 => "uint", + PrimitiveType::Integer { + kind, + signed, + zeroable: _, + } => match (kind, signed) { + (IntKind::Short, true) => "short", + (IntKind::Short, false) => "ushort", + (IntKind::Int, true) => "int", + (IntKind::Int, false) => "uint", + (IntKind::Long, true) => "CLong", + (IntKind::Long, false) => "CULong", + (IntKind::LongLong, true) => "long", + (IntKind::LongLong, false) => "ulong", + (IntKind::SizeT, true) => "nint", + (IntKind::SizeT, false) => "nuint", + (IntKind::Size, true) => "nint", + (IntKind::Size, false) => "nuint", + (IntKind::B8, true) => "sbyte", + (IntKind::B8, false) => "byte", + (IntKind::B16, true) => "short", + (IntKind::B16, false) => "ushort", + (IntKind::B32, true) => "int", + (IntKind::B32, false) => "uint", + (IntKind::B64, true) => "long", + (IntKind::B64, false) => "ulong", + }, + PrimitiveType::Float => "float", + PrimitiveType::Double => "double", + PrimitiveType::PtrDiffT => "nint", + PrimitiveType::VaList => "...", + } + } + fn can_cmp_order(&self) -> bool { !matches!(*self, PrimitiveType::Bool) } diff --git a/src/bindgen/language_backend/csharp.rs b/src/bindgen/language_backend/csharp.rs new file mode 100644 index 000000000..d7b8dbf2e --- /dev/null +++ b/src/bindgen/language_backend/csharp.rs @@ -0,0 +1,952 @@ +use crate::bindgen::ir::{ + to_known_assoc_constant, ConditionWrite, DeprecatedNoteKind, Documentation, Enum, EnumVariant, + Field, GenericParams, Item, Literal, OpaqueItem, 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::{Bindings, Config, Language}; +use crate::bindgen::{DocumentationLength, DocumentationStyle}; +use std::io::Write; + +pub struct CSharpLanguageBackend<'a> { + config: &'a Config, +} + +impl<'a> CSharpLanguageBackend<'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_union_field(&mut self, out: &mut SourceWriter, f: &Field) { + out.write("[FieldOffset(0)]"); + out.new_line(); + self.write_field(out, f); + } + + 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); + + // check if the type is bool + let is_bool = match &f.ty { + Type::Primitive(primitive) => primitive.to_repr_csharp(self.config) == "bool", + _ => false, + }; + + if is_bool { + out.write("[MarshalAs(UnmanagedType.U1)]"); + out.new_line(); + } + + out.write("public "); + self.write_type(out, &f.ty); + + if let Some(bitfield) = f.annotations.atom("bitfield") { + write!(out, ": {}", bitfield.unwrap_or_default()); + } + + write!(out, " {}", f.name); + + 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 namespaces = { + 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 { + write!(out, "namespace {}", namespaces.join(".")); + out.open_brace(); + } else { + out.close_brace(false); + } + } + + 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 CSharpLanguageBackend<'_> { + 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.after_includes.is_none() { + return; + } + + out.new_line_if_not_start(); + + if !self.config.no_includes { + out.write("using System.Runtime.CompilerServices;"); + out.new_line(); + out.write("using System.Runtime.InteropServices;"); + 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) {} + + fn write_enum(&mut self, out: &mut SourceWriter, e: &Enum) { + let mut size = e + .repr + .ty + .map(|ty| ty.to_primitive().to_repr_csharp(self.config)); + if let Some(real_size) = size { + if real_size != "byte" + && real_size != "sbyte" + && real_size != "short" + && real_size != "ushort" + && real_size != "int" + && real_size != "uint" + && real_size != "long" + && real_size != "ulong" + { + out.write( + "// WARNING: Type byte, sbyte, short, ushort, int, uint, long, or ulong expected,", + ); + out.new_line(); + out.write("// but found "); + out.write(real_size); + out.write(" instead."); + out.new_line(); + out.write("// This is a limitation of C# enums, which only support those types."); + out.new_line(); + out.write("// Please consider using a different type for this enum."); + out.new_line(); + out.write("// See https://learn.microsoft.com/en-us/dotnet/csharp/misc/cs1008 for more information."); + out.new_line(); + out.write( + "// The size of the enum will be set to int to avoid compilation errors.", + ); + out.new_line(); + out.write("// It could be different from it's size in rust, use with caution."); + out.new_line(); + size = None; + } + } + 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 { + if inline_tag_field { + out.write("[StructLayout(LayoutKind.Explicit)]"); + } else { + out.write("[StructLayout(LayoutKind.Sequential)]"); + } + out.new_line(); + out.write("internal unsafe partial struct"); + + write!(out, " {}", e.export_name); + + out.open_brace(); + + // Emit the pre_body section, if relevant + if let Some(body) = self.config.export.pre_body(&e.path) { + out.write_raw_block(body); + out.new_line(); + } + } + + // 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(); + + // 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(); + out.new_line(); + + // Open union of all variants with data, only in the non-inline tag scenario. + if !inline_tag_field { + out.write("[StructLayout(LayoutKind.Explicit)]"); + out.new_line(); + out.write("public unsafe partial struct Variants"); + 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(false); + out.new_line(); + out.new_line(); + out.write("public Variants variants;"); + } + + // 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); + } + + 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); + } + + if let Some(_align) = s.alignment { + out.write("// WARNING: `packed` and `align(N)` is not implemented for C# yet."); + out.new_line(); + out.write("// As a result, the size and alignment of this struct could be different from rust."); + out.new_line(); + out.write("// Use with caution."); + } + + out.write("[StructLayout(LayoutKind.Sequential)]"); + out.new_line(); + if s.is_enum_variant_body { + out.write("public"); + } else { + out.write("internal"); + } + out.write(" unsafe partial 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); + + self.write_derived_cpp_ops(out, s); + + // 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::CSharp + // && 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); + + 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); + + if let Some(align) = u.alignment { + out.write("// WARNING: `packed` and `align(N)` is not implemented for C# yet."); + out.new_line(); + out.write("// As a result, the size and alignment of this struct could be different from rust."); + out.new_line(); + out.write("// Use with caution."); + } + + out.write("[StructLayout(LayoutKind.Explicit)]"); + out.new_line(); + out.write("internal unsafe partial struct"); + + 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_union_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); + + 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); + + out.write( + "// WARNING: Opaque type, no details available, so only pointers to it are allowed", + ); + out.new_line(); + write!(out, "internal unsafe struct {}", o.export_name()); + out.open_brace(); + + 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); + + // self.write_generic_param(out, &t.generic_params); + + // if self.config.language == Language::CSharp { + // 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) { + // let condition = s.cfg.to_condition(self.config); + // condition.write_before(self.config, out); + + // 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(";"); + + // condition.write_after(self.config, out); + } + + fn write_type(&mut self, out: &mut SourceWriter, t: &Type) { + match t { + Type::Ptr { ty, .. } => { + self.write_type(out, &**ty); + out.write("*"); + } + + Type::Path(path) => { + write!(out, "{}", path.export_name()) + } + Type::Primitive(primitive) => { + let typ = primitive.to_repr_csharp(self.config); + write!(out, "{typ}") + } + Type::Array(ty, _len) => { + self.write_type(out, ty); + out.write("[]"); + } + Type::FuncPtr { .. } => out.write("Callback"), + } + } + + 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::CSharp => { + 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) => 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 = if self.config.language == Language::C { + "_" + } else 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, + } => { + if self.config.language == Language::C { + write!(out, "({})", export_name); + } else { + 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; + if self.config.language == Language::CSharp { + // 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); + } + } + 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::CSharp { + // 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::CSharp || 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 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); + + // if b.config.cpp_compatible_c() { + // out.new_line(); + // out.write("#ifdef __cplusplus"); + // } + + // if b.config.language == Language::CSharp || 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/mod.rs b/src/bindgen/language_backend/mod.rs index adb7d8507..4fb2dcf67 100644 --- a/src/bindgen/language_backend/mod.rs +++ b/src/bindgen/language_backend/mod.rs @@ -9,9 +9,11 @@ use crate::Config; use std::io::Write; mod clike; +mod csharp; mod cython; pub use clike::CLikeLanguageBackend; +pub use csharp::CSharpLanguageBackend; pub use cython::CythonLanguageBackend; pub trait LanguageBackend: Sized { diff --git a/src/bindgen/library.rs b/src/bindgen/library.rs index 9d61257f9..d5b23a879 100644 --- a/src/bindgen/library.rs +++ b/src/bindgen/library.rs @@ -28,6 +28,7 @@ pub struct Library { functions: Vec, source_files: Vec, package_version: String, + binding_crate_lib_name: String, } impl Library { @@ -44,6 +45,7 @@ impl Library { functions: Vec, source_files: Vec, package_version: String, + binding_crate_lib_name: String, ) -> Library { Library { config, @@ -57,6 +59,7 @@ impl Library { functions, source_files, package_version, + binding_crate_lib_name, } } @@ -145,6 +148,7 @@ impl Library { self.source_files, false, self.package_version, + self.binding_crate_lib_name, )) } diff --git a/src/bindgen/writer.rs b/src/bindgen/writer.rs index f36757d26..02e284d8b 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::CSharp => 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::CSharp => { self.new_line(); if semicolon { self.write("};"); diff --git a/src/main.rs b/src/main.rs index a6a1852c2..3db01d7bd 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", "csharp", "Csharp", "CSharp","CSHARP", "c#", "C#"]), ) .arg( Arg::new("package-version") diff --git a/tests/depfile.rs b/tests/depfile.rs index 512f69b09..022e40014 100644 --- a/tests/depfile.rs +++ b/tests/depfile.rs @@ -1,3 +1,5 @@ +// Skip depfile tests on Windows, as the pathes in the depfile are not normalized. +#![cfg(not(windows))] use std::fs::read_to_string; use std::path::PathBuf; use std::process::Command; diff --git a/tests/expectations/abi_string.cs b/tests/expectations/abi_string.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/abi_string.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/alias.cs b/tests/expectations/alias.cs new file mode 100644 index 000000000..eeee085f7 --- /dev/null +++ b/tests/expectations/alias.cs @@ -0,0 +1,35 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public enum Status : uint { + Ok, + Err, +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Dep { + public int a; + public float b; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo_i32 { + public int a; + public int b; + public Dep c; +} + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo_f64 { + public double a; + public double b; + public Dep c; +} + + + + + + diff --git a/tests/expectations/annotation.cs b/tests/expectations/annotation.cs new file mode 100644 index 000000000..836a7932a --- /dev/null +++ b/tests/expectations/annotation.cs @@ -0,0 +1,91 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public enum C : uint { + X = 2, + Y, +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct A { + public int m0; + + A(public int const& m0) + : m0(m0) + {} + + bool operator<(const A& other) const { + return m0 < other.m0; + } + bool operator<=(const A& other) const { + return m0 <= other.m0; + } +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct B { + public int x; + public float y; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct F { + public enum Tag : byte { + Foo, + Bar, + Baz, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Foo_Body { + public Tag tag; + public short _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Bar_Body { + public Tag tag; + public byte x; + public short y; + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public Foo_Body foo; + [FieldOffset(0)] + public Bar_Body bar; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct H { + public enum Tag : byte { + Hello, + There, + Everyone, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Hello_Body { + public short _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct There_Body { + public byte x; + public short y; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public Hello_Body hello; + [FieldOffset(0)] + public There_Body there; + } + + public Variants variants; +} diff --git a/tests/expectations/array.cs b/tests/expectations/array.cs new file mode 100644 index 000000000..d1b48d6c4 --- /dev/null +++ b/tests/expectations/array.cs @@ -0,0 +1,24 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + public enum Tag { + A, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct A_Body { + public float[] _0; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public A_Body a; + } + + public Variants variants; +} diff --git a/tests/expectations/asserted_cast.cs b/tests/expectations/asserted_cast.cs new file mode 100644 index 000000000..f1d88277b --- /dev/null +++ b/tests/expectations/asserted_cast.cs @@ -0,0 +1,105 @@ +#define MY_ASSERT(...) do { } while (0) +#define MY_ATTRS __attribute((noinline)) + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct I { + +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct H { + public enum Tag : byte { + H_Foo, + H_Bar, + H_Baz, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct H_Foo_Body { + public short _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct H_Bar_Body { + public byte x; + public short y; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public H_Foo_Body foo; + [FieldOffset(0)] + public H_Bar_Body bar; + } + + public Variants variants; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct J { + public enum Tag : byte { + J_Foo, + J_Bar, + J_Baz, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct J_Foo_Body { + public short _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct J_Bar_Body { + public byte x; + public short y; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public J_Foo_Body foo; + [FieldOffset(0)] + public J_Bar_Body bar; + } + + public Variants variants; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct K { + public enum Tag : byte { + K_Foo, + K_Bar, + K_Baz, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct K_Foo_Body { + public Tag tag; + public short _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct K_Bar_Body { + public Tag tag; + public byte x; + public short y; + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public K_Foo_Body foo; + [FieldOffset(0)] + public K_Bar_Body bar; +} diff --git a/tests/expectations/assoc_const_conflict.cs b/tests/expectations/assoc_const_conflict.cs new file mode 100644 index 000000000..f9df36cd5 --- /dev/null +++ b/tests/expectations/assoc_const_conflict.cs @@ -0,0 +1,5 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + diff --git a/tests/expectations/assoc_constant.cs b/tests/expectations/assoc_constant.cs new file mode 100644 index 000000000..b378f5af7 --- /dev/null +++ b/tests/expectations/assoc_constant.cs @@ -0,0 +1,11 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + +} + + + + diff --git a/tests/expectations/associated_constant_panic.cs b/tests/expectations/associated_constant_panic.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/associated_constant_panic.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/associated_in_body.cs b/tests/expectations/associated_in_body.cs new file mode 100644 index 000000000..d51dcdaba --- /dev/null +++ b/tests/expectations/associated_in_body.cs @@ -0,0 +1,78 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +/// Constants shared by multiple CSS Box Alignment properties +/// +/// These constants match Gecko's `NS_STYLE_ALIGN_*` constants. +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct StyleAlignFlags { + public byte bits; + + constexpr explicit operator bool() const { + return !!bits; + } + constexpr StyleAlignFlags operator~() const { + return StyleAlignFlags { static_cast(~bits) }; + } + constexpr StyleAlignFlags operator|(const StyleAlignFlags& other) const { + return StyleAlignFlags { static_cast(this->bits | other.bits) }; + } + StyleAlignFlags& operator|=(const StyleAlignFlags& other) { + *this = (*this | other); + return *this; + } + constexpr StyleAlignFlags operator&(const StyleAlignFlags& other) const { + return StyleAlignFlags { static_cast(this->bits & other.bits) }; + } + StyleAlignFlags& operator&=(const StyleAlignFlags& other) { + *this = (*this & other); + return *this; + } + constexpr StyleAlignFlags operator^(const StyleAlignFlags& other) const { + return StyleAlignFlags { static_cast(this->bits ^ other.bits) }; + } + StyleAlignFlags& operator^=(const StyleAlignFlags& other) { + *this = (*this ^ other); + return *this; + } +} +/// 'auto' + + +/// 'normal' + + +/// 'start' + + +/// 'end' + + + + +/// 'flex-start' + + + + + + + +/// An arbitrary identifier for a native (OS compositor) surface +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct StyleNativeSurfaceId { + public ulong _0; +} +/// A special id for the native surface that is used for debug / profiler overlays. + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct StyleNativeTileId { + public StyleNativeSurfaceId surface_id; + public int x; + public int y; +} +/// A special id for the native surface that is used for debug / profiler overlays. + + diff --git a/tests/expectations/bitfield.cs b/tests/expectations/bitfield.cs new file mode 100644 index 000000000..08d8843aa --- /dev/null +++ b/tests/expectations/bitfield.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct HasBitfields { + public ulong: 8 foo; + public ulong: 56 bar; +} diff --git a/tests/expectations/bitflags.cs b/tests/expectations/bitflags.cs new file mode 100644 index 000000000..b14b0c4b8 --- /dev/null +++ b/tests/expectations/bitflags.cs @@ -0,0 +1,172 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +/// Constants shared by multiple CSS Box Alignment properties +/// +/// These constants match Gecko's `NS_STYLE_ALIGN_*` constants. +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct AlignFlags { + public byte bits; + + constexpr explicit operator bool() const { + return !!bits; + } + constexpr AlignFlags operator~() const { + return AlignFlags { static_cast(~bits) }; + } + constexpr AlignFlags operator|(const AlignFlags& other) const { + return AlignFlags { static_cast(this->bits | other.bits) }; + } + AlignFlags& operator|=(const AlignFlags& other) { + *this = (*this | other); + return *this; + } + constexpr AlignFlags operator&(const AlignFlags& other) const { + return AlignFlags { static_cast(this->bits & other.bits) }; + } + AlignFlags& operator&=(const AlignFlags& other) { + *this = (*this & other); + return *this; + } + constexpr AlignFlags operator^(const AlignFlags& other) const { + return AlignFlags { static_cast(this->bits ^ other.bits) }; + } + AlignFlags& operator^=(const AlignFlags& other) { + *this = (*this ^ other); + return *this; + } +} +/// 'auto' + + +/// 'normal' + + +/// 'start' + + +/// 'end' + + + + +/// 'flex-start' + + + + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct DebugFlags { + public uint bits; + + constexpr explicit operator bool() const { + return !!bits; + } + constexpr DebugFlags operator~() const { + return DebugFlags { static_cast(~bits) }; + } + constexpr DebugFlags operator|(const DebugFlags& other) const { + return DebugFlags { static_cast(this->bits | other.bits) }; + } + DebugFlags& operator|=(const DebugFlags& other) { + *this = (*this | other); + return *this; + } + constexpr DebugFlags operator&(const DebugFlags& other) const { + return DebugFlags { static_cast(this->bits & other.bits) }; + } + DebugFlags& operator&=(const DebugFlags& other) { + *this = (*this & other); + return *this; + } + constexpr DebugFlags operator^(const DebugFlags& other) const { + return DebugFlags { static_cast(this->bits ^ other.bits) }; + } + DebugFlags& operator^=(const DebugFlags& other) { + *this = (*this ^ other); + return *this; + } +} +/// Flag with the topmost bit set of the u32 + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct LargeFlags { + public ulong bits; + + constexpr explicit operator bool() const { + return !!bits; + } + constexpr LargeFlags operator~() const { + return LargeFlags { static_cast(~bits) }; + } + constexpr LargeFlags operator|(const LargeFlags& other) const { + return LargeFlags { static_cast(this->bits | other.bits) }; + } + LargeFlags& operator|=(const LargeFlags& other) { + *this = (*this | other); + return *this; + } + constexpr LargeFlags operator&(const LargeFlags& other) const { + return LargeFlags { static_cast(this->bits & other.bits) }; + } + LargeFlags& operator&=(const LargeFlags& other) { + *this = (*this & other); + return *this; + } + constexpr LargeFlags operator^(const LargeFlags& other) const { + return LargeFlags { static_cast(this->bits ^ other.bits) }; + } + LargeFlags& operator^=(const LargeFlags& other) { + *this = (*this ^ other); + return *this; + } +} +/// Flag with a very large shift that usually would be narrowed. + + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct OutOfLine { + public uint _0; + + constexpr explicit operator bool() const { + return !!_0; + } + constexpr OutOfLine operator~() const { + return OutOfLine { static_cast(~_0) }; + } + constexpr OutOfLine operator|(const OutOfLine& other) const { + return OutOfLine { static_cast(this->_0 | other._0) }; + } + OutOfLine& operator|=(const OutOfLine& other) { + *this = (*this | other); + return *this; + } + constexpr OutOfLine operator&(const OutOfLine& other) const { + return OutOfLine { static_cast(this->_0 & other._0) }; + } + OutOfLine& operator&=(const OutOfLine& other) { + *this = (*this & other); + return *this; + } + constexpr OutOfLine operator^(const OutOfLine& other) const { + return OutOfLine { static_cast(this->_0 ^ other._0) }; + } + OutOfLine& operator^=(const OutOfLine& other) { + *this = (*this ^ other); + return *this; + } +} + + + + + + diff --git a/tests/expectations/body.cs b/tests/expectations/body.cs new file mode 100644 index 000000000..b947b9247 --- /dev/null +++ b/tests/expectations/body.cs @@ -0,0 +1,116 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public enum MyCLikeEnum { + Foo1, + Bar1, + Baz1, +} + +public enum MyCLikeEnum_Prepended { + Foo1_Prepended, + Bar1_Prepended, + Baz1_Prepended, +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct MyFancyStruct { + public int i; +#ifdef __cplusplus + inline void foo(); +#endif +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct MyFancyEnum { + public enum Tag { + Foo, + Bar, + Baz, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Bar_Body { + public int _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Baz_Body { + public int _0; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public Bar_Body bar; + [FieldOffset(0)] + public Baz_Body baz; + } + + public Variants variants; +#ifdef __cplusplus + inline void wohoo(); +#endif +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct MyUnion { + [FieldOffset(0)] + public float f; + [FieldOffset(0)] + public uint u; + int32_t extra_member; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct MyFancyStruct_Prepended { +#ifdef __cplusplus + inline void prepended_wohoo(); +#endif + public int i; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct MyFancyEnum_Prepended { +#ifdef __cplusplus + inline void wohoo(); +#endif + public enum Tag { + Foo_Prepended, + Bar_Prepended, + Baz_Prepended, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Bar_Prepended_Body { + public int _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Baz_Prepended_Body { + public int _0; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public Bar_Prepended_Body bar_prepended; + [FieldOffset(0)] + public Baz_Prepended_Body baz_prepended; + } + + public Variants variants; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct MyUnion_Prepended { + int32_t extra_member; + [FieldOffset(0)] + public float f; + [FieldOffset(0)] + public uint u; +} diff --git a/tests/expectations/box.cs b/tests/expectations/box.cs new file mode 100644 index 000000000..8709e688d --- /dev/null +++ b/tests/expectations/box.cs @@ -0,0 +1,28 @@ +#if 0 +''' ' +#endif + +#ifdef __cplusplus +template +using Box = T*; +#endif + +#if 0 +' ''' +#endif + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct NotReprC_____i32 { + +} + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct MyStruct { + public int* number; +} diff --git a/tests/expectations/cdecl.cs b/tests/expectations/cdecl.cs new file mode 100644 index 000000000..78ff236bb --- /dev/null +++ b/tests/expectations/cdecl.cs @@ -0,0 +1,32 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/expectations/cell.cs b/tests/expectations/cell.cs new file mode 100644 index 000000000..174e3dd7c --- /dev/null +++ b/tests/expectations/cell.cs @@ -0,0 +1,14 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct NotReprC_RefCell_i32 { + +} + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct MyStruct { + public int number; +} diff --git a/tests/expectations/cfg.cs b/tests/expectations/cfg.cs new file mode 100644 index 000000000..f1fd1b86c --- /dev/null +++ b/tests/expectations/cfg.cs @@ -0,0 +1,126 @@ +#if 0 +DEF PLATFORM_UNIX = 0 +DEF PLATFORM_WIN = 0 +DEF X11 = 0 +DEF M_32 = 0 +#endif + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#if (defined(PLATFORM_WIN) || defined(M_32)) +public enum BarType : uint { + A, + B, + C, +} +#endif + +#if (defined(PLATFORM_UNIX) && defined(X11)) +public enum FooType : uint { + A, + B, + C, +} +#endif + +#if (defined(PLATFORM_UNIX) && defined(X11)) +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct FooHandle { + public FooType ty; + public int x; + public float y; + + bool operator==(const FooHandle& other) const { + return ty == other.ty && + x == other.x && + y == other.y; + } + bool operator!=(const FooHandle& other) const { + return ty != other.ty || + x != other.x || + y != other.y; + } +} +#endif + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct C { + public enum Tag : byte { + C1, + C2, +#if defined(PLATFORM_WIN) + C3, +#endif +#if defined(PLATFORM_UNIX) + C5, +#endif + } + +#if defined(PLATFORM_UNIX) + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct C5_Body { + public Tag tag; + public int int_; + + bool operator==(const C5_Body& other) const { + return int_ == other.int_; + } + bool operator!=(const C5_Body& other) const { + return int_ != other.int_; + } + } +#endif + + [FieldOffset(0)] + public Tag tag; + +#if defined(PLATFORM_UNIX) + [FieldOffset(0)] + public C5_Body c5; +#endif +} + +#if (defined(PLATFORM_WIN) || defined(M_32)) +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct BarHandle { + public BarType ty; + public int x; + public float y; + + bool operator==(const BarHandle& other) const { + return ty == other.ty && + x == other.x && + y == other.y; + } + bool operator!=(const BarHandle& other) const { + return ty != other.ty || + x != other.x || + y != other.y; + } +} +#endif + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct ConditionalField { +#if defined(X11) + public int field +#endif + ; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Normal { + public int x; + public float y; + + bool operator==(const Normal& other) const { + return x == other.x && + y == other.y; + } + bool operator!=(const Normal& other) const { + return x != other.x || + y != other.y; + } +} diff --git a/tests/expectations/cfg_2.cs b/tests/expectations/cfg_2.cs new file mode 100644 index 000000000..3340b0b6f --- /dev/null +++ b/tests/expectations/cfg_2.cs @@ -0,0 +1,44 @@ +#if 0 +DEF DEFINED = 1 +DEF NOT_DEFINED = 0 +#endif + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#if defined(NOT_DEFINED) + + +#endif + +#if defined(DEFINED) + + +#endif + +#if (defined(NOT_DEFINED) || defined(DEFINED)) +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + public int x; +} +#endif + +#if defined(NOT_DEFINED) +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Bar { + public Foo y; +} +#endif + +#if defined(DEFINED) +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Bar { + public Foo z; +} +#endif + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Root { + public Bar w; +} diff --git a/tests/expectations/cfg_field.cs b/tests/expectations/cfg_field.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/cfg_field.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/char.cs b/tests/expectations/char.cs new file mode 100644 index 000000000..610c2c949 --- /dev/null +++ b/tests/expectations/char.cs @@ -0,0 +1,7 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + public uint a; +} diff --git a/tests/expectations/const_conflict.cs b/tests/expectations/const_conflict.cs new file mode 100644 index 000000000..f9df36cd5 --- /dev/null +++ b/tests/expectations/const_conflict.cs @@ -0,0 +1,5 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + diff --git a/tests/expectations/const_generics.cs b/tests/expectations/const_generics.cs new file mode 100644 index 000000000..184fc0eea --- /dev/null +++ b/tests/expectations/const_generics.cs @@ -0,0 +1,15 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Book { + public CArrayString_TITLE_SIZE title; + public CArrayString_40 author; +} diff --git a/tests/expectations/const_generics_arrayvec.cs b/tests/expectations/const_generics_arrayvec.cs new file mode 100644 index 000000000..fd21f2265 --- /dev/null +++ b/tests/expectations/const_generics_arrayvec.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct ArrayVec_____u8__100 { + public byte*[] xs; + public uint len; +} diff --git a/tests/expectations/const_generics_bool.cs b/tests/expectations/const_generics_bool.cs new file mode 100644 index 000000000..8ef75cb4b --- /dev/null +++ b/tests/expectations/const_generics_bool.cs @@ -0,0 +1,28 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct HashTable_Str__c_char__false { + public nuint num_buckets; + public nuint capacity; + public byte* occupied; + public Str* keys; + public byte* vals; +} + + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct HashTable_Str__u64__true { + public nuint num_buckets; + public nuint capacity; + public byte* occupied; + public Str* keys; + public ulong* vals; +} + + diff --git a/tests/expectations/const_generics_byte.cs b/tests/expectations/const_generics_byte.cs new file mode 100644 index 000000000..3c7c22f2d --- /dev/null +++ b/tests/expectations/const_generics_byte.cs @@ -0,0 +1,14 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Parser_40__41 { + public byte* buf; + public nuint len; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Parser_123__125 { + public byte* buf; + public nuint len; +} diff --git a/tests/expectations/const_generics_char.cs b/tests/expectations/const_generics_char.cs new file mode 100644 index 000000000..ff7727536 --- /dev/null +++ b/tests/expectations/const_generics_char.cs @@ -0,0 +1,9 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TakeUntil_0 { + public byte* start; + public nuint len; + public nuint point; +} diff --git a/tests/expectations/const_generics_constant.cs b/tests/expectations/const_generics_constant.cs new file mode 100644 index 000000000..842393ed1 --- /dev/null +++ b/tests/expectations/const_generics_constant.cs @@ -0,0 +1,19 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct FixedPoint_FONT_WEIGHT_FRACTION_BITS { + public ushort value; +} + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct FontWeight { + public FontWeightFixedPoint _0; +} + + diff --git a/tests/expectations/const_generics_thru.cs b/tests/expectations/const_generics_thru.cs new file mode 100644 index 000000000..39b08d2bf --- /dev/null +++ b/tests/expectations/const_generics_thru.cs @@ -0,0 +1,22 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Inner_1 { + public byte[] bytes; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Outer_1 { + public Inner_1 inner; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Inner_2 { + public byte[] bytes; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Outer_2 { + public Inner_2 inner; +} diff --git a/tests/expectations/const_transparent.cs b/tests/expectations/const_transparent.cs new file mode 100644 index 000000000..46641c020 --- /dev/null +++ b/tests/expectations/const_transparent.cs @@ -0,0 +1,21 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + + + + + + + + + + + + + + + + diff --git a/tests/expectations/constant.cs b/tests/expectations/constant.cs new file mode 100644 index 000000000..e420af039 --- /dev/null +++ b/tests/expectations/constant.cs @@ -0,0 +1,63 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/// A single-line doc comment. + + + +/// A +/// multi-line +/// doc +/// comment. + + + + + + + + + + + + + + + + + + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + public int[] x; +} diff --git a/tests/expectations/constant_big.cs b/tests/expectations/constant_big.cs new file mode 100644 index 000000000..a35fd577b --- /dev/null +++ b/tests/expectations/constant_big.cs @@ -0,0 +1,14 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + + + + + + + + + diff --git a/tests/expectations/constant_constexpr.cs b/tests/expectations/constant_constexpr.cs new file mode 100644 index 000000000..bc05fd592 --- /dev/null +++ b/tests/expectations/constant_constexpr.cs @@ -0,0 +1,24 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + + + + + + + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + public int x; +} + + + + + diff --git a/tests/expectations/constant_sort_name.cs b/tests/expectations/constant_sort_name.cs new file mode 100644 index 000000000..24480b398 --- /dev/null +++ b/tests/expectations/constant_sort_name.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + + + diff --git a/tests/expectations/constant_sort_none.cs b/tests/expectations/constant_sort_none.cs new file mode 100644 index 000000000..24480b398 --- /dev/null +++ b/tests/expectations/constant_sort_none.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + + + diff --git a/tests/expectations/constant_user_defined_type.cs b/tests/expectations/constant_user_defined_type.cs new file mode 100644 index 000000000..1cfe4dd01 --- /dev/null +++ b/tests/expectations/constant_user_defined_type.cs @@ -0,0 +1,22 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public enum E { + V, +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct S { + public byte field; +} + + + + + + + + + + + diff --git a/tests/expectations/custom_header.cs b/tests/expectations/custom_header.cs new file mode 100644 index 000000000..c2c5d3864 --- /dev/null +++ b/tests/expectations/custom_header.cs @@ -0,0 +1,8 @@ +#if 0 +# This file is generated by cbindgen. DO NOT EDIT +#endif + + +#if 0 +# This is a simple test to ensure that trailers do not cause extra newlines in files +#endif diff --git a/tests/expectations/cython_options.cs b/tests/expectations/cython_options.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/cython_options.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/decl_name_conflicting.cs b/tests/expectations/decl_name_conflicting.cs new file mode 100644 index 000000000..5c94b22f5 --- /dev/null +++ b/tests/expectations/decl_name_conflicting.cs @@ -0,0 +1,12 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public enum BindingType : uint { + Buffer = 0, + NotBuffer = 1, +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct BindGroupLayoutEntry { + public BindingType ty; +} diff --git a/tests/expectations/dep_v2.cs b/tests/expectations/dep_v2.cs new file mode 100644 index 000000000..2792361cf --- /dev/null +++ b/tests/expectations/dep_v2.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct dep_struct { + public uint x; + public double y; +} diff --git a/tests/expectations/deprecated.cs b/tests/expectations/deprecated.cs new file mode 100644 index 000000000..3c6db6732 --- /dev/null +++ b/tests/expectations/deprecated.cs @@ -0,0 +1,76 @@ +#define DEPRECATED_FUNC __attribute__((deprecated)) +#define DEPRECATED_STRUCT __attribute__((deprecated)) +#define DEPRECATED_ENUM __attribute__((deprecated)) +#define DEPRECATED_ENUM_VARIANT __attribute__((deprecated)) +#define DEPRECATED_FUNC_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__))) +#define DEPRECATED_STRUCT_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__))) +#define DEPRECATED_ENUM_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__))) +#define DEPRECATED_ENUM_VARIANT_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__))) + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public enum DEPRECATED_ENUM DeprecatedEnum : int { + A = 0, +} + +public enum DEPRECATED_ENUM_WITH_NOTE("This is a note") DeprecatedEnumWithNote : int { + B = 0, +} + +public enum EnumWithDeprecatedVariants : int { + C = 0, + D DEPRECATED_ENUM_VARIANT = 1, + E DEPRECATED_ENUM_VARIANT_WITH_NOTE("This is a note") = 2, + F DEPRECATED_ENUM_VARIANT_WITH_NOTE("This is a note") = 3, +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct DEPRECATED_STRUCT DeprecatedStruct { + public int a; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct DEPRECATED_STRUCT_WITH_NOTE("This is a note") DeprecatedStructWithNote { + public int a; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct EnumWithDeprecatedStructVariants { + public enum Tag : byte { + Foo, + Bar DEPRECATED_ENUM_VARIANT, + Baz DEPRECATED_ENUM_VARIANT_WITH_NOTE("This is a note"), + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Foo_Body { + public Tag tag; + public short _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct DEPRECATED_STRUCT Bar_Body { + public Tag tag; + public byte x; + public short y; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct DEPRECATED_STRUCT_WITH_NOTE("This is a note") Baz_Body { + public Tag tag; + public byte x; + public byte y; + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public Foo_Body foo; + [FieldOffset(0)] + public Bar_Body bar; + [FieldOffset(0)] + public Baz_Body baz; +} diff --git a/tests/expectations/derive_eq.cs b/tests/expectations/derive_eq.cs new file mode 100644 index 000000000..874c8faa9 --- /dev/null +++ b/tests/expectations/derive_eq.cs @@ -0,0 +1,83 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + [MarshalAs(UnmanagedType.U1)] + public bool a; + public int b; + + bool operator==(const Foo& aOther) const { + return a == aOther.a && + b == aOther.b; + } + bool operator!=(const Foo& aOther) const { + return a != aOther.a || + b != aOther.b; + } +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Bar { + public enum Tag : byte { + Baz, + Bazz, + FooNamed, + FooParen, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Bazz_Body { + public Tag tag; + public Foo named; + + bool operator==(const Bazz_Body& aOther) const { + return named == aOther.named; + } + bool operator!=(const Bazz_Body& aOther) const { + return named != aOther.named; + } + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct FooNamed_Body { + public Tag tag; + public int different; + public uint fields; + + bool operator==(const FooNamed_Body& aOther) const { + return different == aOther.different && + fields == aOther.fields; + } + bool operator!=(const FooNamed_Body& aOther) const { + return different != aOther.different || + fields != aOther.fields; + } + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct FooParen_Body { + public Tag tag; + public int _0; + public Foo _1; + + bool operator==(const FooParen_Body& aOther) const { + return _0 == aOther._0 && + _1 == aOther._1; + } + bool operator!=(const FooParen_Body& aOther) const { + return _0 != aOther._0 || + _1 != aOther._1; + } + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public Bazz_Body bazz; + [FieldOffset(0)] + public FooNamed_Body foo_named; + [FieldOffset(0)] + public FooParen_Body foo_paren; +} diff --git a/tests/expectations/derive_ostream.cs b/tests/expectations/derive_ostream.cs new file mode 100644 index 000000000..face496a4 --- /dev/null +++ b/tests/expectations/derive_ostream.cs @@ -0,0 +1,151 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public enum C : uint { + X = 2, + Y, +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct A { + public int _0; + + friend std::ostream& operator<<(std::ostream& stream, const A& instance) { + return stream << "{ " << "_0=" << instance._0 << " }"; + } +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct B { + public int x; + public float y; + + friend std::ostream& operator<<(std::ostream& stream, const B& instance) { + return stream << "{ " << "x=" << instance.x << ", " + << "y=" << instance.y << " }"; + } +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct D { + public byte List; + public nuint Of; + public B Things; + + friend std::ostream& operator<<(std::ostream& stream, const D& instance) { + return stream << "{ " << "List=" << instance.List << ", " + << "Of=" << instance.Of << ", " + << "Things=" << instance.Things << " }"; + } +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct F { + public enum Tag : byte { + Foo, + Bar, + Baz, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Foo_Body { + public Tag tag; + public short _0; + + friend std::ostream& operator<<(std::ostream& stream, const Foo_Body& instance) { + return stream << "{ " << "tag=" << instance.tag << ", " + << "_0=" << instance._0 << " }"; + } + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Bar_Body { + public Tag tag; + public byte x; + public short y; + + friend std::ostream& operator<<(std::ostream& stream, const Bar_Body& instance) { + return stream << "{ " << "tag=" << instance.tag << ", " + << "x=" << instance.x << ", " + << "y=" << instance.y << " }"; + } + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public Foo_Body foo; + [FieldOffset(0)] + public Bar_Body bar; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct H { + public enum Tag : byte { + Hello, + There, + Everyone, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Hello_Body { + public short _0; + + friend std::ostream& operator<<(std::ostream& stream, const Hello_Body& instance) { + return stream << "{ " << "_0=" << instance._0 << " }"; + } + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct There_Body { + public byte x; + public short y; + + friend std::ostream& operator<<(std::ostream& stream, const There_Body& instance) { + return stream << "{ " << "x=" << instance.x << ", " + << "y=" << instance.y << " }"; + } + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public Hello_Body hello; + [FieldOffset(0)] + public There_Body there; + } + + public Variants variants; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct I { + public enum Tag : byte { + ThereAgain, + SomethingElse, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct ThereAgain_Body { + public byte x; + public short y; + + friend std::ostream& operator<<(std::ostream& stream, const ThereAgain_Body& instance) { + return stream << "{ " << "x=" << instance.x << ", " + << "y=" << instance.y << " }"; + } + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public ThereAgain_Body there_again; + } + + public Variants variants; +} diff --git a/tests/expectations/destructor_and_copy_ctor.cs b/tests/expectations/destructor_and_copy_ctor.cs new file mode 100644 index 000000000..e372a1350 --- /dev/null +++ b/tests/expectations/destructor_and_copy_ctor.cs @@ -0,0 +1,287 @@ +#define NOINLINE __attribute__((noinline)) +#define NODISCARD [[nodiscard]] + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public enum FillRule : byte { + A, + B, +} + +/// This will have a destructor manually implemented via variant_body, and +/// similarly a Drop impl in Rust. +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct OwnedSlice_u32 { + public nuint len; + public uint* ptr; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Polygon_u32 { + public FillRule fill; + public OwnedSlice_u32 coordinates; +} + +/// This will have a destructor manually implemented via variant_body, and +/// similarly a Drop impl in Rust. +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct OwnedSlice_i32 { + public nuint len; + public int* ptr; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo_u32 { + public enum Tag : byte { + Bar_u32, + Polygon1_u32, + Slice1_u32, + Slice2_u32, + Slice3_u32, + Slice4_u32, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Polygon1_Body_u32 { + public Polygon_u32 _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Slice1_Body_u32 { + public OwnedSlice_u32 _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Slice2_Body_u32 { + public OwnedSlice_i32 _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Slice3_Body_u32 { + public FillRule fill; + public OwnedSlice_u32 coords; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Slice4_Body_u32 { + public FillRule fill; + public OwnedSlice_i32 coords; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public Polygon1_Body_u32 polygon1; + [FieldOffset(0)] + public Slice1_Body_u32 slice1; + [FieldOffset(0)] + public Slice2_Body_u32 slice2; + [FieldOffset(0)] + public Slice3_Body_u32 slice3; + [FieldOffset(0)] + public Slice4_Body_u32 slice4; + } + + public Variants variants; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Polygon_i32 { + public FillRule fill; + public OwnedSlice_i32 coordinates; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Baz_i32 { + public enum Tag : byte { + Bar2_i32, + Polygon21_i32, + Slice21_i32, + Slice22_i32, + Slice23_i32, + Slice24_i32, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Polygon21_Body_i32 { + public Tag tag; + public Polygon_i32 _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Slice21_Body_i32 { + public Tag tag; + public OwnedSlice_i32 _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Slice22_Body_i32 { + public Tag tag; + public OwnedSlice_i32 _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Slice23_Body_i32 { + public Tag tag; + public FillRule fill; + public OwnedSlice_i32 coords; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Slice24_Body_i32 { + public Tag tag; + public FillRule fill; + public OwnedSlice_i32 coords; + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public Polygon21_Body_i32 polygon21; + [FieldOffset(0)] + public Slice21_Body_i32 slice21; + [FieldOffset(0)] + public Slice22_Body_i32 slice22; + [FieldOffset(0)] + public Slice23_Body_i32 slice23; + [FieldOffset(0)] + public Slice24_Body_i32 slice24; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Taz { + public enum Tag : byte { + Bar3, + Taz1, + Taz3, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Taz1_Body { + public Tag tag; + public int _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Taz3_Body { + public Tag tag; + public OwnedSlice_i32 _0; + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public Taz1_Body taz1; + [FieldOffset(0)] + public Taz3_Body taz3; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Tazz { + public enum Tag : byte { + Bar4, + Taz2, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Taz2_Body { + public Tag tag; + public int _0; + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public Taz2_Body taz2; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Tazzz { + public enum Tag : byte { + Bar5, + Taz5, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Taz5_Body { + public Tag tag; + public int _0; + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public Taz5_Body taz5; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Tazzzz { + public enum Tag : byte { + Taz6, + Taz7, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Taz6_Body { + public Tag tag; + public int _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Taz7_Body { + public Tag tag; + public uint _0; + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public Taz6_Body taz6; + [FieldOffset(0)] + public Taz7_Body taz7; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Qux { + public enum Tag : byte { + Qux1, + Qux2, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Qux1_Body { + public Tag tag; + public int _0; + + bool operator==(const Qux1_Body& other) const { + return _0 == other._0; + } + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Qux2_Body { + public Tag tag; + public uint _0; + + bool operator==(const Qux2_Body& other) const { + return _0 == other._0; + } + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public Qux1_Body qux1; + [FieldOffset(0)] + public Qux2_Body qux2; +} diff --git a/tests/expectations/display_list.cs b/tests/expectations/display_list.cs new file mode 100644 index 000000000..5f5d2cf68 --- /dev/null +++ b/tests/expectations/display_list.cs @@ -0,0 +1,49 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Rect { + public float x; + public float y; + public float w; + public float h; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Color { + public byte r; + public byte g; + public byte b; + public byte a; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct DisplayItem { + public enum Tag : byte { + Fill, + Image, + ClearScreen, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Fill_Body { + public Tag tag; + public Rect _0; + public Color _1; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Image_Body { + public Tag tag; + public uint id; + public Rect bounds; + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public Fill_Body fill; + [FieldOffset(0)] + public Image_Body image; +} diff --git a/tests/expectations/doclength_short.cs b/tests/expectations/doclength_short.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/doclength_short.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/docstyle_auto.cs b/tests/expectations/docstyle_auto.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/docstyle_auto.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/docstyle_c99.cs b/tests/expectations/docstyle_c99.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/docstyle_c99.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/docstyle_doxy.cs b/tests/expectations/docstyle_doxy.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/docstyle_doxy.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/documentation.cs b/tests/expectations/documentation.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/documentation.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/documentation_attr.cs b/tests/expectations/documentation_attr.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/documentation_attr.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/enum.cs b/tests/expectations/enum.cs new file mode 100644 index 000000000..cb7941d0b --- /dev/null +++ b/tests/expectations/enum.cs @@ -0,0 +1,317 @@ +#if 0 +''' ' +#endif + +#ifdef __cplusplus +template +using Box = T*; +#endif + +#if 0 +' ''' +#endif + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public enum A : ulong { + a1 = 0, + a2 = 2, + a3, + a4 = 5, +} + +public enum B : uint { + b1 = 0, + b2 = 2, + b3, + b4 = 5, +} + +public enum C : ushort { + c1 = 0, + c2 = 2, + c3, + c4 = 5, +} + +public enum D : byte { + d1 = 0, + d2 = 2, + d3, + d4 = 5, +} + +// WARNING: Type byte, sbyte, short, ushort, int, uint, long, or ulong expected, +// but found nuint instead. +// This is a limitation of C# enums, which only support those types. +// Please consider using a different type for this enum. +// See https://learn.microsoft.com/en-us/dotnet/csharp/misc/cs1008 for more information. +// The size of the enum will be set to int to avoid compilation errors. +// It could be different from it's size in rust, use with caution. +public enum E { + e1 = 0, + e2 = 2, + e3, + e4 = 5, +} + +// WARNING: Type byte, sbyte, short, ushort, int, uint, long, or ulong expected, +// but found nint instead. +// This is a limitation of C# enums, which only support those types. +// Please consider using a different type for this enum. +// See https://learn.microsoft.com/en-us/dotnet/csharp/misc/cs1008 for more information. +// The size of the enum will be set to int to avoid compilation errors. +// It could be different from it's size in rust, use with caution. +public enum F { + f1 = 0, + f2 = 2, + f3, + f4 = 5, +} + +public enum L { + l1, + l2, + l3, + l4, +} + +public enum M : sbyte { + m1 = -1, + m2 = 0, + m3 = 1, +} + +public enum N { + n1, + n2, + n3, + n4, +} + +public enum O : sbyte { + o1, + o2, + o3, + o4, +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct J { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct K { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Opaque { + +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct G { + public enum Tag : byte { + Foo, + Bar, + Baz, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Foo_Body { + public Tag tag; + public short _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Bar_Body { + public Tag tag; + public byte x; + public short y; + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public Foo_Body foo; + [FieldOffset(0)] + public Bar_Body bar; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct H { + public enum Tag { + H_Foo, + H_Bar, + H_Baz, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct H_Foo_Body { + public short _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct H_Bar_Body { + public byte x; + public short y; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public H_Foo_Body foo; + [FieldOffset(0)] + public H_Bar_Body bar; + } + + public Variants variants; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct ExI { + public enum Tag : byte { + ExI_Foo, + ExI_Bar, + ExI_Baz, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct ExI_Foo_Body { + public short _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct ExI_Bar_Body { + public byte x; + public short y; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public ExI_Foo_Body foo; + [FieldOffset(0)] + public ExI_Bar_Body bar; + } + + public Variants variants; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct P { + public enum Tag : byte { + P0, + P1, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct P0_Body { + public byte _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct P1_Body { + public byte _0; + public byte _1; + public byte _2; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public P0_Body p0; + [FieldOffset(0)] + public P1_Body p1; + } + + public Variants variants; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Q { + public enum Tag { + Ok, + Err, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Ok_Body { + public uint* _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Err_Body { + public uint _0; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public Ok_Body ok; + [FieldOffset(0)] + public Err_Body err; + } + + public Variants variants; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct R { + public enum Tag { + IRFoo, + IRBar, + IRBaz, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct IRFoo_Body { + public short _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct IRBar_Body { + public byte x; + public short y; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public IRFoo_Body IRFoo; + [FieldOffset(0)] + public IRBar_Body IRBar; + } + + public Variants variants; +} + +#if 0 +''' ' +#endif + +#include +#include "testing-helpers.h" +static_assert(offsetof(CBINDGEN_STRUCT(P), tag) == 0, "unexpected offset for tag"); +static_assert(offsetof(CBINDGEN_STRUCT(P), p0) == 1, "unexpected offset for p0"); +static_assert(offsetof(CBINDGEN_STRUCT(P), p0) == 1, "unexpected offset for p1"); +static_assert(sizeof(CBINDGEN_STRUCT(P)) == 4, "unexpected size for P"); + +#if 0 +' ''' +#endif diff --git a/tests/expectations/enum_discriminant.cs b/tests/expectations/enum_discriminant.cs new file mode 100644 index 000000000..a7c69de99 --- /dev/null +++ b/tests/expectations/enum_discriminant.cs @@ -0,0 +1,15 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + +public enum E : sbyte { + A = 1, + B = -1, + C = (1 + 2), + D = FOURTY_FOUR, + F = 5, + G = (sbyte)54, + H = (sbyte)false, +} diff --git a/tests/expectations/enum_self.cs b/tests/expectations/enum_self.cs new file mode 100644 index 000000000..2735874a5 --- /dev/null +++ b/tests/expectations/enum_self.cs @@ -0,0 +1,36 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo_Bar { + public int* something; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Bar { + public enum Tag : byte { + Min, + Max, + Other, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Min_Body { + public Tag tag; + public Foo_Bar _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Max_Body { + public Tag tag; + public Foo_Bar _0; + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public Min_Body min; + [FieldOffset(0)] + public Max_Body max; +} diff --git a/tests/expectations/euclid.cs b/tests/expectations/euclid.cs new file mode 100644 index 000000000..5c5f29ae1 --- /dev/null +++ b/tests/expectations/euclid.cs @@ -0,0 +1,104 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TypedLength_f32__UnknownUnit { + public float _0; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TypedLength_f32__LayoutUnit { + public float _0; +} + + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TypedSideOffsets2D_f32__UnknownUnit { + public float top; + public float right; + public float bottom; + public float left; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TypedSideOffsets2D_f32__LayoutUnit { + public float top; + public float right; + public float bottom; + public float left; +} + + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TypedSize2D_f32__UnknownUnit { + public float width; + public float height; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TypedSize2D_f32__LayoutUnit { + public float width; + public float height; +} + + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TypedPoint2D_f32__UnknownUnit { + public float x; + public float y; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TypedPoint2D_f32__LayoutUnit { + public float x; + public float y; +} + + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TypedRect_f32__UnknownUnit { + public TypedPoint2D_f32__UnknownUnit origin; + public TypedSize2D_f32__UnknownUnit size; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TypedRect_f32__LayoutUnit { + public TypedPoint2D_f32__LayoutUnit origin; + public TypedSize2D_f32__LayoutUnit size; +} + + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TypedTransform2D_f32__UnknownUnit__LayoutUnit { + public float m11; + public float m12; + public float m21; + public float m22; + public float m31; + public float m32; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TypedTransform2D_f32__LayoutUnit__UnknownUnit { + public float m11; + public float m12; + public float m21; + public float m22; + public float m31; + public float m32; +} diff --git a/tests/expectations/exclude_generic_monomorph.cs b/tests/expectations/exclude_generic_monomorph.cs new file mode 100644 index 000000000..33b4a0e42 --- /dev/null +++ b/tests/expectations/exclude_generic_monomorph.cs @@ -0,0 +1,25 @@ +#include + +#if 0 +''' ' +#endif + +typedef uint64_t Option_Foo; + +#if 0 +' ''' +#endif + +#if 0 +from libc.stdint cimport uint64_t +ctypedef uint64_t Option_Foo +#endif + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Bar { + public Option_Foo foo; +} diff --git a/tests/expectations/expand.cs b/tests/expectations/expand.cs new file mode 100644 index 000000000..5b63ce7a6 --- /dev/null +++ b/tests/expectations/expand.cs @@ -0,0 +1,7 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + +} diff --git a/tests/expectations/expand_default_features.cs b/tests/expectations/expand_default_features.cs new file mode 100644 index 000000000..5b63ce7a6 --- /dev/null +++ b/tests/expectations/expand_default_features.cs @@ -0,0 +1,7 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + +} diff --git a/tests/expectations/expand_dep.cs b/tests/expectations/expand_dep.cs new file mode 100644 index 000000000..2792361cf --- /dev/null +++ b/tests/expectations/expand_dep.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct dep_struct { + public uint x; + public double y; +} diff --git a/tests/expectations/expand_dep_v2.cs b/tests/expectations/expand_dep_v2.cs new file mode 100644 index 000000000..2792361cf --- /dev/null +++ b/tests/expectations/expand_dep_v2.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct dep_struct { + public uint x; + public double y; +} diff --git a/tests/expectations/expand_features.cs b/tests/expectations/expand_features.cs new file mode 100644 index 000000000..5b63ce7a6 --- /dev/null +++ b/tests/expectations/expand_features.cs @@ -0,0 +1,7 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + +} diff --git a/tests/expectations/expand_no_default_features.cs b/tests/expectations/expand_no_default_features.cs new file mode 100644 index 000000000..5b63ce7a6 --- /dev/null +++ b/tests/expectations/expand_no_default_features.cs @@ -0,0 +1,7 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + +} diff --git a/tests/expectations/export_name.cs b/tests/expectations/export_name.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/export_name.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/extern.cs b/tests/expectations/extern.cs new file mode 100644 index 000000000..3a306c3b1 --- /dev/null +++ b/tests/expectations/extern.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Normal { + public int x; + public float y; +} diff --git a/tests/expectations/extern_2.cs b/tests/expectations/extern_2.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/extern_2.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/external_workspace_child.cs b/tests/expectations/external_workspace_child.cs new file mode 100644 index 000000000..fcaa2c91f --- /dev/null +++ b/tests/expectations/external_workspace_child.cs @@ -0,0 +1,7 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct ExtType { + public uint data; +} diff --git a/tests/expectations/fns.cs b/tests/expectations/fns.cs new file mode 100644 index 000000000..496d673d6 --- /dev/null +++ b/tests/expectations/fns.cs @@ -0,0 +1,11 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Fns { + public Callback noArgs; + public Callback anonymousArg; + public Callback returnsNumber; + public Callback namedArgs; + public Callback namedArgsWildcards; +} diff --git a/tests/expectations/forward_declaration.cs b/tests/expectations/forward_declaration.cs new file mode 100644 index 000000000..7e79c21a2 --- /dev/null +++ b/tests/expectations/forward_declaration.cs @@ -0,0 +1,57 @@ +#if 0 +''' ' +#endif +#if defined(CBINDGEN_STYLE_TYPE) +/* ANONYMOUS STRUCTS DO NOT SUPPORT FORWARD DECLARATIONS! +#endif +#if 0 +' ''' +#endif + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct StructInfo { + public TypeInfo** fields; + public nuint num_fields; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TypeData { + public enum Tag { + Primitive, + Struct, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Struct_Body { + public StructInfo _0; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public Struct_Body struct_; + } + + public Variants variants; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TypeInfo { + public TypeData data; +} + +#if 0 +''' ' +#endif +#if defined(CBINDGEN_STYLE_TYPE) +*/ +#endif +#if 0 +' ''' +#endif diff --git a/tests/expectations/function_args.cs b/tests/expectations/function_args.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/function_args.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/function_noreturn.cs b/tests/expectations/function_noreturn.cs new file mode 100644 index 000000000..1882b4146 --- /dev/null +++ b/tests/expectations/function_noreturn.cs @@ -0,0 +1,15 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#ifndef NO_RETURN_ATTR + #ifdef __GNUC__ + #define NO_RETURN_ATTR __attribute__ ((noreturn)) + #else // __GNUC__ + #define NO_RETURN_ATTR + #endif // __GNUC__ +#endif // NO_RETURN_ATTR + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Example { + public Callback f; +} diff --git a/tests/expectations/function_ptr.cs b/tests/expectations/function_ptr.cs new file mode 100644 index 000000000..3f743f5bc --- /dev/null +++ b/tests/expectations/function_ptr.cs @@ -0,0 +1,6 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + diff --git a/tests/expectations/function_sort_name.cs b/tests/expectations/function_sort_name.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/function_sort_name.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/function_sort_none.cs b/tests/expectations/function_sort_none.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/function_sort_none.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/generic_defaults.cs b/tests/expectations/generic_defaults.cs new file mode 100644 index 000000000..652f1a8a3 --- /dev/null +++ b/tests/expectations/generic_defaults.cs @@ -0,0 +1,16 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Bar_i32__u32 { + public Foo_i32 f; + public uint p; +} + + + + diff --git a/tests/expectations/generic_pointer.cs b/tests/expectations/generic_pointer.cs new file mode 100644 index 000000000..9879e2b4f --- /dev/null +++ b/tests/expectations/generic_pointer.cs @@ -0,0 +1,9 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo_____u8 { + public byte* a; +} + + diff --git a/tests/expectations/global_attr.cs b/tests/expectations/global_attr.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/global_attr.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/global_variable.cs b/tests/expectations/global_variable.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/global_variable.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/ignore.cs b/tests/expectations/ignore.cs new file mode 100644 index 000000000..24480b398 --- /dev/null +++ b/tests/expectations/ignore.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + + + diff --git a/tests/expectations/include.cs b/tests/expectations/include.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/include.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/include_guard.cs b/tests/expectations/include_guard.cs new file mode 100644 index 000000000..e69de29bb diff --git a/tests/expectations/include_item.cs b/tests/expectations/include_item.cs new file mode 100644 index 000000000..8b4be35ed --- /dev/null +++ b/tests/expectations/include_item.cs @@ -0,0 +1,13 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct A { + public int x; + public float y; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct B { + public A data; +} diff --git a/tests/expectations/include_specific.cs b/tests/expectations/include_specific.cs new file mode 100644 index 000000000..e69de29bb diff --git a/tests/expectations/infinite_recursion_typedef_monomorph.cs b/tests/expectations/infinite_recursion_typedef_monomorph.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/infinite_recursion_typedef_monomorph.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/inner_mod.cs b/tests/expectations/inner_mod.cs new file mode 100644 index 000000000..7e7b4de26 --- /dev/null +++ b/tests/expectations/inner_mod.cs @@ -0,0 +1,7 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + public float x; +} diff --git a/tests/expectations/item_types.cs b/tests/expectations/item_types.cs new file mode 100644 index 000000000..73822fc0c --- /dev/null +++ b/tests/expectations/item_types.cs @@ -0,0 +1,7 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public enum OnlyThisShouldBeGenerated : byte { + Foo, + Bar, +} diff --git a/tests/expectations/item_types_renamed.cs b/tests/expectations/item_types_renamed.cs new file mode 100644 index 000000000..694cce7ee --- /dev/null +++ b/tests/expectations/item_types_renamed.cs @@ -0,0 +1,7 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public enum StyleOnlyThisShouldBeGenerated : byte { + Foo, + Bar, +} diff --git a/tests/expectations/layout.cs b/tests/expectations/layout.cs new file mode 100644 index 000000000..fb65b294d --- /dev/null +++ b/tests/expectations/layout.cs @@ -0,0 +1,129 @@ +#define CBINDGEN_PACKED __attribute__ ((packed)) +#define CBINDGEN_ALIGNED(n) __attribute__ ((aligned(n))) + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct RustAlign4Struct { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct RustAlign4Union { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct RustPackedStruct { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct RustPackedUnion { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct UnsupportedAlign4Enum { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct UnsupportedPacked4Struct { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct UnsupportedPacked4Union { + +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Align1Struct { + public nuint arg1; + public byte* arg2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Align2Struct { + public nuint arg1; + public byte* arg2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Align4Struct { + public nuint arg1; + public byte* arg2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Align8Struct { + public nuint arg1; + public byte* arg2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Align32Struct { + public nuint arg1; + public byte* arg2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct PackedStruct { + public nuint arg1; + public byte* arg2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Align1Union { + [FieldOffset(0)] + public nuint variant1; + [FieldOffset(0)] + public byte* variant2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Align4Union { + [FieldOffset(0)] + public nuint variant1; + [FieldOffset(0)] + public byte* variant2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Align16Union { + [FieldOffset(0)] + public nuint variant1; + [FieldOffset(0)] + public byte* variant2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct PackedUnion { + [FieldOffset(0)] + public nuint variant1; + [FieldOffset(0)] + public byte* variant2; +} diff --git a/tests/expectations/layout_aligned_opaque.cs b/tests/expectations/layout_aligned_opaque.cs new file mode 100644 index 000000000..da769f8bc --- /dev/null +++ b/tests/expectations/layout_aligned_opaque.cs @@ -0,0 +1,64 @@ +#define CBINDGEN_PACKED __attribute__ ((packed)) +#define CBINDGEN_ALIGNED(n) __attribute__ ((aligned(n))) + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct OpaqueAlign16Union { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct OpaqueAlign1Struct { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct OpaqueAlign1Union { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct OpaqueAlign2Struct { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct OpaqueAlign32Struct { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct OpaqueAlign4Struct { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct OpaqueAlign4Union { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct OpaqueAlign8Struct { + +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct PackedStruct { + public nuint arg1; + public byte* arg2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct PackedUnion { + [FieldOffset(0)] + public nuint variant1; + [FieldOffset(0)] + public byte* variant2; +} diff --git a/tests/expectations/layout_packed_opaque.cs b/tests/expectations/layout_packed_opaque.cs new file mode 100644 index 000000000..fa3552be0 --- /dev/null +++ b/tests/expectations/layout_packed_opaque.cs @@ -0,0 +1,86 @@ +#define CBINDGEN_PACKED __attribute__ ((packed)) +#define CBINDGEN_ALIGNED(n) __attribute__ ((aligned(n))) + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct OpaquePackedStruct { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct OpaquePackedUnion { + +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Align1Union { + [FieldOffset(0)] + public nuint variant1; + [FieldOffset(0)] + public byte* variant2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Align4Union { + [FieldOffset(0)] + public nuint variant1; + [FieldOffset(0)] + public byte* variant2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Align16Union { + [FieldOffset(0)] + public nuint variant1; + [FieldOffset(0)] + public byte* variant2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Align1Struct { + public nuint arg1; + public byte* arg2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Align2Struct { + public nuint arg1; + public byte* arg2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Align4Struct { + public nuint arg1; + public byte* arg2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Align8Struct { + public nuint arg1; + public byte* arg2; +} + +// WARNING: `packed` and `align(N)` is not implemented for C# yet. +// As a result, the size and alignment of this struct could be different from rust. +// Use with caution.[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Align32Struct { + public nuint arg1; + public byte* arg2; +} diff --git a/tests/expectations/lifetime_arg.cs b/tests/expectations/lifetime_arg.cs new file mode 100644 index 000000000..68540f939 --- /dev/null +++ b/tests/expectations/lifetime_arg.cs @@ -0,0 +1,30 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct A { + public int* data; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct E { + public enum Tag { + V, + U, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct U_Body { + public byte* _0; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public U_Body u; + } + + public Variants variants; +} diff --git a/tests/expectations/linestyle_cr.cs b/tests/expectations/linestyle_cr.cs new file mode 100644 index 000000000..529d3de9a --- /dev/null +++ b/tests/expectations/linestyle_cr.cs @@ -0,0 +1 @@ +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential)] internal unsafe partial struct Dummy { public int x; public float y; } \ No newline at end of file diff --git a/tests/expectations/linestyle_crlf.cs b/tests/expectations/linestyle_crlf.cs new file mode 100644 index 000000000..d83f039a5 --- /dev/null +++ b/tests/expectations/linestyle_crlf.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Dummy { + public int x; + public float y; +} diff --git a/tests/expectations/linestyle_lf.cs b/tests/expectations/linestyle_lf.cs new file mode 100644 index 000000000..2ea475622 --- /dev/null +++ b/tests/expectations/linestyle_lf.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Dummy { + public int x; + public float y; +} diff --git a/tests/expectations/literal_target.cs b/tests/expectations/literal_target.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/literal_target.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/mangle.cs b/tests/expectations/mangle.cs new file mode 100644 index 000000000..b8d16c7ff --- /dev/null +++ b/tests/expectations/mangle.cs @@ -0,0 +1,14 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public enum Bar { + BarSome, + BarThing, +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct FooU8 { + public byte a; +} + + diff --git a/tests/expectations/manuallydrop.cs b/tests/expectations/manuallydrop.cs new file mode 100644 index 000000000..0b4e8ea7f --- /dev/null +++ b/tests/expectations/manuallydrop.cs @@ -0,0 +1,34 @@ +#if 0 +''' ' +#endif + +#ifdef __cplusplus +template +using ManuallyDrop = T; +#endif + +#if 0 +' ''' +#endif + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct NotReprC_Point { + +} + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Point { + public int x; + public int y; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct MyStruct { + public Point point; +} diff --git a/tests/expectations/maybeuninit.cs b/tests/expectations/maybeuninit.cs new file mode 100644 index 000000000..955bf6dd5 --- /dev/null +++ b/tests/expectations/maybeuninit.cs @@ -0,0 +1,28 @@ +#if 0 +''' ' +#endif + +#ifdef __cplusplus +template +using MaybeUninit = T; +#endif + +#if 0 +' ''' +#endif + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct NotReprC______i32 { + +} + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct MyStruct { + public int* number; +} diff --git a/tests/expectations/mod_2015.cs b/tests/expectations/mod_2015.cs new file mode 100644 index 000000000..be5c84f99 --- /dev/null +++ b/tests/expectations/mod_2015.cs @@ -0,0 +1,10 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct ExportMe { + public ulong val; +} diff --git a/tests/expectations/mod_2018.cs b/tests/expectations/mod_2018.cs new file mode 100644 index 000000000..66ec35888 --- /dev/null +++ b/tests/expectations/mod_2018.cs @@ -0,0 +1,15 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct ExportMe { + public ulong val; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct ExportMe2 { + public ulong val; +} diff --git a/tests/expectations/mod_attr.cs b/tests/expectations/mod_attr.cs new file mode 100644 index 000000000..16c042bc9 --- /dev/null +++ b/tests/expectations/mod_attr.cs @@ -0,0 +1,32 @@ +#if 0 +DEF FOO = 0 +DEF BAR = 0 +#endif + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#if defined(FOO) + + +#endif + +#if defined(BAR) + + +#endif + +#if defined(FOO) +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + +} +#endif + +#if defined(BAR) +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Bar { + +} +#endif diff --git a/tests/expectations/mod_path.cs b/tests/expectations/mod_path.cs new file mode 100644 index 000000000..be5c84f99 --- /dev/null +++ b/tests/expectations/mod_path.cs @@ -0,0 +1,10 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct ExportMe { + public ulong val; +} diff --git a/tests/expectations/monomorph_1.cs b/tests/expectations/monomorph_1.cs new file mode 100644 index 000000000..0bae3aeca --- /dev/null +++ b/tests/expectations/monomorph_1.cs @@ -0,0 +1,46 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Bar_Bar_f32 { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Bar_Foo_f32 { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Bar_f32 { + +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo_i32 { + public int* data; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo_f32 { + public float* data; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo_Bar_f32 { + public Bar_f32* data; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Tuple_Foo_f32_____f32 { + public Foo_f32* a; + public float* b; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Tuple_f32__f32 { + public float* a; + public float* b; +} + + diff --git a/tests/expectations/monomorph_2.cs b/tests/expectations/monomorph_2.cs new file mode 100644 index 000000000..6fb3d0109 --- /dev/null +++ b/tests/expectations/monomorph_2.cs @@ -0,0 +1,24 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct A { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct B { + +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct List_A { + public A* members; + public nuint count; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct List_B { + public B* members; + public nuint count; +} diff --git a/tests/expectations/monomorph_3.cs b/tests/expectations/monomorph_3.cs new file mode 100644 index 000000000..66590ef16 --- /dev/null +++ b/tests/expectations/monomorph_3.cs @@ -0,0 +1,53 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Bar_Bar_f32 { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Bar_Foo_f32 { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Bar_f32 { + +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Foo_i32 { + [FieldOffset(0)] + public int* data; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Foo_f32 { + [FieldOffset(0)] + public float* data; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Foo_Bar_f32 { + [FieldOffset(0)] + public Bar_f32* data; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Tuple_Foo_f32_____f32 { + [FieldOffset(0)] + public Foo_f32* a; + [FieldOffset(0)] + public float* b; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Tuple_f32__f32 { + [FieldOffset(0)] + public float* a; + [FieldOffset(0)] + public float* b; +} + + diff --git a/tests/expectations/must_use.cs b/tests/expectations/must_use.cs new file mode 100644 index 000000000..c5fff503a --- /dev/null +++ b/tests/expectations/must_use.cs @@ -0,0 +1,35 @@ +#define MUST_USE_FUNC __attribute__((warn_unused_result)) +#define MUST_USE_STRUCT __attribute__((warn_unused)) +#define MUST_USE_ENUM /* nothing */ + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct MaybeOwnedPtr_i32 { + public enum MUST_USE_ENUM Tag : byte { + Owned_i32, + None_i32, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Owned_Body_i32 { + public int* _0; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public Owned_Body_i32 owned; + } + + public Variants variants; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct MUST_USE_STRUCT OwnedPtr_i32 { + public int* ptr; +} diff --git a/tests/expectations/namespace_constant.cs b/tests/expectations/namespace_constant.cs new file mode 100644 index 000000000..bbaadcde2 --- /dev/null +++ b/tests/expectations/namespace_constant.cs @@ -0,0 +1,16 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +namespace constants { + + + + + + + + [StructLayout(LayoutKind.Sequential)] + internal unsafe partial struct Foo { + public int[] x; + } + +} \ No newline at end of file diff --git a/tests/expectations/namespaces_constant.cs b/tests/expectations/namespaces_constant.cs new file mode 100644 index 000000000..0e5aecf4f --- /dev/null +++ b/tests/expectations/namespaces_constant.cs @@ -0,0 +1,16 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +namespace constants.test { + + + + + + + + [StructLayout(LayoutKind.Sequential)] + internal unsafe partial struct Foo { + public int[] x; + } + +} \ No newline at end of file diff --git a/tests/expectations/nested_import.cs b/tests/expectations/nested_import.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/nested_import.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/no_includes.cs b/tests/expectations/no_includes.cs new file mode 100644 index 000000000..e69de29bb diff --git a/tests/expectations/non_pub_extern.cs b/tests/expectations/non_pub_extern.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/non_pub_extern.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/nonnull.cs b/tests/expectations/nonnull.cs new file mode 100644 index 000000000..df11c1a7a --- /dev/null +++ b/tests/expectations/nonnull.cs @@ -0,0 +1,20 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Opaque { + +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo_u64 { + public float* a; + public ulong* b; + public Opaque* c; + public ulong** d; + public float** e; + public Opaque** f; + public ulong* g; + public int* h; + public int** i; +} diff --git a/tests/expectations/nonnull_attribute.cs b/tests/expectations/nonnull_attribute.cs new file mode 100644 index 000000000..99dcd586d --- /dev/null +++ b/tests/expectations/nonnull_attribute.cs @@ -0,0 +1,37 @@ +#ifdef __clang__ +#define CBINDGEN_NONNULL _Nonnull +#else +#define CBINDGEN_NONNULL +#endif + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Opaque { + +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct References { + public Opaque* a; + public Opaque* b; + public Opaque* c; + public Opaque* d; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Pointers_u64 { + public float* a; + public ulong* b; + public Opaque* c; + public ulong** d; + public float** e; + public Opaque** f; + public ulong* g; + public int* h; + public int** i; + public ulong* j; + public ulong* k; +} diff --git a/tests/expectations/nonzero.cs b/tests/expectations/nonzero.cs new file mode 100644 index 000000000..691a7a84e --- /dev/null +++ b/tests/expectations/nonzero.cs @@ -0,0 +1,48 @@ +#if 0 +''' ' +#endif + +#ifdef __cplusplus +struct NonZeroI64; +#endif + +#if 0 +' ''' +#endif + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Option_i64 { + +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct NonZeroAliases { + public byte a; + public ushort b; + public uint c; + public ulong d; + public sbyte e; + public short f; + public int g; + public long h; + public long i; + public Option_i64* j; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct NonZeroGenerics { + public byte a; + public ushort b; + public uint c; + public ulong d; + public sbyte e; + public short f; + public int g; + public long h; + public long i; + public Option_i64* j; +} diff --git a/tests/expectations/opaque.cs b/tests/expectations/opaque.cs new file mode 100644 index 000000000..f345843ad --- /dev/null +++ b/tests/expectations/opaque.cs @@ -0,0 +1,35 @@ +#if 0 +''' ' +#endif + +#ifdef __cplusplus +// These could be added as opaque types I guess. +template +struct BuildHasherDefault; + +struct DefaultHasher; +#endif + +#if 0 +' ''' +#endif + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct HashMap_i32__i32__BuildHasherDefault_DefaultHasher { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Result_Foo { + +} + + + + + + diff --git a/tests/expectations/package_version.cs b/tests/expectations/package_version.cs new file mode 100644 index 000000000..ecaa21360 --- /dev/null +++ b/tests/expectations/package_version.cs @@ -0,0 +1,9 @@ +/* Package version: 0.1.0 */ + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + public ulong bar; +} diff --git a/tests/expectations/pin.cs b/tests/expectations/pin.cs new file mode 100644 index 000000000..58ce7d867 --- /dev/null +++ b/tests/expectations/pin.cs @@ -0,0 +1,24 @@ +#if 0 +''' ' +#endif + +#ifdef __cplusplus +template +using Pin = T; +template +using Box = T*; +#endif + +#if 0 +' ''' +#endif + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct PinTest { + public int* pinned_box; + public int* pinned_ref; +} diff --git a/tests/expectations/pragma_once.cs b/tests/expectations/pragma_once.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/pragma_once.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/prefix.cs b/tests/expectations/prefix.cs new file mode 100644 index 000000000..a81f12f63 --- /dev/null +++ b/tests/expectations/prefix.cs @@ -0,0 +1,36 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + + + + + + + + + + + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct PREFIX_AbsoluteFontWeight { + public enum Tag : byte { + Weight, + Normal, + Bold, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct PREFIX_Weight_Body { + public Tag tag; + public float _0; + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public PREFIX_Weight_Body weight; +} diff --git a/tests/expectations/prefixed_struct_literal.cs b/tests/expectations/prefixed_struct_literal.cs new file mode 100644 index 000000000..e6c1a9a5a --- /dev/null +++ b/tests/expectations/prefixed_struct_literal.cs @@ -0,0 +1,13 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct PREFIXFoo { + public int a; + public uint b; +} + + + + + diff --git a/tests/expectations/prefixed_struct_literal_deep.cs b/tests/expectations/prefixed_struct_literal_deep.cs new file mode 100644 index 000000000..0679e243b --- /dev/null +++ b/tests/expectations/prefixed_struct_literal_deep.cs @@ -0,0 +1,17 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct PREFIXBar { + public int a; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct PREFIXFoo { + public int a; + public uint b; + public PREFIXBar bar; +} + + + diff --git a/tests/expectations/ptrs_as_arrays.cs b/tests/expectations/ptrs_as_arrays.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/ptrs_as_arrays.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/raw_ident.cs b/tests/expectations/raw_ident.cs new file mode 100644 index 000000000..b148fb979 --- /dev/null +++ b/tests/expectations/raw_ident.cs @@ -0,0 +1,12 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public enum Enum : byte { + a, + b, +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Struct { + public Enum field; +} diff --git a/tests/expectations/raw_lines.cs b/tests/expectations/raw_lines.cs new file mode 100644 index 000000000..97a0fd74e --- /dev/null +++ b/tests/expectations/raw_lines.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#define VERSION 1 diff --git a/tests/expectations/rename.cs b/tests/expectations/rename.cs new file mode 100644 index 000000000..5a1142571 --- /dev/null +++ b/tests/expectations/rename.cs @@ -0,0 +1,39 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + +public enum C_E : byte { + x = 0, + y = 1, +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct C_A { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct C_C { + +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct C_AwesomeB { + public int x; + public float y; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct C_D { + [FieldOffset(0)] + public int x; + [FieldOffset(0)] + public float y; +} + + + + + diff --git a/tests/expectations/rename_case.cs b/tests/expectations/rename_case.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/rename_case.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/rename_crate.cs b/tests/expectations/rename_crate.cs new file mode 100644 index 000000000..50b8fb2c6 --- /dev/null +++ b/tests/expectations/rename_crate.cs @@ -0,0 +1,38 @@ +#if 0 +DEF DEFINE_FREEBSD = 0 +#endif + + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + public int x; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct RenamedTy { + public ulong y; +} + +#if !defined(DEFINE_FREEBSD) +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct NoExternTy { + public byte field; +} +#endif + +#if !defined(DEFINE_FREEBSD) +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct ContainsNoExternTy { + public NoExternTy field; +} +#endif + +#if defined(DEFINE_FREEBSD) +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct ContainsNoExternTy { + public ulong field; +} +#endif diff --git a/tests/expectations/renaming_overrides_prefixing.cs b/tests/expectations/renaming_overrides_prefixing.cs new file mode 100644 index 000000000..e2b9115b2 --- /dev/null +++ b/tests/expectations/renaming_overrides_prefixing.cs @@ -0,0 +1,13 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct StyleA { + +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct B { + public int x; + public float y; +} diff --git a/tests/expectations/reserved.cs b/tests/expectations/reserved.cs new file mode 100644 index 000000000..762b17f20 --- /dev/null +++ b/tests/expectations/reserved.cs @@ -0,0 +1,97 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct A { + public int namespace_; + public float float_; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct B { + public int namespace_; + public float float_; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct C { + public enum Tag : byte { + D, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct D_Body { + public int namespace_; + public float float_; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public D_Body d; + } + + public Variants variants; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct E { + public enum Tag : byte { + Double, + Float, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Double_Body { + public double _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct Float_Body { + public float _0; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public Double_Body double_; + [FieldOffset(0)] + public Float_Body float_; + } + + public Variants variants; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct F { + public enum Tag : byte { + double_, + float_, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct double_Body { + public double _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct float_Body { + public float _0; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public double_Body double_; + [FieldOffset(0)] + public float_Body float_; + } + + public Variants variants; +} diff --git a/tests/expectations/sentinel.cs b/tests/expectations/sentinel.cs new file mode 100644 index 000000000..584ece59e --- /dev/null +++ b/tests/expectations/sentinel.cs @@ -0,0 +1,49 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public enum A : byte { + A_A1, + A_A2, + A_A3, + /// Must be last for serialization purposes + A_Sentinel, +} + +public enum B : byte { + B_B1, + B_B2, + B_B3, + /// Must be last for serialization purposes + B_Sentinel, +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct C { + public enum Tag : byte { + C_C1, + C_C2, + C_C3, + /// Must be last for serialization purposes + C_Sentinel, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct C_C1_Body { + public Tag tag; + public uint a; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct C_C2_Body { + public Tag tag; + public uint b; + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public C_C1_Body c1; + [FieldOffset(0)] + public C_C2_Body c2; +} diff --git a/tests/expectations/simplify_option_ptr.cs b/tests/expectations/simplify_option_ptr.cs new file mode 100644 index 000000000..e1cd9c32e --- /dev/null +++ b/tests/expectations/simplify_option_ptr.cs @@ -0,0 +1,32 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Opaque { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Option_____Opaque { + +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + public Opaque* x; + public Opaque* y; + public Callback z; + public Callback* zz; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Bar { + [FieldOffset(0)] + public Opaque* x; + [FieldOffset(0)] + public Opaque* y; + [FieldOffset(0)] + public Callback z; + [FieldOffset(0)] + public Callback* zz; +} diff --git a/tests/expectations/size_types.cs b/tests/expectations/size_types.cs new file mode 100644 index 000000000..a2dc5aaf6 --- /dev/null +++ b/tests/expectations/size_types.cs @@ -0,0 +1,28 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Type byte, sbyte, short, ushort, int, uint, long, or ulong expected, +// but found nint instead. +// This is a limitation of C# enums, which only support those types. +// Please consider using a different type for this enum. +// See https://learn.microsoft.com/en-us/dotnet/csharp/misc/cs1008 for more information. +// The size of the enum will be set to int to avoid compilation errors. +// It could be different from it's size in rust, use with caution. +public enum IE { + IV, +} + +// WARNING: Type byte, sbyte, short, ushort, int, uint, long, or ulong expected, +// but found nuint instead. +// This is a limitation of C# enums, which only support those types. +// Please consider using a different type for this enum. +// See https://learn.microsoft.com/en-us/dotnet/csharp/misc/cs1008 for more information. +// The size of the enum will be set to int to avoid compilation errors. +// It could be different from it's size in rust, use with caution. +public enum UE { + UV, +} + + + + diff --git a/tests/expectations/static.cs b/tests/expectations/static.cs new file mode 100644 index 000000000..01f622368 --- /dev/null +++ b/tests/expectations/static.cs @@ -0,0 +1,12 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Bar { + +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + +} diff --git a/tests/expectations/std_lib.cs b/tests/expectations/std_lib.cs new file mode 100644 index 000000000..72fa7b2dc --- /dev/null +++ b/tests/expectations/std_lib.cs @@ -0,0 +1,17 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Option_i32 { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Result_i32__String { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Vec_String { + +} diff --git a/tests/expectations/struct.c b/tests/expectations/struct.c index 207e9a052..c784b1853 100644 --- a/tests/expectations/struct.c +++ b/tests/expectations/struct.c @@ -25,4 +25,22 @@ typedef struct { float y; } TupleNamed; -void root(Opaque *a, Normal b, NormalWithZST c, TupleRenamed d, TupleNamed e); +typedef struct { + int32_t x; + float y; + bool z; +} WithBool; + +typedef struct { + int32_t _0; + float _1; + bool _2; +} TupleWithBool; + +void root(Opaque *a, + Normal b, + NormalWithZST c, + TupleRenamed d, + TupleNamed e, + WithBool f, + TupleWithBool g); diff --git a/tests/expectations/struct.compat.c b/tests/expectations/struct.compat.c index fd82fcd1e..a58a04adb 100644 --- a/tests/expectations/struct.compat.c +++ b/tests/expectations/struct.compat.c @@ -25,11 +25,29 @@ typedef struct { float y; } TupleNamed; +typedef struct { + int32_t x; + float y; + bool z; +} WithBool; + +typedef struct { + int32_t _0; + float _1; + bool _2; +} TupleWithBool; + #ifdef __cplusplus extern "C" { #endif // __cplusplus -void root(Opaque *a, Normal b, NormalWithZST c, TupleRenamed d, TupleNamed e); +void root(Opaque *a, + Normal b, + NormalWithZST c, + TupleRenamed d, + TupleNamed e, + WithBool f, + TupleWithBool g); #ifdef __cplusplus } // extern "C" diff --git a/tests/expectations/struct.cpp b/tests/expectations/struct.cpp index 4fe03404d..ca02eb9cb 100644 --- a/tests/expectations/struct.cpp +++ b/tests/expectations/struct.cpp @@ -26,8 +26,26 @@ struct TupleNamed { float y; }; +struct WithBool { + int32_t x; + float y; + bool z; +}; + +struct TupleWithBool { + int32_t _0; + float _1; + bool _2; +}; + extern "C" { -void root(Opaque *a, Normal b, NormalWithZST c, TupleRenamed d, TupleNamed e); +void root(Opaque *a, + Normal b, + NormalWithZST c, + TupleRenamed d, + TupleNamed e, + WithBool f, + TupleWithBool g); } // extern "C" diff --git a/tests/expectations/struct.cs b/tests/expectations/struct.cs new file mode 100644 index 000000000..d5961f03e --- /dev/null +++ b/tests/expectations/struct.cs @@ -0,0 +1,47 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Opaque { + +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Normal { + public int x; + public float y; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct NormalWithZST { + public int x; + public float y; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TupleRenamed { + public int m0; + public float m1; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TupleNamed { + public int x; + public float y; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct WithBool { + public int x; + public float y; + [MarshalAs(UnmanagedType.U1)] + public bool z; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TupleWithBool { + public int _0; + public float _1; + [MarshalAs(UnmanagedType.U1)] + public bool _2; +} diff --git a/tests/expectations/struct.pyx b/tests/expectations/struct.pyx index 3342101fa..054e590dd 100644 --- a/tests/expectations/struct.pyx +++ b/tests/expectations/struct.pyx @@ -25,4 +25,20 @@ cdef extern from *: int32_t x; float y; - void root(Opaque *a, Normal b, NormalWithZST c, TupleRenamed d, TupleNamed e); + ctypedef struct WithBool: + int32_t x; + float y; + bool z; + + ctypedef struct TupleWithBool: + int32_t _0; + float _1; + bool _2; + + void root(Opaque *a, + Normal b, + NormalWithZST c, + TupleRenamed d, + TupleNamed e, + WithBool f, + TupleWithBool g); diff --git a/tests/expectations/struct_both.c b/tests/expectations/struct_both.c index 5664185f9..efd14b83f 100644 --- a/tests/expectations/struct_both.c +++ b/tests/expectations/struct_both.c @@ -25,8 +25,22 @@ typedef struct TupleNamed { float y; } TupleNamed; +typedef struct WithBool { + int32_t x; + float y; + bool z; +} WithBool; + +typedef struct TupleWithBool { + int32_t _0; + float _1; + bool _2; +} TupleWithBool; + void root(struct Opaque *a, struct Normal b, struct NormalWithZST c, struct TupleRenamed d, - struct TupleNamed e); + struct TupleNamed e, + struct WithBool f, + struct TupleWithBool g); diff --git a/tests/expectations/struct_both.compat.c b/tests/expectations/struct_both.compat.c index ee0f4c761..9db2a514e 100644 --- a/tests/expectations/struct_both.compat.c +++ b/tests/expectations/struct_both.compat.c @@ -25,6 +25,18 @@ typedef struct TupleNamed { float y; } TupleNamed; +typedef struct WithBool { + int32_t x; + float y; + bool z; +} WithBool; + +typedef struct TupleWithBool { + int32_t _0; + float _1; + bool _2; +} TupleWithBool; + #ifdef __cplusplus extern "C" { #endif // __cplusplus @@ -33,7 +45,9 @@ void root(struct Opaque *a, struct Normal b, struct NormalWithZST c, struct TupleRenamed d, - struct TupleNamed e); + struct TupleNamed e, + struct WithBool f, + struct TupleWithBool g); #ifdef __cplusplus } // extern "C" diff --git a/tests/expectations/struct_literal.cs b/tests/expectations/struct_literal.cs new file mode 100644 index 000000000..991ffd68c --- /dev/null +++ b/tests/expectations/struct_literal.cs @@ -0,0 +1,25 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Bar { + +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo { + public int a; + public uint b; +} + + + + + + + + + + + + diff --git a/tests/expectations/struct_literal_order.cs b/tests/expectations/struct_literal_order.cs new file mode 100644 index 000000000..f1de569e4 --- /dev/null +++ b/tests/expectations/struct_literal_order.cs @@ -0,0 +1,28 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct ABC { + public float a; + public uint b; + public uint c; +} + + + + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct BAC { + public uint b; + public float a; + public int c; +} + + + + + + diff --git a/tests/expectations/struct_self.cs b/tests/expectations/struct_self.cs new file mode 100644 index 000000000..2fb118acd --- /dev/null +++ b/tests/expectations/struct_self.cs @@ -0,0 +1,13 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo_Bar { + public int* something; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Bar { + public int something; + public Foo_Bar subexpressions; +} diff --git a/tests/expectations/struct_tag.c b/tests/expectations/struct_tag.c index cc5ddaa75..9ad33661e 100644 --- a/tests/expectations/struct_tag.c +++ b/tests/expectations/struct_tag.c @@ -25,8 +25,22 @@ struct TupleNamed { float y; }; +struct WithBool { + int32_t x; + float y; + bool z; +}; + +struct TupleWithBool { + int32_t _0; + float _1; + bool _2; +}; + void root(struct Opaque *a, struct Normal b, struct NormalWithZST c, struct TupleRenamed d, - struct TupleNamed e); + struct TupleNamed e, + struct WithBool f, + struct TupleWithBool g); diff --git a/tests/expectations/struct_tag.compat.c b/tests/expectations/struct_tag.compat.c index 092007c03..523a9272d 100644 --- a/tests/expectations/struct_tag.compat.c +++ b/tests/expectations/struct_tag.compat.c @@ -25,6 +25,18 @@ struct TupleNamed { float y; }; +struct WithBool { + int32_t x; + float y; + bool z; +}; + +struct TupleWithBool { + int32_t _0; + float _1; + bool _2; +}; + #ifdef __cplusplus extern "C" { #endif // __cplusplus @@ -33,7 +45,9 @@ void root(struct Opaque *a, struct Normal b, struct NormalWithZST c, struct TupleRenamed d, - struct TupleNamed e); + struct TupleNamed e, + struct WithBool f, + struct TupleWithBool g); #ifdef __cplusplus } // extern "C" diff --git a/tests/expectations/struct_tag.pyx b/tests/expectations/struct_tag.pyx index 768f37521..7132c39d1 100644 --- a/tests/expectations/struct_tag.pyx +++ b/tests/expectations/struct_tag.pyx @@ -25,4 +25,20 @@ cdef extern from *: int32_t x; float y; - void root(Opaque *a, Normal b, NormalWithZST c, TupleRenamed d, TupleNamed e); + cdef struct WithBool: + int32_t x; + float y; + bool z; + + cdef struct TupleWithBool: + int32_t _0; + float _1; + bool _2; + + void root(Opaque *a, + Normal b, + NormalWithZST c, + TupleRenamed d, + TupleNamed e, + WithBool f, + TupleWithBool g); diff --git a/tests/expectations/style_crash.cs b/tests/expectations/style_crash.cs new file mode 100644 index 000000000..a7086a37c --- /dev/null +++ b/tests/expectations/style_crash.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; diff --git a/tests/expectations/swift_name.cs b/tests/expectations/swift_name.cs new file mode 100644 index 000000000..0b67a13d4 --- /dev/null +++ b/tests/expectations/swift_name.cs @@ -0,0 +1,19 @@ +#define CF_SWIFT_NAME(_name) __attribute__((swift_name(#_name))) + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Opaque { + +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct SelfTypeTestStruct { + public byte times; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct PointerToOpaque { + public Opaque* ptr; +} diff --git a/tests/expectations/transform_op.cs b/tests/expectations/transform_op.cs new file mode 100644 index 000000000..9385308ed --- /dev/null +++ b/tests/expectations/transform_op.cs @@ -0,0 +1,204 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct StylePoint_i32 { + public int x; + public int y; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct StylePoint_f32 { + public float x; + public float y; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct StyleFoo_i32 { + public enum Tag : byte { + Foo_i32, + Bar_i32, + Baz_i32, + Bazz_i32, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct StyleFoo_Body_i32 { + public Tag tag; + public int x; + public StylePoint_i32 y; + public StylePoint_f32 z; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct StyleBar_Body_i32 { + public Tag tag; + public int _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct StyleBaz_Body_i32 { + public Tag tag; + public StylePoint_i32 _0; + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public StyleFoo_Body_i32 foo; + [FieldOffset(0)] + public StyleBar_Body_i32 bar; + [FieldOffset(0)] + public StyleBaz_Body_i32 baz; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct StyleBar_i32 { + public enum Tag { + Bar1_i32, + Bar2_i32, + Bar3_i32, + Bar4_i32, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct StyleBar1_Body_i32 { + public int x; + public StylePoint_i32 y; + public StylePoint_f32 z; + public Callback u; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct StyleBar2_Body_i32 { + public int _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct StyleBar3_Body_i32 { + public StylePoint_i32 _0; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public StyleBar1_Body_i32 bar1; + [FieldOffset(0)] + public StyleBar2_Body_i32 bar2; + [FieldOffset(0)] + public StyleBar3_Body_i32 bar3; + } + + public Variants variants; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct StylePoint_u32 { + public uint x; + public uint y; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct StyleBar_u32 { + public enum Tag { + Bar1_u32, + Bar2_u32, + Bar3_u32, + Bar4_u32, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct StyleBar1_Body_u32 { + public int x; + public StylePoint_u32 y; + public StylePoint_f32 z; + public Callback u; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct StyleBar2_Body_u32 { + public uint _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct StyleBar3_Body_u32 { + public StylePoint_u32 _0; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public StyleBar1_Body_u32 bar1; + [FieldOffset(0)] + public StyleBar2_Body_u32 bar2; + [FieldOffset(0)] + public StyleBar3_Body_u32 bar3; + } + + public Variants variants; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct StyleBaz { + public enum Tag : byte { + Baz1, + Baz2, + Baz3, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct StyleBaz1_Body { + public Tag tag; + public StyleBar_u32 _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct StyleBaz2_Body { + public Tag tag; + public StylePoint_i32 _0; + } + + [FieldOffset(0)] + public Tag tag; + + [FieldOffset(0)] + public StyleBaz1_Body baz1; + [FieldOffset(0)] + public StyleBaz2_Body baz2; +} + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct StyleTaz { + public enum Tag : byte { + Taz1, + Taz2, + Taz3, + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct StyleTaz1_Body { + public StyleBar_u32 _0; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct StyleTaz2_Body { + public StyleBaz _0; + } + + public Tag tag; + + [StructLayout(LayoutKind.Explicit)] + public unsafe partial struct Variants { + [FieldOffset(0)] + public StyleTaz1_Body taz1; + [FieldOffset(0)] + public StyleTaz2_Body taz2; + } + + public Variants variants; +} diff --git a/tests/expectations/transparent.cs b/tests/expectations/transparent.cs new file mode 100644 index 000000000..0d499d168 --- /dev/null +++ b/tests/expectations/transparent.cs @@ -0,0 +1,33 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct DummyStruct { + +} + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct EnumWithAssociatedConstantInImpl { + +} + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/expectations/typedef.cs b/tests/expectations/typedef.cs new file mode 100644 index 000000000..e3f032b9b --- /dev/null +++ b/tests/expectations/typedef.cs @@ -0,0 +1,10 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo_i32__i32 { + public int x; + public int y; +} + + diff --git a/tests/expectations/union.cs b/tests/expectations/union.cs new file mode 100644 index 000000000..cadcd8b9d --- /dev/null +++ b/tests/expectations/union.cs @@ -0,0 +1,23 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// WARNING: Opaque type, no details available, so only pointers to it are allowed +internal unsafe struct Opaque { + +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Normal { + [FieldOffset(0)] + public int x; + [FieldOffset(0)] + public float y; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct NormalWithZST { + [FieldOffset(0)] + public int x; + [FieldOffset(0)] + public float y; +} diff --git a/tests/expectations/union_self.cs b/tests/expectations/union_self.cs new file mode 100644 index 000000000..366c554da --- /dev/null +++ b/tests/expectations/union_self.cs @@ -0,0 +1,15 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Foo_Bar { + public int* something; +} + +[StructLayout(LayoutKind.Explicit)] +internal unsafe partial struct Bar { + [FieldOffset(0)] + public int something; + [FieldOffset(0)] + public Foo_Bar subexpressions; +} diff --git a/tests/expectations/using_namespaces.cs b/tests/expectations/using_namespaces.cs new file mode 100644 index 000000000..88e388ec0 --- /dev/null +++ b/tests/expectations/using_namespaces.cs @@ -0,0 +1,5 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +namespace root { + +} \ No newline at end of file diff --git a/tests/expectations/va_list.cs b/tests/expectations/va_list.cs new file mode 100644 index 000000000..874b87e8c --- /dev/null +++ b/tests/expectations/va_list.cs @@ -0,0 +1,11 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct Interface_______i32_______i32_______va_list { + public Callback fn1; +} diff --git a/tests/expectations/workspace.cs b/tests/expectations/workspace.cs new file mode 100644 index 000000000..d48a672eb --- /dev/null +++ b/tests/expectations/workspace.cs @@ -0,0 +1,10 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + + + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct ExtType { + public uint data; +} diff --git a/tests/expectations/zst.cs b/tests/expectations/zst.cs new file mode 100644 index 000000000..fbae20f1d --- /dev/null +++ b/tests/expectations/zst.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe partial struct TraitObject { + public void* data; + public void* vtable; +} diff --git a/tests/rust/struct.rs b/tests/rust/struct.rs index 7569901a4..61841eaf5 100644 --- a/tests/rust/struct.rs +++ b/tests/rust/struct.rs @@ -28,11 +28,23 @@ struct TupleRenamed(i32, f32); #[repr(C)] struct TupleNamed(i32, f32); +#[repr(C)] +struct WithBool { + x: i32, + y: f32, + z: bool, +} + +#[repr(C)] +struct TupleWithBool(i32, f32, bool); + #[no_mangle] pub extern "C" fn root( a: *mut Opaque, b: Normal, c: NormalWithZST, d: TupleRenamed, - e: TupleNamed + e: TupleNamed, + f: WithBool, + g: TupleWithBool, ) { } diff --git a/tests/tests.rs b/tests/tests.rs index eebabefbe..8e0d0b021 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -2,10 +2,11 @@ extern crate cbindgen; use cbindgen::*; use std::collections::HashSet; +use std::fmt::format; use std::fs::File; use std::io::Read; use std::path::Path; -use std::process::Command; +use std::process::{Command, CommandArgs}; use std::{env, fs, str}; use pretty_assertions::assert_eq; @@ -59,6 +60,9 @@ fn run_cbindgen( Language::Cython => { command.arg("--lang").arg("cython"); } + Language::CSharp => { + command.arg("--lang").arg("csharp"); + } } if package_version { @@ -123,15 +127,36 @@ 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::CSharp => { + // find out dotnet from environment variable DOTNET_INSTALL_DIR, + // if it's set, use `$DOTNET_INSTALL_DIR/dotnet`, otherwise find `dotnet` with which + let dotnet = env::var("DOTNET_INSTALL_DIR") + .map(|dotnet_install_dir| { + let mut dotnet: std::path::PathBuf = dotnet_install_dir.into(); + dotnet.push("dotnet"); + dotnet.to_str().unwrap().to_owned() + }) + .unwrap_or_else(|_| { + // we need the full path of dotnet for SDK path later + let result = which::which("dotnet") + .expect("dotnet not found in PATH, please install .NET SDK"); + result.to_str().unwrap().to_owned() + }); + dotnet + } }; let file_name = cbindgen_output .file_name() .expect("cbindgen output should be a file"); let mut object = tmp_dir.join(file_name); - object.set_extension("o"); + if language == Language::CSharp { + object.set_extension("dll"); + } else { + object.set_extension("o"); + } - let mut command = Command::new(cc); + let mut command = Command::new(cc.clone()); match language { Language::Cxx | Language::C => { command.arg("-D").arg("DEFINED"); @@ -185,6 +210,80 @@ fn compile( command.arg("-o").arg(&object); command.arg(cbindgen_output); } + Language::CSharp => { + // We need to compile the generated C# code with the .NET SDK. + let dotnet: std::path::PathBuf = cc.into(); + let dotnet_install_dir = dotnet.parent().unwrap(); + let sdk_path = dotnet_install_dir.join("sdk"); + let sdk_version = env::var("DOTNET_VERSION").unwrap_or_else(|_| { + fs::read_dir(&sdk_path) + .unwrap() + .map(|entry| entry.unwrap().file_name()) + .max() + .unwrap() + .to_str() + .unwrap() + .to_owned() + }); + let csc = sdk_path.join(sdk_version).join("Roslyn/bincore/csc.dll"); + + // find latest from dotnet\packs\Microsoft.NETCore.App.Ref\, like dotnet\packs\Microsoft.NETCore.App.Ref\7.0.5\ref\net7.0 + let ref_path = dotnet_install_dir.join("packs/Microsoft.NETCore.App.Ref"); + let ref_version = fs::read_dir(&ref_path) + .unwrap() + .map(|entry| entry.unwrap().file_name()) + .max() + .unwrap() + .to_str() + .unwrap() + .to_owned(); + let ref_path = ref_path.join(ref_version).join("ref"); + // find latest from ref_lib_path + let net_version = fs::read_dir(&ref_path) + .unwrap() + .map(|entry| entry.unwrap().file_name()) + .max() + .unwrap() + .to_str() + .unwrap() + .to_owned(); + let ref_path = ref_path.join(net_version); + + command.arg("exec"); + command.arg(csc); + command.arg("/nostdlib"); + command.arg("/noconfig"); + command.arg("-target:library"); + command.arg(format!("-out:{:?}", object)); + + // add all the ref dlls + for entry in fs::read_dir(&ref_path).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + if path.extension().unwrap() == "dll" { + command.arg(format!("/r:{:?}", path)); + } + } + command.arg(cbindgen_output); + command.arg("-langversion:9.0"); + command.arg("/unsafe+"); + command.arg("/deterministic"); + command.arg("/optimize-"); + command.arg("/debug:portable"); + command.arg("/nologo"); + command.arg("/RuntimeMetadataVersion:v4.0.30319"); + command.arg("/nowarn:0169"); + command.arg("/nowarn:0649"); + command.arg("/nowarn:0282"); + command.arg("/nowarn:1701"); + command.arg("/nowarn:1702"); + command.arg("/utf8output"); + command.arg("/preferreduilang:en-US"); + + if !skip_warning_as_error { + command.arg("/warnaserror"); + } + } } println!("Running: {:?}", command); @@ -209,7 +308,16 @@ fn run_compile_test( cbindgen_outputs: &mut HashSet>, package_version: bool, ) { - let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| { + // When debugging in VSCode, the CARGO_MANIFEST_DIR is not set, fallback to current directory. + // See https://github.com/rust-lang/rust-analyzer/issues/13022 + std::env::current_dir() + .unwrap() + .to_str() + .unwrap() + .to_owned() + }); + let tests_path = Path::new(&crate_dir).join("tests"); let mut generated_file = tests_path.join("expectations"); fs::create_dir_all(&generated_file).unwrap(); @@ -230,6 +338,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::CSharp => ".cs", }; let skip_warning_as_error = name.rfind(SKIP_WARNING_AS_ERROR_SUFFIX).is_some(); @@ -258,6 +367,8 @@ fn run_compile_test( generate_depfile, package_version, ); + // Skip depfile verification on Windows, as the paths are not normalized. + #[cfg(not(windows))] if generate_depfile { let depfile = depfile_contents.expect("No depfile generated"); assert!(!depfile.is_empty()); @@ -330,15 +441,47 @@ fn test_file(name: &'static str, filename: &'static str) { let tmp_dir = tmp_dir.path(); // Run tests in deduplication priority order. C++ compatibility tests are run first, // otherwise we would lose the C++ compiler run if they were deduplicated. - let mut cbindgen_outputs = HashSet::new(); - for cpp_compat in &[true, false] { - for style in &[Style::Type, Style::Tag, Style::Both] { + if env::var_os("CBINDGEN_TEST_SKIP_C").is_none() { + let mut cbindgen_outputs = HashSet::new(); + for cpp_compat in &[true, false] { + for style in &[Style::Type, Style::Tag, Style::Both] { + run_compile_test( + name, + test, + tmp_dir, + Language::C, + *cpp_compat, + Some(*style), + &mut cbindgen_outputs, + false, + ); + } + } + } + + if env::var_os("CBINDGEN_TEST_SKIP_CXX").is_none() { + run_compile_test( + name, + test, + tmp_dir, + Language::Cxx, + /* cpp_compat = */ false, + None, + &mut HashSet::new(), + false, + ); + } + + if env::var_os("CBINDGEN_TEST_SKIP_CYTHON").is_none() { + // `Style::Both` should be identical to `Style::Tag` for Cython. + let mut cbindgen_outputs = HashSet::new(); + for style in &[Style::Type, Style::Tag] { run_compile_test( name, test, tmp_dir, - Language::C, - *cpp_compat, + Language::Cython, + /* cpp_compat = */ false, Some(*style), &mut cbindgen_outputs, false, @@ -346,28 +489,15 @@ fn test_file(name: &'static str, filename: &'static str) { } } - run_compile_test( - name, - test, - tmp_dir, - Language::Cxx, - /* cpp_compat = */ false, - None, - &mut HashSet::new(), - false, - ); - - // `Style::Both` should be identical to `Style::Tag` for Cython. - let mut cbindgen_outputs = HashSet::new(); - for style in &[Style::Type, Style::Tag] { + if env::var_os("CBINDGEN_TEST_SKIP_CSHARP").is_none() { run_compile_test( name, test, tmp_dir, - Language::Cython, + Language::CSharp, /* cpp_compat = */ false, - Some(*style), - &mut cbindgen_outputs, + None, + &mut HashSet::new(), false, ); }