diff --git a/hs-bindgen/fixtures/globals.rs b/hs-bindgen/fixtures/globals.rs new file mode 100644 index 000000000..aee98e4af --- /dev/null +++ b/hs-bindgen/fixtures/globals.rs @@ -0,0 +1,158 @@ +/* automatically generated by rust-bindgen 0.71.1 */ + +unsafe extern "C" { + /// Global variables + pub static mut simpleGlobal: ::std::os::raw::c_int; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct config { + pub x: ::std::os::raw::c_int, + pub y: ::std::os::raw::c_int, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of config"][::std::mem::size_of::() - 8usize]; + ["Alignment of config"][::std::mem::align_of::() - 4usize]; + ["Offset of field: config::x"][::std::mem::offset_of!(config, x) - 0usize]; + ["Offset of field: config::y"][::std::mem::offset_of!(config, y) - 4usize]; +}; +unsafe extern "C" { + pub static mut compoundGlobal1: config; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct inline_struct { + pub x: ::std::os::raw::c_int, + pub y: ::std::os::raw::c_int, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of inline_struct"][::std::mem::size_of::() - 8usize]; + ["Alignment of inline_struct"][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: inline_struct::x", + ][::std::mem::offset_of!(inline_struct, x) - 0usize]; + [ + "Offset of field: inline_struct::y", + ][::std::mem::offset_of!(inline_struct, y) - 4usize]; +}; +unsafe extern "C" { + pub static mut compoundGlobal2: inline_struct; +} +/** Non-extern non-static global variables + + These kinds of variables need to be treated with care, to avoid duplicate + symbols, but do exist in the wild. + + We test with various kinds of initializers as we must explicitly ignore them + in our parser. The list here roughly follows the definition of `CXCursor` + [1], starting at `CXCursor_IntegerLiteral`; see also definition of 'varDecl' + in `HsBindgen.Frontend.Pass.Parse.Decl`. + + [1]: https://clang.llvm.org/doxygen/group__CINDEX.html#gaaccc432245b4cd9f2d470913f9ef0013*/ +pub const nesInteger: ::std::os::raw::c_int = 1; +pub const nesFloating: f32 = 1.2000000476837158; +pub const nesString1: &[u8; 3] = b"hi\0"; +pub const nesString2: &[u8; 3] = b"hi\0"; +pub const nesCharacter: ::std::os::raw::c_char = 97; +pub const nesParen: ::std::os::raw::c_int = 1; +pub const nesUnary: ::std::os::raw::c_int = 5; +pub const nesBinary: ::std::os::raw::c_int = 3; +pub const nesConditional: ::std::os::raw::c_int = 2; +pub const nesCast: f32 = 1.0; +unsafe extern "C" { + pub static mut nesCompound: *mut ::std::os::raw::c_int; +} +unsafe extern "C" { + pub static mut nesInitList: [u8; 4usize]; +} +pub const nesBool: bool = true; +unsafe extern "C" { + /** Additional examples of global variables, abstracted from real examples + + The `streamBinary`/`streamBinary_len` example comes from [1], and is an + example of a non-extern non-static global (indeed, the header does not even + use @pragma once@ or similar). + + [1]: https://github.com/analogdevicesinc/no-OS/blob/855c4b3c34f2297865e448661ba4fcc0931bf430/drivers/rf-transceiver/talise/firmware/talise_stream_binary.h#L322-L325*/ + pub static mut streamBinary: [u8; 4096usize]; +} +pub const streamBinary_len: u32 = 4096; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct version_t { + pub major: u8, + pub minor: u16, + pub patch: u8, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of version_t"][::std::mem::size_of::() - 6usize]; + ["Alignment of version_t"][::std::mem::align_of::() - 2usize]; + [ + "Offset of field: version_t::major", + ][::std::mem::offset_of!(version_t, major) - 0usize]; + [ + "Offset of field: version_t::minor", + ][::std::mem::offset_of!(version_t, minor) - 2usize]; + [ + "Offset of field: version_t::patch", + ][::std::mem::offset_of!(version_t, patch) - 4usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct struct1_t { + pub x: u16, + pub y: bool, + pub version: version_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of struct1_t"][::std::mem::size_of::() - 10usize]; + ["Alignment of struct1_t"][::std::mem::align_of::() - 2usize]; + ["Offset of field: struct1_t::x"][::std::mem::offset_of!(struct1_t, x) - 0usize]; + ["Offset of field: struct1_t::y"][::std::mem::offset_of!(struct1_t, y) - 2usize]; + [ + "Offset of field: struct1_t::version", + ][::std::mem::offset_of!(struct1_t, version) - 4usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct struct2_t { + pub field1: struct1_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of struct2_t"][::std::mem::size_of::() - 10usize]; + ["Alignment of struct2_t"][::std::mem::align_of::() - 2usize]; + [ + "Offset of field: struct2_t::field1", + ][::std::mem::offset_of!(struct2_t, field1) - 0usize]; +}; +unsafe extern "C" { + pub static mut some_global_struct: struct2_t; +} +/** Error cases + + NOTE: We test the error for thread_local separately (as it requires C23).*/ +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _bindgen_ty_1 { + pub x: ::std::os::raw::c_int, + pub y: ::std::os::raw::c_int, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of _bindgen_ty_1"][::std::mem::size_of::<_bindgen_ty_1>() - 8usize]; + ["Alignment of _bindgen_ty_1"][::std::mem::align_of::<_bindgen_ty_1>() - 4usize]; + [ + "Offset of field: _bindgen_ty_1::x", + ][::std::mem::offset_of!(_bindgen_ty_1, x) - 0usize]; + [ + "Offset of field: _bindgen_ty_1::y", + ][::std::mem::offset_of!(_bindgen_ty_1, y) - 4usize]; +}; +unsafe extern "C" { + pub static mut unusableAnon: _bindgen_ty_1; +} diff --git a/hs-bindgen/test/hs-bindgen/Test/HsBindgen/Golden.hs b/hs-bindgen/test/hs-bindgen/Test/HsBindgen/Golden.hs index 671ff1281..27e4ed8ea 100644 --- a/hs-bindgen/test/hs-bindgen/Test/HsBindgen/Golden.hs +++ b/hs-bindgen/test/hs-bindgen/Test/HsBindgen/Golden.hs @@ -135,6 +135,37 @@ testCases = [ Just $ Expected () _otherwise -> Nothing + , let declsWithWarnings :: [DeclId] + declsWithWarnings = [ + -- non-extern non-static globals + "nesInteger" + , "nesFloating" + , "nesString1" + , "nesString2" + , "nesCharacter" + , "nesParen" + , "nesUnary" + , "nesBinary" + , "nesConditional" + , "nesCast" + , "nesCompound" + , "nesInitList" + , "nesBool" + , "streamBinary" + , "streamBinary_len" + , "some_global_struct" + -- Other warnings + , "unusableAnon" + ] + in (defaultTest "globals") { + testTracePredicate = customTracePredicate' declsWithWarnings $ \case + TraceFrontend (FrontendParse (PotentialDuplicateGlobal info)) -> + Just $ Expected (C.declId info) + TraceFrontend (FrontendParse (UnexpectedAnonInExtern info)) -> + Just $ Expected (C.declId info) + _otherwise -> + Nothing + } , testTraceCustom "skip_over_long_double" ["fun1", "struct1"] $ \case TraceFrontend (FrontendParse (UnsupportedType info UnsupportedLongDouble)) -> Just $ Expected $ C.declId info @@ -243,41 +274,6 @@ testCases = [ Nothing , testRustBindgen = RustBindgenFail } - , let declsWithWarnings :: [DeclId] - declsWithWarnings = [ - -- non-extern non-static globals - "nesInteger" - , "nesFloating" - , "nesString1" - , "nesString2" - , "nesCharacter" - , "nesParen" - , "nesUnary" - , "nesBinary" - , "nesConditional" - , "nesCast" - , "nesCompound" - , "nesInitList" - , "nesBool" - , "streamBinary" - , "streamBinary_len" - , "some_global_struct" - -- Other warnings - , "unusableAnon" - ] - in (defaultTest "globals") { - -- Getting different output from (the same version of) rust-bindgen - -- for this test on CI than locally. Unsure why, compiled against - -- different llvm version? For now we just disable it. - testRustBindgen = RustBindgenIgnore - , testTracePredicate = customTracePredicate' declsWithWarnings $ \case - TraceFrontend (FrontendParse (PotentialDuplicateGlobal info)) -> - Just $ Expected (C.declId info) - TraceFrontend (FrontendParse (UnexpectedAnonInExtern info)) -> - Just $ Expected (C.declId info) - _otherwise -> - Nothing - } , (defaultTest "macro_strings") { testRustBindgen = RustBindgenFail }