From 1137369977c959356603420d1593a8d9be2d40c4 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Thu, 8 Sep 2022 04:35:39 +0200 Subject: [PATCH] Support complex macros. --- .github/workflows/bindgen.yml | 8 +- Cargo.lock | 123 +++--- bindgen-integration/Cargo.toml | 1 + bindgen-integration/build.rs | 19 +- .../tests/expectations/tests/func_wrapper.rs | 16 + .../expectations/tests/infinite-macro.rs | 4 +- .../expectations/tests/jsval_layout_opaque.rs | 2 +- .../tests/jsval_layout_opaque_1_0.rs | 2 +- .../expectations/tests/macro-expr-basic.rs | 2 +- .../tests/expectations/tests/macro-redef.rs | 4 +- .../tests/expectations/tests/macro_const.rs | 18 +- .../expectations/tests/macro_const_1_0.rs | 18 +- .../expectations/tests/memory_barrier.rs | 15 + bindgen-tests/tests/headers/func_wrapper.h | 4 + bindgen-tests/tests/headers/layout_array.h | 2 +- .../tests/headers/layout_array_too_long.h | 2 +- .../tests/headers/layout_large_align_field.h | 2 +- bindgen-tests/tests/headers/layout_mbuf.h | 2 +- bindgen-tests/tests/headers/layout_mbuf_1_0.h | 3 +- bindgen-tests/tests/headers/memory_barrier.h | 1 + .../tests/quickchecking/src/fuzzers.rs | 2 +- bindgen/Cargo.toml | 5 +- bindgen/callbacks.rs | 11 +- bindgen/clang.rs | 50 +-- bindgen/codegen/helpers.rs | 63 ++- bindgen/codegen/macro_def.rs | 162 ++++++++ bindgen/codegen/mod.rs | 173 +++----- bindgen/ir/context.rs | 223 +++++++++-- bindgen/ir/function.rs | 17 +- bindgen/ir/int.rs | 4 +- bindgen/ir/item.rs | 29 +- bindgen/ir/item_kind.rs | 6 + bindgen/ir/macro_def.rs | 128 ++++++ bindgen/ir/mod.rs | 1 + bindgen/ir/ty.rs | 3 +- bindgen/ir/var.rs | 374 +++++------------- 36 files changed, 927 insertions(+), 572 deletions(-) create mode 100644 bindgen-tests/tests/expectations/tests/func_wrapper.rs create mode 100644 bindgen-tests/tests/expectations/tests/memory_barrier.rs create mode 100644 bindgen-tests/tests/headers/func_wrapper.h create mode 100644 bindgen-tests/tests/headers/memory_barrier.h create mode 100644 bindgen/codegen/macro_def.rs create mode 100644 bindgen/ir/macro_def.rs diff --git a/.github/workflows/bindgen.yml b/.github/workflows/bindgen.yml index 8faa4988ec..58365bb512 100644 --- a/.github/workflows/bindgen.yml +++ b/.github/workflows/bindgen.yml @@ -2,11 +2,11 @@ name: bindgen on: push: - branches: - - main + # branches: + # - master pull_request: - branches: - - main + # branches: + # - master jobs: rustfmt-clippy: diff --git a/Cargo.lock b/Cargo.lock index e435ba0b43..dfcab415a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,8 +16,8 @@ name = "bindgen" version = "0.64.0" dependencies = [ "bitflags", - "cexpr", "clang-sys", + "cmacro", "lazy_static", "lazycell", "log", @@ -75,18 +75,9 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -96,9 +87,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clang-sys" -version = "1.4.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" dependencies = [ "glob", "libc", @@ -107,9 +98,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.4" +version = "4.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" +checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" dependencies = [ "bitflags", "clap_derive", @@ -122,9 +113,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.1.0" +version = "4.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" dependencies = [ "heck", "proc-macro-error", @@ -135,13 +126,26 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" +checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" dependencies = [ "os_str_bytes", ] +[[package]] +name = "cmacro" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3591eea3551b2c8fc9dc565a37e7147872800426da26f5b7d624ab252fbf4dc0" +dependencies = [ + "nom", + "proc-macro2", + "quote", + "syn", + "unicode-ident", +] + [[package]] name = "diff" version = "0.1.13" @@ -200,9 +204,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -226,18 +230,15 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "humantime" @@ -256,24 +257,24 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] name = "is-terminal" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" dependencies = [ "hermit-abi", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -290,9 +291,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "libloading" @@ -361,9 +362,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "os_str_bytes" @@ -403,9 +404,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] @@ -492,16 +493,16 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.36.7" +version = "0.36.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" +checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -518,9 +519,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -537,7 +538,7 @@ dependencies = [ "fastrand", "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -560,9 +561,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "version_check" @@ -633,6 +634,30 @@ dependencies = [ "windows_x86_64_msvc", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.1" diff --git a/bindgen-integration/Cargo.toml b/bindgen-integration/Cargo.toml index 60f0426a76..190f37ec52 100644 --- a/bindgen-integration/Cargo.toml +++ b/bindgen-integration/Cargo.toml @@ -2,6 +2,7 @@ name = "bindgen-integration" description = "A package to test various bindgen features" version = "0.1.0" +edition = "2018" authors = ["Emilio Cobos Álvarez "] publish = false build = "build.rs" diff --git a/bindgen-integration/build.rs b/bindgen-integration/build.rs index 3cc0edb99b..f349a40779 100644 --- a/bindgen-integration/build.rs +++ b/bindgen-integration/build.rs @@ -4,7 +4,7 @@ extern crate cc; use bindgen::callbacks::{ DeriveInfo, IntKind, MacroParsingBehavior, ParseCallbacks, }; -use bindgen::{Builder, CargoCallbacks, EnumVariation}; +use bindgen::{Builder, EnumVariation}; use std::collections::HashSet; use std::env; use std::path::PathBuf; @@ -60,36 +60,41 @@ impl ParseCallbacks for MacroCallback { } } - fn func_macro(&self, name: &str, value: &[&[u8]]) { + fn func_macro(&self, name: &str, args: &[&str], value: &[&[u8]]) { match name { "TESTMACRO_NONFUNCTIONAL" => { panic!("func_macro was called for a non-functional macro"); } - "TESTMACRO_FUNCTIONAL_NONEMPTY(TESTMACRO_INTEGER)" => { + "TESTMACRO_FUNCTIONAL_NONEMPTY" => { // Spaces are inserted into the right-hand side of a functional // macro during reconstruction from the tokenization. This might // change in the future, but it is safe by the definition of a // token in C, whereas leaving the spaces out could change // tokenization. + assert_eq!(args, &["TESTMACRO_INTEGER"]); assert_eq!(value, &[b"-" as &[u8], b"TESTMACRO_INTEGER"]); *self.seen_funcs.lock().unwrap() += 1; } - "TESTMACRO_FUNCTIONAL_EMPTY(TESTMACRO_INTEGER)" => { + "TESTMACRO_FUNCTIONAL_EMPTY" => { + assert_eq!(args, &["TESTMACRO_INTEGER"]); assert_eq!(value, &[] as &[&[u8]]); *self.seen_funcs.lock().unwrap() += 1; } - "TESTMACRO_FUNCTIONAL_TOKENIZED(a,b,c,d,e)" => { + "TESTMACRO_FUNCTIONAL_TOKENIZED" => { + assert_eq!(args, &["a", "b", "c", "d", "e"]); assert_eq!( value, &[b"a" as &[u8], b"/", b"b", b"c", b"d", b"##", b"e"] ); *self.seen_funcs.lock().unwrap() += 1; } - "TESTMACRO_FUNCTIONAL_SPLIT(a,b)" => { + "TESTMACRO_FUNCTIONAL_SPLIT" => { + assert_eq!(args, &["a", "b"]); assert_eq!(value, &[b"b", b",", b"a"]); *self.seen_funcs.lock().unwrap() += 1; } - "TESTMACRO_STRING_FUNC_NON_UTF8(x)" => { + "TESTMACRO_STRING_FUNC_NON_UTF8" => { + assert_eq!(args, &["x"]); assert_eq!( value, &[b"(" as &[u8], b"x", b"\"\xff\xff\"", b")"] diff --git a/bindgen-tests/tests/expectations/tests/func_wrapper.rs b/bindgen-tests/tests/expectations/tests/func_wrapper.rs new file mode 100644 index 0000000000..0a7cd6693b --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/func_wrapper.rs @@ -0,0 +1,16 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +pub const Y: u32 = 7; +#[allow(non_snake_case, unused_mut)] +#[inline(always)] +pub unsafe extern "C" fn wrapper_func(mut x: ::std::os::raw::c_int) { + func(x, 7); +} +extern "C" { + pub fn func(arg1: ::std::os::raw::c_int, arg2: ::std::os::raw::c_int); +} diff --git a/bindgen-tests/tests/expectations/tests/infinite-macro.rs b/bindgen-tests/tests/expectations/tests/infinite-macro.rs index 081a0c296b..22a5137739 100644 --- a/bindgen-tests/tests/expectations/tests/infinite-macro.rs +++ b/bindgen-tests/tests/expectations/tests/infinite-macro.rs @@ -5,5 +5,5 @@ non_upper_case_globals )] -pub const INFINITY: f64 = ::std::f64::INFINITY; -pub const NAN: f64 = ::std::f64::NAN; +pub const INFINITY: f32 = ::std::f32::INFINITY; +pub const NAN: f32 = ::std::f32::NAN; diff --git a/bindgen-tests/tests/expectations/tests/jsval_layout_opaque.rs b/bindgen-tests/tests/expectations/tests/jsval_layout_opaque.rs index a812e90560..2315fb53db 100644 --- a/bindgen-tests/tests/expectations/tests/jsval_layout_opaque.rs +++ b/bindgen-tests/tests/expectations/tests/jsval_layout_opaque.rs @@ -93,7 +93,7 @@ where } pub const JSVAL_TAG_SHIFT: u32 = 47; pub const JSVAL_PAYLOAD_MASK: u64 = 140737488355327; -pub const JSVAL_TAG_MASK: i64 = -140737488355328; +pub const JSVAL_TAG_MASK: u64 = 18446603336221196288; #[repr(u8)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum JSValueType { diff --git a/bindgen-tests/tests/expectations/tests/jsval_layout_opaque_1_0.rs b/bindgen-tests/tests/expectations/tests/jsval_layout_opaque_1_0.rs index b439499355..25afb65b4d 100644 --- a/bindgen-tests/tests/expectations/tests/jsval_layout_opaque_1_0.rs +++ b/bindgen-tests/tests/expectations/tests/jsval_layout_opaque_1_0.rs @@ -136,7 +136,7 @@ impl ::std::cmp::PartialEq for __BindgenUnionField { impl ::std::cmp::Eq for __BindgenUnionField {} pub const JSVAL_TAG_SHIFT: u32 = 47; pub const JSVAL_PAYLOAD_MASK: u64 = 140737488355327; -pub const JSVAL_TAG_MASK: i64 = -140737488355328; +pub const JSVAL_TAG_MASK: u64 = 18446603336221196288; #[repr(u8)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum JSValueType { diff --git a/bindgen-tests/tests/expectations/tests/macro-expr-basic.rs b/bindgen-tests/tests/expectations/tests/macro-expr-basic.rs index 4eeb310c8f..5fde4eebaf 100644 --- a/bindgen-tests/tests/expectations/tests/macro-expr-basic.rs +++ b/bindgen-tests/tests/expectations/tests/macro-expr-basic.rs @@ -8,7 +8,7 @@ pub const FOO: u32 = 1; pub const BAR: u32 = 4; pub const BAZ: u32 = 5; -pub const MIN: i64 = -9223372036854775808; +pub const MIN: u64 = 9223372036854775808; pub const BARR: u32 = 1; pub const BAZZ: u32 = 7; pub const I_RAN_OUT_OF_DUMB_NAMES: u32 = 7; diff --git a/bindgen-tests/tests/expectations/tests/macro-redef.rs b/bindgen-tests/tests/expectations/tests/macro-redef.rs index ff5654f47b..a9b14c41cb 100644 --- a/bindgen-tests/tests/expectations/tests/macro-redef.rs +++ b/bindgen-tests/tests/expectations/tests/macro-redef.rs @@ -5,6 +5,6 @@ non_upper_case_globals )] -pub const FOO: u32 = 4; -pub const BAR: u32 = 5; +pub const BAR: u32 = 6; +pub const FOO: u32 = 5; pub const BAZ: u32 = 6; diff --git a/bindgen-tests/tests/expectations/tests/macro_const.rs b/bindgen-tests/tests/expectations/tests/macro_const.rs index de423a2af0..7ff2a79806 100644 --- a/bindgen-tests/tests/expectations/tests/macro_const.rs +++ b/bindgen-tests/tests/expectations/tests/macro_const.rs @@ -5,10 +5,16 @@ non_upper_case_globals )] -pub const foo: &[u8; 4usize] = b"bar\0"; -pub const CHAR: u8 = 98u8; -pub const CHARR: u8 = 0u8; -pub const FLOAT: f64 = 5.09; -pub const FLOAT_EXPR: f64 = 0.005; +pub const foo: *const ::std::os::raw::c_char = { + const BYTES: [u8; 4] = *b"bar\0"; + BYTES.as_ptr() as *const ::std::os::raw::c_char +}; +pub const CHAR: ::std::os::raw::c_char = 'b' as ::std::os::raw::c_char; +pub const CHARR: ::std::os::raw::c_char = '\0' as ::std::os::raw::c_char; +pub const FLOAT: f32 = 5.09; +pub const FLOAT_EXPR: f32 = 0.005; pub const LONG: u32 = 3; -pub const INVALID_UTF8: [u8; 5usize] = [240u8, 40u8, 140u8, 40u8, 0u8]; +pub const INVALID_UTF8: *const ::std::os::raw::c_char = { + const BYTES: [u8; 5] = *b"\xF0(\x8C(\0"; + BYTES.as_ptr() as *const ::std::os::raw::c_char +}; diff --git a/bindgen-tests/tests/expectations/tests/macro_const_1_0.rs b/bindgen-tests/tests/expectations/tests/macro_const_1_0.rs index e135661121..7ff2a79806 100644 --- a/bindgen-tests/tests/expectations/tests/macro_const_1_0.rs +++ b/bindgen-tests/tests/expectations/tests/macro_const_1_0.rs @@ -5,10 +5,16 @@ non_upper_case_globals )] -pub const foo: &'static [u8; 4usize] = b"bar\0"; -pub const CHAR: u8 = 98u8; -pub const CHARR: u8 = 0u8; -pub const FLOAT: f64 = 5.09; -pub const FLOAT_EXPR: f64 = 0.005; +pub const foo: *const ::std::os::raw::c_char = { + const BYTES: [u8; 4] = *b"bar\0"; + BYTES.as_ptr() as *const ::std::os::raw::c_char +}; +pub const CHAR: ::std::os::raw::c_char = 'b' as ::std::os::raw::c_char; +pub const CHARR: ::std::os::raw::c_char = '\0' as ::std::os::raw::c_char; +pub const FLOAT: f32 = 5.09; +pub const FLOAT_EXPR: f32 = 0.005; pub const LONG: u32 = 3; -pub const INVALID_UTF8: [u8; 5usize] = [240u8, 40u8, 140u8, 40u8, 0u8]; +pub const INVALID_UTF8: *const ::std::os::raw::c_char = { + const BYTES: [u8; 5] = *b"\xF0(\x8C(\0"; + BYTES.as_ptr() as *const ::std::os::raw::c_char +}; diff --git a/bindgen-tests/tests/expectations/tests/memory_barrier.rs b/bindgen-tests/tests/expectations/tests/memory_barrier.rs new file mode 100644 index 0000000000..c8f2eefe03 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/memory_barrier.rs @@ -0,0 +1,15 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[doc(hidden)] +#[macro_export] +macro_rules! __cmacro__MEMORY_BARRIER { + () => { + ::std::arch::asm!(clobber_abi("C"), options(preserves_flags),) + }; +} +pub use __cmacro__MEMORY_BARRIER as MEMORY_BARRIER; diff --git a/bindgen-tests/tests/headers/func_wrapper.h b/bindgen-tests/tests/headers/func_wrapper.h new file mode 100644 index 0000000000..46cb9ab41e --- /dev/null +++ b/bindgen-tests/tests/headers/func_wrapper.h @@ -0,0 +1,4 @@ +void func(int, int); + +#define Y 7 +#define wrapper_func(x) func(x, Y) diff --git a/bindgen-tests/tests/headers/layout_array.h b/bindgen-tests/tests/headers/layout_array.h index e6a57f7ca4..37b3cc7269 100644 --- a/bindgen-tests/tests/headers/layout_array.h +++ b/bindgen-tests/tests/headers/layout_array.h @@ -1,4 +1,4 @@ -// bindgen-flags: --with-derive-hash --with-derive-partialeq --with-derive-eq --impl-partialeq --rust-target 1.40 +// bindgen-flags: --with-derive-hash --with-derive-partialeq --with-derive-eq --impl-partialeq --rust-target 1.40 --blocklist-function "__rte_aligned" typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; diff --git a/bindgen-tests/tests/headers/layout_array_too_long.h b/bindgen-tests/tests/headers/layout_array_too_long.h index 53e4d8bed4..93d19896a6 100644 --- a/bindgen-tests/tests/headers/layout_array_too_long.h +++ b/bindgen-tests/tests/headers/layout_array_too_long.h @@ -1,4 +1,4 @@ -// bindgen-flags: --with-derive-hash --with-derive-partialeq --with-derive-eq --impl-partialeq --rustified-enum ".*" --rust-target 1.40 +// bindgen-flags: --with-derive-hash --with-derive-partialeq --with-derive-eq --impl-partialeq --rustified-enum ".*" --rust-target 1.40 --blocklist-function "__rte_aligned" typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; diff --git a/bindgen-tests/tests/headers/layout_large_align_field.h b/bindgen-tests/tests/headers/layout_large_align_field.h index 63aea90bd9..7f0abdc7de 100644 --- a/bindgen-tests/tests/headers/layout_large_align_field.h +++ b/bindgen-tests/tests/headers/layout_large_align_field.h @@ -1,4 +1,4 @@ -// bindgen-flags: --rustified-enum ".*" --rust-target 1.40 +// bindgen-flags: --rustified-enum ".*" --rust-target 1.40 --blocklist-function "__rte_aligned" typedef unsigned char uint8_t; typedef unsigned short uint16_t; diff --git a/bindgen-tests/tests/headers/layout_mbuf.h b/bindgen-tests/tests/headers/layout_mbuf.h index 0e342f45e0..5185525286 100644 --- a/bindgen-tests/tests/headers/layout_mbuf.h +++ b/bindgen-tests/tests/headers/layout_mbuf.h @@ -1,4 +1,4 @@ -// bindgen-flags: --with-derive-hash --with-derive-partialeq --with-derive-eq +// bindgen-flags: --with-derive-hash --with-derive-partialeq --with-derive-eq --blocklist-function "__rte_aligned" #define RTE_CACHE_LINE_MIN_SIZE 64 /**< Minimum Cache line size. */ diff --git a/bindgen-tests/tests/headers/layout_mbuf_1_0.h b/bindgen-tests/tests/headers/layout_mbuf_1_0.h index 2854de5038..1593922264 100644 --- a/bindgen-tests/tests/headers/layout_mbuf_1_0.h +++ b/bindgen-tests/tests/headers/layout_mbuf_1_0.h @@ -1,5 +1,4 @@ -// bindgen-flags: --rust-target 1.0 --with-derive-hash --with-derive-partialeq --with-derive-eq - +// bindgen-flags: --rust-target 1.0 --with-derive-hash --with-derive-partialeq --with-derive-eq --blocklist-function "__rte_aligned" #define RTE_CACHE_LINE_MIN_SIZE 64 /**< Minimum Cache line size. */ diff --git a/bindgen-tests/tests/headers/memory_barrier.h b/bindgen-tests/tests/headers/memory_barrier.h new file mode 100644 index 0000000000..ff900e9211 --- /dev/null +++ b/bindgen-tests/tests/headers/memory_barrier.h @@ -0,0 +1 @@ +#define MEMORY_BARRIER() __asm__ volatile ( "" ::: "memory" ) diff --git a/bindgen-tests/tests/quickchecking/src/fuzzers.rs b/bindgen-tests/tests/quickchecking/src/fuzzers.rs index 003e9bf3f1..569ed6e09b 100644 --- a/bindgen-tests/tests/quickchecking/src/fuzzers.rs +++ b/bindgen-tests/tests/quickchecking/src/fuzzers.rs @@ -231,7 +231,7 @@ impl fmt::Display for DeclarationListC { } } -/// A qucickcheck trait for describing how BaseTypeC types can be +/// A quickcheck trait for describing how BaseTypeC types can be /// randomly generated and shrunk. impl Arbitrary for BaseTypeC { fn arbitrary(g: &mut Gen) -> BaseTypeC { diff --git a/bindgen/Cargo.toml b/bindgen/Cargo.toml index c2e8543bb0..8d378ce533 100644 --- a/bindgen/Cargo.toml +++ b/bindgen/Cargo.toml @@ -26,7 +26,7 @@ path = "lib.rs" [dependencies] bitflags = "1.0.3" -cexpr = "0.6" +cmacro = "0.1.2" clang-sys = { version = "1", features = ["clang_6_0"] } lazycell = "1" lazy_static = "1" @@ -56,3 +56,6 @@ testing_only_docs = [] testing_only_extra_assertions = [] testing_only_libclang_9 = [] testing_only_libclang_5 = [] + +# [patch."https://github.com/reitermarkus/cmacro-rs"] +# cmacro = { path = "../cmacro-rs" } diff --git a/bindgen/callbacks.rs b/bindgen/callbacks.rs index dc20e2581e..89f8b5e319 100644 --- a/bindgen/callbacks.rs +++ b/bindgen/callbacks.rs @@ -62,7 +62,16 @@ pub trait ParseCallbacks: fmt::Debug { /// The first parameter represents the name and argument list (including the /// parentheses) of the function-like macro. The second parameter represents /// the expansion of the macro as a sequence of tokens. - fn func_macro(&self, _name: &str, _value: &[&[u8]]) {} + fn func_macro(&self, _name: &str, _args: &[&str], _value: &[&[u8]]) {} + + /// Specify the type of a macro argument. + /// + /// This is needed if you want to generate a function instead of a macro. + /// If all argument types and the return type of a macro can be inferred, + /// a function will be generated instead of a macro. + fn func_macro_arg_type(&self, _name: &str, _arg: &str) -> Option { + None + } /// This function should return whether, given an enum variant /// name, and value, this enum variant will forcibly be a constant. diff --git a/bindgen/clang.rs b/bindgen/clang.rs index 32a25449bc..d758ff75d9 100644 --- a/bindgen/clang.rs +++ b/bindgen/clang.rs @@ -5,11 +5,12 @@ use crate::ir::context::BindgenContext; use clang_sys::*; +use std::convert::TryFrom; use std::ffi::{CStr, CString}; use std::fmt; use std::hash::Hash; use std::hash::Hasher; -use std::os::raw::{c_char, c_int, c_longlong, c_uint, c_ulong, c_ulonglong}; +use std::os::raw::{c_char, c_int, c_longlong, c_uint, c_ulong}; use std::{mem, ptr, slice}; /// Type representing a clang attribute. @@ -854,14 +855,6 @@ impl Cursor { RawTokens::new(self) } - /// Gets the tokens that correspond to that cursor as `cexpr` tokens. - pub fn cexpr_tokens(self) -> Vec { - self.tokens() - .iter() - .filter_map(|token| token.as_cexpr_token()) - .collect() - } - /// Obtain the real path name of a cursor of InclusionDirective kind. /// /// Returns None if the cursor does not include a file, otherwise the file's full name @@ -952,30 +945,6 @@ impl ClangToken { }; c_str.to_bytes() } - - /// Converts a ClangToken to a `cexpr` token if possible. - pub fn as_cexpr_token(&self) -> Option { - use cexpr::token; - - let kind = match self.kind { - CXToken_Punctuation => token::Kind::Punctuation, - CXToken_Literal => token::Kind::Literal, - CXToken_Identifier => token::Kind::Identifier, - CXToken_Keyword => token::Kind::Keyword, - // NB: cexpr is not too happy about comments inside - // expressions, so we strip them down here. - CXToken_Comment => return None, - _ => { - warn!("Found unexpected token kind: {:?}", self); - return None; - } - }; - - Some(token::Token { - kind, - raw: self.spelling().to_vec().into_boxed_slice(), - }) - } } impl Drop for ClangToken { @@ -2148,29 +2117,22 @@ impl EvalResult { } /// Try to get back the result as an integer. - pub fn as_int(&self) -> Option { + pub fn as_int(&self) -> Option { if self.kind() != CXEval_Int { return None; } if unsafe { clang_EvalResult_isUnsignedInt(self.x) } != 0 { let value = unsafe { clang_EvalResult_getAsUnsigned(self.x) }; - if value > i64::max_value() as c_ulonglong { + if value as u128 > i128::MAX as u128 { return None; } - return Some(value as i64); + return Some(value as i128); } let value = unsafe { clang_EvalResult_getAsLongLong(self.x) }; - if value > i64::max_value() as c_longlong { - return None; - } - if value < i64::min_value() as c_longlong { - return None; - } - #[allow(clippy::unnecessary_cast)] - Some(value as i64) + i128::try_from(value).ok() } /// Evaluates the expression as a literal string, that may or may not be diff --git a/bindgen/codegen/helpers.rs b/bindgen/codegen/helpers.rs index 088c7f9363..7f66935ac6 100644 --- a/bindgen/codegen/helpers.rs +++ b/bindgen/codegen/helpers.rs @@ -134,7 +134,7 @@ pub mod ast_ty { use crate::ir::context::BindgenContext; use crate::ir::function::FunctionSig; use crate::ir::layout::Layout; - use crate::ir::ty::FloatKind; + use crate::ir::ty::{FloatKind, IntKind}; use proc_macro2::{self, TokenStream}; use std::str::FromStr; @@ -184,6 +184,63 @@ pub mod ast_ty { } } + pub fn int_kind_rust_type( + ctx: &BindgenContext, + ik: IntKind, + layout: Option, + ) -> TokenStream { + match ik { + IntKind::Bool => quote! { bool }, + IntKind::Char { .. } => raw_type(ctx, "c_char"), + IntKind::SChar => raw_type(ctx, "c_schar"), + IntKind::UChar => raw_type(ctx, "c_uchar"), + IntKind::Short => raw_type(ctx, "c_short"), + IntKind::UShort => raw_type(ctx, "c_ushort"), + IntKind::Int => raw_type(ctx, "c_int"), + IntKind::UInt => raw_type(ctx, "c_uint"), + IntKind::Long => raw_type(ctx, "c_long"), + IntKind::ULong => raw_type(ctx, "c_ulong"), + IntKind::LongLong => raw_type(ctx, "c_longlong"), + IntKind::ULongLong => raw_type(ctx, "c_ulonglong"), + IntKind::WChar => { + let layout = + layout.expect("Couldn't compute wchar_t's layout?"); + let ty = Layout::known_type_for_size(ctx, layout.size) + .expect("Non-representable wchar_t?"); + let ident = ctx.rust_ident_raw(ty); + quote! { #ident } + } + + IntKind::I8 => quote! { i8 }, + IntKind::U8 => quote! { u8 }, + IntKind::I16 => quote! { i16 }, + IntKind::U16 => quote! { u16 }, + IntKind::I32 => quote! { i32 }, + IntKind::U32 => quote! { u32 }, + IntKind::I64 => quote! { i64 }, + IntKind::U64 => quote! { u64 }, + IntKind::Custom { name, .. } => { + proc_macro2::TokenStream::from_str(name).unwrap() + } + IntKind::U128 => { + if ctx.options().rust_features.i128_and_u128 { + quote! { u128 } + } else { + // Best effort thing, but wrong alignment + // unfortunately. + quote! { [u64; 2] } + } + } + IntKind::I128 => { + if ctx.options().rust_features.i128_and_u128 { + quote! { i128 } + } else { + quote! { [u64; 2] } + } + } + } + } + pub fn float_kind_rust_type( ctx: &BindgenContext, fk: FloatKind, @@ -229,9 +286,9 @@ pub mod ast_ty { } } - pub fn int_expr(val: i64) -> TokenStream { + pub fn int_expr(val: i128) -> TokenStream { // Don't use quote! { #val } because that adds the type suffix. - let val = proc_macro2::Literal::i64_unsuffixed(val); + let val = proc_macro2::Literal::i128_unsuffixed(val); quote!(#val) } diff --git a/bindgen/codegen/macro_def.rs b/bindgen/codegen/macro_def.rs new file mode 100644 index 0000000000..51082eea9d --- /dev/null +++ b/bindgen/codegen/macro_def.rs @@ -0,0 +1,162 @@ +use cmacro::{Expr, Lit, LitInt}; + +use crate::callbacks::IntKind; +use crate::ir::context::BindgenContext; +use crate::ir::item::{Item, ItemCanonicalName}; +use crate::ir::macro_def::MacroDef; + +use super::{ + attributes, helpers::ast_ty, CodeGenerator, CodegenResult, + MacroTypeVariation, +}; + +fn default_macro_constant_type(ctx: &BindgenContext, value: i128) -> IntKind { + if value < 0 || + ctx.options().default_macro_constant_type == + MacroTypeVariation::Signed + { + if value < i64::MIN as i128 || value > i64::MAX as i128 { + IntKind::I128 + } else if value < i32::MIN as i128 || value > i32::MAX as i128 { + IntKind::I64 + } else if !ctx.options().fit_macro_constants || + value < i16::MIN as i128 || + value > i16::MAX as i128 + { + IntKind::I32 + } else if value < i8::MIN as i128 || value > i8::MAX as i128 { + IntKind::I16 + } else { + IntKind::I8 + } + } else if value > u32::MAX as i128 { + IntKind::U64 + } else if !ctx.options().fit_macro_constants || value > u16::MAX as i128 { + IntKind::U32 + } else if value > u8::MAX as i128 { + IntKind::U16 + } else { + IntKind::U8 + } +} + +impl CodeGenerator for MacroDef { + type Extra = Item; + type Return = (); + + fn codegen( + &self, + ctx: &BindgenContext, + result: &mut CodegenResult<'_>, + item: &Item, + ) { + debug!("::codegen: item = {:?}", item); + debug_assert!(item.is_enabled_for_codegen(ctx)); + + let canonical_name = item.canonical_name(ctx); + + let mut attrs = vec![]; + if let Some(comment) = item.comment(ctx) { + attrs.push(attributes::doc(comment)); + } + + match self { + Self::Fn(name) => { + if result.seen_function(&canonical_name) { + return; + } + result.saw_function(&canonical_name); + + let mut fn_macro = ctx.fn_macro(name).unwrap().clone(); + let generated_value = match fn_macro.generate(ctx) { + Ok(value) => value, + Err(err) => { + warn!( + "Cannot generate function macro: {:?}\n{:?}", + err, fn_macro + ); + return; + } + }; + + result.push(quote! { + #(#attrs)* + #generated_value + }); + } + Self::Var(name) => { + if result.seen_var(&canonical_name) { + return; + } + result.saw_var(&canonical_name); + + let canonical_ident = ctx.rust_ident(&canonical_name); + + let mut var_macro = ctx.var_macro(name).unwrap().clone(); + let (generated_value, generated_type) = + match var_macro.generate(ctx) { + Ok((value, ty)) => (value, ty), + Err(err) => { + warn!( + "Cannot generate variable macro: {:?}\n{:?}", + err, var_macro + ); + return; + } + }; + + match var_macro.value { + Expr::Literal(Lit::Int(LitInt { value, .. })) => { + let int_kind = ctx + .options() + .last_callback(|c| { + if value >= i64::MIN as i128 && + value <= i64::MAX as i128 + { + let value = value as i64; + c.int_macro(self.name(), value) + } else { + None + } + }) + .unwrap_or_else(|| { + default_macro_constant_type(ctx, value) + }); + + let ty = + ast_ty::int_kind_rust_type(ctx, int_kind, None); + let value = if int_kind.is_signed() { + ast_ty::int_expr(value) + } else { + ast_ty::uint_expr(value as _) + }; + + result.push(quote! { + #(#attrs)* + pub const #canonical_ident : #ty = #value; + }); + } + expr => { + if let Expr::Literal(Lit::String(ref s)) = expr { + for callbacks in &ctx.options().parse_callbacks { + callbacks.str_macro(self.name(), s.as_bytes()); + } + } + + if let Some(ty) = generated_type { + result.push(quote! { + #(#attrs)* + pub const #canonical_ident : #ty = #generated_value; + }); + } else { + warn!( + "Unhandled variable macro: {} = {:?}", + var_macro.name, expr + ); + } + } + } + } + } + } +} diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index b5a7b27cf4..67ae8201d0 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -12,6 +12,8 @@ pub mod struct_layout; pub(crate) mod bitfield_unit; #[cfg(all(test, target_endian = "little"))] mod bitfield_unit_tests; +mod macro_def; +pub use macro_def::*; use self::dyngen::DynamicItems; use self::helpers::attributes; @@ -526,6 +528,9 @@ impl CodeGenerator for Item { ItemKind::Var(ref var) => { var.codegen(ctx, result, self); } + ItemKind::MacroDef(ref macro_def) => { + macro_def.codegen(ctx, result, self); + } ItemKind::Type(ref ty) => { ty.codegen(ctx, result, self); } @@ -660,7 +665,8 @@ impl CodeGenerator for Var { attrs.push(attributes::doc(comment)); } - let ty = self.ty().to_rust_ty_or_opaque(ctx, &()); + let var_ty = self.ty(); + let ty = var_ty.to_rust_ty_or_opaque(ctx, &()); if let Some(val) = self.val() { match *val { @@ -671,8 +677,7 @@ impl CodeGenerator for Var { }); } VarType::Int(val) => { - let int_kind = self - .ty() + let int_kind = var_ty .into_resolver() .through_type_aliases() .through_type_refs() @@ -2088,7 +2093,7 @@ impl CodeGenerator for CompInfo { if let Some(explicit) = explicit_align { // Ensure that the struct has the correct alignment even in // presence of alignas. - let explicit = helpers::ast_ty::int_expr(explicit as i64); + let explicit = helpers::ast_ty::int_expr(explicit as i128); attributes.push(quote! { #[repr(align(#explicit))] }); @@ -2813,7 +2818,7 @@ impl<'a> EnumBuilder<'a> { helpers::ast_ty::uint_expr(v as u64) } EnumVariantValue::Boolean(v) => quote!(#v), - EnumVariantValue::Signed(v) => helpers::ast_ty::int_expr(v), + EnumVariantValue::Signed(v) => helpers::ast_ty::int_expr(v as i128), EnumVariantValue::Unsigned(v) => helpers::ast_ty::uint_expr(v), }; @@ -3481,7 +3486,7 @@ impl std::str::FromStr for NonCopyUnionStyle { /// Implementors of this trait should provide the `try_get_layout` method to /// fallibly get this thing's layout, which the provided `try_to_opaque` trait /// method will use to convert the `Layout` into an opaque blob Rust type. -trait TryToOpaque { +pub trait TryToOpaque { type Extra; /// Get the layout for this thing, if one is available. @@ -3512,7 +3517,7 @@ trait TryToOpaque { /// /// Don't implement this directly. Instead implement `TryToOpaque`, and then /// leverage the blanket impl for this trait. -trait ToOpaque: TryToOpaque { +pub trait ToOpaque: TryToOpaque { fn get_layout(&self, ctx: &BindgenContext, extra: &Self::Extra) -> Layout { self.try_get_layout(ctx, extra) .unwrap_or_else(|_| Layout::for_size(ctx, 1)) @@ -3537,7 +3542,7 @@ impl ToOpaque for T where T: TryToOpaque {} /// const-value generic parameters) then the impl should return an `Err`. It /// should *not* attempt to return an opaque blob with the correct size and /// alignment. That is the responsibility of the `TryToOpaque` trait. -trait TryToRustTy { +pub trait TryToRustTy { type Extra; fn try_to_rust_ty( @@ -3552,7 +3557,7 @@ trait TryToRustTy { /// /// Don't implement this directly. Instead implement `TryToRustTy` and /// `TryToOpaque`, and then leverage the blanket impl for this trait below. -trait TryToRustTyOrOpaque: TryToRustTy + TryToOpaque { +pub trait TryToRustTyOrOpaque: TryToRustTy + TryToOpaque { type Extra; fn try_to_rust_ty_or_opaque( @@ -3600,7 +3605,7 @@ where /// `ToRustTyOrOpaque`. The further out we push error recovery, the more likely /// we are to get a usable `Layout` even if we can't generate an equivalent Rust /// type for a C++ construct. -trait ToRustTyOrOpaque: TryToRustTy + ToOpaque { +pub trait ToRustTyOrOpaque: TryToRustTy + ToOpaque { type Extra; fn to_rust_ty_or_opaque( @@ -3708,57 +3713,7 @@ impl TryToRustTy for Type { // c_void is enough? TypeKind::NullPtr => Ok(c_void(ctx).to_ptr(true)), TypeKind::Int(ik) => { - match ik { - IntKind::Bool => Ok(quote! { bool }), - IntKind::Char { .. } => Ok(raw_type(ctx, "c_char")), - IntKind::SChar => Ok(raw_type(ctx, "c_schar")), - IntKind::UChar => Ok(raw_type(ctx, "c_uchar")), - IntKind::Short => Ok(raw_type(ctx, "c_short")), - IntKind::UShort => Ok(raw_type(ctx, "c_ushort")), - IntKind::Int => Ok(raw_type(ctx, "c_int")), - IntKind::UInt => Ok(raw_type(ctx, "c_uint")), - IntKind::Long => Ok(raw_type(ctx, "c_long")), - IntKind::ULong => Ok(raw_type(ctx, "c_ulong")), - IntKind::LongLong => Ok(raw_type(ctx, "c_longlong")), - IntKind::ULongLong => Ok(raw_type(ctx, "c_ulonglong")), - IntKind::WChar => { - let layout = self - .layout(ctx) - .expect("Couldn't compute wchar_t's layout?"); - let ty = Layout::known_type_for_size(ctx, layout.size) - .expect("Non-representable wchar_t?"); - let ident = ctx.rust_ident_raw(ty); - Ok(quote! { #ident }) - } - - IntKind::I8 => Ok(quote! { i8 }), - IntKind::U8 => Ok(quote! { u8 }), - IntKind::I16 => Ok(quote! { i16 }), - IntKind::U16 => Ok(quote! { u16 }), - IntKind::I32 => Ok(quote! { i32 }), - IntKind::U32 => Ok(quote! { u32 }), - IntKind::I64 => Ok(quote! { i64 }), - IntKind::U64 => Ok(quote! { u64 }), - IntKind::Custom { name, .. } => { - Ok(proc_macro2::TokenStream::from_str(name).unwrap()) - } - IntKind::U128 => { - Ok(if ctx.options().rust_features.i128_and_u128 { - quote! { u128 } - } else { - // Best effort thing, but wrong alignment - // unfortunately. - quote! { [u64; 2] } - }) - } - IntKind::I128 => { - Ok(if ctx.options().rust_features.i128_and_u128 { - quote! { i128 } - } else { - quote! { [u64; 2] } - }) - } - } + Ok(int_kind_rust_type(ctx, ik, self.layout(ctx))) } TypeKind::Float(fk) => { Ok(float_kind_rust_type(ctx, fk, self.layout(ctx))) @@ -4072,12 +4027,7 @@ impl CodeGenerator for Function { result.saw_function(seen_symbol_name); } - let signature_item = ctx.resolve_item(self.signature()); - let signature = signature_item.kind().expect_type().canonical_type(ctx); - let signature = match *signature.kind() { - TypeKind::Function(ref sig) => sig, - _ => panic!("Signature kind is not a Function: {:?}", signature), - }; + let signature = ctx.resolve_sig(self.signature()); let args = utils::fnsig_arguments(ctx, signature); let ret = utils::fnsig_return_ty(ctx, signature); @@ -4540,6 +4490,7 @@ pub mod utils { use super::serialize::CSerialize; use super::{error, CodegenError, CodegenResult, ToRustTyOrOpaque}; use crate::ir::context::BindgenContext; + use crate::ir::context::TypeId; use crate::ir::function::{Abi, ClangAbi, FunctionSig}; use crate::ir::item::{Item, ItemCanonicalPath}; use crate::ir::ty::TypeKind; @@ -4910,7 +4861,7 @@ pub mod utils { }) } - fn fnsig_return_ty_internal( + pub(crate) fn fnsig_return_ty_internal( ctx: &BindgenContext, sig: &FunctionSig, include_arrow: bool, @@ -4956,54 +4907,60 @@ pub mod utils { fnsig_return_ty_internal(ctx, sig, /* include_arrow = */ true) } + pub fn fnsig_argument_type( + ctx: &BindgenContext, + ty: &TypeId, + ) -> proc_macro2::TokenStream { + use super::ToPtr; + + let arg_item = ctx.resolve_item(ty); + let arg_ty = arg_item.kind().expect_type(); + + // From the C90 standard[1]: + // + // A declaration of a parameter as "array of type" shall be + // adjusted to "qualified pointer to type", where the type + // qualifiers (if any) are those specified within the [ and ] of + // the array type derivation. + // + // [1]: http://c0x.coding-guidelines.com/6.7.5.3.html + match *arg_ty.canonical_type(ctx).kind() { + TypeKind::Array(t, _) => { + let stream = if ctx.options().array_pointers_in_arguments { + arg_ty.to_rust_ty_or_opaque(ctx, arg_item) + } else { + t.to_rust_ty_or_opaque(ctx, &()) + }; + stream.to_ptr(ctx.resolve_type(t).is_const()) + } + TypeKind::Pointer(inner) => { + let inner = ctx.resolve_item(inner); + let inner_ty = inner.expect_type(); + if let TypeKind::ObjCInterface(ref interface) = + *inner_ty.canonical_type(ctx).kind() + { + let name = ctx.rust_ident(interface.name()); + quote! { + #name + } + } else { + arg_item.to_rust_ty_or_opaque(ctx, &()) + } + } + _ => arg_item.to_rust_ty_or_opaque(ctx, &()), + } + } + pub fn fnsig_arguments( ctx: &BindgenContext, sig: &FunctionSig, ) -> Vec { - use super::ToPtr; - let mut unnamed_arguments = 0; let mut args = sig .argument_types() .iter() - .map(|&(ref name, ty)| { - let arg_item = ctx.resolve_item(ty); - let arg_ty = arg_item.kind().expect_type(); - - // From the C90 standard[1]: - // - // A declaration of a parameter as "array of type" shall be - // adjusted to "qualified pointer to type", where the type - // qualifiers (if any) are those specified within the [ and ] of - // the array type derivation. - // - // [1]: http://c0x.coding-guidelines.com/6.7.5.3.html - let arg_ty = match *arg_ty.canonical_type(ctx).kind() { - TypeKind::Array(t, _) => { - let stream = - if ctx.options().array_pointers_in_arguments { - arg_ty.to_rust_ty_or_opaque(ctx, arg_item) - } else { - t.to_rust_ty_or_opaque(ctx, &()) - }; - stream.to_ptr(ctx.resolve_type(t).is_const()) - } - TypeKind::Pointer(inner) => { - let inner = ctx.resolve_item(inner); - let inner_ty = inner.expect_type(); - if let TypeKind::ObjCInterface(ref interface) = - *inner_ty.canonical_type(ctx).kind() - { - let name = ctx.rust_ident(interface.name()); - quote! { - #name - } - } else { - arg_item.to_rust_ty_or_opaque(ctx, &()) - } - } - _ => arg_item.to_rust_ty_or_opaque(ctx, &()), - }; + .map(|(name, ty)| { + let arg_ty = fnsig_argument_type(ctx, ty); let arg_name = match *name { Some(ref name) => ctx.rust_mangle(name).into_owned(), diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs index b693a7047e..16b17584a5 100644 --- a/bindgen/ir/context.rs +++ b/bindgen/ir/context.rs @@ -11,19 +11,21 @@ use super::derive::{ CanDerive, CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveEq, CanDeriveHash, CanDeriveOrd, CanDerivePartialEq, CanDerivePartialOrd, }; -use super::function::Function; +use super::function::{Function, FunctionSig, Linkage}; use super::int::IntKind; use super::item::{IsOpaque, Item, ItemAncestors, ItemSet}; use super::item_kind::ItemKind; +use super::macro_def::MacroDef; use super::module::{Module, ModuleKind}; use super::template::{TemplateInstantiation, TemplateParameters}; use super::traversal::{self, Edge, ItemTraversal}; use super::ty::{FloatKind, Type, TypeKind}; use crate::clang::{self, Cursor}; +use crate::codegen::utils::type_from_named; +use crate::codegen::utils::{fnsig_argument_type, fnsig_return_ty_internal}; use crate::codegen::CodegenError; use crate::BindgenOptions; use crate::{Entry, HashMap, HashSet}; -use cexpr; use clang_sys; use proc_macro2::{Ident, Span, TokenStream}; use quote::ToTokens; @@ -32,6 +34,7 @@ use std::cell::{Cell, RefCell}; use std::collections::{BTreeSet, HashMap as StdHashMap}; use std::iter::IntoIterator; use std::mem; +use std::str::FromStr; /// An identifier for some kind of IR item. #[derive(Debug, Copy, Clone, Eq, PartialOrd, Ord, Hash)] @@ -347,12 +350,31 @@ pub struct BindgenContext { /// potentially break that assumption. currently_parsed_types: Vec, - /// A map with all the already parsed macro names. This is done to avoid - /// hard errors while parsing duplicated macros, as well to allow macro - /// expression parsing. + /// A map with all the already parsed macro names. /// - /// This needs to be an std::HashMap because the cexpr API requires it. - parsed_macros: StdHashMap, cexpr::expr::EvalResult>, + /// This is needed to handle redefined macros so they are not + /// generated multiple times. + parsed_macros: StdHashMap, + + /// A map with all defined functions. + /// + /// This is needed for inferring types for function calls in macros. + functions: StdHashMap, + + /// A map with all defined variable-like macros. + /// + /// This is needed to expand nested macros. + variable_macros: StdHashMap, + + /// A map with all defined function-like macros. + /// + /// This is needed to expand nested macros. + function_macros: StdHashMap, + + /// A map with all defined types. + /// + /// This is needed to resolve types used in macros. + type_names: StdHashMap, /// A set of all the included filenames. deps: BTreeSet, @@ -570,6 +592,10 @@ If you encounter an error missing from this list, please file an issue or a PR!" semantic_parents: Default::default(), currently_parsed_types: vec![], parsed_macros: Default::default(), + functions: Default::default(), + variable_macros: Default::default(), + function_macros: Default::default(), + type_names: Default::default(), replacements: Default::default(), collected_typerefs: false, in_codegen: false, @@ -672,6 +698,17 @@ If you encounter an error missing from this list, please file an issue or a PR!" "Adding a type without declaration?" ); + let macro_name = if let ItemKind::MacroDef(ref m) = item.kind() { + if let Some(id) = self.parsed_macros.get(m.name()) { + warn!("Macro {} redefined.", m.name()); + self.items[id.0] = None; + } + + Some(m.name().to_owned()) + } else { + None + }; + let id = item.id(); let is_type = item.kind().is_type(); let is_unnamed = is_type && item.expect_type().name().is_none(); @@ -692,6 +729,10 @@ If you encounter an error missing from this list, please file an issue or a PR!" "should not have already associated an item with the given id" ); + if let Some(macro_name) = macro_name { + self.parsed_macros.insert(macro_name, id); + } + // Unnamed items can have an USR, but they can't be referenced from // other sites explicitly and the USR can match if the unnamed items are // nested, so don't bother tracking them. @@ -1444,6 +1485,14 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.resolve_item(func_id).kind().expect_function() } + pub(crate) fn resolve_sig(&self, sig_id: TypeId) -> &FunctionSig { + let signature = self.resolve_type(sig_id).canonical_type(self); + match *signature.kind() { + TypeKind::Function(ref sig) => sig, + _ => panic!("Signature kind is not a Function: {:?}", signature), + } + } + /// Resolve the given `ItemId` as a type, or `None` if there is no item with /// the given id. /// @@ -1909,13 +1958,13 @@ If you encounter an error missing from this list, please file an issue or a PR!" /// Needed to handle const methods in C++, wrapping the type . pub fn build_const_wrapper( &mut self, - with_id: ItemId, wrapped_id: TypeId, parent_id: Option, ty: &clang::Type, ) -> TypeId { + let id = self.next_item_id(); self.build_wrapper( - with_id, wrapped_id, parent_id, ty, /* is_const = */ true, + id, wrapped_id, parent_id, ty, /* is_const = */ true, ) } @@ -1931,7 +1980,8 @@ If you encounter an error missing from this list, please file an issue or a PR!" let layout = ty.fallible_layout(self).ok(); let location = ty.declaration().location(); let type_kind = TypeKind::ResolvedTypeRef(wrapped_id); - let ty = Type::new(Some(spelling), layout, type_kind, is_const); + + let ty = Type::new(Some(spelling.clone()), layout, type_kind, is_const); let item = Item::new( with_id, None, @@ -1941,7 +1991,42 @@ If you encounter an error missing from this list, please file an issue or a PR!" Some(location), ); self.add_builtin_item(item); - with_id.as_type_id_unchecked() + let type_id = with_id.as_type_id_unchecked(); + + self.type_names + .insert(spelling.replace("const ", ""), type_id); + + type_id + } + + pub(crate) fn add_function(&mut self, name: &str, sig: Function) { + self.functions.insert(name.to_owned(), sig); + } + + pub(crate) fn function(&self, name: &str) -> Option<&Function> { + self.functions.get(name) + } + + pub(crate) fn add_fn_macro(&mut self, fn_macro: cmacro::FnMacro) { + self.function_macros + .insert(fn_macro.name.to_owned(), fn_macro); + } + + pub(crate) fn fn_macro(&self, name: &str) -> Option<&cmacro::FnMacro> { + self.function_macros.get(name) + } + + pub(crate) fn add_var_macro(&mut self, var_macro: cmacro::VarMacro) { + self.variable_macros + .insert(var_macro.name.to_owned(), var_macro); + } + + pub(crate) fn var_macro(&self, name: &str) -> Option<&cmacro::VarMacro> { + self.variable_macros.get(name) + } + + pub(crate) fn type_by_name(&self, name: &str) -> Option<&TypeId> { + self.type_names.get(name) } /// Returns the next item id to be used for an item. @@ -2000,6 +2085,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" let is_const = ty.is_const(); let layout = ty.fallible_layout(self).ok(); let location = ty.declaration().location(); + let ty = Type::new(Some(spelling), layout, type_kind, is_const); let id = self.next_item_id(); let item = Item::new( @@ -2019,28 +2105,6 @@ If you encounter an error missing from this list, please file an issue or a PR!" &self.translation_unit } - /// Have we parsed the macro named `macro_name` already? - pub fn parsed_macro(&self, macro_name: &[u8]) -> bool { - self.parsed_macros.contains_key(macro_name) - } - - /// Get the currently parsed macros. - pub fn parsed_macros( - &self, - ) -> &StdHashMap, cexpr::expr::EvalResult> { - debug_assert!(!self.in_codegen_phase()); - &self.parsed_macros - } - - /// Mark the macro named `macro_name` as parsed. - pub fn note_parsed_macro( - &mut self, - id: Vec, - value: cexpr::expr::EvalResult, - ) { - self.parsed_macros.insert(id, value); - } - /// Are we in the codegen phase? pub fn in_codegen_phase(&self) -> bool { self.in_codegen @@ -2350,6 +2414,15 @@ If you encounter an error missing from this list, please file an issue or a PR!" ItemKind::Var(_) => { self.options().allowlisted_vars.matches(&name) } + ItemKind::MacroDef(ref macro_def) => match macro_def { + MacroDef::Fn(_) => self + .options() + .allowlisted_functions + .matches(&name), + MacroDef::Var(_) => { + self.options().allowlisted_vars.matches(&name) + } + }, ItemKind::Type(ref ty) => { if self.options().allowlisted_types.matches(&name) { return true; @@ -2802,6 +2875,90 @@ If you encounter an error missing from this list, please file an issue or a PR!" } } +impl cmacro::CodegenContext for BindgenContext { + fn ffi_prefix(&self) -> Option { + Some(match self.options().ctypes_prefix { + Some(ref prefix) => { + let prefix = TokenStream::from_str(prefix.as_str()).unwrap(); + quote! { #prefix:: } + } + None => quote! { ::std::os::raw:: }, + }) + } + + fn trait_prefix(&self) -> Option { + let trait_prefix = self.trait_prefix(); + Some(quote! { ::#trait_prefix:: }) + } + + fn macro_arg_ty(&self, name: &str, arg: &str) -> Option { + self.options() + .last_callback(|c| c.func_macro_arg_type(name, arg)) + } + + fn resolve_ty(&self, ty: &str) -> Option { + if let Some(ty) = type_from_named(self, ty) { + return Some(ty.to_string()); + } + + if let Some(ty) = self.type_by_name(ty) { + let ty = ty + .into_resolver() + .through_type_aliases() + .through_type_refs() + .resolve(self) + .expect_type(); + + if let Some(ty) = ty.name().map(|n| n.to_owned()) { + return Some(ty); + } + } + + None + } + + fn function(&self, name: &str) -> Option<(Vec, String)> { + let allowlisted_functions = &self.options().allowlisted_functions; + if !allowlisted_functions.is_empty() && + !allowlisted_functions.matches(name) + { + return None; + } + + if let Some(f) = self.function(name) { + // Cannot call functions with internal linkage. + if f.linkage() == Linkage::Internal { + return None; + } + + let sig = self.resolve_sig(f.signature()); + + let arg_types = sig + .argument_types() + .iter() + .map(|(_, ty)| fnsig_argument_type(self, ty).to_string()) + .collect(); + + let ret_type = fnsig_return_ty_internal( + self, sig, /* include_arrow = */ false, + ) + .to_string(); + + return Some((arg_types, ret_type)); + } + + None + } + + fn function_macro(&self, name: &str) -> Option<&cmacro::FnMacro> { + self.function_macros.get(name) + } + + fn variable_macro(&self, name: &str) -> Option<&cmacro::VarMacro> { + self.variable_macros.get(name) + } +} + /// A builder struct for configuring item resolution options. #[derive(Debug, Copy, Clone)] pub struct ItemResolver { diff --git a/bindgen/ir/function.rs b/bindgen/ir/function.rs index baa2c36ca4..4fcf73b0c0 100644 --- a/bindgen/ir/function.rs +++ b/bindgen/ir/function.rs @@ -63,7 +63,7 @@ impl FunctionKind { } /// The style of linkage -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Linkage { /// Externally visible and can be linked against External, @@ -75,7 +75,7 @@ pub enum Linkage { /// /// The argument names vector must be the same length as the ones in the /// signature. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Function { /// The name of this function. name: String, @@ -261,7 +261,7 @@ impl quote::ToTokens for ClangAbi { } /// A function signature. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct FunctionSig { /// The return type of the function. return_type: TypeId, @@ -515,13 +515,7 @@ impl FunctionSig { let class = class.as_type_id_unchecked(); let class = if is_const { - let const_class_id = ctx.next_item_id(); - ctx.build_const_wrapper( - const_class_id, - class, - None, - &parent.cur_type(), - ) + ctx.build_const_wrapper(class, None, &parent.cur_type()) } else { class }; @@ -664,6 +658,7 @@ impl ClangSubItemParser for Function { }; debug!("Function::parse({:?}, {:?})", cursor, cursor.cur_type()); + let visibility = cursor.visibility(); if visibility != CXVisibility_Default { return Err(ParseError::Continue); @@ -739,6 +734,8 @@ impl ClangSubItemParser for Function { let function = Self::new(name.clone(), mangled_name, sig, comment, kind, linkage); + context.add_function(&name, function.clone()); + Ok(ParseResult::New(function, Some(cursor))) } } diff --git a/bindgen/ir/int.rs b/bindgen/ir/int.rs index 22838e897c..8f7c879ea9 100644 --- a/bindgen/ir/int.rs +++ b/bindgen/ir/int.rs @@ -12,7 +12,7 @@ pub enum IntKind { /// An `unsigned char`. UChar, - /// An `wchar_t`. + /// A `wchar_t`. WChar, /// A platform-dependent `char` type, with the signedness support. @@ -121,7 +121,7 @@ impl IntKind { } /// Whether this type's signedness matches the value. - pub fn signedness_matches(&self, val: i64) -> bool { + pub fn signedness_matches(&self, val: i128) -> bool { val >= 0 || self.is_signed() } } diff --git a/bindgen/ir/item.rs b/bindgen/ir/item.rs index 40f6f7d927..6c753b9179 100644 --- a/bindgen/ir/item.rs +++ b/bindgen/ir/item.rs @@ -18,6 +18,7 @@ use super::template::{AsTemplateParam, TemplateParameters}; use super::traversal::{EdgeKind, Trace, Tracer}; use super::ty::{Type, TypeKind}; use crate::clang; +use crate::ir::{macro_def::MacroDef, var::Var}; use crate::parse::{ClangSubItemParser, ParseError, ParseResult}; use clang_sys; use lazycell::LazyCell; @@ -193,6 +194,7 @@ impl AsTemplateParam for ItemKind { ItemKind::Module(..) | ItemKind::Function(..) | ItemKind::Var(..) => None, + ItemKind::MacroDef(..) => None, } } } @@ -296,6 +298,7 @@ impl Trace for Item { ItemKind::Var(ref var) => { tracer.visit_kind(var.ty().into(), EdgeKind::VarType); } + ItemKind::MacroDef(_) => {} ItemKind::Module(_) => { // Module -> children edges are "weak", and we do not want to // trace them. If we did, then allowlisting wouldn't work as @@ -662,11 +665,14 @@ impl Item { ctx.options().blocklisted_types.matches(&name) || ctx.is_replaced_type(path, self.id) } - ItemKind::Function(..) => { + ItemKind::Function(..) | + ItemKind::MacroDef(MacroDef::Fn(..)) => { ctx.options().blocklisted_functions.matches(&name) } // TODO: Add constant / namespace blocklisting? - ItemKind::Var(..) | ItemKind::Module(..) => false, + ItemKind::Var(..) | + ItemKind::MacroDef(MacroDef::Var(..)) | + ItemKind::Module(..) => false, } } @@ -675,11 +681,6 @@ impl Item { self.as_type().map_or(false, |ty| ty.is_type_ref()) } - /// Is this item a var type? - pub fn is_var(&self) -> bool { - matches!(*self.kind(), ItemKind::Var(..)) - } - /// Take out item NameOptions pub fn name<'a>(&'a self, ctx: &'a BindgenContext) -> NameOptions<'a> { NameOptions::new(self, ctx) @@ -791,6 +792,7 @@ impl Item { match *self.kind() { ItemKind::Var(ref var) => var.name().to_owned(), + ItemKind::MacroDef(ref var) => var.name().to_owned(), ItemKind::Module(ref module) => { module.name().map(ToOwned::to_owned).unwrap_or_else(|| { format!("_bindgen_mod_{}", self.exposed_id(ctx)) @@ -821,6 +823,7 @@ impl Item { ItemKind::Type(ty) => ty.name().is_none(), ItemKind::Function(_) => false, ItemKind::Var(_) => false, + ItemKind::MacroDef(_) => false, } } @@ -1014,8 +1017,11 @@ impl Item { let cc = &ctx.options().codegen_config; match *self.kind() { ItemKind::Module(..) => true, - ItemKind::Var(_) => cc.vars(), + ItemKind::Var(_) | ItemKind::MacroDef(MacroDef::Var(_)) => { + cc.vars() + } ItemKind::Type(_) => cc.types(), + ItemKind::MacroDef(MacroDef::Fn(_)) => cc.functions(), ItemKind::Function(ref f) => match f.kind() { FunctionKind::Function => cc.functions(), FunctionKind::Method(MethodKind::Constructor) => { @@ -1272,7 +1278,10 @@ impl TemplateParameters for ItemKind { // If we start emitting bindings to explicitly instantiated // functions, then we'll need to check ItemKind::Function for // template params. - ItemKind::Function(_) | ItemKind::Module(_) | ItemKind::Var(_) => { + ItemKind::Function(_) | + ItemKind::Module(_) | + ItemKind::Var(_) | + ItemKind::MacroDef(_) => { vec![] } } @@ -1336,7 +1345,6 @@ impl Item { parent_id: Option, ctx: &mut BindgenContext, ) -> Result { - use crate::ir::var::Var; use clang_sys::*; if !cursor.is_valid() { @@ -1389,6 +1397,7 @@ impl Item { // I guess we can try. try_parse!(Function); try_parse!(Var); + try_parse!(MacroDef); // Types are sort of special, so to avoid parsing template classes // twice, handle them separately. diff --git a/bindgen/ir/item_kind.rs b/bindgen/ir/item_kind.rs index 4a12fef40d..273f170592 100644 --- a/bindgen/ir/item_kind.rs +++ b/bindgen/ir/item_kind.rs @@ -3,6 +3,7 @@ use super::context::BindgenContext; use super::dot::DotAttributes; use super::function::Function; +use super::macro_def::MacroDef; use super::module::Module; use super::ty::Type; use super::var::Var; @@ -23,6 +24,9 @@ pub enum ItemKind { /// A variable declaration, most likely a static. Var(Var), + + /// A macro definition. + MacroDef(MacroDef), } impl ItemKind { @@ -42,6 +46,7 @@ impl ItemKind { ItemKind::Type(..) => "Type", ItemKind::Function(..) => "Function", ItemKind::Var(..) => "Var", + ItemKind::MacroDef(..) => "MacroDef", } } @@ -142,6 +147,7 @@ impl DotAttributes for ItemKind { ItemKind::Type(ref ty) => ty.dot_attributes(ctx, out), ItemKind::Function(ref func) => func.dot_attributes(ctx, out), ItemKind::Var(ref var) => var.dot_attributes(ctx, out), + ItemKind::MacroDef(ref var) => var.dot_attributes(ctx, out), } } } diff --git a/bindgen/ir/macro_def.rs b/bindgen/ir/macro_def.rs new file mode 100644 index 0000000000..a00eacd325 --- /dev/null +++ b/bindgen/ir/macro_def.rs @@ -0,0 +1,128 @@ +//! Intermediate representation of variables. + +use std::io; +use std::str; + +use crate::callbacks::MacroParsingBehavior; +use crate::clang; +use crate::parse::{ClangSubItemParser, ParseError, ParseResult}; + +use super::context::BindgenContext; +use super::dot::DotAttributes; + +/// A `MacroDef` is our intermediate representation of a macro definition. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MacroDef { + /// A function-like macro. + Fn(String), + /// A variable-like macro. + Var(String), +} + +impl MacroDef { + /// Get the macro name. + pub fn name(&self) -> &str { + match self { + Self::Fn(name) => name, + Self::Var(name) => name, + } + } +} + +impl DotAttributes for MacroDef { + fn dot_attributes( + &self, + _ctx: &BindgenContext, + out: &mut W, + ) -> io::Result<()> + where + W: io::Write, + { + writeln!(out, "macrotrue") + } +} + +impl ClangSubItemParser for MacroDef { + fn parse( + cursor: clang::Cursor, + ctx: &mut BindgenContext, + ) -> Result, ParseError> { + use clang_sys::CXCursor_MacroDefinition; + + if cursor.kind() != CXCursor_MacroDefinition { + return Err(ParseError::Continue); + } + + match ctx + .options() + .last_callback(|c| Some(c.will_parse_macro(&cursor.spelling()))) + .unwrap_or_default() + { + MacroParsingBehavior::Ignore => { + return Err(ParseError::Continue); + } + MacroParsingBehavior::Default => (), + } + + let clang_tokens = cursor.tokens().iter().collect::>(); + let args_boundary = if cursor.is_macro_function_like() { + clang_tokens.iter().position(|t| { + t.kind == clang_sys::CXToken_Punctuation && t.spelling() == b")" + }) + } else { + None + }; + let mut cmacro_tokens = clang_tokens + .iter() + .map(|t| t.spelling()) + .collect::>(); + let name = str::from_utf8(cmacro_tokens.remove(0)).unwrap(); + + let args = if let Some(args_boundary) = args_boundary { + let args: Vec<_> = cmacro_tokens + .drain(0..args_boundary) + .skip(1) + .take(args_boundary - 2) + .filter(|&token| token != b",") + .collect(); + Some(args) + } else { + None + }; + + if let Some(args) = args { + if !ctx.options().parse_callbacks.is_empty() { + let args = args + .iter() + .map(|t| str::from_utf8(t).unwrap()) + .collect::>(); + + for callbacks in &ctx.options().parse_callbacks { + callbacks.func_macro(name, &args, &cmacro_tokens); + } + } + + let fn_macro = + cmacro::FnMacro::parse(name.as_bytes(), &args, &cmacro_tokens); + if let Ok(fn_macro) = fn_macro { + ctx.add_fn_macro(fn_macro); + return Ok(ParseResult::New( + MacroDef::Fn(name.to_owned()), + Some(cursor), + )); + } + } else { + let macro_def = + cmacro::VarMacro::parse(name.as_bytes(), &cmacro_tokens); + if let Ok(macro_def) = macro_def { + ctx.add_var_macro(macro_def); + return Ok(ParseResult::New( + MacroDef::Var(name.to_owned()), + Some(cursor), + )); + } + } + + Err(ParseError::Continue) + } +} diff --git a/bindgen/ir/mod.rs b/bindgen/ir/mod.rs index 8f6a2dac88..0ba6e80c3a 100644 --- a/bindgen/ir/mod.rs +++ b/bindgen/ir/mod.rs @@ -16,6 +16,7 @@ pub mod int; pub mod item; pub mod item_kind; pub mod layout; +pub mod macro_def; pub mod module; pub mod objc; pub mod template; diff --git a/bindgen/ir/ty.rs b/bindgen/ir/ty.rs index fef340dee9..4d03370bab 100644 --- a/bindgen/ir/ty.rs +++ b/bindgen/ir/ty.rs @@ -5,7 +5,6 @@ use super::context::{BindgenContext, ItemId, TypeId}; use super::dot::DotAttributes; use super::enum_ty::Enum; use super::function::FunctionSig; -use super::int::IntKind; use super::item::{IsOpaque, Item}; use super::layout::{Layout, Opaque}; use super::objc::ObjCInterface; @@ -18,6 +17,8 @@ use crate::parse::{ParseError, ParseResult}; use std::borrow::Cow; use std::io; +pub use super::int::IntKind; + /// The base representation of a type in bindgen. /// /// A type has an optional name, which if present cannot be empty, a `layout` diff --git a/bindgen/ir/var.rs b/bindgen/ir/var.rs index 903e1ff549..006b43a970 100644 --- a/bindgen/ir/var.rs +++ b/bindgen/ir/var.rs @@ -1,19 +1,15 @@ //! Intermediate representation of variables. -use super::super::codegen::MacroTypeVariation; use super::context::{BindgenContext, TypeId}; use super::dot::DotAttributes; use super::function::cursor_mangling; use super::int::IntKind; use super::item::Item; -use super::ty::{FloatKind, TypeKind}; -use crate::callbacks::{ItemInfo, ItemKind, MacroParsingBehavior}; +use super::ty::TypeKind; +use crate::callbacks::{ItemInfo, ItemKind}; use crate::clang; -use crate::clang::ClangToken; use crate::parse::{ClangSubItemParser, ParseError, ParseResult}; -use cexpr; use std::io; -use std::num::Wrapping; /// The type for a constant variable. #[derive(Debug)] @@ -21,7 +17,7 @@ pub enum VarType { /// A boolean. Bool(bool), /// An integer. - Int(i64), + Int(i128), /// A floating point number. Float(f64), /// A character. @@ -53,9 +49,9 @@ impl Var { ty: TypeId, val: Option, is_const: bool, - ) -> Var { + ) -> Self { assert!(!name.is_empty()); - Var { + Self { name, mangled_name, ty, @@ -115,294 +111,126 @@ impl DotAttributes for Var { } } -fn default_macro_constant_type(ctx: &BindgenContext, value: i64) -> IntKind { - if value < 0 || - ctx.options().default_macro_constant_type == - MacroTypeVariation::Signed - { - if value < i32::min_value() as i64 || value > i32::max_value() as i64 { - IntKind::I64 - } else if !ctx.options().fit_macro_constants || - value < i16::min_value() as i64 || - value > i16::max_value() as i64 - { - IntKind::I32 - } else if value < i8::min_value() as i64 || - value > i8::max_value() as i64 - { - IntKind::I16 - } else { - IntKind::I8 - } - } else if value > u32::max_value() as i64 { - IntKind::U64 - } else if !ctx.options().fit_macro_constants || - value > u16::max_value() as i64 - { - IntKind::U32 - } else if value > u8::max_value() as i64 { - IntKind::U16 - } else { - IntKind::U8 - } -} - -/// Parses tokens from a CXCursor_MacroDefinition pointing into a function-like -/// macro, and calls the func_macro callback. -fn handle_function_macro( - cursor: &clang::Cursor, - callbacks: &dyn crate::callbacks::ParseCallbacks, -) { - let is_closing_paren = |t: &ClangToken| { - // Test cheap token kind before comparing exact spellings. - t.kind == clang_sys::CXToken_Punctuation && t.spelling() == b")" - }; - let tokens: Vec<_> = cursor.tokens().iter().collect(); - if let Some(boundary) = tokens.iter().position(is_closing_paren) { - let mut spelled = tokens.iter().map(ClangToken::spelling); - // Add 1, to convert index to length. - let left = spelled.by_ref().take(boundary + 1); - let left = left.collect::>().concat(); - if let Ok(left) = String::from_utf8(left) { - let right: Vec<_> = spelled.collect(); - callbacks.func_macro(&left, &right); - } - } -} - impl ClangSubItemParser for Var { fn parse( cursor: clang::Cursor, ctx: &mut BindgenContext, ) -> Result, ParseError> { - use cexpr::expr::EvalResult; - use cexpr::literal::CChar; - use clang_sys::*; - match cursor.kind() { - CXCursor_MacroDefinition => { - for callbacks in &ctx.options().parse_callbacks { - match callbacks.will_parse_macro(&cursor.spelling()) { - MacroParsingBehavior::Ignore => { - return Err(ParseError::Continue); - } - MacroParsingBehavior::Default => {} - } - - if cursor.is_macro_function_like() { - handle_function_macro(&cursor, callbacks.as_ref()); - // We handled the macro, skip macro processing below. - return Err(ParseError::Continue); - } - } - - let value = parse_macro(ctx, &cursor); - - let (id, value) = match value { - Some(v) => v, - None => return Err(ParseError::Continue), - }; - - assert!(!id.is_empty(), "Empty macro name?"); - - let previously_defined = ctx.parsed_macro(&id); - - // NB: It's important to "note" the macro even if the result is - // not an integer, otherwise we might loose other kind of - // derived macros. - ctx.note_parsed_macro(id.clone(), value.clone()); + use clang_sys::{ + CXCursor_VarDecl, CXLinkage_External, CXType_Auto, + CXType_ConstantArray, CXType_IncompleteArray, CXType_Unexposed, + }; - if previously_defined { - let name = String::from_utf8(id).unwrap(); - warn!("Duplicated macro definition: {}", name); - return Err(ParseError::Continue); - } - - // NOTE: Unwrapping, here and above, is safe, because the - // identifier of a token comes straight from clang, and we - // enforce utf8 there, so we should have already panicked at - // this point. - let name = String::from_utf8(id).unwrap(); - let (type_kind, val) = match value { - EvalResult::Invalid => return Err(ParseError::Continue), - EvalResult::Float(f) => { - (TypeKind::Float(FloatKind::Double), VarType::Float(f)) - } - EvalResult::Char(c) => { - let c = match c { - CChar::Char(c) => { - assert_eq!(c.len_utf8(), 1); - c as u8 - } - CChar::Raw(c) => { - assert!(c <= ::std::u8::MAX as u64); - c as u8 - } - }; - - (TypeKind::Int(IntKind::U8), VarType::Char(c)) - } - EvalResult::Str(val) => { - let char_ty = Item::builtin_type( - TypeKind::Int(IntKind::U8), - true, - ctx, - ); - for callbacks in &ctx.options().parse_callbacks { - callbacks.str_macro(&name, &val); - } - (TypeKind::Pointer(char_ty), VarType::String(val)) - } - EvalResult::Int(Wrapping(value)) => { - let kind = ctx - .options() - .last_callback(|c| c.int_macro(&name, value)) - .unwrap_or_else(|| { - default_macro_constant_type(ctx, value) - }); - - (TypeKind::Int(kind), VarType::Int(value)) - } - }; - - let ty = Item::builtin_type(type_kind, true, ctx); + if cursor.kind() != CXCursor_VarDecl { + return Err(ParseError::Continue); + } - Ok(ParseResult::New( - Var::new(name, None, ty, Some(val), true), - Some(cursor), - )) + let mut name = cursor.spelling(); + if cursor.linkage() == CXLinkage_External { + if let Some(nm) = ctx.options().last_callback(|callbacks| { + callbacks.generated_name_override(ItemInfo { + name: name.as_str(), + kind: ItemKind::Var, + }) + }) { + name = nm; } - CXCursor_VarDecl => { - let mut name = cursor.spelling(); - if cursor.linkage() == CXLinkage_External { - if let Some(nm) = ctx.options().last_callback(|callbacks| { - callbacks.generated_name_override(ItemInfo { - name: name.as_str(), - kind: ItemKind::Var, - }) - }) { - name = nm; - } - } - // No more changes to name - let name = name; - - if name.is_empty() { - warn!("Empty constant name?"); - return Err(ParseError::Continue); - } - - let ty = cursor.cur_type(); - - // TODO(emilio): do we have to special-case constant arrays in - // some other places? - let is_const = ty.is_const() || - ([CXType_ConstantArray, CXType_IncompleteArray] - .contains(&ty.kind()) && - ty.elem_type() - .map_or(false, |element| element.is_const())); - - let ty = match Item::from_ty(&ty, cursor, None, ctx) { - Ok(ty) => ty, - Err(e) => { - assert!( - matches!(ty.kind(), CXType_Auto | CXType_Unexposed), - "Couldn't resolve constant type, and it \ - wasn't an nondeductible auto type or unexposed \ - type!" - ); - return Err(e); - } - }; - - // Note: Ty might not be totally resolved yet, see - // tests/headers/inner_const.hpp - // - // That's fine because in that case we know it's not a literal. - let canonical_ty = ctx - .safe_resolve_type(ty) - .and_then(|t| t.safe_canonical_type(ctx)); - - let is_integer = canonical_ty.map_or(false, |t| t.is_integer()); - let is_float = canonical_ty.map_or(false, |t| t.is_float()); - - // TODO: We could handle `char` more gracefully. - // TODO: Strings, though the lookup is a bit more hard (we need - // to look at the canonical type of the pointee too, and check - // is char, u8, or i8 I guess). - let value = if is_integer { - let kind = match *canonical_ty.unwrap().kind() { - TypeKind::Int(kind) => kind, - _ => unreachable!(), - }; - - let mut val = cursor.evaluate().and_then(|v| v.as_int()); - if val.is_none() || !kind.signedness_matches(val.unwrap()) { - val = get_integer_literal_from_cursor(&cursor); - } - - val.map(|val| { - if kind == IntKind::Bool { - VarType::Bool(val != 0) - } else { - VarType::Int(val) - } - }) - } else if is_float { - cursor - .evaluate() - .and_then(|v| v.as_double()) - .map(VarType::Float) - } else { - cursor - .evaluate() - .and_then(|v| v.as_literal_string()) - .map(VarType::String) - }; + } + // No more changes to name + let name = name; - let mangling = cursor_mangling(ctx, &cursor); - let var = Var::new(name, mangling, ty, value, is_const); + if name.is_empty() { + warn!("Empty constant name?"); + return Err(ParseError::Continue); + } - Ok(ParseResult::New(var, Some(cursor))) + let ty = cursor.cur_type(); + + // TODO(emilio): do we have to special-case constant arrays in + // some other places? + let is_const = ty.is_const() || + ([CXType_ConstantArray, CXType_IncompleteArray] + .contains(&ty.kind()) && + ty.elem_type() + .map_or(false, |element| element.is_const())); + + let ty = match Item::from_ty(&ty, cursor, None, ctx) { + Ok(ty) => ty, + Err(e) => { + assert!( + matches!(ty.kind(), CXType_Auto | CXType_Unexposed), + "Couldn't resolve constant type, and it \ + wasn't an nondeductible auto type or unexposed \ + type!" + ); + return Err(e); } - _ => { - /* TODO */ - Err(ParseError::Continue) + }; + + // Note: Ty might not be totally resolved yet, see + // tests/headers/inner_const.hpp + // + // That's fine because in that case we know it's not a literal. + let canonical_ty = ctx + .safe_resolve_type(ty) + .and_then(|t| t.safe_canonical_type(ctx)); + + let is_integer = canonical_ty.map_or(false, |t| t.is_integer()); + let is_float = canonical_ty.map_or(false, |t| t.is_float()); + + // TODO: We could handle `char` more gracefully. + // TODO: Strings, though the lookup is a bit more hard (we need + // to look at the canonical type of the pointee too, and check + // is char, u8, or i8 I guess). + let value = if is_integer { + let kind = match *canonical_ty.unwrap().kind() { + TypeKind::Int(kind) => kind, + _ => unreachable!(), + }; + + let mut val = cursor.evaluate().and_then(|v| v.as_int()); + if val.is_none() || !kind.signedness_matches(val.unwrap()) { + val = get_integer_literal_from_cursor(&cursor); } - } - } -} - -/// Try and parse a macro using all the macros parsed until now. -fn parse_macro( - ctx: &BindgenContext, - cursor: &clang::Cursor, -) -> Option<(Vec, cexpr::expr::EvalResult)> { - use cexpr::expr; - let cexpr_tokens = cursor.cexpr_tokens(); + val.map(|val| { + if kind == IntKind::Bool { + VarType::Bool(val != 0) + } else { + VarType::Int(val) + } + }) + } else if is_float { + cursor + .evaluate() + .and_then(|v| v.as_double()) + .map(VarType::Float) + } else { + cursor + .evaluate() + .and_then(|v| v.as_literal_string()) + .map(VarType::String) + }; - let parser = expr::IdentifierParser::new(ctx.parsed_macros()); + let mangling = cursor_mangling(ctx, &cursor); + let var = Self::new(name, mangling, ty, value, is_const); - match parser.macro_definition(&cexpr_tokens) { - Ok((_, (id, val))) => Some((id.into(), val)), - _ => None, + Ok(ParseResult::New(var, Some(cursor))) } } -fn parse_int_literal_tokens(cursor: &clang::Cursor) -> Option { - use cexpr::expr; - use cexpr::expr::EvalResult; - - let cexpr_tokens = cursor.cexpr_tokens(); +fn parse_int_literal_tokens(cursor: &clang::Cursor) -> Option { + let tokens = cursor.tokens().iter().collect::>(); + let cmacro_tokens = tokens.iter().map(|t| t.spelling()).collect::>(); // TODO(emilio): We can try to parse other kinds of literals. - match expr::expr(&cexpr_tokens) { - Ok((_, EvalResult::Int(Wrapping(val)))) => Some(val), + match cmacro::LitInt::parse(&cmacro_tokens) { + Ok((_, cmacro::LitInt { value, .. })) => Some(value), _ => None, } } -fn get_integer_literal_from_cursor(cursor: &clang::Cursor) -> Option { +fn get_integer_literal_from_cursor(cursor: &clang::Cursor) -> Option { use clang_sys::*; let mut value = None; cursor.visit(|c| {