diff options
-rw-r--r-- | .cargo_vcs_info.json | 7 | ||||
-rw-r--r-- | Android.bp | 55 | ||||
-rw-r--r-- | Cargo.lock | 446 | ||||
-rw-r--r-- | Cargo.toml | 80 | ||||
-rw-r--r-- | Cargo.toml.orig | 53 | ||||
-rw-r--r-- | METADATA | 14 | ||||
-rw-r--r-- | OWNERS | 2 | ||||
-rw-r--r-- | README.md | 83 | ||||
-rw-r--r-- | TEST_MAPPING | 14 | ||||
-rw-r--r-- | android/bindgen_cmd/Android.bp | 1 | ||||
-rw-r--r-- | android/bindgen_cmd/src/lib.rs | 6 | ||||
-rw-r--r-- | build.rs | 83 | ||||
-rw-r--r-- | callbacks.rs (renamed from src/callbacks.rs) | 65 | ||||
-rw-r--r-- | cargo2android.json | 4 | ||||
-rw-r--r-- | clang.rs (renamed from src/clang.rs) | 209 | ||||
-rw-r--r--[-rwxr-xr-x] | codegen/bitfield_unit.rs (renamed from src/codegen/bitfield_unit.rs) | 0 | ||||
-rw-r--r-- | codegen/bitfield_unit_tests.rs (renamed from src/codegen/bitfield_unit_tests.rs) | 0 | ||||
-rw-r--r-- | codegen/dyngen.rs (renamed from src/codegen/dyngen.rs) | 43 | ||||
-rw-r--r-- | codegen/error.rs (renamed from src/codegen/error.rs) | 0 | ||||
-rw-r--r-- | codegen/helpers.rs (renamed from src/codegen/helpers.rs) | 26 | ||||
-rw-r--r-- | codegen/impl_debug.rs (renamed from src/codegen/impl_debug.rs) | 0 | ||||
-rw-r--r-- | codegen/impl_partialeq.rs (renamed from src/codegen/impl_partialeq.rs) | 0 | ||||
-rw-r--r-- | codegen/mod.rs (renamed from src/codegen/mod.rs) | 994 | ||||
-rw-r--r-- | codegen/postprocessing/merge_extern_blocks.rs | 66 | ||||
-rw-r--r-- | codegen/postprocessing/mod.rs | 66 | ||||
-rw-r--r-- | codegen/postprocessing/sort_semantically.rs | 38 | ||||
-rw-r--r-- | codegen/serialize.rs | 356 | ||||
-rw-r--r-- | codegen/struct_layout.rs (renamed from src/codegen/struct_layout.rs) | 10 | ||||
-rw-r--r-- | csmith-fuzzing/README.md | 65 | ||||
-rw-r--r-- | deps.rs (renamed from src/deps.rs) | 2 | ||||
-rw-r--r-- | extra_assertions.rs (renamed from src/extra_assertions.rs) | 0 | ||||
-rw-r--r-- | features.rs (renamed from src/features.rs) | 43 | ||||
-rw-r--r-- | ir/analysis/derive.rs (renamed from src/ir/analysis/derive.rs) | 10 | ||||
-rw-r--r-- | ir/analysis/has_destructor.rs (renamed from src/ir/analysis/has_destructor.rs) | 0 | ||||
-rw-r--r-- | ir/analysis/has_float.rs (renamed from src/ir/analysis/has_float.rs) | 0 | ||||
-rw-r--r-- | ir/analysis/has_type_param_in_array.rs (renamed from src/ir/analysis/has_type_param_in_array.rs) | 0 | ||||
-rw-r--r-- | ir/analysis/has_vtable.rs (renamed from src/ir/analysis/has_vtable.rs) | 0 | ||||
-rw-r--r-- | ir/analysis/mod.rs (renamed from src/ir/analysis/mod.rs) | 16 | ||||
-rw-r--r-- | ir/analysis/sizedness.rs (renamed from src/ir/analysis/sizedness.rs) | 0 | ||||
-rw-r--r-- | ir/analysis/template_params.rs (renamed from src/ir/analysis/template_params.rs) | 5 | ||||
-rw-r--r-- | ir/annotations.rs (renamed from src/ir/annotations.rs) | 4 | ||||
-rw-r--r-- | ir/comment.rs (renamed from src/ir/comment.rs) | 51 | ||||
-rw-r--r-- | ir/comp.rs (renamed from src/ir/comp.rs) | 80 | ||||
-rw-r--r-- | ir/context.rs (renamed from src/ir/context.rs) | 350 | ||||
-rw-r--r-- | ir/derive.rs (renamed from src/ir/derive.rs) | 0 | ||||
-rw-r--r-- | ir/dot.rs (renamed from src/ir/dot.rs) | 0 | ||||
-rw-r--r-- | ir/enum_ty.rs (renamed from src/ir/enum_ty.rs) | 33 | ||||
-rw-r--r-- | ir/function.rs (renamed from src/ir/function.rs) | 233 | ||||
-rw-r--r-- | ir/int.rs (renamed from src/ir/int.rs) | 0 | ||||
-rw-r--r-- | ir/item.rs (renamed from src/ir/item.rs) | 45 | ||||
-rw-r--r-- | ir/item_kind.rs (renamed from src/ir/item_kind.rs) | 0 | ||||
-rw-r--r-- | ir/layout.rs (renamed from src/ir/layout.rs) | 4 | ||||
-rw-r--r-- | ir/mod.rs (renamed from src/ir/mod.rs) | 0 | ||||
-rw-r--r-- | ir/module.rs (renamed from src/ir/module.rs) | 0 | ||||
-rw-r--r-- | ir/objc.rs (renamed from src/ir/objc.rs) | 13 | ||||
-rw-r--r-- | ir/template.rs (renamed from src/ir/template.rs) | 1 | ||||
-rw-r--r-- | ir/traversal.rs (renamed from src/ir/traversal.rs) | 58 | ||||
-rw-r--r-- | ir/ty.rs (renamed from src/ir/ty.rs) | 78 | ||||
-rw-r--r-- | ir/var.rs (renamed from src/ir/var.rs) | 132 | ||||
-rw-r--r-- | lib.rs (renamed from src/lib.rs) | 1299 | ||||
-rw-r--r-- | log_stubs.rs (renamed from src/log_stubs.rs) | 0 | ||||
-rw-r--r-- | out/tests.rs | 0 | ||||
-rw-r--r-- | parse.rs | 40 | ||||
-rw-r--r-- | patches/Android.bp.diff | 13 | ||||
-rw-r--r-- | patches/bindgen_cmd.diff | 90 | ||||
-rwxr-xr-x | post_update.sh | 9 | ||||
-rw-r--r-- | regex_set.rs (renamed from src/regex_set.rs) | 17 | ||||
-rw-r--r-- | src/main.rs | 113 | ||||
-rw-r--r-- | src/options.rs | 1000 | ||||
-rw-r--r-- | src/parse.rs | 102 | ||||
-rw-r--r-- | time.rs (renamed from src/time.rs) | 0 |
71 files changed, 3256 insertions, 3371 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index f1c8552..32c33e6 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,6 @@ { "git": { - "sha1": "89032649044d875983a851fff6fbde2d4e2ceaeb" - } -} + "sha1": "ae6817256ac557981906e93a1f866349db85053e" + }, + "path_in_vcs": "bindgen" +}
\ No newline at end of file @@ -35,70 +35,30 @@ genrule { name: "copy_bindgen_build_out", srcs: ["out/*"], cmd: "cp $(in) $(genDir)", - out: [ - "host-target.txt", - "tests.rs", - ], -} - -rust_binary_host { - name: "bindgen", - // has rustc warnings - crate_name: "bindgen", - cargo_env_compat: true, - cargo_pkg_version: "0.59.2", - srcs: [ - "src/main.rs", - ":copy_bindgen_build_out", - ], - edition: "2018", - features: [ - "clap", - "runtime", - "which", - "which-rustfmt", - ], - rustlibs: [ - "libbindgen", - "libbitflags", - "libcexpr", - "libclang_sys", - "libclap", - "liblazy_static", - "liblazycell", - "libpeeking_take_while", - "libproc_macro2", - "libquote", - "libregex", - "librustc_hash", - "libshlex", - "libwhich", - ], - compile_multilib: "first", + out: ["host-target.txt"], } rust_library_host { name: "libbindgen", - // has rustc warnings crate_name: "bindgen", cargo_env_compat: true, - cargo_pkg_version: "0.59.2", + cargo_pkg_version: "0.64.0", srcs: [ - "src/lib.rs", + "lib.rs", ":copy_bindgen_build_out", ], edition: "2018", features: [ - "clap", + "cli", + "experimental", "runtime", "which", "which-rustfmt", ], rustlibs: [ - "libbitflags", + "libbitflags-1.3.2", "libcexpr", "libclang_sys", - "libclap", "liblazy_static", "liblazycell", "libpeeking_take_while", @@ -107,7 +67,10 @@ rust_library_host { "libregex", "librustc_hash", "libshlex", + "libsyn", "libwhich", ], compile_multilib: "first", + product_available: true, + vendor_available: true, } diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index d79a408..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,446 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" -dependencies = [ - "memchr", -] - -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "bindgen" -version = "0.59.2" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "clap", - "diff", - "env_logger", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "tempfile", - "which", -] - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clang-sys" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "853eda514c284c2287f4bf20ae614f8781f40a81d32ecda6e91449304dfe077c" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "2.33.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "diff" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "env_logger" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "getrandom" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "libc" -version = "0.2.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" - -[[package]] -name = "libloading" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "memchr" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" - -[[package]] -name = "minimal-lexical" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c64630dcdd71f1a64c435f54885086a0de5d6a12d104d69b165fb7d5286d677" - -[[package]] -name = "nom" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" -dependencies = [ - "memchr", - "minimal-lexical", - "version_check", -] - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - -[[package]] -name = "ppv-lite86" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" - -[[package]] -name = "proc-macro2" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - -[[package]] -name = "redox_syscall" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "shlex" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a568c8f2cd051a4d283bd6eb0343ac214c1b0f1ac19f93e1175b2dee38c73d" - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "tempfile" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" -dependencies = [ - "cfg-if", - "libc", - "rand", - "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "unicode-width" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - -[[package]] -name = "which" -version = "4.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc009ab82a2afc94b9e467ab4214aee9cad1356cd9191264203d7d72006e00d" -dependencies = [ - "either", - "lazy_static", - "libc", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" @@ -11,28 +11,36 @@ [package] edition = "2018" +rust-version = "1.60.0" name = "bindgen" -version = "0.59.2" -authors = ["Jyun-Yan You <jyyou.tw@gmail.com>", "Emilio Cobos Álvarez <emilio@crisal.io>", "Nick Fitzgerald <fitzgen@gmail.com>", "The Servo project developers"] +version = "0.64.0" +authors = [ + "Jyun-Yan You <jyyou.tw@gmail.com>", + "Emilio Cobos Álvarez <emilio@crisal.io>", + "Nick Fitzgerald <fitzgen@gmail.com>", + "The Servo project developers", +] build = "build.rs" -include = ["LICENSE", "README.md", "Cargo.toml", "build.rs", "src/*.rs", "src/**/*.rs"] description = "Automatically generates Rust FFI bindings to C and C++ libraries." homepage = "https://rust-lang.github.io/rust-bindgen/" documentation = "https://docs.rs/bindgen" -readme = "README.md" -keywords = ["bindings", "ffi", "code-generation"] -categories = ["external-ffi-bindings", "development-tools::ffi"] +readme = "../README.md" +keywords = [ + "bindings", + "ffi", + "code-generation", +] +categories = [ + "external-ffi-bindings", + "development-tools::ffi", +] license = "BSD-3-Clause" repository = "https://github.com/rust-lang/rust-bindgen" [lib] -path = "src/lib.rs" - -[[bin]] name = "bindgen" -path = "src/main.rs" -doc = false -required-features = ["clap"] +path = "./lib.rs" + [dependencies.bitflags] version = "1.0.3" @@ -43,14 +51,6 @@ version = "0.6" version = "1" features = ["clang_6_0"] -[dependencies.clap] -version = "2" -optional = true - -[dependencies.env_logger] -version = "0.9.0" -optional = true - [dependencies.lazy_static] version = "1" @@ -73,8 +73,11 @@ version = "1" default-features = false [dependencies.regex] -version = "1.0" -features = ["std", "unicode"] +version = "1.5" +features = [ + "std", + "unicode", +] default-features = false [dependencies.rustc-hash] @@ -83,33 +86,32 @@ version = "1.0.1" [dependencies.shlex] version = "1" +[dependencies.syn] +version = "1.0.99" +features = [ + "full", + "extra-traits", + "visit-mut", +] + [dependencies.which] version = "4.2.1" optional = true default-features = false -[dev-dependencies.clap] -version = "2" - -[dev-dependencies.diff] -version = "0.1" - -[dev-dependencies.shlex] -version = "1" - -[dev-dependencies.tempfile] -version = "3" [features] -default = ["logging", "clap", "runtime", "which-rustfmt"] -logging = ["env_logger", "log"] +cli = [] +default = [ + "logging", + "runtime", + "which-rustfmt", +] +experimental = [] +logging = ["log"] runtime = ["clang-sys/runtime"] static = ["clang-sys/static"] testing_only_docs = [] testing_only_extra_assertions = [] -testing_only_libclang_3_9 = [] -testing_only_libclang_4 = [] testing_only_libclang_5 = [] testing_only_libclang_9 = [] which-rustfmt = ["which"] -[badges.travis-ci] -repository = "rust-lang/rust-bindgen" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index ba41a22..bc53be5 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -10,72 +10,45 @@ keywords = ["bindings", "ffi", "code-generation"] categories = ["external-ffi-bindings", "development-tools::ffi"] license = "BSD-3-Clause" name = "bindgen" -readme = "README.md" +readme = "../README.md" repository = "https://github.com/rust-lang/rust-bindgen" documentation = "https://docs.rs/bindgen" homepage = "https://rust-lang.github.io/rust-bindgen/" -version = "0.59.2" +version = "0.64.0" edition = "2018" build = "build.rs" - -include = [ - "LICENSE", - "README.md", - "Cargo.toml", - "build.rs", - "src/*.rs", - "src/**/*.rs", -] - -[badges] -travis-ci = { repository = "rust-lang/rust-bindgen" } +# If you change this, also update README.md and msrv in .github/workflows/bindgen.yml +rust-version = "1.60.0" [lib] -path = "src/lib.rs" - -[[bin]] name = "bindgen" -path = "src/main.rs" -doc = false -required-features = ["clap"] - -[dev-dependencies] -diff = "0.1" -clap = "2" -shlex = "1" -tempfile = "3" +path = "./lib.rs" [dependencies] bitflags = "1.0.3" cexpr = "0.6" -# This kinda sucks: https://github.com/rust-lang/cargo/issues/1982 -clap = { version = "2", optional = true } clang-sys = { version = "1", features = ["clang_6_0"] } lazycell = "1" lazy_static = "1" peeking_take_while = "0.1.2" quote = { version = "1", default-features = false } -regex = { version = "1.0", default-features = false , features = [ "std", "unicode"]} +syn = { version = "1.0.99", features = ["full", "extra-traits", "visit-mut"]} +regex = { version = "1.5", default-features = false , features = ["std", "unicode"] } which = { version = "4.2.1", optional = true, default-features = false } shlex = "1" rustc-hash = "1.0.1" proc-macro2 = { version = "1", default-features = false } - -[dependencies.env_logger] -optional = true -version = "0.9.0" - -[dependencies.log] -optional = true -version = "0.4" +log = { version = "0.4", optional = true } [features] -default = ["logging", "clap", "runtime", "which-rustfmt"] -logging = ["env_logger", "log"] +default = ["logging", "runtime", "which-rustfmt"] +logging = ["log"] static = ["clang-sys/static"] runtime = ["clang-sys/runtime"] # Dynamically discover a `rustfmt` binary using the `which` crate which-rustfmt = ["which"] +cli = [] +experimental = [] # These features only exist for CI testing -- don't use them if you're not hacking # on bindgen! @@ -83,5 +56,3 @@ testing_only_docs = [] testing_only_extra_assertions = [] testing_only_libclang_9 = [] testing_only_libclang_5 = [] -testing_only_libclang_4 = [] -testing_only_libclang_3_9 = [] @@ -1,3 +1,7 @@ +# This project was upgraded with external_updater. +# Usage: tools/external_updater/updater.sh update rust/crates/bindgen +# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md + name: "bindgen" description: "Automatically generates Rust FFI bindings to C and C++ libraries." third_party { @@ -7,13 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/bindgen/bindgen-0.59.2.crate" + value: "https://static.crates.io/crates/bindgen/bindgen-0.64.0.crate" } - version: "0.59.2" + version: "0.64.0" license_type: NOTICE last_upgrade_date { - year: 2022 - month: 3 - day: 1 + year: 2023 + month: 2 + day: 16 } } @@ -1 +1 @@ -include platform/prebuilts/rust:/OWNERS +include platform/prebuilts/rust:master:/OWNERS diff --git a/README.md b/README.md deleted file mode 100644 index 7b2dbbc..0000000 --- a/README.md +++ /dev/null @@ -1,83 +0,0 @@ -[![crates.io](https://img.shields.io/crates/v/bindgen.svg)](https://crates.io/crates/bindgen) -[![docs.rs](https://docs.rs/bindgen/badge.svg)](https://docs.rs/bindgen/) - -# `bindgen` - -**`bindgen` automatically generates Rust FFI bindings to C (and some C++) libraries.** - -For example, given the C header `doggo.h`: - -```c -typedef struct Doggo { - int many; - char wow; -} Doggo; - -void eleven_out_of_ten_majestic_af(Doggo* pupper); -``` - -`bindgen` produces Rust FFI code allowing you to call into the `doggo` library's -functions and use its types: - -```rust -/* automatically generated by rust-bindgen 0.99.9 */ - -#[repr(C)] -pub struct Doggo { - pub many: ::std::os::raw::c_int, - pub wow: ::std::os::raw::c_char, -} - -extern "C" { - pub fn eleven_out_of_ten_majestic_af(pupper: *mut Doggo); -} -``` - -## Users Guide - -[📚 Read the `bindgen` users guide here! 📚](https://rust-lang.github.io/rust-bindgen) - -## MSRV - -The minimum supported Rust version is **1.46**. - -No MSRV bump policy has been established yet, so MSRV may increase in any release. - -## API Reference - -[API reference documentation is on docs.rs](https://docs.rs/bindgen) - -## Environment Variables - -In addition to the [library API](https://docs.rs/bindgen) and [executable command-line API][bindgen-cmdline], -`bindgen` can be controlled through environment variables. - -End-users should set these environment variables to modify `bindgen`'s behavior without modifying the source code of direct consumers of `bindgen`. - -- `BINDGEN_EXTRA_CLANG_ARGS`: extra arguments to pass to `clang` - - Arguments are whitespace-separated - - Use shell-style quoting to pass through whitespace - - Examples: - - Specify alternate sysroot: `--sysroot=/path/to/sysroot` - - Add include search path with spaces: `-I"/path/with spaces"` -- `BINDGEN_EXTRA_CLANG_ARGS_<TARGET>`: similar to `BINDGEN_EXTRA_CLANG_ARGS`, - but used to set per-target arguments to pass to clang. Useful to set system include - directories in a target-specific way in cross-compilation environments with multiple targets. - Has precedence over `BINDGEN_EXTRA_CLANG_ARGS`. - -Additionally, `bindgen` uses `libclang` to parse C and C++ header files. -To modify how `bindgen` searches for `libclang`, see the [`clang-sys` documentation][clang-sys-env]. -For more details on how `bindgen` uses `libclang`, see the [`bindgen` users guide][bindgen-book-clang]. - -## Releases - -We don't follow a specific release calendar, but if you need a release please -file an issue requesting that (ping `@emilio` for increased effectiveness). - -## Contributing - -[See `CONTRIBUTING.md` for hacking on `bindgen`!](./CONTRIBUTING.md) - -[bindgen-cmdline]: https://rust-lang.github.io/rust-bindgen/command-line-usage.html -[clang-sys-env]: https://github.com/KyleMayes/clang-sys#environment-variables -[bindgen-book-clang]: https://rust-lang.github.io/rust-bindgen/requirements.html#clang diff --git a/TEST_MAPPING b/TEST_MAPPING index e4ec3b3..23bbdf9 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -3,22 +3,12 @@ "imports": [ { "path": "external/rust/crates/libsqlite3-sys" - } - ], - "presubmit": [ - { - "name": "keystore2_test" }, { - "name": "legacykeystore_test" - } - ], - "presubmit-rust": [ - { - "name": "keystore2_test" + "path": "system/security/keystore2" }, { - "name": "legacykeystore_test" + "path": "system/security/keystore2/legacykeystore" } ] } diff --git a/android/bindgen_cmd/Android.bp b/android/bindgen_cmd/Android.bp index 758007b..689e7ae 100644 --- a/android/bindgen_cmd/Android.bp +++ b/android/bindgen_cmd/Android.bp @@ -20,6 +20,7 @@ rust_library_host { ], rustlibs: [ "libbindgen", + "libbindgen_cli", "libclap", "libenv_logger", ], diff --git a/android/bindgen_cmd/src/lib.rs b/android/bindgen_cmd/src/lib.rs index a92ac80..d33da7f 100644 --- a/android/bindgen_cmd/src/lib.rs +++ b/android/bindgen_cmd/src/lib.rs @@ -23,11 +23,9 @@ //! on the the builder before it is used. use bindgen; +use bindgen_cli; use std::env; -#[path = "../../../src/options.rs"] -mod options; - /// Takes in a function describing adjustments to make to a builder /// initialized by the command line. `build(|x| x)` is equivalent to /// running bindgen. When converting a build.rs, you will want to convert the @@ -36,7 +34,7 @@ mod options; pub fn build<C: FnOnce(bindgen::Builder) -> bindgen::Builder>(configure: C) { env_logger::init(); - match options::builder_from_flags(env::args()) { + match bindgen_cli::builder_from_flags(env::args()) { Ok((builder, output, _)) => { configure(builder) .generate() @@ -1,76 +1,15 @@ -mod target { - use std::env; - use std::fs::File; - use std::io::Write; - use std::path::{Path, PathBuf}; - - pub fn main() { - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - - let mut dst = - File::create(Path::new(&out_dir).join("host-target.txt")).unwrap(); - dst.write_all(env::var("TARGET").unwrap().as_bytes()) - .unwrap(); - } -} - -mod testgen { - use std::char; - use std::env; - use std::ffi::OsStr; - use std::fs::{self, File}; - use std::io::Write; - use std::path::{Path, PathBuf}; - - pub fn main() { - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - let mut dst = - File::create(Path::new(&out_dir).join("tests.rs")).unwrap(); - - let manifest_dir = - PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); - let headers_dir = manifest_dir.join("tests").join("headers"); - - let headers = match fs::read_dir(headers_dir) { - Ok(dir) => dir, - // We may not have headers directory after packaging. - Err(..) => return, - }; - - let entries = - headers.map(|result| result.expect("Couldn't read header file")); - - println!("cargo:rerun-if-changed=tests/headers"); - - for entry in entries { - match entry.path().extension().and_then(OsStr::to_str) { - Some("h") | Some("hpp") => { - let func = entry - .file_name() - .to_str() - .unwrap() - .replace(|c| !char::is_alphanumeric(c), "_") - .replace("__", "_") - .to_lowercase(); - writeln!( - dst, - "test_header!(header_{}, {:?});", - func, - entry.path(), - ) - .unwrap(); - } - _ => {} - } - } - - dst.flush().unwrap(); - } -} +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::{Path, PathBuf}; fn main() { - target::main(); - testgen::main(); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + + let mut dst = + File::create(Path::new(&out_dir).join("host-target.txt")).unwrap(); + dst.write_all(env::var("TARGET").unwrap().as_bytes()) + .unwrap(); // On behalf of clang_sys, rebuild ourselves if important configuration // variables change, to ensure that bindings get rebuilt if the @@ -85,6 +24,6 @@ fn main() { ); println!( "cargo:rerun-if-env-changed=BINDGEN_EXTRA_CLANG_ARGS_{}", - std::env::var("TARGET").unwrap().replace("-", "_") + std::env::var("TARGET").unwrap().replace('-', "_") ); } diff --git a/src/callbacks.rs b/callbacks.rs index 9b34544..dc20e25 100644 --- a/src/callbacks.rs +++ b/callbacks.rs @@ -5,7 +5,6 @@ pub use crate::ir::derive::CanDerive as ImplementsTrait; pub use crate::ir::enum_ty::{EnumVariantCustomBehavior, EnumVariantValue}; pub use crate::ir::int::IntKind; use std::fmt; -use std::panic::UnwindSafe; /// An enum to allow ignoring parsing of macros. #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -25,12 +24,27 @@ impl Default for MacroParsingBehavior { /// A trait to allow configuring different kinds of types in different /// situations. -pub trait ParseCallbacks: fmt::Debug + UnwindSafe { +pub trait ParseCallbacks: fmt::Debug { + #[cfg(feature = "cli")] + #[doc(hidden)] + fn cli_args(&self) -> Vec<String> { + vec![] + } + /// This function will be run on every macro that is identified. fn will_parse_macro(&self, _name: &str) -> MacroParsingBehavior { MacroParsingBehavior::Default } + /// This function will run for every extern variable and function. The returned value determines + /// the name visible in the bindings. + fn generated_name_override( + &self, + _item_info: ItemInfo<'_>, + ) -> Option<String> { + None + } + /// The integer kind an integer macro should have, given a name and the /// value of that macro, or `None` if you want the default to be chosen. fn int_macro(&self, _name: &str, _value: i64) -> Option<IntKind> { @@ -100,7 +114,52 @@ pub trait ParseCallbacks: fmt::Debug + UnwindSafe { /// /// If no additional attributes are wanted, this function should return an /// empty `Vec`. - fn add_derives(&self, _name: &str) -> Vec<String> { + fn add_derives(&self, _info: &DeriveInfo<'_>) -> Vec<String> { vec![] } + + /// Process a source code comment. + fn process_comment(&self, _comment: &str) -> Option<String> { + None + } +} + +/// Relevant information about a type to which new derive attributes will be added using +/// [`ParseCallbacks::add_derives`]. +#[derive(Debug)] +#[non_exhaustive] +pub struct DeriveInfo<'a> { + /// The name of the type. + pub name: &'a str, + /// The kind of the type. + pub kind: TypeKind, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// The kind of the current type. +pub enum TypeKind { + /// The type is a Rust `struct`. + Struct, + /// The type is a Rust `enum`. + Enum, + /// The type is a Rust `union`. + Union, +} + +/// An struct providing information about the item being passed to `ParseCallbacks::generated_name_override`. +#[non_exhaustive] +pub struct ItemInfo<'a> { + /// The name of the item + pub name: &'a str, + /// The kind of item + pub kind: ItemKind, +} + +/// An enum indicating the kind of item for an ItemInfo. +#[non_exhaustive] +pub enum ItemKind { + /// A Function + Function, + /// A Variable + Var, } diff --git a/cargo2android.json b/cargo2android.json index 9e5e68b..fd62b50 100644 --- a/cargo2android.json +++ b/cargo2android.json @@ -1,6 +1,6 @@ { "copy-out": true, - "features": "clap,runtime,which-rustfmt", + "features": "cli,experimental,runtime,which-rustfmt", "host-first-multilib": true, "run": true -}
\ No newline at end of file +} @@ -12,6 +12,40 @@ use std::hash::Hasher; use std::os::raw::{c_char, c_int, c_longlong, c_uint, c_ulong, c_ulonglong}; use std::{mem, ptr, slice}; +/// Type representing a clang attribute. +/// +/// Values of this type can be used to check for different attributes using the `has_attrs` +/// function. +pub struct Attribute { + name: &'static [u8], + kind: Option<CXCursorKind>, + token_kind: CXTokenKind, +} + +impl Attribute { + /// A `warn_unused_result` attribute. + pub const MUST_USE: Self = Self { + name: b"warn_unused_result", + // FIXME(emilio): clang-sys doesn't expose `CXCursor_WarnUnusedResultAttr` (from clang 9). + kind: Some(440), + token_kind: CXToken_Identifier, + }; + + /// A `_Noreturn` attribute. + pub const NO_RETURN: Self = Self { + name: b"_Noreturn", + kind: None, + token_kind: CXToken_Keyword, + }; + + /// A `[[noreturn]]` attribute. + pub const NO_RETURN_CPP: Self = Self { + name: b"noreturn", + kind: None, + token_kind: CXToken_Identifier, + }; +} + /// A cursor into the Clang AST, pointing to an AST node. /// /// We call the AST node pointed to by the cursor the cursor's "referent". @@ -52,6 +86,11 @@ impl Cursor { unsafe { clang_isDeclaration(self.kind()) != 0 } } + /// Is this cursor's referent an anonymous record or so? + pub fn is_anonymous(&self) -> bool { + unsafe { clang_Cursor_isAnonymous(self.x) != 0 } + } + /// Get this cursor's referent's spelling. pub fn spelling(&self) -> String { unsafe { cxstring_into_string(clang_getCursorSpelling(self.x)) } @@ -276,6 +315,56 @@ impl Cursor { true } + /// Is the referent any kind of template parameter? + pub fn is_template_parameter(&self) -> bool { + matches!( + self.kind(), + CXCursor_TemplateTemplateParameter | + CXCursor_TemplateTypeParameter | + CXCursor_NonTypeTemplateParameter + ) + } + + /// Does the referent's type or value depend on a template parameter? + pub fn is_dependent_on_template_parameter(&self) -> bool { + fn visitor( + found_template_parameter: &mut bool, + cur: Cursor, + ) -> CXChildVisitResult { + // If we found a template parameter, it is dependent. + if cur.is_template_parameter() { + *found_template_parameter = true; + return CXChildVisit_Break; + } + + // Get the referent and traverse it as well. + if let Some(referenced) = cur.referenced() { + if referenced.is_template_parameter() { + *found_template_parameter = true; + return CXChildVisit_Break; + } + + referenced + .visit(|next| visitor(found_template_parameter, next)); + if *found_template_parameter { + return CXChildVisit_Break; + } + } + + // Continue traversing the AST at the original cursor. + CXChildVisit_Recurse + } + + if self.is_template_parameter() { + return true; + } + + let mut found_template_parameter = false; + self.visit(|next| visitor(&mut found_template_parameter, next)); + + found_template_parameter + } + /// Is this cursor pointing a valid referent? pub fn is_valid(&self) -> bool { unsafe { clang_isInvalid(self.kind()) == 0 } @@ -485,9 +574,45 @@ impl Cursor { !self.is_defaulted_function() } + /// Is the referent a bit field declaration? + pub fn is_bit_field(&self) -> bool { + unsafe { clang_Cursor_isBitField(self.x) != 0 } + } + + /// Get a cursor to the bit field's width expression, or `None` if it's not + /// a bit field. + pub fn bit_width_expr(&self) -> Option<Cursor> { + if !self.is_bit_field() { + return None; + } + + let mut result = None; + self.visit(|cur| { + // The first child may or may not be a TypeRef, depending on whether + // the field's type is builtin. Skip it. + if cur.kind() == CXCursor_TypeRef { + return CXChildVisit_Continue; + } + + // The next expression or literal is the bit width. + result = Some(cur); + + CXChildVisit_Break + }); + + result + } + /// Get the width of this cursor's referent bit field, or `None` if the - /// referent is not a bit field. + /// referent is not a bit field or if the width could not be evaluated. pub fn bit_width(&self) -> Option<u32> { + // It is not safe to check the bit width without ensuring it doesn't + // depend on a template parameter. See + // https://github.com/rust-lang/rust-bindgen/issues/2239 + if self.bit_width_expr()?.is_dependent_on_template_parameter() { + return None; + } + unsafe { let w = clang_getFieldDeclBitWidth(self.x); if w == -1 { @@ -532,6 +657,7 @@ impl Cursor { pub fn enum_val_signed(&self) -> Option<i64> { unsafe { if self.kind() == CXCursor_EnumConstantDecl { + #[allow(clippy::unnecessary_cast)] Some(clang_getEnumConstantDeclValue(self.x) as i64) } else { None @@ -545,6 +671,7 @@ impl Cursor { pub fn enum_val_unsigned(&self) -> Option<u64> { unsafe { if self.kind() == CXCursor_EnumConstantDecl { + #[allow(clippy::unnecessary_cast)] Some(clang_getEnumConstantDeclUnsignedValue(self.x) as u64) } else { None @@ -552,35 +679,41 @@ impl Cursor { } } - /// Whether this cursor has the `warn_unused_result` attribute. - pub fn has_warn_unused_result_attr(&self) -> bool { - // FIXME(emilio): clang-sys doesn't expose this (from clang 9). - const CXCursor_WarnUnusedResultAttr: CXCursorKind = 440; - self.has_attr("warn_unused_result", Some(CXCursor_WarnUnusedResultAttr)) - } + /// Does this cursor have the given attributes? + pub fn has_attrs<const N: usize>( + &self, + attrs: &[Attribute; N], + ) -> [bool; N] { + let mut found_attrs = [false; N]; + let mut found_count = 0; - /// Does this cursor have the given attribute? - /// - /// `name` is checked against unexposed attributes. - fn has_attr(&self, name: &str, clang_kind: Option<CXCursorKind>) -> bool { - let mut found_attr = false; self.visit(|cur| { let kind = cur.kind(); - found_attr = clang_kind.map_or(false, |k| k == kind) || - (kind == CXCursor_UnexposedAttr && - cur.tokens().iter().any(|t| { - t.kind == CXToken_Identifier && - t.spelling() == name.as_bytes() - })); - - if found_attr { - CXChildVisit_Break - } else { - CXChildVisit_Continue + for (idx, attr) in attrs.iter().enumerate() { + let found_attr = &mut found_attrs[idx]; + if !*found_attr { + // `attr.name` and` attr.token_kind` are checked against unexposed attributes only. + if attr.kind.map_or(false, |k| k == kind) || + (kind == CXCursor_UnexposedAttr && + cur.tokens().iter().any(|t| { + t.kind == attr.token_kind && + t.spelling() == attr.name + })) + { + *found_attr = true; + found_count += 1; + + if found_count == N { + return CXChildVisit_Break; + } + } + } } + + CXChildVisit_Continue }); - found_attr + found_attrs } /// Given that this cursor's referent is a `typedef`, get the `Type` that is @@ -1697,7 +1830,7 @@ pub struct UnsavedFile { impl UnsavedFile { /// Construct a new unsaved file with the given `name` and `contents`. - pub fn new(name: &str, contents: &str) -> UnsavedFile { + pub fn new(name: String, contents: String) -> UnsavedFile { let name = CString::new(name).unwrap(); let contents = CString::new(contents).unwrap(); let x = CXUnsavedFile { @@ -1789,9 +1922,15 @@ pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult { format!(" {}number-of-template-args = {}", prefix, num), ); } - if let Some(width) = c.bit_width() { + + if c.is_bit_field() { + let width = match c.bit_width() { + Some(w) => w.to_string(), + None => "<unevaluable>".to_string(), + }; print_indent(depth, format!(" {}bit-width = {}", prefix, width)); } + if let Some(ty) = c.enum_type() { print_indent( depth, @@ -2002,7 +2141,7 @@ impl EvalResult { pub fn as_double(&self) -> Option<f64> { match self.kind() { CXEval_Float => { - Some(unsafe { clang_EvalResult_getAsDouble(self.x) } as f64) + Some(unsafe { clang_EvalResult_getAsDouble(self.x) }) } _ => None, } @@ -2014,12 +2153,6 @@ impl EvalResult { return None; } - if !clang_EvalResult_isUnsignedInt::is_loaded() { - // FIXME(emilio): There's no way to detect underflow here, and clang - // will just happily give us a value. - return Some(unsafe { clang_EvalResult_getAsInt(self.x) } as i64); - } - if unsafe { clang_EvalResult_isUnsignedInt(self.x) } != 0 { let value = unsafe { clang_EvalResult_getAsUnsigned(self.x) }; if value > i64::max_value() as c_ulonglong { @@ -2036,6 +2169,7 @@ impl EvalResult { if value < i64::min_value() as c_longlong { return None; } + #[allow(clippy::unnecessary_cast)] Some(value as i64) } @@ -2071,10 +2205,7 @@ pub struct TargetInfo { impl TargetInfo { /// Tries to obtain target information from libclang. - pub fn new(tu: &TranslationUnit) -> Option<Self> { - if !clang_getTranslationUnitTargetInfo::is_loaded() { - return None; - } + pub fn new(tu: &TranslationUnit) -> Self { let triple; let pointer_width; unsafe { @@ -2085,9 +2216,9 @@ impl TargetInfo { } assert!(pointer_width > 0); assert_eq!(pointer_width % 8, 0); - Some(TargetInfo { + TargetInfo { triple, pointer_width: pointer_width as usize, - }) + } } } diff --git a/src/codegen/bitfield_unit.rs b/codegen/bitfield_unit.rs index 73ec2bd..73ec2bd 100755..100644 --- a/src/codegen/bitfield_unit.rs +++ b/codegen/bitfield_unit.rs diff --git a/src/codegen/bitfield_unit_tests.rs b/codegen/bitfield_unit_tests.rs index e143e4e..e143e4e 100644 --- a/src/codegen/bitfield_unit_tests.rs +++ b/codegen/bitfield_unit_tests.rs diff --git a/src/codegen/dyngen.rs b/codegen/dyngen.rs index 71c4dab..d8ea811 100644 --- a/src/codegen/dyngen.rs +++ b/codegen/dyngen.rs @@ -1,5 +1,6 @@ use crate::codegen; -use crate::ir::function::Abi; +use crate::ir::context::BindgenContext; +use crate::ir::function::ClangAbi; use proc_macro2::Ident; /// Used to build the output tokens for dynamic bindings. @@ -72,12 +73,22 @@ impl DynamicItems { Self::default() } - pub fn get_tokens(&self, lib_ident: Ident) -> proc_macro2::TokenStream { + pub fn get_tokens( + &self, + lib_ident: Ident, + ctx: &BindgenContext, + ) -> proc_macro2::TokenStream { let struct_members = &self.struct_members; let constructor_inits = &self.constructor_inits; let init_fields = &self.init_fields; let struct_implementation = &self.struct_implementation; + let from_library = if ctx.options().wrap_unsafe_ops { + quote!(unsafe { Self::from_library(library) }) + } else { + quote!(Self::from_library(library)) + }; + quote! { extern crate libloading; @@ -92,7 +103,7 @@ impl DynamicItems { ) -> Result<Self, ::libloading::Error> where P: AsRef<::std::ffi::OsStr> { let library = ::libloading::Library::new(path)?; - Self::from_library(library) + #from_library } pub unsafe fn from_library<L>( @@ -112,16 +123,19 @@ impl DynamicItems { } } - pub fn push( + #[allow(clippy::too_many_arguments)] + pub(crate) fn push( &mut self, ident: Ident, - abi: Abi, + abi: ClangAbi, is_variadic: bool, is_required: bool, args: Vec<proc_macro2::TokenStream>, args_identifiers: Vec<proc_macro2::TokenStream>, ret: proc_macro2::TokenStream, ret_ty: proc_macro2::TokenStream, + attributes: Vec<proc_macro2::TokenStream>, + ctx: &BindgenContext, ) { if !is_variadic { assert_eq!(args.len(), args_identifiers.len()); @@ -145,15 +159,18 @@ impl DynamicItems { } else { quote! { self.#ident.as_ref().expect("Expected function, got error.") } }; - let call_body = quote! { - (#fn_)(#( #args_identifiers ),*) + let call_body = if ctx.options().wrap_unsafe_ops { + quote!(unsafe { (#fn_)(#( #args_identifiers ),*) }) + } else { + quote!((#fn_)(#( #args_identifiers ),*) ) }; // We can't implement variadic functions from C easily, so we allow to // access the function pointer so that the user can call it just fine. if !is_variadic { self.struct_implementation.push(quote! { - pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty { + #(#attributes)* + pub unsafe fn #ident ( &self, #( #args ),* ) #ret_ty { #call_body } }); @@ -161,13 +178,19 @@ impl DynamicItems { // N.B: Unwrap the signature upon construction if it is required to be resolved. let ident_str = codegen::helpers::ast_ty::cstr_expr(ident.to_string()); + let library_get = if ctx.options().wrap_unsafe_ops { + quote!(unsafe { __library.get(#ident_str) }) + } else { + quote!(__library.get(#ident_str)) + }; + self.constructor_inits.push(if is_required { quote! { - let #ident = __library.get(#ident_str).map(|sym| *sym)?; + let #ident = #library_get.map(|sym| *sym)?; } } else { quote! { - let #ident = __library.get(#ident_str).map(|sym| *sym); + let #ident = #library_get.map(|sym| *sym); } }); diff --git a/src/codegen/error.rs b/codegen/error.rs index c1bcf4e..c1bcf4e 100644 --- a/src/codegen/error.rs +++ b/codegen/error.rs diff --git a/src/codegen/helpers.rs b/codegen/helpers.rs index 2ce6894..088c7f9 100644 --- a/src/codegen/helpers.rs +++ b/codegen/helpers.rs @@ -30,7 +30,7 @@ pub mod attributes { let which_ones = which_ones .iter() .cloned() - .map(|one| Ident::new(one, Span::call_site())); + .map(|one| TokenStream::from_str(one).expect("derive to be valid")); quote! { #[derive( #( #which_ones ),* )] } @@ -55,9 +55,11 @@ pub mod attributes { } pub fn doc(comment: String) -> TokenStream { - // NOTE(emilio): By this point comments are already preprocessed and in - // `///` form. Quote turns them into `#[doc]` comments, but oh well. - TokenStream::from_str(&comment).unwrap() + if comment.is_empty() { + quote!() + } else { + quote!(#[doc = #comment]) + } } pub fn link_name(name: &str) -> TokenStream { @@ -166,9 +168,19 @@ pub mod ast_ty { #prefix::#ident } } - None => quote! { - ::std::os::raw::#ident - }, + None => { + if ctx.options().use_core && + ctx.options().rust_features().core_ffi_c + { + quote! { + ::core::ffi::#ident + } + } else { + quote! { + ::std::os::raw::#ident + } + } + } } } diff --git a/src/codegen/impl_debug.rs b/codegen/impl_debug.rs index 0e2cd33..0e2cd33 100644 --- a/src/codegen/impl_debug.rs +++ b/codegen/impl_debug.rs diff --git a/src/codegen/impl_partialeq.rs b/codegen/impl_partialeq.rs index 960306f..960306f 100644 --- a/src/codegen/impl_partialeq.rs +++ b/codegen/impl_partialeq.rs diff --git a/src/codegen/mod.rs b/codegen/mod.rs index 19886e3..b6fb70e 100644 --- a/src/codegen/mod.rs +++ b/codegen/mod.rs @@ -3,6 +3,8 @@ mod error; mod helpers; mod impl_debug; mod impl_partialeq; +mod postprocessing; +mod serialize; pub mod struct_layout; #[cfg(test)] @@ -17,12 +19,12 @@ use self::struct_layout::StructLayoutTracker; use super::BindgenOptions; +use crate::callbacks::{DeriveInfo, TypeKind as DeriveTypeKind}; use crate::ir::analysis::{HasVtable, Sizedness}; use crate::ir::annotations::FieldAccessorKind; -use crate::ir::comment; use crate::ir::comp::{ - Base, Bitfield, BitfieldUnit, CompInfo, CompKind, Field, FieldData, - FieldMethods, Method, MethodKind, + Bitfield, BitfieldUnit, CompInfo, CompKind, Field, FieldData, FieldMethods, + Method, MethodKind, }; use crate::ir::context::{BindgenContext, ItemId}; use crate::ir::derive::{ @@ -31,7 +33,9 @@ use crate::ir::derive::{ }; use crate::ir::dot; use crate::ir::enum_ty::{Enum, EnumVariant, EnumVariantValue}; -use crate::ir::function::{Abi, Function, FunctionKind, FunctionSig, Linkage}; +use crate::ir::function::{ + Abi, ClangAbi, Function, FunctionKind, FunctionSig, Linkage, +}; use crate::ir::int::IntKind; use crate::ir::item::{IsOpaque, Item, ItemCanonicalName, ItemCanonicalPath}; use crate::ir::item_kind::ItemKind; @@ -56,6 +60,29 @@ use std::iter; use std::ops; use std::str::FromStr; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum CodegenError { + Serialize { msg: String, loc: String }, + Io(String), +} + +impl From<std::io::Error> for CodegenError { + fn from(err: std::io::Error) -> Self { + Self::Io(err.to_string()) + } +} + +impl std::fmt::Display for CodegenError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CodegenError::Serialize { msg, loc } => { + write!(f, "serialization error at {}: {}", loc, msg) + } + CodegenError::Io(err) => err.fmt(f), + } + } +} + // Name of type defined in constified enum module pub static CONSTIFIED_ENUM_MODULE_REPR_NAME: &str = "Type"; @@ -84,7 +111,7 @@ fn root_import( let mut path = top_level_path(ctx, module); let root = ctx.root_module().canonical_name(ctx); - let root_ident = ctx.rust_ident(&root); + let root_ident = ctx.rust_ident(root); path.push(quote! { #root_ident }); let mut tokens = quote! {}; @@ -110,17 +137,13 @@ bitflags! { } } -fn derives_of_item(item: &Item, ctx: &BindgenContext) -> DerivableTraits { +fn derives_of_item( + item: &Item, + ctx: &BindgenContext, + packed: bool, +) -> DerivableTraits { let mut derivable_traits = DerivableTraits::empty(); - if item.can_derive_debug(ctx) && !item.annotations().disallow_debug() { - derivable_traits |= DerivableTraits::DEBUG; - } - - if item.can_derive_default(ctx) && !item.annotations().disallow_default() { - derivable_traits |= DerivableTraits::DEFAULT; - } - let all_template_params = item.all_template_params(ctx); if item.can_derive_copy(ctx) && !item.annotations().disallow_copy() { @@ -137,6 +160,18 @@ fn derives_of_item(item: &Item, ctx: &BindgenContext) -> DerivableTraits { // It's not hard to fix though. derivable_traits |= DerivableTraits::CLONE; } + } else if packed { + // If the struct or union is packed, deriving from Copy is required for + // deriving from any other trait. + return derivable_traits; + } + + if item.can_derive_debug(ctx) && !item.annotations().disallow_debug() { + derivable_traits |= DerivableTraits::DEBUG; + } + + if item.can_derive_default(ctx) && !item.annotations().disallow_default() { + derivable_traits |= DerivableTraits::DEFAULT; } if item.can_derive_hash(ctx) { @@ -230,6 +265,8 @@ struct CodegenResult<'a> { /// function name to the number of overloads we have already codegen'd for /// that name. This lets us give each overload a unique suffix. overload_counters: HashMap<String, u32>, + + items_to_serialize: Vec<ItemId>, } impl<'a> CodegenResult<'a> { @@ -247,6 +284,7 @@ impl<'a> CodegenResult<'a> { functions_seen: Default::default(), vars_seen: Default::default(), overload_counters: Default::default(), + items_to_serialize: Default::default(), } } @@ -424,10 +462,10 @@ trait CodeGenerator { /// Extra information returned to the caller. type Return; - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, extra: &Self::Extra, ) -> Self::Return; } @@ -467,10 +505,10 @@ impl CodeGenerator for Item { type Extra = (); type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, _extra: &(), ) { debug!("<Item as CodeGenerator>::codegen: self = {:?}", self); @@ -499,10 +537,10 @@ impl CodeGenerator for Module { type Extra = Item; type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) { debug!("<Module as CodeGenerator>::codegen: item = {:?}", item); @@ -591,10 +629,10 @@ impl CodeGenerator for Var { type Extra = Item; type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) { use crate::ir::var::VarType; @@ -738,10 +776,10 @@ impl CodeGenerator for Type { type Extra = Item; type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) { debug!("<Type as CodeGenerator>::codegen: item = {:?}", item); @@ -788,7 +826,7 @@ impl CodeGenerator for Type { } }; - let rust_name = ctx.rust_ident(&name); + let rust_name = ctx.rust_ident(name); let mut tokens = if let Some(comment) = item.comment(ctx) { attributes::doc(comment) @@ -828,9 +866,34 @@ impl CodeGenerator for Type { } // If this is a known named type, disallow generating anything - // for it too. + // for it too. If size_t -> usize conversions are enabled, we + // need to check that these conversions are permissible, but + // nothing needs to be generated, still. let spelling = self.name().expect("Unnamed alias?"); if utils::type_from_named(ctx, spelling).is_some() { + if let "size_t" | "ssize_t" = spelling { + let layout = inner_item + .kind() + .expect_type() + .layout(ctx) + .expect("No layout?"); + assert_eq!( + layout.size, + ctx.target_pointer_size(), + "Target platform requires `--no-size_t-is-usize`. The size of `{}` ({}) does not match the target pointer size ({})", + spelling, + layout.size, + ctx.target_pointer_size(), + ); + assert_eq!( + layout.align, + ctx.target_pointer_size(), + "Target platform requires `--no-size_t-is-usize`. The alignment of `{}` ({}) does not match the target pointer size ({})", + spelling, + layout.align, + ctx.target_pointer_size(), + ); + } return; } @@ -926,7 +989,9 @@ impl CodeGenerator for Type { let mut attributes = vec![attributes::repr("transparent")]; - let derivable_traits = derives_of_item(item, ctx); + let packed = false; // Types can't be packed in Rust. + let derivable_traits = + derives_of_item(item, ctx, packed); if !derivable_traits.is_empty() { let derives: Vec<_> = derivable_traits.into(); attributes.push(attributes::derives(&derives)) @@ -1017,23 +1082,14 @@ impl CodeGenerator for Type { struct Vtable<'a> { item_id: ItemId, + /// A reference to the originating compound object. #[allow(dead_code)] - methods: &'a [Method], - #[allow(dead_code)] - base_classes: &'a [Base], + comp_info: &'a CompInfo, } impl<'a> Vtable<'a> { - fn new( - item_id: ItemId, - methods: &'a [Method], - base_classes: &'a [Base], - ) -> Self { - Vtable { - item_id, - methods, - base_classes, - } + fn new(item_id: ItemId, comp_info: &'a CompInfo) -> Self { + Vtable { item_id, comp_info } } } @@ -1041,23 +1097,77 @@ impl<'a> CodeGenerator for Vtable<'a> { type Extra = Item; type Return = (); - fn codegen<'b>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'b>, + result: &mut CodegenResult<'_>, item: &Item, ) { assert_eq!(item.id(), self.item_id); debug_assert!(item.is_enabled_for_codegen(ctx)); + let name = ctx.rust_ident(self.canonical_name(ctx)); - // For now, generate an empty struct, later we should generate function - // pointers and whatnot. - let name = ctx.rust_ident(&self.canonical_name(ctx)); - let void = helpers::ast_ty::c_void(ctx); - result.push(quote! { - #[repr(C)] - pub struct #name ( #void ); - }); + // For now, we will only generate vtables for classes that: + // - do not inherit from others (compilers merge VTable from primary parent class). + // - do not contain a virtual destructor (requires ordering; platforms generate different vtables). + if ctx.options().vtable_generation && + self.comp_info.base_members().is_empty() && + self.comp_info.destructor().is_none() + { + let class_ident = ctx.rust_ident(self.item_id.canonical_name(ctx)); + + let methods = self + .comp_info + .methods() + .iter() + .filter_map(|m| { + if !m.is_virtual() { + return None; + } + + let function_item = ctx.resolve_item(m.signature()); + let function = function_item.expect_function(); + let signature_item = ctx.resolve_item(function.signature()); + let signature = match signature_item.expect_type().kind() { + TypeKind::Function(ref sig) => sig, + _ => panic!("Function signature type mismatch"), + }; + + // FIXME: Is there a canonical name without the class prepended? + let function_name = function_item.canonical_name(ctx); + + // FIXME: Need to account for overloading with times_seen (separately from regular function path). + let function_name = ctx.rust_ident(function_name); + let mut args = utils::fnsig_arguments(ctx, signature); + let ret = utils::fnsig_return_ty(ctx, signature); + + args[0] = if m.is_const() { + quote! { this: *const #class_ident } + } else { + quote! { this: *mut #class_ident } + }; + + Some(quote! { + pub #function_name : unsafe extern "C" fn( #( #args ),* ) #ret + }) + }) + .collect::<Vec<_>>(); + + result.push(quote! { + #[repr(C)] + pub struct #name { + #( #methods ),* + } + }) + } else { + // For the cases we don't support, simply generate an empty struct. + let void = helpers::ast_ty::c_void(ctx); + + result.push(quote! { + #[repr(C)] + pub struct #name ( #void ); + }); + } } } @@ -1086,10 +1196,10 @@ impl CodeGenerator for TemplateInstantiation { type Extra = Item; type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) { debug_assert!(item.is_enabled_for_codegen(ctx)); @@ -1157,11 +1267,11 @@ impl CodeGenerator for TemplateInstantiation { trait FieldCodegen<'a> { type Extra; + #[allow(clippy::too_many_arguments)] fn codegen<F, M>( &self, ctx: &BindgenContext, fields_should_be_private: bool, - codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, result: &mut CodegenResult, @@ -1181,7 +1291,6 @@ impl<'a> FieldCodegen<'a> for Field { &self, ctx: &BindgenContext, fields_should_be_private: bool, - codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, result: &mut CodegenResult, @@ -1198,7 +1307,6 @@ impl<'a> FieldCodegen<'a> for Field { data.codegen( ctx, fields_should_be_private, - codegen_depth, accessor_kind, parent, result, @@ -1212,7 +1320,6 @@ impl<'a> FieldCodegen<'a> for Field { unit.codegen( ctx, fields_should_be_private, - codegen_depth, accessor_kind, parent, result, @@ -1226,6 +1333,35 @@ impl<'a> FieldCodegen<'a> for Field { } } +fn wrap_union_field_if_needed( + ctx: &BindgenContext, + struct_layout: &StructLayoutTracker, + ty: proc_macro2::TokenStream, + result: &mut CodegenResult, +) -> proc_macro2::TokenStream { + if struct_layout.is_rust_union() { + if struct_layout.can_copy_union_fields() { + ty + } else { + let prefix = ctx.trait_prefix(); + quote! { + ::#prefix::mem::ManuallyDrop<#ty> + } + } + } else { + result.saw_bindgen_union(); + if ctx.options().enable_cxx_namespaces { + quote! { + root::__BindgenUnionField<#ty> + } + } else { + quote! { + __BindgenUnionField<#ty> + } + } + } +} + impl<'a> FieldCodegen<'a> for FieldData { type Extra = (); @@ -1233,7 +1369,6 @@ impl<'a> FieldCodegen<'a> for FieldData { &self, ctx: &BindgenContext, fields_should_be_private: bool, - codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, result: &mut CodegenResult, @@ -1256,17 +1391,8 @@ impl<'a> FieldCodegen<'a> for FieldData { ty.append_implicit_template_params(ctx, field_item); // NB: If supported, we use proper `union` types. - let ty = if parent.is_union() && !struct_layout.is_rust_union() { - result.saw_bindgen_union(); - if ctx.options().enable_cxx_namespaces { - quote! { - root::__BindgenUnionField<#ty> - } - } else { - quote! { - __BindgenUnionField<#ty> - } - } + let ty = if parent.is_union() { + wrap_union_field_if_needed(ctx, struct_layout, ty, result) } else if let Some(item) = field_ty.is_incomplete_array(ctx) { result.saw_incomplete_array(); @@ -1288,8 +1414,7 @@ impl<'a> FieldCodegen<'a> for FieldData { let mut field = quote! {}; if ctx.options().generate_comments { if let Some(raw_comment) = self.comment() { - let comment = - comment::preprocess(raw_comment, codegen_depth + 1); + let comment = ctx.options().process_comment(raw_comment); field = attributes::doc(comment); } } @@ -1449,7 +1574,6 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { &self, ctx: &BindgenContext, fields_should_be_private: bool, - codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, result: &mut CodegenResult, @@ -1467,26 +1591,20 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { let layout = self.layout(); let unit_field_ty = helpers::bitfield_unit(ctx, layout); - let field_ty = { - if parent.is_union() && !struct_layout.is_rust_union() { - result.saw_bindgen_union(); - if ctx.options().enable_cxx_namespaces { - quote! { - root::__BindgenUnionField<#unit_field_ty> - } - } else { - quote! { - __BindgenUnionField<#unit_field_ty> - } - } - } else { - unit_field_ty.clone() - } + let field_ty = if parent.is_union() { + wrap_union_field_if_needed( + ctx, + struct_layout, + unit_field_ty.clone(), + result, + ) + } else { + unit_field_ty.clone() }; { let align_field_name = format!("_bitfield_align_{}", self.nth()); - let align_field_ident = ctx.rust_ident(&align_field_name); + let align_field_ident = ctx.rust_ident(align_field_name); let align_ty = match self.layout().align { n if n >= 8 => quote! { u64 }, 4 => quote! { u32 }, @@ -1532,7 +1650,6 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { bf.codegen( ctx, fields_should_be_private, - codegen_depth, accessor_kind, parent, result, @@ -1607,7 +1724,6 @@ impl<'a> FieldCodegen<'a> for Bitfield { &self, ctx: &BindgenContext, fields_should_be_private: bool, - _codegen_depth: usize, _accessor_kind: FieldAccessorKind, parent: &CompInfo, _result: &mut CodegenResult, @@ -1708,10 +1824,10 @@ impl CodeGenerator for CompInfo { type Extra = Item; type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) { debug!("<CompInfo as CodeGenerator>::codegen: item = {:?}", item); @@ -1749,8 +1865,7 @@ impl CodeGenerator for CompInfo { if !is_opaque { if item.has_vtable_ptr(ctx) { - let vtable = - Vtable::new(item.id(), self.methods(), self.base_members()); + let vtable = Vtable::new(item.id(), self); vtable.codegen(ctx, result, item); let vtable_type = vtable @@ -1786,7 +1901,6 @@ impl CodeGenerator for CompInfo { let mut methods = vec![]; if !is_opaque { - let codegen_depth = item.codegen_depth(ctx); let fields_should_be_private = item.annotations().private_fields().unwrap_or(false); let struct_accessor_kind = item @@ -1797,7 +1911,6 @@ impl CodeGenerator for CompInfo { field.codegen( ctx, fields_should_be_private, - codegen_depth, struct_accessor_kind, self, result, @@ -1982,7 +2095,7 @@ impl CodeGenerator for CompInfo { } } - let derivable_traits = derives_of_item(item, ctx); + let derivable_traits = derives_of_item(item, ctx, packed); if !derivable_traits.contains(DerivableTraits::DEBUG) { needs_debug_impl = ctx.options().derive_debug && ctx.options().impl_debug && @@ -2015,25 +2128,32 @@ impl CodeGenerator for CompInfo { let mut derives: Vec<_> = derivable_traits.into(); derives.extend(item.annotations().derives().iter().map(String::as_str)); + let is_rust_union = is_union && struct_layout.is_rust_union(); + // The custom derives callback may return a list of derive attributes; // add them to the end of the list. - let custom_derives; - if let Some(cb) = &ctx.options().parse_callbacks { - custom_derives = cb.add_derives(&canonical_name); - // In most cases this will be a no-op, since custom_derives will be empty. - derives.extend(custom_derives.iter().map(|s| s.as_str())); - }; + let custom_derives = ctx.options().all_callbacks(|cb| { + cb.add_derives(&DeriveInfo { + name: &canonical_name, + kind: if is_rust_union { + DeriveTypeKind::Union + } else { + DeriveTypeKind::Struct + }, + }) + }); + // In most cases this will be a no-op, since custom_derives will be empty. + derives.extend(custom_derives.iter().map(|s| s.as_str())); if !derives.is_empty() { attributes.push(attributes::derives(&derives)) } - if item.annotations().must_use_type() || ctx.must_use_type_by_name(item) - { + if item.must_use(ctx) { attributes.push(attributes::must_use()); } - let mut tokens = if is_union && struct_layout.is_rust_union() { + let mut tokens = if is_rust_union { quote! { #( #attributes )* pub union #canonical_ident @@ -2108,56 +2228,58 @@ impl CodeGenerator for CompInfo { }) }; - // FIXME when [issue #465](https://github.com/rust-lang/rust-bindgen/issues/465) ready - let too_many_base_vtables = self - .base_members() - .iter() - .filter(|base| base.ty.has_vtable(ctx)) - .count() > - 1; - - let should_skip_field_offset_checks = - is_opaque || too_many_base_vtables; + let should_skip_field_offset_checks = is_opaque; let check_field_offset = if should_skip_field_offset_checks { vec![] } else { - let asserts = self.fields() - .iter() - .filter_map(|field| match *field { - Field::DataMember(ref f) if f.name().is_some() => Some(f), - _ => None, - }) - .flat_map(|field| { - let name = field.name().unwrap(); - field.offset().map(|offset| { - let field_offset = offset / 8; - let field_name = ctx.rust_ident(name); - - quote! { - assert_eq!( - unsafe { - &(*(::#prefix::ptr::null::<#canonical_ident>())).#field_name as *const _ as usize - }, - #field_offset, - concat!("Offset of field: ", stringify!(#canonical_ident), "::", stringify!(#field_name)) - ); - } - }) + self.fields() + .iter() + .filter_map(|field| match *field { + Field::DataMember(ref f) if f.name().is_some() => Some(f), + _ => None, + }) + .flat_map(|field| { + let name = field.name().unwrap(); + field.offset().map(|offset| { + let field_offset = offset / 8; + let field_name = ctx.rust_ident(name); + quote! { + assert_eq!( + unsafe { + ::#prefix::ptr::addr_of!((*ptr).#field_name) as usize - ptr as usize + }, + #field_offset, + concat!("Offset of field: ", stringify!(#canonical_ident), "::", stringify!(#field_name)) + ); + } }) - .collect::<Vec<proc_macro2::TokenStream>>(); + }) + .collect() + }; - asserts + let uninit_decl = if !check_field_offset.is_empty() { + // FIXME: When MSRV >= 1.59.0, we can use + // > const PTR: *const #canonical_ident = ::#prefix::mem::MaybeUninit::uninit().as_ptr(); + Some(quote! { + // Use a shared MaybeUninit so that rustc with + // opt-level=0 doesn't take too much stack space, + // see #2218. + const UNINIT: ::#prefix::mem::MaybeUninit<#canonical_ident> = ::#prefix::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + }) + } else { + None }; let item = quote! { #[test] fn #fn_name() { + #uninit_decl assert_eq!(#size_of_expr, #size, concat!("Size of: ", stringify!(#canonical_ident))); - #check_struct_align #( #check_field_offset )* } @@ -2310,24 +2432,13 @@ impl CodeGenerator for CompInfo { } } -trait MethodCodegen { - fn codegen_method<'a>( +impl Method { + fn codegen_method( &self, ctx: &BindgenContext, methods: &mut Vec<proc_macro2::TokenStream>, - method_names: &mut HashMap<String, usize>, - result: &mut CodegenResult<'a>, - parent: &CompInfo, - ); -} - -impl MethodCodegen for Method { - fn codegen_method<'a>( - &self, - ctx: &BindgenContext, - methods: &mut Vec<proc_macro2::TokenStream>, - method_names: &mut HashMap<String, usize>, - result: &mut CodegenResult<'a>, + method_names: &mut HashSet<String>, + result: &mut CodegenResult<'_>, _parent: &CompInfo, ) { assert!({ @@ -2370,9 +2481,20 @@ impl MethodCodegen for Method { _ => panic!("How in the world?"), }; - if let (Abi::ThisCall, false) = - (signature.abi(), ctx.options().rust_features().thiscall_abi) - { + let supported_abi = match signature.abi(ctx, Some(&*name)) { + ClangAbi::Known(Abi::ThisCall) => { + ctx.options().rust_features().thiscall_abi + } + ClangAbi::Known(Abi::Vectorcall) => { + ctx.options().rust_features().vectorcall_abi + } + ClangAbi::Known(Abi::CUnwind) => { + ctx.options().rust_features().c_unwind_abi + } + _ => true, + }; + + if !supported_abi { return; } @@ -2382,16 +2504,22 @@ impl MethodCodegen for Method { return; } - let count = { - let count = method_names.entry(name.clone()).or_insert(0); - *count += 1; - *count - 1 - }; + if method_names.contains(&name) { + let mut count = 1; + let mut new_name; - if count != 0 { - name.push_str(&count.to_string()); + while { + new_name = format!("{}{}", name, count); + method_names.contains(&new_name) + } { + count += 1; + } + + name = new_name; } + method_names.insert(name.clone()); + let mut function_name = function_item.canonical_name(ctx); if times_seen > 0 { write!(&mut function_name, "{}", times_seen).unwrap(); @@ -2472,9 +2600,7 @@ impl MethodCodegen for Method { }) } - let block = quote! { - #( #stmts );* - }; + let block = ctx.wrap_unsafe_ops(quote! ( #( #stmts );*)); let mut attrs = vec![attributes::inline()]; @@ -2495,7 +2621,7 @@ impl MethodCodegen for Method { } /// A helper type that represents different enum variations. -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum EnumVariation { /// The code for this enum will use a Rust enum. Note that creating this in unsafe code /// (including FFI) with an invalid value will invoke undefined behaviour, whether or not @@ -2508,6 +2634,8 @@ pub enum EnumVariation { NewType { /// Indicates whether the newtype will have bitwise operators is_bitfield: bool, + /// Indicates whether the variants will be represented as global constants + is_global: bool, }, /// The code for this enum will use consts Consts, @@ -2545,16 +2673,26 @@ impl std::str::FromStr for EnumVariation { "rust_non_exhaustive" => Ok(EnumVariation::Rust { non_exhaustive: true, }), - "bitfield" => Ok(EnumVariation::NewType { is_bitfield: true }), + "bitfield" => Ok(EnumVariation::NewType { + is_bitfield: true, + is_global: false, + }), "consts" => Ok(EnumVariation::Consts), "moduleconsts" => Ok(EnumVariation::ModuleConsts), - "newtype" => Ok(EnumVariation::NewType { is_bitfield: false }), + "newtype" => Ok(EnumVariation::NewType { + is_bitfield: false, + is_global: false, + }), + "newtype_global" => Ok(EnumVariation::NewType { + is_bitfield: false, + is_global: true, + }), _ => Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, concat!( "Got an invalid EnumVariation. Accepted values ", "are 'rust', 'rust_non_exhaustive', 'bitfield', 'consts',", - "'moduleconsts', and 'newtype'." + "'moduleconsts', 'newtype' and 'newtype_global'." ), )), } @@ -2564,41 +2702,27 @@ impl std::str::FromStr for EnumVariation { /// A helper type to construct different enum variations. enum EnumBuilder<'a> { Rust { - codegen_depth: usize, attrs: Vec<proc_macro2::TokenStream>, ident: Ident, tokens: proc_macro2::TokenStream, emitted_any_variants: bool, }, NewType { - codegen_depth: usize, canonical_name: &'a str, tokens: proc_macro2::TokenStream, is_bitfield: bool, + is_global: bool, }, Consts { - repr: proc_macro2::TokenStream, variants: Vec<proc_macro2::TokenStream>, - codegen_depth: usize, }, ModuleConsts { - codegen_depth: usize, module_name: &'a str, module_items: Vec<proc_macro2::TokenStream>, }, } impl<'a> EnumBuilder<'a> { - /// Returns the depth of the code generation for a variant of this enum. - fn codegen_depth(&self) -> usize { - match *self { - EnumBuilder::Rust { codegen_depth, .. } | - EnumBuilder::NewType { codegen_depth, .. } | - EnumBuilder::ModuleConsts { codegen_depth, .. } | - EnumBuilder::Consts { codegen_depth, .. } => codegen_depth, - } - } - /// Returns true if the builder is for a rustified enum. fn is_rust_enum(&self) -> bool { matches!(*self, EnumBuilder::Rust { .. }) @@ -2611,19 +2735,22 @@ impl<'a> EnumBuilder<'a> { mut attrs: Vec<proc_macro2::TokenStream>, repr: proc_macro2::TokenStream, enum_variation: EnumVariation, - enum_codegen_depth: usize, + has_typedef: bool, ) -> Self { let ident = Ident::new(name, Span::call_site()); match enum_variation { - EnumVariation::NewType { is_bitfield } => EnumBuilder::NewType { - codegen_depth: enum_codegen_depth, + EnumVariation::NewType { + is_bitfield, + is_global, + } => EnumBuilder::NewType { canonical_name: name, tokens: quote! { #( #attrs )* pub struct #ident (pub #repr); }, is_bitfield, + is_global, }, EnumVariation::Rust { .. } => { @@ -2631,7 +2758,6 @@ impl<'a> EnumBuilder<'a> { attrs.insert(0, quote! { #[repr( #repr )] }); let tokens = quote!(); EnumBuilder::Rust { - codegen_depth: enum_codegen_depth + 1, attrs, ident, tokens, @@ -2642,16 +2768,14 @@ impl<'a> EnumBuilder<'a> { EnumVariation::Consts => { let mut variants = Vec::new(); - variants.push(quote! { - #( #attrs )* - pub type #ident = #repr; - }); - - EnumBuilder::Consts { - repr, - variants, - codegen_depth: enum_codegen_depth, + if !has_typedef { + variants.push(quote! { + #( #attrs )* + pub type #ident = #repr; + }); } + + EnumBuilder::Consts { variants } } EnumVariation::ModuleConsts => { @@ -2665,7 +2789,6 @@ impl<'a> EnumBuilder<'a> { }; EnumBuilder::ModuleConsts { - codegen_depth: enum_codegen_depth + 1, module_name: name, module_items: vec![type_definition], } @@ -2674,13 +2797,13 @@ impl<'a> EnumBuilder<'a> { } /// Add a variant to this enum. - fn with_variant<'b>( + fn with_variant( self, ctx: &BindgenContext, variant: &EnumVariant, mangling_prefix: Option<&str>, rust_ty: proc_macro2::TokenStream, - result: &mut CodegenResult<'b>, + result: &mut CodegenResult<'_>, is_ty_named: bool, ) -> Self { let variant_name = ctx.rust_mangle(variant.name()); @@ -2697,8 +2820,7 @@ impl<'a> EnumBuilder<'a> { let mut doc = quote! {}; if ctx.options().generate_comments { if let Some(raw_comment) = variant.comment() { - let comment = - comment::preprocess(raw_comment, self.codegen_depth()); + let comment = ctx.options().process_comment(raw_comment); doc = attributes::doc(comment); } } @@ -2709,13 +2831,11 @@ impl<'a> EnumBuilder<'a> { ident, tokens, emitted_any_variants: _, - codegen_depth, } => { let name = ctx.rust_ident(variant_name); EnumBuilder::Rust { attrs, ident, - codegen_depth, tokens: quote! { #tokens #doc @@ -2725,11 +2845,18 @@ impl<'a> EnumBuilder<'a> { } } - EnumBuilder::NewType { canonical_name, .. } => { - if ctx.options().rust_features().associated_const && is_ty_named + EnumBuilder::NewType { + canonical_name, + is_global, + .. + } => { + if ctx.options().rust_features().associated_const && + is_ty_named && + !is_global { let enum_ident = ctx.rust_ident(canonical_name); let variant_ident = ctx.rust_ident(variant_name); + result.push(quote! { impl #enum_ident { #doc @@ -2752,7 +2879,7 @@ impl<'a> EnumBuilder<'a> { self } - EnumBuilder::Consts { ref repr, .. } => { + EnumBuilder::Consts { .. } => { let constant_name = match mangling_prefix { Some(prefix) => { Cow::Owned(format!("{}_{}", prefix, variant_name)) @@ -2760,18 +2887,15 @@ impl<'a> EnumBuilder<'a> { None => variant_name, }; - let ty = if is_ty_named { &rust_ty } else { repr }; - let ident = ctx.rust_ident(constant_name); result.push(quote! { #doc - pub const #ident : #ty = #expr ; + pub const #ident : #rust_ty = #expr ; }); self } EnumBuilder::ModuleConsts { - codegen_depth, module_name, mut module_items, } => { @@ -2785,17 +2909,16 @@ impl<'a> EnumBuilder<'a> { EnumBuilder::ModuleConsts { module_name, module_items, - codegen_depth, } } } } - fn build<'b>( + fn build( self, ctx: &BindgenContext, rust_ty: proc_macro2::TokenStream, - result: &mut CodegenResult<'b>, + result: &mut CodegenResult<'_>, ) -> proc_macro2::TokenStream { match self { EnumBuilder::Rust { @@ -2894,10 +3017,10 @@ impl CodeGenerator for Enum { type Extra = Item; type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) { debug!("<Enum as CodeGenerator>::codegen: item = {:?}", item); @@ -2998,21 +3121,16 @@ impl CodeGenerator for Enum { attrs.push(attributes::doc(comment)); } - if item.annotations().must_use_type() || ctx.must_use_type_by_name(item) - { + if item.must_use(ctx) { attrs.push(attributes::must_use()); } if !variation.is_const() { - let mut derives = derives_of_item(item, ctx); - // For backwards compat, enums always derive Debug/Clone/Eq/PartialEq/Hash, even - // if we don't generate those by default. - if !item.annotations().disallow_debug() { - derives.insert(DerivableTraits::DEBUG); - } - if !item.annotations().disallow_copy() { - derives.insert(DerivableTraits::COPY); - } + let packed = false; // Enums can't be packed in Rust. + let mut derives = derives_of_item(item, ctx, packed); + // For backwards compat, enums always derive + // Clone/Eq/PartialEq/Hash, even if we don't generate those by + // default. derives.insert( DerivableTraits::CLONE | DerivableTraits::HASH | @@ -3028,17 +3146,19 @@ impl CodeGenerator for Enum { // The custom derives callback may return a list of derive attributes; // add them to the end of the list. - let custom_derives; - if let Some(cb) = &ctx.options().parse_callbacks { - custom_derives = cb.add_derives(&name); - // In most cases this will be a no-op, since custom_derives will be empty. - derives.extend(custom_derives.iter().map(|s| s.as_str())); - }; + let custom_derives = ctx.options().all_callbacks(|cb| { + cb.add_derives(&DeriveInfo { + name: &name, + kind: DeriveTypeKind::Enum, + }) + }); + // In most cases this will be a no-op, since custom_derives will be empty. + derives.extend(custom_derives.iter().map(|s| s.as_str())); attrs.push(attributes::derives(&derives)); } - fn add_constant<'a>( + fn add_constant( ctx: &BindgenContext, enum_: &Type, // Only to avoid recomputing every time. @@ -3049,7 +3169,7 @@ impl CodeGenerator for Enum { variant_name: &Ident, referenced_name: &Ident, enum_rust_ty: proc_macro2::TokenStream, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, ) { let constant_name = if enum_.name().is_some() { if ctx.options().prepend_enum_name { @@ -3069,14 +3189,10 @@ impl CodeGenerator for Enum { } let repr = repr.to_rust_ty_or_opaque(ctx, item); + let has_typedef = ctx.is_enum_typedef_combo(item.id()); - let mut builder = EnumBuilder::new( - &name, - attrs, - repr, - variation, - item.codegen_depth(ctx), - ); + let mut builder = + EnumBuilder::new(&name, attrs, repr, variation, has_typedef); // A map where we keep a value -> variant relation. let mut seen_values = HashMap::<_, Ident>::default(); @@ -3154,7 +3270,7 @@ impl CodeGenerator for Enum { ctx, enum_ty, &ident, - &Ident::new(&*mangled_name, Span::call_site()), + &Ident::new(&mangled_name, Span::call_site()), existing_variant_name, enum_rust_ty.clone(), result, @@ -3223,7 +3339,7 @@ impl CodeGenerator for Enum { } /// Enum for the default type of macro constants. -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum MacroTypeVariation { /// Use i32 or i64 Signed, @@ -3267,7 +3383,7 @@ impl std::str::FromStr for MacroTypeVariation { } /// Enum for how aliases should be translated. -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum AliasVariation { /// Convert to regular Rust alias TypeAlias, @@ -3314,6 +3430,52 @@ impl std::str::FromStr for AliasVariation { } } +/// Enum for how non-Copy unions should be translated. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum NonCopyUnionStyle { + /// Wrap members in a type generated by bindgen. + BindgenWrapper, + /// Wrap members in [`::core::mem::ManuallyDrop`]. + /// + /// Note: `ManuallyDrop` was stabilized in Rust 1.20.0, do not use it if your + /// MSRV is lower. + ManuallyDrop, +} + +impl NonCopyUnionStyle { + /// Convert an `NonCopyUnionStyle` to its str representation. + pub fn as_str(&self) -> &'static str { + match self { + Self::BindgenWrapper => "bindgen_wrapper", + Self::ManuallyDrop => "manually_drop", + } + } +} + +impl Default for NonCopyUnionStyle { + fn default() -> Self { + Self::BindgenWrapper + } +} + +impl std::str::FromStr for NonCopyUnionStyle { + type Err = std::io::Error; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "bindgen_wrapper" => Ok(Self::BindgenWrapper), + "manually_drop" => Ok(Self::ManuallyDrop), + _ => Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + concat!( + "Got an invalid NonCopyUnionStyle. Accepted values ", + "are 'bindgen_wrapper' and 'manually_drop'" + ), + )), + } + } +} + /// Fallible conversion to an opaque blob. /// /// Implementors of this trait should provide the `try_get_layout` method to @@ -3705,7 +3867,7 @@ impl TryToRustTy for Type { } TypeKind::TypeParam => { let name = item.canonical_name(ctx); - let ident = ctx.rust_ident(&name); + let ident = ctx.rust_ident(name); Ok(quote! { #ident }) @@ -3821,13 +3983,27 @@ impl TryToRustTy for FunctionSig { // TODO: we might want to consider ignoring the reference return value. let ret = utils::fnsig_return_ty(ctx, self); let arguments = utils::fnsig_arguments(ctx, self); - let abi = self.abi(); + let abi = self.abi(ctx, None); match abi { - Abi::ThisCall if !ctx.options().rust_features().thiscall_abi => { + ClangAbi::Known(Abi::ThisCall) + if !ctx.options().rust_features().thiscall_abi => + { warn!("Skipping function with thiscall ABI that isn't supported by the configured Rust target"); Ok(proc_macro2::TokenStream::new()) } + ClangAbi::Known(Abi::Vectorcall) + if !ctx.options().rust_features().vectorcall_abi => + { + warn!("Skipping function with vectorcall ABI that isn't supported by the configured Rust target"); + Ok(proc_macro2::TokenStream::new()) + } + ClangAbi::Known(Abi::CUnwind) + if !ctx.options().rust_features().c_unwind_abi => + { + warn!("Skipping function with C-unwind ABI that isn't supported by the configured Rust target"); + Ok(proc_macro2::TokenStream::new()) + } _ => Ok(quote! { unsafe extern #abi fn ( #( #arguments ),* ) #ret }), @@ -3842,32 +4018,40 @@ impl CodeGenerator for Function { /// it. type Return = Option<u32>; - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) -> Self::Return { debug!("<Function as CodeGenerator>::codegen: item = {:?}", item); debug_assert!(item.is_enabled_for_codegen(ctx)); - // We can't currently do anything with Internal functions so just - // avoid generating anything for them. - match self.linkage() { - Linkage::Internal => return None, - Linkage::External => {} + let is_internal = matches!(self.linkage(), Linkage::Internal); + + if is_internal { + if ctx.options().wrap_static_fns { + result.items_to_serialize.push(item.id()); + } else { + // We can't do anything with Internal functions if we are not wrapping them so just + // avoid generating anything for them. + return None; + } } // Pure virtual methods have no actual symbol, so we can't generate // something meaningful for them. - match self.kind() { + let is_dynamic_function = match self.kind() { FunctionKind::Method(ref method_kind) if method_kind.is_pure_virtual() => { return None; } - _ => {} - } + FunctionKind::Function => { + ctx.options().dynamic_library_name.is_some() + } + _ => false, + }; // Similar to static member variables in a class template, we can't // generate bindings to template functions, because the set of @@ -3904,26 +4088,49 @@ impl CodeGenerator for Function { let mut attributes = vec![]; - if signature.must_use() && - ctx.options().rust_features().must_use_function - { - attributes.push(attributes::must_use()); + if ctx.options().rust_features().must_use_function { + let must_use = signature.must_use() || { + let ret_ty = signature + .return_type() + .into_resolver() + .through_type_refs() + .resolve(ctx); + ret_ty.must_use(ctx) + }; + + if must_use { + attributes.push(attributes::must_use()); + } } if let Some(comment) = item.comment(ctx) { attributes.push(attributes::doc(comment)); } - let abi = match signature.abi() { - Abi::ThisCall if !ctx.options().rust_features().thiscall_abi => { + let abi = match signature.abi(ctx, Some(name)) { + ClangAbi::Known(Abi::ThisCall) + if !ctx.options().rust_features().thiscall_abi => + { warn!("Skipping function with thiscall ABI that isn't supported by the configured Rust target"); return None; } - Abi::Win64 if signature.is_variadic() => { + ClangAbi::Known(Abi::Vectorcall) + if !ctx.options().rust_features().vectorcall_abi => + { + warn!("Skipping function with vectorcall ABI that isn't supported by the configured Rust target"); + return None; + } + ClangAbi::Known(Abi::CUnwind) + if !ctx.options().rust_features().c_unwind_abi => + { + warn!("Skipping function with C-unwind ABI that isn't supported by the configured Rust target"); + return None; + } + ClangAbi::Known(Abi::Win64) if signature.is_variadic() => { warn!("Skipping variadic function with Win64 ABI that isn't supported"); return None; } - Abi::Unknown(unknown_abi) => { + ClangAbi::Unknown(unknown_abi) => { panic!( "Invalid or unknown abi {:?} for function {:?} ({:?})", unknown_abi, canonical_name, self @@ -3939,13 +4146,17 @@ impl CodeGenerator for Function { write!(&mut canonical_name, "{}", times_seen).unwrap(); } + let mut has_link_name_attr = false; let link_name = mangled_name.unwrap_or(name); - if !utils::names_will_be_identical_after_mangling( - &canonical_name, - link_name, - Some(abi), - ) { + if !is_dynamic_function && + !utils::names_will_be_identical_after_mangling( + &canonical_name, + link_name, + Some(abi), + ) + { attributes.push(attributes::link_name(link_name)); + has_link_name_attr = true; } // Unfortunately this can't piggyback on the `attributes` list because @@ -3956,6 +4167,11 @@ impl CodeGenerator for Function { quote! { #[link(wasm_import_module = #name)] } }); + if is_internal && ctx.options().wrap_static_fns && !has_link_name_attr { + let name = canonical_name.clone() + ctx.wrap_static_fns_suffix(); + attributes.push(attributes::link_name(&name)); + } + let ident = ctx.rust_ident(canonical_name); let tokens = quote! { #wasm_link_attribute @@ -3966,16 +4182,10 @@ impl CodeGenerator for Function { }; // If we're doing dynamic binding generation, add to the dynamic items. - if ctx.options().dynamic_library_name.is_some() && - self.kind() == FunctionKind::Function - { + if is_dynamic_function { let args_identifiers = utils::fnsig_argument_identifiers(ctx, signature); - let return_item = ctx.resolve_item(signature.return_type()); - let ret_ty = match *return_item.kind().expect_type().kind() { - TypeKind::Void => quote! {()}, - _ => return_item.to_rust_ty_or_opaque(ctx, &()), - }; + let ret_ty = utils::fnsig_return_ty(ctx, signature); result.dynamic_items().push( ident, abi, @@ -3985,6 +4195,8 @@ impl CodeGenerator for Function { args_identifiers, ret, ret_ty, + attributes, + ctx, ); } else { result.push(tokens); @@ -3996,9 +4208,19 @@ impl CodeGenerator for Function { fn objc_method_codegen( ctx: &BindgenContext, method: &ObjCMethod, + methods: &mut Vec<proc_macro2::TokenStream>, class_name: Option<&str>, + rust_class_name: &str, prefix: &str, -) -> proc_macro2::TokenStream { +) { + // This would ideally resolve the method into an Item, and use + // Item::process_before_codegen; however, ObjC methods are not currently + // made into function items. + let name = format!("{}::{}{}", rust_class_name, prefix, method.rust_name()); + if ctx.options().blocklisted_items.matches(name) { + return; + } + let signature = method.signature(); let fn_args = utils::fnsig_arguments(ctx, signature); let fn_ret = utils::fnsig_return_ty(ctx, signature); @@ -4018,48 +4240,54 @@ fn objc_method_codegen( let methods_and_args = method.format_method_call(&fn_args); - let body = if method.is_class_method() { - let class_name = ctx.rust_ident( - class_name - .expect("Generating a class method without class name?") - .to_owned(), - ); - quote! { - msg_send!(class!(#class_name), #methods_and_args) - } - } else { - quote! { - msg_send!(*self, #methods_and_args) - } + let body = { + let body = if method.is_class_method() { + let class_name = ctx.rust_ident( + class_name + .expect("Generating a class method without class name?"), + ); + quote!(msg_send!(class!(#class_name), #methods_and_args)) + } else { + quote!(msg_send!(*self, #methods_and_args)) + }; + + ctx.wrap_unsafe_ops(body) }; let method_name = ctx.rust_ident(format!("{}{}", prefix, method.rust_name())); - quote! { + methods.push(quote! { unsafe fn #method_name #sig where <Self as std::ops::Deref>::Target: objc::Message + Sized { #body } - } + }); } impl CodeGenerator for ObjCInterface { type Extra = Item; type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) { debug_assert!(item.is_enabled_for_codegen(ctx)); let mut impl_items = vec![]; + let rust_class_name = item.path_for_allowlisting(ctx)[1..].join("::"); for method in self.methods() { - let impl_item = objc_method_codegen(ctx, method, None, ""); - impl_items.push(impl_item); + objc_method_codegen( + ctx, + method, + &mut impl_items, + None, + &rust_class_name, + "", + ); } for class_method in self.class_methods() { @@ -4069,13 +4297,14 @@ impl CodeGenerator for ObjCInterface { .map(|m| m.rust_name()) .any(|x| x == class_method.rust_name()); let prefix = if ambiquity { "class_" } else { "" }; - let impl_item = objc_method_codegen( + objc_method_codegen( ctx, class_method, + &mut impl_items, Some(self.name()), + &rust_class_name, prefix, ); - impl_items.push(impl_item); } let trait_name = ctx.rust_ident(self.rust_name()); @@ -4090,7 +4319,7 @@ impl CodeGenerator for ObjCInterface { .collect(); quote! { - pub trait #trait_name <#(#template_names),*> : #trait_constraints { + pub trait #trait_name <#(#template_names:'static),*> : #trait_constraints { #( #impl_items )* } } @@ -4106,7 +4335,7 @@ impl CodeGenerator for ObjCInterface { if !self.is_category() && !self.is_protocol() { let struct_block = quote! { #[repr(transparent)] - #[derive(Clone)] + #[derive(Debug, Copy, Clone)] pub struct #class_name(pub id); impl std::ops::Deref for #class_name { type Target = objc::runtime::Object; @@ -4120,7 +4349,7 @@ impl CodeGenerator for ObjCInterface { impl #class_name { pub fn alloc() -> Self { Self(unsafe { - msg_send!(objc::class!(#class_name), alloc) + msg_send!(class!(#class_name), alloc) }) } } @@ -4247,7 +4476,8 @@ impl CodeGenerator for ObjCInterface { pub(crate) fn codegen( context: BindgenContext, -) -> (Vec<proc_macro2::TokenStream>, BindgenOptions) { +) -> Result<(proc_macro2::TokenStream, BindgenOptions, Vec<String>), CodegenError> +{ context.gen(|context| { let _t = context.timer("codegen"); let counter = Cell::new(0); @@ -4293,25 +4523,77 @@ pub(crate) fn codegen( if let Some(ref lib_name) = context.options().dynamic_library_name { let lib_ident = context.rust_ident(lib_name); let dynamic_items_tokens = - result.dynamic_items().get_tokens(lib_ident); + result.dynamic_items().get_tokens(lib_ident, context); result.push(dynamic_items_tokens); } - result.items + utils::serialize_items(&result, context)?; + + Ok(postprocessing::postprocessing( + result.items, + context.options(), + )) }) } pub mod utils { - use super::{error, ToRustTyOrOpaque}; + use super::serialize::CSerialize; + use super::{error, CodegenError, CodegenResult, ToRustTyOrOpaque}; use crate::ir::context::BindgenContext; - use crate::ir::function::{Abi, FunctionSig}; + use crate::ir::function::{Abi, ClangAbi, FunctionSig}; use crate::ir::item::{Item, ItemCanonicalPath}; use crate::ir::ty::TypeKind; + use crate::{args_are_cpp, file_is_cpp}; use proc_macro2; use std::borrow::Cow; use std::mem; + use std::path::PathBuf; use std::str::FromStr; + pub(super) fn serialize_items( + result: &CodegenResult, + context: &BindgenContext, + ) -> Result<(), CodegenError> { + if result.items_to_serialize.is_empty() { + return Ok(()); + } + + let path = context + .options() + .wrap_static_fns_path + .as_ref() + .map(PathBuf::from) + .unwrap_or_else(|| { + std::env::temp_dir().join("bindgen").join("extern") + }); + + let dir = path.parent().unwrap(); + + if !dir.exists() { + std::fs::create_dir_all(&dir)?; + } + + let is_cpp = args_are_cpp(&context.options().clang_args) || + context + .options() + .input_headers + .iter() + .any(|h| file_is_cpp(h)); + + let source_path = path.with_extension(if is_cpp { "cpp" } else { "c" }); + + let mut code = Vec::new(); + + for &id in &result.items_to_serialize { + let item = context.resolve_item(id); + item.serialize(context, (), &mut vec![], &mut code)?; + } + + std::fs::write(source_path, code)?; + + Ok(()) + } + pub fn prepend_bitfield_unit_type( ctx: &BindgenContext, result: &mut Vec<proc_macro2::TokenStream>, @@ -4342,7 +4624,7 @@ pub mod utils { } } else { quote! { - use objc; + use objc::{self, msg_send, sel, sel_impl, class}; } }; @@ -4396,6 +4678,9 @@ pub mod utils { pub struct __BindgenUnionField<T>(::#prefix::marker::PhantomData<T>); }; + let transmute = + ctx.wrap_unsafe_ops(quote!(::#prefix::mem::transmute(self))); + let union_field_impl = quote! { impl<T> __BindgenUnionField<T> { #[inline] @@ -4405,12 +4690,12 @@ pub mod utils { #[inline] pub unsafe fn as_ref(&self) -> &T { - ::#prefix::mem::transmute(self) + #transmute } #[inline] pub unsafe fn as_mut(&mut self) -> &mut T { - ::#prefix::mem::transmute(self) + #transmute } } }; @@ -4505,6 +4790,13 @@ pub mod utils { ::#prefix::marker::PhantomData<T>, [T; 0]); }; + let from_raw_parts = ctx.wrap_unsafe_ops(quote! ( + ::#prefix::slice::from_raw_parts(self.as_ptr(), len) + )); + let from_raw_parts_mut = ctx.wrap_unsafe_ops(quote! ( + ::#prefix::slice::from_raw_parts_mut(self.as_mut_ptr(), len) + )); + let incomplete_array_impl = quote! { impl<T> __IncompleteArrayField<T> { #[inline] @@ -4524,12 +4816,12 @@ pub mod utils { #[inline] pub unsafe fn as_slice(&self, len: usize) -> &[T] { - ::#prefix::slice::from_raw_parts(self.as_ptr(), len) + #from_raw_parts } #[inline] pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { - ::#prefix::slice::from_raw_parts_mut(self.as_mut_ptr(), len) + #from_raw_parts_mut } } }; @@ -4618,21 +4910,52 @@ pub mod utils { }) } - pub fn fnsig_return_ty( + fn fnsig_return_ty_internal( ctx: &BindgenContext, sig: &FunctionSig, + include_arrow: bool, ) -> proc_macro2::TokenStream { - let return_item = ctx.resolve_item(sig.return_type()); - if let TypeKind::Void = *return_item.kind().expect_type().kind() { - quote! {} + if sig.is_divergent() { + return if include_arrow { + quote! { -> ! } + } else { + quote! { ! } + }; + } + + let canonical_type_kind = sig + .return_type() + .into_resolver() + .through_type_refs() + .through_type_aliases() + .resolve(ctx) + .kind() + .expect_type() + .kind(); + + if let TypeKind::Void = canonical_type_kind { + return if include_arrow { + quote! {} + } else { + quote! { () } + }; + } + + let ret_ty = sig.return_type().to_rust_ty_or_opaque(ctx, &()); + if include_arrow { + quote! { -> #ret_ty } } else { - let ret_ty = return_item.to_rust_ty_or_opaque(ctx, &()); - quote! { - -> #ret_ty - } + ret_ty } } + pub fn fnsig_return_ty( + ctx: &BindgenContext, + sig: &FunctionSig, + ) -> proc_macro2::TokenStream { + fnsig_return_ty_internal(ctx, sig, /* include_arrow = */ true) + } + pub fn fnsig_arguments( ctx: &BindgenContext, sig: &FunctionSig, @@ -4745,14 +5068,9 @@ pub mod utils { arg_item.to_rust_ty_or_opaque(ctx, &()) }); - let return_item = ctx.resolve_item(sig.return_type()); - let ret_ty = - if let TypeKind::Void = *return_item.kind().expect_type().kind() { - quote! { () } - } else { - return_item.to_rust_ty_or_opaque(ctx, &()) - }; - + let ret_ty = fnsig_return_ty_internal( + ctx, sig, /* include_arrow = */ false, + ); quote! { *const ::block::Block<(#(#args,)*), #ret_ty> } @@ -4761,10 +5079,10 @@ pub mod utils { // Returns true if `canonical_name` will end up as `mangled_name` at the // machine code level, i.e. after LLVM has applied any target specific // mangling. - pub fn names_will_be_identical_after_mangling( + pub(crate) fn names_will_be_identical_after_mangling( canonical_name: &str, mangled_name: &str, - call_conv: Option<Abi>, + call_conv: Option<ClangAbi>, ) -> bool { // If the mangled name and the canonical name are the same then no // mangling can have happened between the two versions. @@ -4777,13 +5095,13 @@ pub mod utils { let mangled_name = mangled_name.as_bytes(); let (mangling_prefix, expect_suffix) = match call_conv { - Some(Abi::C) | + Some(ClangAbi::Known(Abi::C)) | // None is the case for global variables None => { (b'_', false) } - Some(Abi::Stdcall) => (b'_', true), - Some(Abi::Fastcall) => (b'@', true), + Some(ClangAbi::Known(Abi::Stdcall)) => (b'_', true), + Some(ClangAbi::Known(Abi::Fastcall)) => (b'@', true), // This is something we don't recognize, stay on the safe side // by emitting the `#[link_name]` attribute diff --git a/codegen/postprocessing/merge_extern_blocks.rs b/codegen/postprocessing/merge_extern_blocks.rs new file mode 100644 index 0000000..05e7e9e --- /dev/null +++ b/codegen/postprocessing/merge_extern_blocks.rs @@ -0,0 +1,66 @@ +use syn::{ + visit_mut::{visit_item_mod_mut, VisitMut}, + Item, ItemForeignMod, ItemMod, +}; + +pub(super) fn merge_extern_blocks(item_mod: &mut ItemMod) { + Visitor.visit_item_mod_mut(item_mod) +} + +struct Visitor; + +impl VisitMut for Visitor { + fn visit_item_mod_mut(&mut self, item_mod: &mut ItemMod) { + if let Some((_, ref mut items)) = item_mod.content { + // Keep all the extern blocks in a different `Vec` for faster search. + let mut extern_blocks = Vec::<ItemForeignMod>::new(); + + for item in std::mem::take(items) { + if let Item::ForeignMod(ItemForeignMod { + attrs, + abi, + brace_token, + items: extern_block_items, + }) = item + { + let mut exists = false; + for extern_block in &mut extern_blocks { + // Check if there is a extern block with the same ABI and + // attributes. + if extern_block.attrs == attrs && + extern_block.abi == abi + { + // Merge the items of the two blocks. + extern_block + .items + .extend_from_slice(&extern_block_items); + exists = true; + break; + } + } + // If no existing extern block had the same ABI and attributes, store + // it. + if !exists { + extern_blocks.push(ItemForeignMod { + attrs, + abi, + brace_token, + items: extern_block_items, + }); + } + } else { + // If the item is not an extern block, we don't have to do anything and just + // push it back. + items.push(item); + } + } + + // Move all the extern blocks alongside the rest of the items. + for extern_block in extern_blocks { + items.push(Item::ForeignMod(extern_block)); + } + } + + visit_item_mod_mut(self, item_mod) + } +} diff --git a/codegen/postprocessing/mod.rs b/codegen/postprocessing/mod.rs new file mode 100644 index 0000000..1d5a498 --- /dev/null +++ b/codegen/postprocessing/mod.rs @@ -0,0 +1,66 @@ +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::{parse2, ItemMod}; + +use crate::BindgenOptions; + +mod merge_extern_blocks; +mod sort_semantically; + +use merge_extern_blocks::merge_extern_blocks; +use sort_semantically::sort_semantically; + +struct PostProcessingPass { + should_run: fn(&BindgenOptions) -> bool, + run: fn(&mut ItemMod), +} + +// TODO: This can be a const fn when mutable references are allowed in const +// context. +macro_rules! pass { + ($pass:ident) => { + PostProcessingPass { + should_run: |options| options.$pass, + run: |item_mod| $pass(item_mod), + } + }; +} + +const PASSES: &[PostProcessingPass] = + &[pass!(merge_extern_blocks), pass!(sort_semantically)]; + +pub(crate) fn postprocessing( + items: Vec<TokenStream>, + options: &BindgenOptions, +) -> TokenStream { + let require_syn = PASSES.iter().any(|pass| (pass.should_run)(options)); + if !require_syn { + return items.into_iter().collect(); + } + let module_wrapped_tokens = + quote!(mod wrapper_for_postprocessing_hack { #( #items )* }); + + // This syn business is a hack, for now. This means that we are re-parsing already + // generated code using `syn` (as opposed to `quote`) because `syn` provides us more + // control over the elements. + // One caveat is that some of the items coming from `quote`d output might have + // multiple items within them. Hence, we have to wrap the incoming in a `mod`. + // The `unwrap` here is deliberate because bindgen should generate valid rust items at all + // times. + let mut item_mod = parse2::<ItemMod>(module_wrapped_tokens).unwrap(); + + for pass in PASSES { + if (pass.should_run)(options) { + (pass.run)(&mut item_mod); + } + } + + let synful_items = item_mod + .content + .map(|(_, items)| items) + .unwrap_or_default() + .into_iter() + .map(|item| item.into_token_stream()); + + quote! { #( #synful_items )* } +} diff --git a/codegen/postprocessing/sort_semantically.rs b/codegen/postprocessing/sort_semantically.rs new file mode 100644 index 0000000..4f23ab7 --- /dev/null +++ b/codegen/postprocessing/sort_semantically.rs @@ -0,0 +1,38 @@ +use syn::{ + visit_mut::{visit_item_mod_mut, VisitMut}, + Item, ItemMod, +}; + +pub(super) fn sort_semantically(item_mod: &mut ItemMod) { + Visitor.visit_item_mod_mut(item_mod) +} + +struct Visitor; + +impl VisitMut for Visitor { + fn visit_item_mod_mut(&mut self, item_mod: &mut ItemMod) { + if let Some((_, ref mut items)) = item_mod.content { + items.sort_by_key(|item| match item { + Item::Type(_) => 0, + Item::Struct(_) => 1, + Item::Const(_) => 2, + Item::Fn(_) => 3, + Item::Enum(_) => 4, + Item::Union(_) => 5, + Item::Static(_) => 6, + Item::Trait(_) => 7, + Item::TraitAlias(_) => 8, + Item::Impl(_) => 9, + Item::Mod(_) => 10, + Item::Use(_) => 11, + Item::Verbatim(_) => 12, + Item::ExternCrate(_) => 13, + Item::ForeignMod(_) => 14, + Item::Macro(_) => 15, + Item::Macro2(_) => 16, + _ => 18, + }); + } + visit_item_mod_mut(self, item_mod) + } +} diff --git a/codegen/serialize.rs b/codegen/serialize.rs new file mode 100644 index 0000000..217098e --- /dev/null +++ b/codegen/serialize.rs @@ -0,0 +1,356 @@ +use std::io::Write; + +use crate::callbacks::IntKind; + +use crate::ir::comp::CompKind; +use crate::ir::context::{BindgenContext, TypeId}; +use crate::ir::function::{Function, FunctionKind}; +use crate::ir::item::Item; +use crate::ir::item::ItemCanonicalName; +use crate::ir::item_kind::ItemKind; +use crate::ir::ty::{FloatKind, Type, TypeKind}; + +use super::CodegenError; + +fn get_loc(item: &Item) -> String { + item.location() + .map(|x| x.to_string()) + .unwrap_or_else(|| "unknown".to_owned()) +} + +pub(crate) trait CSerialize<'a> { + type Extra; + + fn serialize<W: Write>( + &self, + ctx: &BindgenContext, + extra: Self::Extra, + stack: &mut Vec<String>, + writer: &mut W, + ) -> Result<(), CodegenError>; +} + +impl<'a> CSerialize<'a> for Item { + type Extra = (); + + fn serialize<W: Write>( + &self, + ctx: &BindgenContext, + (): Self::Extra, + stack: &mut Vec<String>, + writer: &mut W, + ) -> Result<(), CodegenError> { + match self.kind() { + ItemKind::Function(func) => { + func.serialize(ctx, self, stack, writer) + } + kind => { + return Err(CodegenError::Serialize { + msg: format!("Cannot serialize item kind {:?}", kind), + loc: get_loc(self), + }); + } + } + } +} + +impl<'a> CSerialize<'a> for Function { + type Extra = &'a Item; + + fn serialize<W: Write>( + &self, + ctx: &BindgenContext, + item: Self::Extra, + stack: &mut Vec<String>, + writer: &mut W, + ) -> Result<(), CodegenError> { + if self.kind() != FunctionKind::Function { + return Err(CodegenError::Serialize { + msg: format!( + "Cannot serialize function kind {:?}", + self.kind(), + ), + loc: get_loc(item), + }); + } + + let signature = match ctx.resolve_type(self.signature()).kind() { + TypeKind::Function(signature) => signature, + _ => unreachable!(), + }; + + let name = self.name(); + + // Function argoments stored as `(name, type_id)` tuples. + let args = { + let mut count = 0; + + signature + .argument_types() + .iter() + .cloned() + .map(|(opt_name, type_id)| { + ( + opt_name.unwrap_or_else(|| { + let name = format!("arg_{}", count); + count += 1; + name + }), + type_id, + ) + }) + .collect::<Vec<_>>() + }; + + // The name used for the wrapper self. + let wrap_name = format!("{}{}", name, ctx.wrap_static_fns_suffix()); + // The function's return type + let ret_ty = signature.return_type(); + + // Write `ret_ty wrap_name(args) asm("wrap_name");` + ret_ty.serialize(ctx, (), stack, writer)?; + write!(writer, " {}(", wrap_name)?; + if args.is_empty() { + write!(writer, "void")?; + } else { + serialize_sep( + ", ", + args.iter(), + ctx, + writer, + |(name, type_id), ctx, buf| { + type_id.serialize(ctx, (), &mut vec![name.clone()], buf) + }, + )?; + } + writeln!(writer, ") asm(\"{}\");", wrap_name)?; + + // Write `ret_ty wrap_name(args) { return name(arg_names)' }` + ret_ty.serialize(ctx, (), stack, writer)?; + write!(writer, " {}(", wrap_name)?; + serialize_sep( + ", ", + args.iter(), + ctx, + writer, + |(name, type_id), _, buf| { + type_id.serialize(ctx, (), &mut vec![name.clone()], buf) + }, + )?; + write!(writer, ") {{ return {}(", name)?; + serialize_sep(", ", args.iter(), ctx, writer, |(name, _), _, buf| { + write!(buf, "{}", name).map_err(From::from) + })?; + writeln!(writer, "); }}")?; + + Ok(()) + } +} + +impl<'a> CSerialize<'a> for TypeId { + type Extra = (); + + fn serialize<W: Write>( + &self, + ctx: &BindgenContext, + (): Self::Extra, + stack: &mut Vec<String>, + writer: &mut W, + ) -> Result<(), CodegenError> { + let item = ctx.resolve_item(*self); + item.expect_type().serialize(ctx, item, stack, writer) + } +} + +impl<'a> CSerialize<'a> for Type { + type Extra = &'a Item; + + fn serialize<W: Write>( + &self, + ctx: &BindgenContext, + item: Self::Extra, + stack: &mut Vec<String>, + writer: &mut W, + ) -> Result<(), CodegenError> { + match self.kind() { + TypeKind::Void => { + if self.is_const() { + write!(writer, "const ")?; + } + write!(writer, "void")? + } + TypeKind::NullPtr => { + if self.is_const() { + write!(writer, "const ")?; + } + write!(writer, "nullptr_t")? + } + TypeKind::Int(int_kind) => { + if self.is_const() { + write!(writer, "const ")?; + } + match int_kind { + IntKind::Bool => write!(writer, "bool")?, + IntKind::SChar => write!(writer, "signed char")?, + IntKind::UChar => write!(writer, "unsigned char")?, + IntKind::WChar => write!(writer, "wchar_t")?, + IntKind::Short => write!(writer, "short")?, + IntKind::UShort => write!(writer, "unsigned short")?, + IntKind::Int => write!(writer, "int")?, + IntKind::UInt => write!(writer, "unsigned int")?, + IntKind::Long => write!(writer, "long")?, + IntKind::ULong => write!(writer, "unsigned long")?, + IntKind::LongLong => write!(writer, "long long")?, + IntKind::ULongLong => write!(writer, "unsigned long long")?, + IntKind::Char { .. } => write!(writer, "char")?, + int_kind => { + return Err(CodegenError::Serialize { + msg: format!( + "Cannot serialize integer kind {:?}", + int_kind + ), + loc: get_loc(item), + }) + } + } + } + TypeKind::Float(float_kind) => { + if self.is_const() { + write!(writer, "const ")?; + } + match float_kind { + FloatKind::Float => write!(writer, "float")?, + FloatKind::Double => write!(writer, "double")?, + FloatKind::LongDouble => write!(writer, "long double")?, + FloatKind::Float128 => write!(writer, "__float128")?, + } + } + TypeKind::Complex(float_kind) => { + if self.is_const() { + write!(writer, "const ")?; + } + match float_kind { + FloatKind::Float => write!(writer, "float complex")?, + FloatKind::Double => write!(writer, "double complex")?, + FloatKind::LongDouble => { + write!(writer, "long double complex")? + } + FloatKind::Float128 => write!(writer, "__complex128")?, + } + } + TypeKind::Alias(type_id) => { + if let Some(name) = self.name() { + if self.is_const() { + write!(writer, "const {}", name)?; + } else { + write!(writer, "{}", name)?; + } + } else { + type_id.serialize(ctx, (), stack, writer)?; + } + } + TypeKind::Array(type_id, length) => { + type_id.serialize(ctx, (), stack, writer)?; + write!(writer, " [{}]", length)? + } + TypeKind::Function(signature) => { + if self.is_const() { + stack.push("const ".to_string()); + } + + signature.return_type().serialize( + ctx, + (), + &mut vec![], + writer, + )?; + + write!(writer, " (")?; + while let Some(item) = stack.pop() { + write!(writer, "{}", item)?; + } + write!(writer, ")")?; + + write!(writer, " (")?; + serialize_sep( + ", ", + signature.argument_types().iter(), + ctx, + writer, + |(name, type_id), ctx, buf| { + let mut stack = vec![]; + if let Some(name) = name { + stack.push(name.clone()); + } + type_id.serialize(ctx, (), &mut stack, buf) + }, + )?; + write!(writer, ")")? + } + TypeKind::ResolvedTypeRef(type_id) => { + if self.is_const() { + write!(writer, "const ")?; + } + type_id.serialize(ctx, (), stack, writer)? + } + TypeKind::Pointer(type_id) => { + if self.is_const() { + stack.push("*const ".to_owned()); + } else { + stack.push("*".to_owned()); + } + type_id.serialize(ctx, (), stack, writer)? + } + TypeKind::Comp(comp_info) => { + if self.is_const() { + write!(writer, "const ")?; + } + + let name = item.canonical_name(ctx); + + match comp_info.kind() { + CompKind::Struct => write!(writer, "struct {}", name)?, + CompKind::Union => write!(writer, "union {}", name)?, + }; + } + ty => { + return Err(CodegenError::Serialize { + msg: format!("Cannot serialize type kind {:?}", ty), + loc: get_loc(item), + }) + } + }; + + if !stack.is_empty() { + write!(writer, " ")?; + while let Some(item) = stack.pop() { + write!(writer, "{}", item)?; + } + } + + Ok(()) + } +} + +fn serialize_sep< + W: Write, + F: FnMut(I::Item, &BindgenContext, &mut W) -> Result<(), CodegenError>, + I: Iterator, +>( + sep: &str, + mut iter: I, + ctx: &BindgenContext, + buf: &mut W, + mut f: F, +) -> Result<(), CodegenError> { + if let Some(item) = iter.next() { + f(item, ctx, buf)?; + let sep = sep.as_bytes(); + for item in iter { + buf.write_all(sep)?; + f(item, ctx, buf)?; + } + } + + Ok(()) +} diff --git a/src/codegen/struct_layout.rs b/codegen/struct_layout.rs index 657be0b..ddac1b0 100644 --- a/src/codegen/struct_layout.rs +++ b/codegen/struct_layout.rs @@ -20,6 +20,7 @@ pub struct StructLayoutTracker<'a> { is_packed: bool, known_type_layout: Option<Layout>, is_rust_union: bool, + can_copy_union_fields: bool, latest_offset: usize, padding_count: usize, latest_field_layout: Option<Layout>, @@ -90,8 +91,8 @@ impl<'a> StructLayoutTracker<'a> { ) -> Self { let known_type_layout = ty.layout(ctx); let is_packed = comp.is_packed(ctx, known_type_layout.as_ref()); - let is_rust_union = comp.is_union() && - comp.can_be_rust_union(ctx, known_type_layout.as_ref()); + let (is_rust_union, can_copy_union_fields) = + comp.is_rust_union(ctx, known_type_layout.as_ref(), name); StructLayoutTracker { name, ctx, @@ -99,6 +100,7 @@ impl<'a> StructLayoutTracker<'a> { is_packed, known_type_layout, is_rust_union, + can_copy_union_fields, latest_offset: 0, padding_count: 0, latest_field_layout: None, @@ -107,6 +109,10 @@ impl<'a> StructLayoutTracker<'a> { } } + pub fn can_copy_union_fields(&self) -> bool { + self.can_copy_union_fields + } + pub fn is_rust_union(&self) -> bool { self.is_rust_union } diff --git a/csmith-fuzzing/README.md b/csmith-fuzzing/README.md deleted file mode 100644 index cdd6d08..0000000 --- a/csmith-fuzzing/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Fuzzing `bindgen` with `csmith` - -[`csmith`][csmith] generates random C and C++ programs that can be used as test -cases for compilers. When testing `bindgen` with `csmith`, we interpret the -generated programs as header files, and emit Rust bindings to them. If `bindgen` -panics, the emitted bindings won't compile with `rustc`, or the generated layout -tests in the bindings fail, then we report an issue containing the test case! - -<!-- START doctoc generated TOC please keep comment here to allow auto update --> -<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> - - -- [Prerequisites](#prerequisites) -- [Running the Fuzzer](#running-the-fuzzer) -- [Reporting Issues](#reporting-issues) - -<!-- END doctoc generated TOC please keep comment here to allow auto update --> - -## Prerequisites - -Requires `python3`, `csmith`, and `creduce` to be in `$PATH`. - -Many OS package managers have `csmith` and `creduce` packages: - -``` -$ sudo apt install csmith creduce -$ brew install csmith creduce -$ # Etc... -``` - -## Running the Fuzzer - -Run `csmith` and test `bindgen` on the generated test cases with this command: - -``` -$ ./driver.py -``` - -The driver will keep running until it encounters an error in `bindgen`. - -Each invocation of `./driver.py` will use its own temporary directories, so -running it in multiple terminals in parallel is supported. - -`csmith` is run with `--no-checksum --nomain --max-block-size 1 ---max-block-depth 1` which disables the `main` function, and makes function -bodies as simple as possible as `bindgen` does not care about them, but they -cannot be completely disabled in `csmith`. Run `csmith --help` to see what -exactly those options do. - -## Reporting Issues - -Once the fuzz driver finds a test case that causes some kind of error in -`bindgen` or its emitted bindings, it is helpful to -[run C-Reduce on the test case][creducing] to remove the parts that are -irrelevant to reproducing the error. This is ***very*** helpful for the folks -who further investigate the issue and come up with a fix! - -Additionally, mention that you discovered the issue via `csmith` and we will add -the `A-csmith` label. You can find all the issues discovered with `csmith`, and -related to fuzzing with `csmith`, by looking up -[all issues tagged with the `A-csmith` label][csmith-issues]. - -[csmith]: https://github.com/csmith-project/csmith -[creducing]: ../CONTRIBUTING.md#using-creduce-to-minimize-test-cases -[csmith-issues]: https://github.com/rust-lang/rust-bindgen/issues?q=label%3AA-csmith @@ -1,7 +1,7 @@ /// Generating build depfiles from parsed bindings. use std::{collections::BTreeSet, path::PathBuf}; -#[derive(Debug)] +#[derive(Clone, Debug)] pub(crate) struct DepfileSpec { pub output_module: String, pub depfile_path: PathBuf, diff --git a/src/extra_assertions.rs b/extra_assertions.rs index 0888bf3..0888bf3 100644 --- a/src/extra_assertions.rs +++ b/extra_assertions.rs diff --git a/src/features.rs b/features.rs index a786f07..4fee5d6 100644 --- a/src/features.rs +++ b/features.rs @@ -2,6 +2,7 @@ #![deny(missing_docs)] #![deny(unused_extern_crates)] +#![allow(deprecated)] use std::io; use std::str::FromStr; @@ -86,35 +87,35 @@ macro_rules! rust_target_base { ( $x_macro:ident ) => { $x_macro!( /// Rust stable 1.0 - => Stable_1_0 => 1.0; + #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_0 => 1.0; /// Rust stable 1.17 /// * Static lifetime elision ([RFC 1623](https://github.com/rust-lang/rfcs/blob/master/text/1623-static.md)) - => Stable_1_17 => 1.17; + #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_17 => 1.17; /// Rust stable 1.19 /// * Untagged unions ([RFC 1444](https://github.com/rust-lang/rfcs/blob/master/text/1444-union.md)) - => Stable_1_19 => 1.19; + #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_19 => 1.19; /// Rust stable 1.20 /// * Associated constants ([PR](https://github.com/rust-lang/rust/pull/42809)) - => Stable_1_20 => 1.20; + #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_20 => 1.20; /// Rust stable 1.21 /// * Builtin impls for `Clone` ([PR](https://github.com/rust-lang/rust/pull/43690)) - => Stable_1_21 => 1.21; + #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_21 => 1.21; /// Rust stable 1.25 /// * `repr(align)` ([PR](https://github.com/rust-lang/rust/pull/47006)) - => Stable_1_25 => 1.25; + #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_25 => 1.25; /// Rust stable 1.26 /// * [i128 / u128 support](https://doc.rust-lang.org/std/primitive.i128.html) - => Stable_1_26 => 1.26; + #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_26 => 1.26; /// Rust stable 1.27 /// * `must_use` attribute on functions ([PR](https://github.com/rust-lang/rust/pull/48925)) - => Stable_1_27 => 1.27; + #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_27 => 1.27; /// Rust stable 1.28 /// * `repr(transparent)` ([PR](https://github.com/rust-lang/rust/pull/51562)) - => Stable_1_28 => 1.28; + #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_28 => 1.28; /// Rust stable 1.30 /// * `const fn` support for limited cases ([PR](https://github.com/rust-lang/rust/pull/54835/) /// * [c_void available in core](https://doc.rust-lang.org/core/ffi/enum.c_void.html) - => Stable_1_30 => 1.30; + #[deprecated = "This rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues"] => Stable_1_30 => 1.30; /// Rust stable 1.33 /// * repr(packed(N)) ([PR](https://github.com/rust-lang/rust/pull/57049)) => Stable_1_33 => 1.33; @@ -127,8 +128,13 @@ macro_rules! rust_target_base { /// Rust stable 1.47 /// * `larger_arrays` ([Tracking issue](https://github.com/rust-lang/rust/pull/74060)) => Stable_1_47 => 1.47; + /// Rust stable 1.64 + /// * `core_ffi_c` ([Tracking issue](https://github.com/rust-lang/rust/issues/94501)) + => Stable_1_64 => 1.64; /// Nightly rust /// * `thiscall` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/42202)) + /// * `vectorcall` calling convention (no tracking issue) + /// * `c_unwind` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/74990)) => Nightly => nightly; ); } @@ -138,7 +144,7 @@ rust_target_base!(rust_target_def); rust_target_base!(rust_target_values_def); /// Latest stable release of Rust -pub const LATEST_STABLE_RUST: RustTarget = RustTarget::Stable_1_47; +pub const LATEST_STABLE_RUST: RustTarget = RustTarget::Stable_1_64; /// Create RustFeatures struct definition, new(), and a getter for each field macro_rules! rust_feature_def { @@ -232,8 +238,13 @@ rust_feature_def!( Stable_1_47 { => larger_arrays; } + Stable_1_64 { + => core_ffi_c; + } Nightly { => thiscall_abi; + => vectorcall_abi; + => c_unwind_abi; } ); @@ -259,7 +270,8 @@ mod test { !f_1_0.associated_const && !f_1_0.builtin_clone_impls && !f_1_0.repr_align && - !f_1_0.thiscall_abi + !f_1_0.thiscall_abi && + !f_1_0.vectorcall_abi ); let f_1_21 = RustFeatures::from(RustTarget::Stable_1_21); assert!( @@ -269,7 +281,8 @@ mod test { f_1_21.associated_const && f_1_21.builtin_clone_impls && !f_1_21.repr_align && - !f_1_21.thiscall_abi + !f_1_21.thiscall_abi && + !f_1_21.vectorcall_abi ); let f_nightly = RustFeatures::from(RustTarget::Nightly); assert!( @@ -280,7 +293,9 @@ mod test { f_nightly.builtin_clone_impls && f_nightly.maybe_uninit && f_nightly.repr_align && - f_nightly.thiscall_abi + f_nightly.thiscall_abi && + f_nightly.vectorcall_abi && + f_nightly.c_unwind_abi ); } diff --git a/src/ir/analysis/derive.rs b/ir/analysis/derive.rs index f63458e..d888cd5 100644 --- a/src/ir/analysis/derive.rs +++ b/ir/analysis/derive.rs @@ -485,11 +485,11 @@ impl DeriveTrait { fn consider_edge_tmpl_inst(&self) -> EdgePredicate { match self { DeriveTrait::PartialEqOrPartialOrd => consider_edge_default, - _ => |kind| match kind { - EdgeKind::TemplateArgument | EdgeKind::TemplateDeclaration => { - true - } - _ => false, + _ => |kind| { + matches!( + kind, + EdgeKind::TemplateArgument | EdgeKind::TemplateDeclaration + ) }, } } diff --git a/src/ir/analysis/has_destructor.rs b/ir/analysis/has_destructor.rs index 74fd73d..74fd73d 100644 --- a/src/ir/analysis/has_destructor.rs +++ b/ir/analysis/has_destructor.rs diff --git a/src/ir/analysis/has_float.rs b/ir/analysis/has_float.rs index bbf2126..bbf2126 100644 --- a/src/ir/analysis/has_float.rs +++ b/ir/analysis/has_float.rs diff --git a/src/ir/analysis/has_type_param_in_array.rs b/ir/analysis/has_type_param_in_array.rs index aa52304..aa52304 100644 --- a/src/ir/analysis/has_type_param_in_array.rs +++ b/ir/analysis/has_type_param_in_array.rs diff --git a/src/ir/analysis/has_vtable.rs b/ir/analysis/has_vtable.rs index 8ac47a6..8ac47a6 100644 --- a/src/ir/analysis/has_vtable.rs +++ b/ir/analysis/has_vtable.rs diff --git a/src/ir/analysis/mod.rs b/ir/analysis/mod.rs index eb9a1c0..40dfc6d 100644 --- a/src/ir/analysis/mod.rs +++ b/ir/analysis/mod.rs @@ -281,9 +281,13 @@ mod tests { fn reverse(&self) -> Graph { let mut reversed = Graph::default(); for (node, edges) in self.0.iter() { - reversed.0.entry(*node).or_insert(vec![]); + reversed.0.entry(*node).or_insert_with(Vec::new); for referent in edges.iter() { - reversed.0.entry(*referent).or_insert(vec![]).push(*node); + reversed + .0 + .entry(*referent) + .or_insert_with(Vec::new) + .push(*node); } } reversed @@ -306,8 +310,8 @@ mod tests { let reversed = graph.reverse(); ReachableFrom { reachable: Default::default(), - graph: graph, - reversed: reversed, + graph, + reversed, } } @@ -328,7 +332,7 @@ mod tests { let original_size = self .reachable .entry(node) - .or_insert(HashSet::default()) + .or_insert_with(HashSet::default) .len(); for sub_node in self.graph.0[&node].iter() { @@ -337,7 +341,7 @@ mod tests { let sub_reachable = self .reachable .entry(*sub_node) - .or_insert(HashSet::default()) + .or_insert_with(HashSet::default) .clone(); for transitive in sub_reachable { diff --git a/src/ir/analysis/sizedness.rs b/ir/analysis/sizedness.rs index 251c374..251c374 100644 --- a/src/ir/analysis/sizedness.rs +++ b/ir/analysis/sizedness.rs diff --git a/src/ir/analysis/template_params.rs b/ir/analysis/template_params.rs index e88b774..f4f0c59 100644 --- a/src/ir/analysis/template_params.rs +++ b/ir/analysis/template_params.rs @@ -424,8 +424,7 @@ impl<'ctx> MonotoneFramework for UsedTemplateParameters<'ctx> { // generic template parameters are used. let item_kind = ctx.resolve_item(item).as_type().map(|ty| ty.kind()); - if let Some(&TypeKind::TemplateInstantiation(ref inst)) = item_kind - { + if let Some(TypeKind::TemplateInstantiation(inst)) = item_kind { let decl = ctx.resolve_type(inst.template_definition()); let args = inst.template_arguments(); @@ -540,7 +539,7 @@ impl<'ctx> MonotoneFramework for UsedTemplateParameters<'ctx> { } // Template instantiations only use their template arguments if the // template definition uses the corresponding template parameter. - Some(&TypeKind::TemplateInstantiation(ref inst)) => { + Some(TypeKind::TemplateInstantiation(inst)) => { if self .allowlisted_items .contains(&inst.template_definition().into()) diff --git a/src/ir/annotations.rs b/ir/annotations.rs index 9bcda50..288c11e 100644 --- a/src/ir/annotations.rs +++ b/ir/annotations.rs @@ -7,7 +7,7 @@ use crate::clang; /// What kind of accessor should we provide for a field? -#[derive(Copy, PartialEq, Clone, Debug)] +#[derive(Copy, PartialEq, Eq, Clone, Debug)] pub enum FieldAccessorKind { /// No accessor. None, @@ -25,7 +25,7 @@ pub enum FieldAccessorKind { /// documentation: /// /// http://www.stack.nl/~dimitri/doxygen/manual/docblocks.html -#[derive(Default, Clone, PartialEq, Debug)] +#[derive(Default, Clone, PartialEq, Eq, Debug)] pub struct Annotations { /// Whether this item is marked as opaque. Only applies to types. opaque: bool, diff --git a/src/ir/comment.rs b/ir/comment.rs index c96e3eb..3eb17aa 100644 --- a/src/ir/comment.rs +++ b/ir/comment.rs @@ -12,10 +12,10 @@ enum Kind { } /// Preprocesses a C/C++ comment so that it is a valid Rust comment. -pub fn preprocess(comment: &str, indent: usize) -> String { +pub fn preprocess(comment: &str) -> String { match self::kind(comment) { - Some(Kind::SingleLines) => preprocess_single_lines(comment, indent), - Some(Kind::MultiLine) => preprocess_multi_line(comment, indent), + Some(Kind::SingleLines) => preprocess_single_lines(comment), + Some(Kind::MultiLine) => preprocess_multi_line(comment), None => comment.to_owned(), } } @@ -31,56 +31,34 @@ fn kind(comment: &str) -> Option<Kind> { } } -fn make_indent(indent: usize) -> String { - const RUST_INDENTATION: usize = 4; - " ".repeat(indent * RUST_INDENTATION) -} - /// Preprocesses multiple single line comments. /// /// Handles lines starting with both `//` and `///`. -fn preprocess_single_lines(comment: &str, indent: usize) -> String { +fn preprocess_single_lines(comment: &str) -> String { debug_assert!(comment.starts_with("//"), "comment is not single line"); - let indent = make_indent(indent); - let mut is_first = true; let lines: Vec<_> = comment .lines() .map(|l| l.trim().trim_start_matches('/')) - .map(|l| { - let indent = if is_first { "" } else { &*indent }; - is_first = false; - format!("{}///{}", indent, l) - }) .collect(); lines.join("\n") } -fn preprocess_multi_line(comment: &str, indent: usize) -> String { +fn preprocess_multi_line(comment: &str) -> String { let comment = comment .trim_start_matches('/') .trim_end_matches('/') .trim_end_matches('*'); - let indent = make_indent(indent); // Strip any potential `*` characters preceding each line. - let mut is_first = true; let mut lines: Vec<_> = comment .lines() .map(|line| line.trim().trim_start_matches('*').trim_start_matches('!')) .skip_while(|line| line.trim().is_empty()) // Skip the first empty lines. - .map(|line| { - let indent = if is_first { "" } else { &*indent }; - is_first = false; - format!("{}///{}", indent, line) - }) .collect(); // Remove the trailing line corresponding to the `*/`. - if lines - .last() - .map_or(false, |l| l.trim().is_empty() || l.trim() == "///") - { + if lines.last().map_or(false, |l| l.trim().is_empty()) { lines.pop(); } @@ -99,21 +77,24 @@ mod test { #[test] fn processes_single_lines_correctly() { - assert_eq!(preprocess("/// hello", 0), "/// hello"); - assert_eq!(preprocess("// hello", 0), "/// hello"); - assert_eq!(preprocess("// hello", 0), "/// hello"); + assert_eq!(preprocess("///"), ""); + assert_eq!(preprocess("/// hello"), " hello"); + assert_eq!(preprocess("// hello"), " hello"); + assert_eq!(preprocess("// hello"), " hello"); } #[test] fn processes_multi_lines_correctly() { + assert_eq!(preprocess("/**/"), ""); + assert_eq!( - preprocess("/** hello \n * world \n * foo \n */", 0), - "/// hello\n/// world\n/// foo" + preprocess("/** hello \n * world \n * foo \n */"), + " hello\n world\n foo" ); assert_eq!( - preprocess("/**\nhello\n*world\n*foo\n*/", 0), - "///hello\n///world\n///foo" + preprocess("/**\nhello\n*world\n*foo\n*/"), + "hello\nworld\nfoo" ); } } diff --git a/src/ir/comp.rs b/ir/comp.rs index a221e52..18a4291 100644 --- a/src/ir/comp.rs +++ b/ir/comp.rs @@ -12,15 +12,16 @@ use super::ty::RUST_DERIVE_IN_ARRAY_LIMIT; use crate::clang; use crate::codegen::struct_layout::{align_to, bytes_from_bits_pow2}; use crate::ir::derive::CanDeriveCopy; -use crate::parse::{ClangItemParser, ParseError}; +use crate::parse::ParseError; use crate::HashMap; +use crate::NonCopyUnionStyle; use peeking_take_while::PeekableExt; use std::cmp; use std::io; use std::mem; /// The kind of compound type. -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum CompKind { /// A struct. Struct, @@ -29,7 +30,7 @@ pub enum CompKind { } /// The kind of C++ method. -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum MethodKind { /// A constructor. We represent it as method for convenience, to avoid code /// duplication. @@ -55,12 +56,10 @@ pub enum MethodKind { impl MethodKind { /// Is this a destructor method? pub fn is_destructor(&self) -> bool { - match *self { - MethodKind::Destructor | MethodKind::VirtualDestructor { .. } => { - true - } - _ => false, - } + matches!( + *self, + MethodKind::Destructor | MethodKind::VirtualDestructor { .. } + ) } /// Is this a pure virtual method? @@ -1045,6 +1044,11 @@ pub struct CompInfo { /// size_t) has_non_type_template_params: bool, + /// Whether this type has a bit field member whose width couldn't be + /// evaluated (e.g. if it depends on a template parameter). We generate an + /// opaque type in this case. + has_unevaluable_bit_field_width: bool, + /// Whether we saw `__attribute__((packed))` on or within this type. packed_attr: bool, @@ -1078,6 +1082,7 @@ impl CompInfo { has_destructor: false, has_nonempty_base: false, has_non_type_template_params: false, + has_unevaluable_bit_field_width: false, packed_attr: false, found_unknown_attr: false, is_forward_declaration: false, @@ -1317,7 +1322,21 @@ impl CompInfo { } } - let bit_width = cur.bit_width(); + let bit_width = if cur.is_bit_field() { + let width = cur.bit_width(); + + // Make opaque type if the bit width couldn't be + // evaluated. + if width.is_none() { + ci.has_unevaluable_bit_field_width = true; + return CXChildVisit_Break; + } + + width + } else { + None + }; + let field_type = Item::from_ty_or_ref( cur.cur_type(), cur, @@ -1403,8 +1422,7 @@ impl CompInfo { // A declaration of an union or a struct without name // could also be an unnamed field, unfortunately. - if cur.spelling().is_empty() && - cur.kind() != CXCursor_EnumDecl + if cur.is_anonymous() && cur.kind() != CXCursor_EnumDecl { let ty = cur.cur_type(); let public = cur.public_accessible(); @@ -1662,21 +1680,37 @@ impl CompInfo { /// /// Requirements: /// 1. Current RustTarget allows for `untagged_union` - /// 2. Each field can derive `Copy` + /// 2. Each field can derive `Copy` or we use ManuallyDrop. /// 3. It's not zero-sized. - pub fn can_be_rust_union( + /// + /// Second boolean returns whether all fields can be copied (and thus + /// ManuallyDrop is not needed). + pub fn is_rust_union( &self, ctx: &BindgenContext, layout: Option<&Layout>, - ) -> bool { + name: &str, + ) -> (bool, bool) { + if !self.is_union() { + return (false, false); + } + if !ctx.options().rust_features().untagged_union { - return false; + return (false, false); } if self.is_forward_declaration() { - return false; + return (false, false); } + let union_style = if ctx.options().bindgen_wrapper_union.matches(name) { + NonCopyUnionStyle::BindgenWrapper + } else if ctx.options().manually_drop_union.matches(name) { + NonCopyUnionStyle::ManuallyDrop + } else { + ctx.options().default_non_copy_union_style + }; + let all_can_copy = self.fields().iter().all(|f| match *f { Field::DataMember(ref field_data) => { field_data.ty().can_derive_copy(ctx) @@ -1684,15 +1718,15 @@ impl CompInfo { Field::Bitfields(_) => true, }); - if !all_can_copy { - return false; + if !all_can_copy && union_style == NonCopyUnionStyle::BindgenWrapper { + return (false, false); } if layout.map_or(false, |l| l.size == 0) { - return false; + return (false, false); } - true + (true, all_can_copy) } } @@ -1753,7 +1787,9 @@ impl IsOpaque for CompInfo { type Extra = Option<Layout>; fn is_opaque(&self, ctx: &BindgenContext, layout: &Option<Layout>) -> bool { - if self.has_non_type_template_params { + if self.has_non_type_template_params || + self.has_unevaluable_bit_field_width + { return true; } diff --git a/src/ir/context.rs b/ir/context.rs index a9e19fb..b693a70 100644 --- a/src/ir/context.rs +++ b/ir/context.rs @@ -19,14 +19,14 @@ use super::module::{Module, ModuleKind}; use super::template::{TemplateInstantiation, TemplateParameters}; use super::traversal::{self, Edge, ItemTraversal}; use super::ty::{FloatKind, Type, TypeKind}; -use crate::callbacks::ParseCallbacks; use crate::clang::{self, Cursor}; -use crate::parse::ClangItemParser; +use crate::codegen::CodegenError; use crate::BindgenOptions; use crate::{Entry, HashMap, HashSet}; use cexpr; use clang_sys; -use proc_macro2::{Ident, Span}; +use proc_macro2::{Ident, Span, TokenStream}; +use quote::ToTokens; use std::borrow::Cow; use std::cell::{Cell, RefCell}; use std::collections::{BTreeSet, HashMap as StdHashMap}; @@ -364,14 +364,11 @@ pub struct BindgenContext { in_codegen: bool, - /// The clang index for parsing. - index: clang::Index, - /// The translation unit for parsing. translation_unit: clang::TranslationUnit, /// Target information that can be useful for some stuff. - target_info: Option<clang::TargetInfo>, + target_info: clang::TargetInfo, /// The options given by the user via cli or other medium. options: BindgenOptions, @@ -402,6 +399,22 @@ pub struct BindgenContext { /// bitfield allocation units computed. Drained in `compute_bitfield_units`. need_bitfield_allocation: Vec<ItemId>, + /// The set of enums that are defined by a pair of `enum` and `typedef`, + /// which is legal in C (but not C++). + /// + /// ```c++ + /// // in either order + /// enum Enum { Variants... }; + /// typedef int16_t Enum; + /// ``` + /// + /// The stored `ItemId` is that of the `TypeKind::Enum`, not of the + /// `TypeKind::Alias`. + /// + /// This is populated when we enter codegen by `compute_enum_typedef_combos` + /// and is always `None` before that and `Some` after. + enum_typedef_combos: Option<HashSet<ItemId>>, + /// The set of (`ItemId`s of) types that can't derive debug. /// /// This is populated when we enter codegen by `compute_cannot_derive_debug` @@ -420,12 +433,6 @@ pub struct BindgenContext { /// and is always `None` before that and `Some` after. cannot_derive_copy: Option<HashSet<ItemId>>, - /// The set of (`ItemId`s of) types that can't derive copy in array. - /// - /// This is populated when we enter codegen by `compute_cannot_derive_copy` - /// and is always `None` before that and `Some` after. - cannot_derive_copy_in_array: Option<HashSet<ItemId>>, - /// The set of (`ItemId`s of) types that can't derive hash. /// /// This is populated when we enter codegen by `compute_can_derive_hash` @@ -468,17 +475,15 @@ pub struct BindgenContext { /// Populated when we enter codegen by `compute_has_float`; always `None` /// before that and `Some` after. has_float: Option<HashSet<ItemId>>, + + /// The set of warnings raised during binding generation. + warnings: Vec<String>, } /// A traversal of allowlisted items. struct AllowlistedItemsTraversal<'ctx> { ctx: &'ctx BindgenContext, - traversal: ItemTraversal< - 'ctx, - ItemSet, - Vec<ItemId>, - for<'a> fn(&'a BindgenContext, Edge) -> bool, - >, + traversal: ItemTraversal<'ctx, ItemSet, Vec<ItemId>>, } impl<'ctx> Iterator for AllowlistedItemsTraversal<'ctx> { @@ -516,7 +521,10 @@ impl<'ctx> AllowlistedItemsTraversal<'ctx> { impl BindgenContext { /// Construct the context for the given `options`. - pub(crate) fn new(options: BindgenOptions) -> Self { + pub(crate) fn new( + options: BindgenOptions, + input_unsaved_files: &[clang::UnsavedFile], + ) -> Self { // TODO(emilio): Use the CXTargetInfo here when available. // // see: https://reviews.llvm.org/D32389 @@ -533,7 +541,7 @@ impl BindgenContext { &index, "", &options.clang_args, - &options.input_unsaved_files, + input_unsaved_files, parse_options, ).expect("libclang error; possible causes include: - Invalid flag syntax @@ -549,11 +557,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" let root_module_id = root_module.id().as_module_id_unchecked(); // depfiles need to include the explicitly listed headers too - let mut deps = BTreeSet::default(); - if let Some(filename) = &options.input_header { - deps.insert(filename.clone()); - } - deps.extend(options.extra_input_headers.iter().cloned()); + let deps = options.input_headers.iter().cloned().collect(); BindgenContext { items: vec![Some(root_module)], @@ -569,7 +573,6 @@ If you encounter an error missing from this list, please file an issue or a PR!" replacements: Default::default(), collected_typerefs: false, in_codegen: false, - index, translation_unit, target_info, options, @@ -579,10 +582,10 @@ If you encounter an error missing from this list, please file an issue or a PR!" codegen_items: None, used_template_parameters: None, need_bitfield_allocation: Default::default(), + enum_typedef_combos: None, cannot_derive_debug: None, cannot_derive_default: None, cannot_derive_copy: None, - cannot_derive_copy_in_array: None, cannot_derive_hash: None, cannot_derive_partialeq_or_partialord: None, sizedness: None, @@ -590,15 +593,13 @@ If you encounter an error missing from this list, please file an issue or a PR!" have_destructor: None, has_type_param_in_array: None, has_float: None, + warnings: Vec::new(), } } /// Returns `true` if the target architecture is wasm32 pub fn is_target_wasm32(&self) -> bool { - match self.target_info { - Some(ref ti) => ti.triple.starts_with("wasm32-"), - None => false, - } + self.target_info.triple.starts_with("wasm32-") } /// Creates a timer for the current bindgen phase. If time_phases is `true`, @@ -611,10 +612,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" /// Returns the pointer width to use for the target for the current /// translation. pub fn target_pointer_size(&self) -> usize { - if let Some(ref ti) = self.target_info { - return ti.pointer_width / 8; - } - mem::size_of::<*mut ()>() + self.target_info.pointer_width / 8 } /// Get the stack of partially parsed types that we are in the middle of @@ -638,15 +636,10 @@ If you encounter an error missing from this list, please file an issue or a PR!" ) } - /// Get the user-provided callbacks by reference, if any. - pub fn parse_callbacks(&self) -> Option<&dyn ParseCallbacks> { - self.options().parse_callbacks.as_deref() - } - /// Add another path to the set of included files. pub fn include_file(&mut self, filename: String) { - if let Some(cbs) = self.parse_callbacks() { - cbs.include_file(&filename); + for cb in &self.options().parse_callbacks { + cb.include_file(&filename); } self.deps.insert(filename); } @@ -838,7 +831,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" name.contains('$') || matches!( name, - "abstract" | "alignof" | "as" | "async" | "become" | + "abstract" | "alignof" | "as" | "async" | "await" | "become" | "box" | "break" | "const" | "continue" | "crate" | "do" | "dyn" | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | @@ -853,9 +846,9 @@ If you encounter an error missing from this list, please file an issue or a PR!" ) { let mut s = name.to_owned(); - s = s.replace("@", "_"); - s = s.replace("?", "_"); - s = s.replace("$", "_"); + s = s.replace('@', "_"); + s = s.replace('?', "_"); + s = s.replace('$', "_"); s.push('_'); return Cow::Owned(s); } @@ -1151,9 +1144,12 @@ If you encounter an error missing from this list, please file an issue or a PR!" /// Enter the code generation phase, invoke the given callback `cb`, and /// leave the code generation phase. - pub(crate) fn gen<F, Out>(mut self, cb: F) -> (Out, BindgenOptions) + pub(crate) fn gen<F, Out>( + mut self, + cb: F, + ) -> Result<(Out, BindgenOptions, Vec<String>), CodegenError> where - F: FnOnce(&Self) -> Out, + F: FnOnce(&Self) -> Result<Out, CodegenError>, { self.in_codegen = true; @@ -1179,6 +1175,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.compute_sizedness(); self.compute_has_destructor(); self.find_used_template_parameters(); + self.compute_enum_typedef_combos(); self.compute_cannot_derive_debug(); self.compute_cannot_derive_default(); self.compute_cannot_derive_copy(); @@ -1187,8 +1184,8 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.compute_cannot_derive_hash(); self.compute_cannot_derive_partialord_partialeq_or_eq(); - let ret = cb(&self); - (ret, self.options) + let ret = cb(&self)?; + Ok((ret, self.options, self.warnings)) } /// When the `testing_only_extra_assertions` feature is enabled, this @@ -2092,7 +2089,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.in_codegen_phase(), "You're not supposed to call this yet" ); - self.options.opaque_types.matches(&path[1..].join("::")) + self.options.opaque_types.matches(path[1..].join("::")) } /// Get the options used to configure this bindgen context. @@ -2119,13 +2116,18 @@ If you encounter an error missing from this list, please file an issue or a PR!" } let mut kind = ModuleKind::Normal; - let mut found_namespace_keyword = false; + let mut looking_for_name = false; for token in cursor.tokens().iter() { match token.spelling() { b"inline" => { - assert!(!found_namespace_keyword); - assert!(kind != ModuleKind::Inline); + debug_assert!( + kind != ModuleKind::Inline, + "Multiple inline keywords?" + ); kind = ModuleKind::Inline; + // When hitting a nested inline namespace we get a spelling + // that looks like ["inline", "foo"]. Deal with it properly. + looking_for_name = true; } // The double colon allows us to handle nested namespaces like // namespace foo::bar { } @@ -2134,45 +2136,39 @@ If you encounter an error missing from this list, please file an issue or a PR!" // but the tokenization of the second begins with the double // colon. That's ok, so we only need to handle the weird // tokenization here. - // - // Fortunately enough, inline nested namespace specifiers aren't - // a thing, and are invalid C++ :) b"namespace" | b"::" => { - found_namespace_keyword = true; + looking_for_name = true; } b"{" => { - assert!(found_namespace_keyword); + // This should be an anonymous namespace. + assert!(looking_for_name); break; } - name if found_namespace_keyword => { - if module_name.is_none() { - module_name = - Some(String::from_utf8_lossy(name).into_owned()); + name => { + if looking_for_name { + if module_name.is_none() { + module_name = Some( + String::from_utf8_lossy(name).into_owned(), + ); + } + break; + } else { + // This is _likely_, but not certainly, a macro that's + // been placed just before the namespace keyword. + // Unfortunately, clang tokens don't let us easily see + // through the ifdef tokens, so we don't know what this + // token should really be. Instead of panicking though, + // we warn the user that we assumed the token was blank, + // and then move on. + // + // See also https://github.com/rust-lang/rust-bindgen/issues/1676. + warn!( + "Ignored unknown namespace prefix '{}' at {:?} in {:?}", + String::from_utf8_lossy(name), + token, + cursor + ); } - break; - } - spelling if !found_namespace_keyword => { - // This is _likely_, but not certainly, a macro that's been placed just before - // the namespace keyword. Unfortunately, clang tokens don't let us easily see - // through the ifdef tokens, so we don't know what this token should really be. - // Instead of panicking though, we warn the user that we assumed the token was - // blank, and then move on. - // - // See also https://github.com/rust-lang/rust-bindgen/issues/1676. - warn!( - "Ignored unknown namespace prefix '{}' at {:?} in {:?}", - String::from_utf8_lossy(spelling), - token, - cursor - ); - } - spelling => { - panic!( - "Unknown token '{}' while processing namespace at {:?} in {:?}", - String::from_utf8_lossy(spelling), - token, - cursor - ); } } } @@ -2257,32 +2253,40 @@ If you encounter an error missing from this list, please file an issue or a PR!" .or_insert_with(|| { item.expect_type() .name() - .and_then(|name| match self.options.parse_callbacks { - Some(ref cb) => cb.blocklisted_type_implements_trait( - name, - derive_trait, - ), - // Sized integer types from <stdint.h> get mapped to Rust primitive - // types regardless of whether they are blocklisted, so ensure that - // standard traits are considered derivable for them too. - None => match name { - "int8_t" | "uint8_t" | "int16_t" | "uint16_t" | - "int32_t" | "uint32_t" | "int64_t" | - "uint64_t" | "uintptr_t" | "intptr_t" | - "ptrdiff_t" => Some(CanDerive::Yes), - "size_t" if self.options.size_t_is_usize => { + .and_then(|name| { + if self.options.parse_callbacks.is_empty() { + // Sized integer types from <stdint.h> get mapped to Rust primitive + // types regardless of whether they are blocklisted, so ensure that + // standard traits are considered derivable for them too. + if self.is_stdint_type(name) { Some(CanDerive::Yes) + } else { + Some(CanDerive::No) } - "ssize_t" if self.options.size_t_is_usize => { - Some(CanDerive::Yes) - } - _ => Some(CanDerive::No), - }, + } else { + self.options.last_callback(|cb| { + cb.blocklisted_type_implements_trait( + name, + derive_trait, + ) + }) + } }) .unwrap_or(CanDerive::No) }) } + /// Is the given type a type from <stdint.h> that corresponds to a Rust primitive type? + pub fn is_stdint_type(&self, name: &str) -> bool { + match name { + "int8_t" | "uint8_t" | "int16_t" | "uint16_t" | "int32_t" | + "uint32_t" | "int64_t" | "uint64_t" | "uintptr_t" | + "intptr_t" | "ptrdiff_t" => true, + "size_t" | "ssize_t" => self.options.size_t_is_usize, + _ => false, + } + } + /// Get a reference to the set of items we should generate. pub fn codegen_items(&self) -> &ItemSet { assert!(self.in_codegen_phase()); @@ -2307,7 +2311,8 @@ If you encounter an error missing from this list, please file an issue or a PR!" // game. if self.options().allowlisted_types.is_empty() && self.options().allowlisted_functions.is_empty() && - self.options().allowlisted_vars.is_empty() + self.options().allowlisted_vars.is_empty() && + self.options().allowlisted_files.is_empty() { return true; } @@ -2318,6 +2323,23 @@ If you encounter an error missing from this list, please file an issue or a PR!" return true; } + // Items with a source location in an explicitly allowlisted file + // are always included. + if !self.options().allowlisted_files.is_empty() { + if let Some(location) = item.location() { + let (file, _, _, _) = location.location(); + if let Some(filename) = file.name() { + if self + .options() + .allowlisted_files + .matches(filename) + { + return true; + } + } + } + } + let name = item.path_for_allowlisting(self)[1..].join("::"); debug!("allowlisted_items: testing {:?}", name); match *item.kind() { @@ -2352,7 +2374,10 @@ If you encounter an error missing from this list, please file an issue or a PR!" TypeKind::Opaque | TypeKind::TypeParam => return true, _ => {} - }; + } + if self.is_stdint_type(&name) { + return true; + } } // Unnamed top-level enums are special and we @@ -2382,7 +2407,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" ); let name = prefix_path[1..].join("::"); prefix_path.pop().unwrap(); - self.options().allowlisted_vars.matches(&name) + self.options().allowlisted_vars.matches(name) }) } } @@ -2429,16 +2454,24 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.allowlisted = Some(allowlisted); self.codegen_items = Some(codegen_items); + let mut warnings = Vec::new(); + for item in self.options().allowlisted_functions.unmatched_items() { - warn!("unused option: --allowlist-function {}", item); + warnings + .push(format!("unused option: --allowlist-function {}", item)); } for item in self.options().allowlisted_vars.unmatched_items() { - warn!("unused option: --allowlist-var {}", item); + warnings.push(format!("unused option: --allowlist-var {}", item)); } for item in self.options().allowlisted_types.unmatched_items() { - warn!("unused option: --allowlist-type {}", item); + warnings.push(format!("unused option: --allowlist-type {}", item)); + } + + for msg in warnings { + warn!("{}", msg); + self.warnings.push(msg); } } @@ -2462,6 +2495,70 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.generated_bindgen_complex.get() } + /// Compute which `enum`s have an associated `typedef` definition. + fn compute_enum_typedef_combos(&mut self) { + let _t = self.timer("compute_enum_typedef_combos"); + assert!(self.enum_typedef_combos.is_none()); + + let mut enum_typedef_combos = HashSet::default(); + for item in &self.items { + if let Some(ItemKind::Module(module)) = + item.as_ref().map(Item::kind) + { + // Find typedefs in this module, and build set of their names. + let mut names_of_typedefs = HashSet::default(); + for child_id in module.children() { + if let Some(ItemKind::Type(ty)) = + self.items[child_id.0].as_ref().map(Item::kind) + { + if let (Some(name), TypeKind::Alias(type_id)) = + (ty.name(), ty.kind()) + { + // We disregard aliases that refer to the enum + // itself, such as in `typedef enum { ... } Enum;`. + if type_id + .into_resolver() + .through_type_refs() + .through_type_aliases() + .resolve(self) + .expect_type() + .is_int() + { + names_of_typedefs.insert(name); + } + } + } + } + + // Find enums in this module, and record the id of each one that + // has a typedef. + for child_id in module.children() { + if let Some(ItemKind::Type(ty)) = + self.items[child_id.0].as_ref().map(Item::kind) + { + if let (Some(name), true) = (ty.name(), ty.is_enum()) { + if names_of_typedefs.contains(name) { + enum_typedef_combos.insert(*child_id); + } + } + } + } + } + } + + self.enum_typedef_combos = Some(enum_typedef_combos); + } + + /// Look up whether `id` refers to an `enum` whose underlying type is + /// defined by a `typedef`. + pub fn is_enum_typedef_combo(&self, id: ItemId) -> bool { + assert!( + self.in_codegen_phase(), + "We only compute enum_typedef_combos when we enter codegen", + ); + self.enum_typedef_combos.as_ref().unwrap().contains(&id) + } + /// Compute whether we can derive debug. fn compute_cannot_derive_debug(&mut self) { let _t = self.timer("compute_cannot_derive_debug"); @@ -2656,37 +2753,52 @@ If you encounter an error missing from this list, please file an issue or a PR!" /// Check if `--no-partialeq` flag is enabled for this item. pub fn no_partialeq_by_name(&self, item: &Item) -> bool { let name = item.path_for_allowlisting(self)[1..].join("::"); - self.options().no_partialeq_types.matches(&name) + self.options().no_partialeq_types.matches(name) } /// Check if `--no-copy` flag is enabled for this item. pub fn no_copy_by_name(&self, item: &Item) -> bool { let name = item.path_for_allowlisting(self)[1..].join("::"); - self.options().no_copy_types.matches(&name) + self.options().no_copy_types.matches(name) } /// Check if `--no-debug` flag is enabled for this item. pub fn no_debug_by_name(&self, item: &Item) -> bool { let name = item.path_for_allowlisting(self)[1..].join("::"); - self.options().no_debug_types.matches(&name) + self.options().no_debug_types.matches(name) } /// Check if `--no-default` flag is enabled for this item. pub fn no_default_by_name(&self, item: &Item) -> bool { let name = item.path_for_allowlisting(self)[1..].join("::"); - self.options().no_default_types.matches(&name) + self.options().no_default_types.matches(name) } /// Check if `--no-hash` flag is enabled for this item. pub fn no_hash_by_name(&self, item: &Item) -> bool { let name = item.path_for_allowlisting(self)[1..].join("::"); - self.options().no_hash_types.matches(&name) + self.options().no_hash_types.matches(name) } /// Check if `--must-use-type` flag is enabled for this item. pub fn must_use_type_by_name(&self, item: &Item) -> bool { let name = item.path_for_allowlisting(self)[1..].join("::"); - self.options().must_use_types.matches(&name) + self.options().must_use_types.matches(name) + } + + pub(crate) fn wrap_unsafe_ops(&self, tokens: impl ToTokens) -> TokenStream { + if self.options.wrap_unsafe_ops { + quote!(unsafe { #tokens }) + } else { + tokens.into_token_stream() + } + } + + pub(crate) fn wrap_static_fns_suffix(&self) -> &str { + self.options() + .wrap_static_fns_suffix + .as_deref() + .unwrap_or(crate::DEFAULT_NON_EXTERN_FNS_SUFFIX) } } diff --git a/src/ir/derive.rs b/ir/derive.rs index 594ce2a..594ce2a 100644 --- a/src/ir/derive.rs +++ b/ir/derive.rs diff --git a/src/ir/dot.rs b/ir/dot.rs index f7d07f1..f7d07f1 100644 --- a/src/ir/dot.rs +++ b/ir/dot.rs diff --git a/src/ir/enum_ty.rs b/ir/enum_ty.rs index 97455c9..63871fd 100644 --- a/src/ir/enum_ty.rs +++ b/ir/enum_ty.rs @@ -6,7 +6,7 @@ use super::item::Item; use super::ty::{Type, TypeKind}; use crate::clang; use crate::ir::annotations::Annotations; -use crate::parse::{ClangItemParser, ParseError}; +use crate::parse::ParseError; use crate::regex_set::RegexSet; /// An enum representing custom handling that can be given to a variant. @@ -102,8 +102,8 @@ impl Enum { let name = cursor.spelling(); let annotations = Annotations::new(&cursor); let custom_behavior = ctx - .parse_callbacks() - .and_then(|callbacks| { + .options() + .last_callback(|callbacks| { callbacks .enum_variant_behavior(type_name, &name, val) }) @@ -119,8 +119,8 @@ impl Enum { }); let new_name = ctx - .parse_callbacks() - .and_then(|callbacks| { + .options() + .last_callback(|callbacks| { callbacks.enum_variant_name(type_name, &name, val) }) .or_else(|| { @@ -156,7 +156,7 @@ impl Enum { let path = item.path_for_allowlisting(ctx); let enum_ty = item.expect_type(); - if enums.matches(&path[1..].join("::")) { + if enums.matches(path[1..].join("::")) { return true; } @@ -165,7 +165,7 @@ impl Enum { return false; } - self.variants().iter().any(|v| enums.matches(&v.name())) + self.variants().iter().any(|v| enums.matches(v.name())) } /// Returns the final representation of the enum. @@ -187,10 +187,25 @@ impl Enum { &ctx.options().bitfield_enums, item, ) { - EnumVariation::NewType { is_bitfield: true } + EnumVariation::NewType { + is_bitfield: true, + is_global: false, + } } else if self.is_matching_enum(ctx, &ctx.options().newtype_enums, item) { - EnumVariation::NewType { is_bitfield: false } + EnumVariation::NewType { + is_bitfield: false, + is_global: false, + } + } else if self.is_matching_enum( + ctx, + &ctx.options().newtype_global_enums, + item, + ) { + EnumVariation::NewType { + is_bitfield: false, + is_global: true, + } } else if self.is_matching_enum( ctx, &ctx.options().rustified_enums, diff --git a/src/ir/function.rs b/ir/function.rs index a3a2bbf..baa2c36 100644 --- a/src/ir/function.rs +++ b/ir/function.rs @@ -6,20 +6,20 @@ use super::dot::DotAttributes; use super::item::Item; use super::traversal::{EdgeKind, Trace, Tracer}; use super::ty::TypeKind; -use crate::clang; -use crate::parse::{ - ClangItemParser, ClangSubItemParser, ParseError, ParseResult, -}; +use crate::callbacks::{ItemInfo, ItemKind}; +use crate::clang::{self, Attribute}; +use crate::parse::{ClangSubItemParser, ParseError, ParseResult}; use clang_sys::{self, CXCallingConv}; use proc_macro2; use quote; use quote::TokenStreamExt; use std::io; +use std::str::FromStr; const RUST_DERIVE_FUNPTR_LIMIT: usize = 12; /// What kind of a function are we looking at? -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum FunctionKind { /// A plain, free function. Function, @@ -131,6 +131,11 @@ impl Function { self.signature } + /// Get this function's comment. + pub fn comment(&self) -> Option<&str> { + self.comment.as_deref() + } + /// Get this function's kind. pub fn kind(&self) -> FunctionKind { self.kind @@ -165,8 +170,8 @@ impl DotAttributes for Function { } } -/// An ABI extracted from a clang cursor. -#[derive(Debug, Copy, Clone)] +/// A valid rust ABI. +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] pub enum Abi { /// The default C ABI. C, @@ -176,35 +181,82 @@ pub enum Abi { Fastcall, /// The "thiscall" ABI. ThisCall, + /// The "vectorcall" ABI. + Vectorcall, /// The "aapcs" ABI. Aapcs, /// The "win64" ABI. Win64, + /// The "C-unwind" ABI. + CUnwind, +} + +impl FromStr for Abi { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "C" => Ok(Self::C), + "stdcall" => Ok(Self::Stdcall), + "fastcall" => Ok(Self::Fastcall), + "thiscall" => Ok(Self::ThisCall), + "vectorcall" => Ok(Self::Vectorcall), + "aapcs" => Ok(Self::Aapcs), + "win64" => Ok(Self::Win64), + "C-unwind" => Ok(Self::CUnwind), + _ => Err(format!("Invalid or unknown ABI {:?}", s)), + } + } +} + +impl std::fmt::Display for Abi { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match *self { + Self::C => "C", + Self::Stdcall => "stdcall", + Self::Fastcall => "fastcall", + Self::ThisCall => "thiscall", + Self::Vectorcall => "vectorcall", + Self::Aapcs => "aapcs", + Self::Win64 => "win64", + Self::CUnwind => "C-unwind", + }; + + s.fmt(f) + } +} + +impl quote::ToTokens for Abi { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let abi = self.to_string(); + tokens.append_all(quote! { #abi }); + } +} + +/// An ABI extracted from a clang cursor. +#[derive(Debug, Copy, Clone)] +pub(crate) enum ClangAbi { + Known(Abi), /// An unknown or invalid ABI. Unknown(CXCallingConv), } -impl Abi { +impl ClangAbi { /// Returns whether this Abi is known or not. fn is_unknown(&self) -> bool { - matches!(*self, Abi::Unknown(..)) + matches!(*self, ClangAbi::Unknown(..)) } } -impl quote::ToTokens for Abi { +impl quote::ToTokens for ClangAbi { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - tokens.append_all(match *self { - Abi::C => quote! { "C" }, - Abi::Stdcall => quote! { "stdcall" }, - Abi::Fastcall => quote! { "fastcall" }, - Abi::ThisCall => quote! { "thiscall" }, - Abi::Aapcs => quote! { "aapcs" }, - Abi::Win64 => quote! { "win64" }, - Abi::Unknown(cc) => panic!( + match *self { + Self::Known(abi) => abi.to_tokens(tokens), + Self::Unknown(cc) => panic!( "Cannot turn unknown calling convention to tokens: {:?}", cc ), - }); + } } } @@ -220,25 +272,27 @@ pub struct FunctionSig { /// Whether this function is variadic. is_variadic: bool, + is_divergent: bool, /// Whether this function's return value must be used. must_use: bool, /// The ABI of this function. - abi: Abi, + abi: ClangAbi, } -fn get_abi(cc: CXCallingConv) -> Abi { +fn get_abi(cc: CXCallingConv) -> ClangAbi { use clang_sys::*; match cc { - CXCallingConv_Default => Abi::C, - CXCallingConv_C => Abi::C, - CXCallingConv_X86StdCall => Abi::Stdcall, - CXCallingConv_X86FastCall => Abi::Fastcall, - CXCallingConv_X86ThisCall => Abi::ThisCall, - CXCallingConv_AAPCS => Abi::Aapcs, - CXCallingConv_X86_64Win64 => Abi::Win64, - other => Abi::Unknown(other), + CXCallingConv_Default => ClangAbi::Known(Abi::C), + CXCallingConv_C => ClangAbi::Known(Abi::C), + CXCallingConv_X86StdCall => ClangAbi::Known(Abi::Stdcall), + CXCallingConv_X86FastCall => ClangAbi::Known(Abi::Fastcall), + CXCallingConv_X86ThisCall => ClangAbi::Known(Abi::ThisCall), + CXCallingConv_X86VectorCall => ClangAbi::Known(Abi::Vectorcall), + CXCallingConv_AAPCS => ClangAbi::Known(Abi::Aapcs), + CXCallingConv_X86_64Win64 => ClangAbi::Known(Abi::Win64), + other => ClangAbi::Unknown(other), } } @@ -344,23 +398,6 @@ fn args_from_ty_and_cursor( } impl FunctionSig { - /// Construct a new function signature. - pub fn new( - return_type: TypeId, - argument_types: Vec<(Option<String>, TypeId)>, - is_variadic: bool, - must_use: bool, - abi: Abi, - ) -> Self { - FunctionSig { - return_type, - argument_types, - is_variadic, - must_use, - abi, - } - } - /// Construct a new function signature from the given Clang type. pub fn from_ty( ty: &clang::Type, @@ -438,8 +475,23 @@ impl FunctionSig { } }; - let must_use = ctx.options().enable_function_attribute_detection && - cursor.has_warn_unused_result_attr(); + let (must_use, mut is_divergent) = + if ctx.options().enable_function_attribute_detection { + let [must_use, no_return, no_return_cpp] = cursor.has_attrs(&[ + Attribute::MUST_USE, + Attribute::NO_RETURN, + Attribute::NO_RETURN_CPP, + ]); + (must_use, no_return || no_return_cpp) + } else { + Default::default() + }; + + // This looks easy to break but the clang parser keeps the type spelling clean even if + // other attributes are added. + is_divergent = + is_divergent || ty.spelling().contains("__attribute__((noreturn))"); + let is_method = kind == CXCursor_CXXMethod; let is_constructor = kind == CXCursor_Constructor; let is_destructor = kind == CXCursor_Destructor; @@ -513,13 +565,21 @@ impl FunctionSig { call_conv = cursor_call_conv; } } + let abi = get_abi(call_conv); if abi.is_unknown() { warn!("Unknown calling convention: {:?}", call_conv); } - Ok(Self::new(ret, args, ty.is_variadic(), must_use, abi)) + Ok(FunctionSig { + return_type: ret, + argument_types: args, + is_variadic: ty.is_variadic(), + is_divergent, + must_use, + abi, + }) } /// Get this function signature's return type. @@ -533,8 +593,27 @@ impl FunctionSig { } /// Get this function signature's ABI. - pub fn abi(&self) -> Abi { - self.abi + pub(crate) fn abi( + &self, + ctx: &BindgenContext, + name: Option<&str>, + ) -> ClangAbi { + // FIXME (pvdrz): Try to do this check lazily instead. Maybe store the ABI inside `ctx` + // instead?. + if let Some(name) = name { + if let Some((abi, _)) = ctx + .options() + .abi_overrides + .iter() + .find(|(_, regex_set)| regex_set.matches(name)) + { + ClangAbi::Known(*abi) + } else { + self.abi + } + } else { + self.abi + } } /// Is this function signature variadic? @@ -564,7 +643,11 @@ impl FunctionSig { return false; } - matches!(self.abi, Abi::C | Abi::Unknown(..)) + matches!(self.abi, ClangAbi::Known(Abi::C) | ClangAbi::Unknown(..)) + } + + pub(crate) fn is_divergent(&self) -> bool { + self.is_divergent } } @@ -581,7 +664,6 @@ impl ClangSubItemParser for Function { }; debug!("Function::parse({:?}, {:?})", cursor, cursor.cur_type()); - let visibility = cursor.visibility(); if visibility != CXVisibility_Default { return Err(ParseError::Continue); @@ -591,15 +673,6 @@ impl ClangSubItemParser for Function { return Err(ParseError::Continue); } - if cursor.is_inlined_function() { - if !context.options().generate_inline_functions { - return Err(ParseError::Continue); - } - if cursor.is_deleted_function() { - return Err(ParseError::Continue); - } - } - let linkage = cursor.linkage(); let linkage = match linkage { CXLinkage_External | CXLinkage_UniqueExternal => Linkage::External, @@ -607,6 +680,30 @@ impl ClangSubItemParser for Function { _ => return Err(ParseError::Continue), }; + if cursor.is_inlined_function() || + cursor + .definition() + .map_or(false, |x| x.is_inlined_function()) + { + if !context.options().generate_inline_functions && + !context.options().wrap_static_fns + { + return Err(ParseError::Continue); + } + + if cursor.is_deleted_function() { + return Err(ParseError::Continue); + } + + // We cannot handle `inline` functions that are not `static`. + if context.options().wrap_static_fns && + cursor.is_inlined_function() && + matches!(linkage, Linkage::External) + { + return Err(ParseError::Continue); + } + } + // Grab the signature using Item::from_ty. let sig = Item::from_ty(&cursor.cur_type(), cursor, None, context)?; @@ -626,12 +723,22 @@ impl ClangSubItemParser for Function { // but seems easy enough to handle it here. name.push_str("_destructor"); } + if let Some(nm) = context.options().last_callback(|callbacks| { + callbacks.generated_name_override(ItemInfo { + name: name.as_str(), + kind: ItemKind::Function, + }) + }) { + name = nm; + } + assert!(!name.is_empty(), "Empty function name."); let mangled_name = cursor_mangling(context, &cursor); let comment = cursor.raw_comment(); let function = - Self::new(name, mangled_name, sig, comment, kind, linkage); + Self::new(name.clone(), mangled_name, sig, comment, kind, linkage); + Ok(ParseResult::New(function, Some(cursor))) } } diff --git a/src/ir/int.rs b/ir/int.rs index 22838e8..22838e8 100644 --- a/src/ir/int.rs +++ b/ir/int.rs diff --git a/src/ir/item.rs b/ir/item.rs index 8692575..40f6f7d 100644 --- a/src/ir/item.rs +++ b/ir/item.rs @@ -3,7 +3,6 @@ use super::super::codegen::{EnumVariation, CONSTIFIED_ENUM_MODULE_REPR_NAME}; use super::analysis::{HasVtable, HasVtableResult, Sizedness, SizednessResult}; use super::annotations::Annotations; -use super::comment; use super::comp::{CompKind, MethodKind}; use super::context::{BindgenContext, ItemId, PartialType, TypeId}; use super::derive::{ @@ -19,9 +18,7 @@ use super::template::{AsTemplateParam, TemplateParameters}; use super::traversal::{EdgeKind, Trace, Tracer}; use super::ty::{Type, TypeKind}; use crate::clang; -use crate::parse::{ - ClangItemParser, ClangSubItemParser, ParseError, ParseResult, -}; +use crate::parse::{ClangSubItemParser, ParseError, ParseResult}; use clang_sys; use lazycell::LazyCell; use regex; @@ -515,9 +512,9 @@ impl Item { return None; } - self.comment.as_ref().map(|comment| { - comment::preprocess(comment, self.codegen_depth(ctx)) - }) + self.comment + .as_ref() + .map(|comment| ctx.options().process_comment(comment)) } /// What kind of item is this? @@ -530,6 +527,11 @@ impl Item { &mut self.kind } + /// Where in the source is this item located? + pub fn location(&self) -> Option<&clang::SourceLocation> { + self.location.as_ref() + } + /// Get an identifier that differentiates this item from its siblings. /// /// This should stay relatively stable in the face of code motion outside or @@ -645,7 +647,7 @@ impl Item { if let Some(location) = &self.location { let (file, _, _, _) = location.location(); if let Some(filename) = file.name() { - if ctx.options().blocklisted_files.matches(&filename) { + if ctx.options().blocklisted_files.matches(filename) { return true; } } @@ -927,8 +929,8 @@ impl Item { let name = names.join("_"); let name = if opt.user_mangled == UserMangled::Yes { - ctx.parse_callbacks() - .and_then(|callbacks| callbacks.item_name(&name)) + ctx.options() + .last_callback(|callbacks| callbacks.item_name(&name)) .unwrap_or(name) } else { name @@ -1091,6 +1093,11 @@ impl Item { _ => return None, }) } + + /// Whether this is a #[must_use] type. + pub fn must_use(&self, ctx: &BindgenContext) -> bool { + self.annotations().must_use_type() || ctx.must_use_type_by_name(self) + } } impl<T> IsOpaque for T @@ -1298,8 +1305,8 @@ fn visit_child( } } -impl ClangItemParser for Item { - fn builtin_type( +impl Item { + pub(crate) fn builtin_type( kind: TypeKind, is_const: bool, ctx: &mut BindgenContext, @@ -1324,7 +1331,7 @@ impl ClangItemParser for Item { id.as_type_id_unchecked() } - fn parse( + pub(crate) fn parse( cursor: clang::Cursor, parent_id: Option<ItemId>, ctx: &mut BindgenContext, @@ -1470,7 +1477,7 @@ impl ClangItemParser for Item { } } - fn from_ty_or_ref( + pub(crate) fn from_ty_or_ref( ty: clang::Type, location: clang::Cursor, parent_id: Option<ItemId>, @@ -1490,7 +1497,7 @@ impl ClangItemParser for Item { /// /// Typerefs are resolved once parsing is completely done, see /// `BindgenContext::resolve_typerefs`. - fn from_ty_or_ref_with_id( + pub(crate) fn from_ty_or_ref_with_id( potential_id: ItemId, ty: clang::Type, location: clang::Cursor, @@ -1545,7 +1552,7 @@ impl ClangItemParser for Item { potential_id.as_type_id_unchecked() } - fn from_ty( + pub(crate) fn from_ty( ty: &clang::Type, location: clang::Cursor, parent_id: Option<ItemId>, @@ -1563,7 +1570,7 @@ impl ClangItemParser for Item { /// critical some times to obtain information, an optional parent item id, /// that will, if it's `None`, become the current module id, and the /// context. - fn from_ty_with_id( + pub(crate) fn from_ty_with_id( id: ItemId, ty: &clang::Type, location: clang::Cursor, @@ -1733,7 +1740,7 @@ impl ClangItemParser for Item { /// A named type is a template parameter, e.g., the "T" in Foo<T>. They're /// always local so it's the only exception when there's no declaration for /// a type. - fn type_param( + pub(crate) fn type_param( with_id: Option<ItemId>, location: clang::Cursor, ctx: &mut BindgenContext, @@ -1889,7 +1896,7 @@ impl ClangItemParser for Item { // See tests/headers/const_tparam.hpp and // tests/headers/variadic_tname.hpp. - let name = ty_spelling.replace("const ", "").replace(".", ""); + let name = ty_spelling.replace("const ", "").replace('.', ""); let id = with_id.unwrap_or_else(|| ctx.next_item_id()); let item = Item::new( diff --git a/src/ir/item_kind.rs b/ir/item_kind.rs index 4a12fef..4a12fef 100644 --- a/src/ir/item_kind.rs +++ b/ir/item_kind.rs diff --git a/src/ir/layout.rs b/ir/layout.rs index 6cf9113..6f45030 100644 --- a/src/ir/layout.rs +++ b/ir/layout.rs @@ -7,7 +7,7 @@ use crate::ir::context::BindgenContext; use std::cmp; /// A type that represents the struct layout of a type. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Layout { /// The size (in bytes) of this layout. pub size: usize, @@ -93,7 +93,7 @@ impl Layout { } /// When we are treating a type as opaque, it is just a blob with a `Layout`. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Opaque(pub Layout); impl Opaque { diff --git a/src/ir/mod.rs b/ir/mod.rs index 8f6a2da..8f6a2da 100644 --- a/src/ir/mod.rs +++ b/ir/mod.rs diff --git a/src/ir/module.rs b/ir/module.rs index d5aca94..d5aca94 100644 --- a/src/ir/module.rs +++ b/ir/module.rs diff --git a/src/ir/objc.rs b/ir/objc.rs index 0845ad0..4f340f6 100644 --- a/src/ir/objc.rs +++ b/ir/objc.rs @@ -6,7 +6,6 @@ use super::item::Item; use super::traversal::{Trace, Tracer}; use super::ty::TypeKind; use crate::clang; -use crate::parse::ClangItemParser; use clang_sys::CXChildVisit_Continue; use clang_sys::CXCursor_ObjCCategoryDecl; use clang_sys::CXCursor_ObjCClassMethodDecl; @@ -261,7 +260,17 @@ impl ObjCMethod { if name.is_empty() { None } else { - Some(Ident::new(name, Span::call_site())) + // Try to parse the current name as an identifier. This might fail if the + // name is a keyword so we try to prepend "r#" to it and parse again. If + // this also fails, we panic with the first error. + Some( + syn::parse_str::<Ident>(name) + .or_else(|err| { + syn::parse_str::<Ident>(&format!("r#{}", name)) + .map_err(|_| err) + }) + .expect("Invalid identifier"), + ) } }) .collect(); diff --git a/src/ir/template.rs b/ir/template.rs index 8b06748..e3ef6a9 100644 --- a/src/ir/template.rs +++ b/ir/template.rs @@ -31,7 +31,6 @@ use super::context::{BindgenContext, ItemId, TypeId}; use super::item::{IsOpaque, Item, ItemAncestors}; use super::traversal::{EdgeKind, Trace, Tracer}; use crate::clang; -use crate::parse::ClangItemParser; /// Template declaration (and such declaration's template parameters) related /// methods. diff --git a/src/ir/traversal.rs b/ir/traversal.rs index 088e744..f14483f 100644 --- a/src/ir/traversal.rs +++ b/ir/traversal.rs @@ -179,17 +179,10 @@ pub enum EdgeKind { /// A predicate to allow visiting only sub-sets of the whole IR graph by /// excluding certain edges from being followed by the traversal. -pub trait TraversalPredicate { - /// Should the traversal follow this edge, and visit everything that is - /// reachable through it? - fn should_follow(&self, ctx: &BindgenContext, edge: Edge) -> bool; -} - -impl TraversalPredicate for for<'a> fn(&'a BindgenContext, Edge) -> bool { - fn should_follow(&self, ctx: &BindgenContext, edge: Edge) -> bool { - (*self)(ctx, edge) - } -} +/// +/// The predicate must return true if the traversal should follow this edge +/// and visit everything that is reachable through it. +pub type TraversalPredicate = for<'a> fn(&'a BindgenContext, Edge) -> bool; /// A `TraversalPredicate` implementation that follows all edges, and therefore /// traversals using this predicate will see the whole IR graph reachable from @@ -378,11 +371,10 @@ pub trait Trace { /// An graph traversal of the transitive closure of references between items. /// /// See `BindgenContext::allowlisted_items` for more information. -pub struct ItemTraversal<'ctx, Storage, Queue, Predicate> +pub struct ItemTraversal<'ctx, Storage, Queue> where Storage: TraversalStorage<'ctx>, Queue: TraversalQueue, - Predicate: TraversalPredicate, { ctx: &'ctx BindgenContext, @@ -393,25 +385,23 @@ where queue: Queue, /// The predicate that determines which edges this traversal will follow. - predicate: Predicate, + predicate: TraversalPredicate, /// The item we are currently traversing. currently_traversing: Option<ItemId>, } -impl<'ctx, Storage, Queue, Predicate> - ItemTraversal<'ctx, Storage, Queue, Predicate> +impl<'ctx, Storage, Queue> ItemTraversal<'ctx, Storage, Queue> where Storage: TraversalStorage<'ctx>, Queue: TraversalQueue, - Predicate: TraversalPredicate, { /// Begin a new traversal, starting from the given roots. pub fn new<R>( ctx: &'ctx BindgenContext, roots: R, - predicate: Predicate, - ) -> ItemTraversal<'ctx, Storage, Queue, Predicate> + predicate: TraversalPredicate, + ) -> ItemTraversal<'ctx, Storage, Queue> where R: IntoIterator<Item = ItemId>, { @@ -433,16 +423,14 @@ where } } -impl<'ctx, Storage, Queue, Predicate> Tracer - for ItemTraversal<'ctx, Storage, Queue, Predicate> +impl<'ctx, Storage, Queue> Tracer for ItemTraversal<'ctx, Storage, Queue> where Storage: TraversalStorage<'ctx>, Queue: TraversalQueue, - Predicate: TraversalPredicate, { fn visit_kind(&mut self, item: ItemId, kind: EdgeKind) { let edge = Edge::new(item, kind); - if !self.predicate.should_follow(self.ctx, edge) { + if !(self.predicate)(self.ctx, edge) { return; } @@ -454,12 +442,10 @@ where } } -impl<'ctx, Storage, Queue, Predicate> Iterator - for ItemTraversal<'ctx, Storage, Queue, Predicate> +impl<'ctx, Storage, Queue> Iterator for ItemTraversal<'ctx, Storage, Queue> where Storage: TraversalStorage<'ctx>, Queue: TraversalQueue, - Predicate: TraversalPredicate, { type Item = ItemId; @@ -488,21 +474,5 @@ where /// /// See `BindgenContext::assert_no_dangling_item_traversal` for more /// information. -pub type AssertNoDanglingItemsTraversal<'ctx> = ItemTraversal< - 'ctx, - Paths<'ctx>, - VecDeque<ItemId>, - for<'a> fn(&'a BindgenContext, Edge) -> bool, ->; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - #[allow(dead_code)] - fn traversal_predicate_is_object_safe() { - // This should compile only if TraversalPredicate is object safe. - fn takes_by_trait_object(_: &dyn TraversalPredicate) {} - } -} +pub type AssertNoDanglingItemsTraversal<'ctx> = + ItemTraversal<'ctx, Paths<'ctx>, VecDeque<ItemId>>; @@ -14,7 +14,7 @@ use super::template::{ }; use super::traversal::{EdgeKind, Trace, Tracer}; use crate::clang::{self, Cursor}; -use crate::parse::{ClangItemParser, ParseError, ParseResult}; +use crate::parse::{ParseError, ParseResult}; use std::borrow::Cow; use std::io; @@ -95,6 +95,11 @@ impl Type { matches!(self.kind, TypeKind::BlockPointer(..)) } + /// Is this an integer type, including `bool` or `char`? + pub fn is_int(&self) -> bool { + matches!(self.kind, TypeKind::Int(_)) + } + /// Is this a compound type? pub fn is_comp(&self) -> bool { matches!(self.kind, TypeKind::Comp(..)) @@ -564,7 +569,7 @@ impl TemplateParameters for TypeKind { } /// The kind of float this type represents. -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum FloatKind { /// A `float`. Float, @@ -698,7 +703,12 @@ impl Type { let layout = ty.fallible_layout(ctx).ok(); let cursor = ty.declaration(); - let mut name = cursor.spelling(); + let is_anonymous = cursor.is_anonymous(); + let mut name = if is_anonymous { + None + } else { + Some(cursor.spelling()).filter(|n| !n.is_empty()) + }; debug!( "from_clang_ty: {:?}, ty: {:?}, loc: {:?}", @@ -732,7 +742,7 @@ impl Type { if is_canonical_objcpointer && is_template_type_param { // Objective-C generics are just ids with fancy name. // To keep it simple, just name them ids - name = "id".to_owned(); + name = Some("id".to_owned()); } } @@ -861,7 +871,7 @@ impl Type { return Err(ParseError::Recurse); } } else { - name = location.spelling(); + name = Some(location.spelling()); } let complex = CompInfo::from_ty( @@ -903,7 +913,7 @@ impl Type { CXType_Typedef ); - name = current.spelling(); + name = Some(location.spelling()); let inner_ty = cur .typedef_type() @@ -1031,7 +1041,16 @@ impl Type { CXType_ObjCObjectPointer | CXType_MemberPointer | CXType_Pointer => { - let pointee = ty.pointee_type().unwrap(); + let mut pointee = ty.pointee_type().unwrap(); + if *ty != canonical_ty { + let canonical_pointee = + canonical_ty.pointee_type().unwrap(); + // clang sometimes loses pointee constness here, see + // #2244. + if canonical_pointee.is_const() != pointee.is_const() { + pointee = canonical_pointee; + } + } let inner = Item::from_ty_or_ref(pointee, location, None, ctx); TypeKind::Pointer(inner) @@ -1080,9 +1099,9 @@ impl Type { } CXType_Typedef => { let inner = cursor.typedef_type().expect("Not valid Type?"); - let inner = + let inner_id = Item::from_ty_or_ref(inner, location, None, ctx); - if inner == potential_id { + if inner_id == potential_id { warn!( "Generating oqaque type instead of self-referential \ typedef"); @@ -1090,16 +1109,31 @@ impl Type { // within the clang parsing. TypeKind::Opaque } else { - TypeKind::Alias(inner) + // Check if this type definition is an alias to a pointer of a `struct` / + // `union` / `enum` with the same name and add the `_ptr` suffix to it to + // avoid name collisions. + if let Some(ref mut name) = name { + if inner.kind() == CXType_Pointer && + !ctx.options().c_naming + { + let pointee = inner.pointee_type().unwrap(); + if pointee.kind() == CXType_Elaborated && + pointee.declaration().spelling() == *name + { + *name += "_ptr"; + } + } + } + TypeKind::Alias(inner_id) } } CXType_Enum => { let enum_ = Enum::from_ty(ty, ctx).expect("Not an enum?"); - if name.is_empty() { + if !is_anonymous { let pretty_name = ty.spelling(); if clang::is_valid_identifier(&pretty_name) { - name = pretty_name; + name = Some(pretty_name); } } @@ -1114,12 +1148,12 @@ impl Type { ) .expect("Not a complex type?"); - if name.is_empty() { + if !is_anonymous { // The pretty-printed name may contain typedefed name, // but may also be "struct (anonymous at .h:1)" let pretty_name = ty.spelling(); if clang::is_valid_identifier(&pretty_name) { - name = pretty_name; + name = Some(pretty_name); } } @@ -1131,8 +1165,7 @@ impl Type { location, None, ctx, - ) - .expect("Not able to resolve vector element?"); + )?; TypeKind::Vector(inner, ty.num_elements().unwrap()) } CXType_ConstantArray => { @@ -1159,7 +1192,9 @@ impl Type { CXType_ObjCClass | CXType_ObjCInterface => { let interface = ObjCInterface::from_ty(&location, ctx) .expect("Not a valid objc interface?"); - name = interface.rust_name(); + if !is_anonymous { + name = Some(interface.rust_name()); + } TypeKind::ObjCInterface(interface) } CXType_Dependent => { @@ -1177,7 +1212,7 @@ impl Type { } }; - let name = if name.is_empty() { None } else { Some(name) }; + name = name.filter(|n| !n.is_empty()); let is_const = ty.is_const() || (ty.kind() == CXType_ConstantArray && @@ -1197,6 +1232,13 @@ impl Trace for Type { where T: Tracer, { + if self + .name() + .map_or(false, |name| context.is_stdint_type(name)) + { + // These types are special-cased in codegen and don't need to be traversed. + return; + } match *self.kind() { TypeKind::Pointer(inner) | TypeKind::Reference(inner) | diff --git a/src/ir/var.rs b/ir/var.rs index cd17937..903e1ff 100644 --- a/src/ir/var.rs +++ b/ir/var.rs @@ -7,12 +7,10 @@ use super::function::cursor_mangling; use super::int::IntKind; use super::item::Item; use super::ty::{FloatKind, TypeKind}; -use crate::callbacks::MacroParsingBehavior; +use crate::callbacks::{ItemInfo, ItemKind, MacroParsingBehavior}; use crate::clang; use crate::clang::ClangToken; -use crate::parse::{ - ClangItemParser, ClangSubItemParser, ParseError, ParseResult, -}; +use crate::parse::{ClangSubItemParser, ParseError, ParseResult}; use cexpr; use std::io; use std::num::Wrapping; @@ -149,52 +147,27 @@ fn default_macro_constant_type(ctx: &BindgenContext, value: i64) -> IntKind { } } -/// Determines whether a set of tokens from a CXCursor_MacroDefinition -/// represent a function-like macro. If so, calls the func_macro callback -/// and returns `Err(ParseError::Continue)` to signal to skip further -/// processing. If conversion to UTF-8 fails (it is performed only where it -/// should be infallible), then `Err(ParseError::Continue)` is returned as well. +/// 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, - tokens: &[ClangToken], callbacks: &dyn crate::callbacks::ParseCallbacks, -) -> Result<(), ParseError> { - // TODO: Hoist the `is_macro_function_like` check into this function's - // caller, and thus avoid allocating the `tokens` vector for non-functional - // macros. - let is_functional_macro = cursor.is_macro_function_like(); - - if !is_functional_macro { - return Ok(()); - } - +) { let is_closing_paren = |t: &ClangToken| { // Test cheap token kind before comparing exact spellings. t.kind == clang_sys::CXToken_Punctuation && t.spelling() == b")" }; - let 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.ok_or(ParseError::Continue)? + 1); - let left = left.collect::<Vec<_>>().concat(); - let left = String::from_utf8(left).map_err(|_| ParseError::Continue)?; - let right = spelled; - // Drop last token with LLVM < 4.0, due to an LLVM bug. - // - // See: - // https://bugs.llvm.org//show_bug.cgi?id=9069 - let len = match (right.len(), crate::clang_version().parsed) { - (len, Some((v, _))) if len > 0 && v < 4 => len - 1, - (len, _) => len, - }; - let right: Vec<_> = right.take(len).collect(); - callbacks.func_macro(&left, &right); - - // We handled the macro, skip future macro processing. - Err(ParseError::Continue) + 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::<Vec<_>>().concat(); + if let Ok(left) = String::from_utf8(left) { + let right: Vec<_> = spelled.collect(); + callbacks.func_macro(&left, &right); + } + } } impl ClangSubItemParser for Var { @@ -207,9 +180,7 @@ impl ClangSubItemParser for Var { use clang_sys::*; match cursor.kind() { CXCursor_MacroDefinition => { - let tokens: Vec<_> = cursor.tokens().iter().collect(); - - if let Some(callbacks) = ctx.parse_callbacks() { + for callbacks in &ctx.options().parse_callbacks { match callbacks.will_parse_macro(&cursor.spelling()) { MacroParsingBehavior::Ignore => { return Err(ParseError::Continue); @@ -217,10 +188,14 @@ impl ClangSubItemParser for Var { MacroParsingBehavior::Default => {} } - handle_function_macro(&cursor, &tokens, callbacks)?; + 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, &tokens); + let value = parse_macro(ctx, &cursor); let (id, value) = match value { Some(v) => v, @@ -272,15 +247,15 @@ impl ClangSubItemParser for Var { true, ctx, ); - if let Some(callbacks) = ctx.parse_callbacks() { + 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 - .parse_callbacks() - .and_then(|c| c.int_macro(&name, value)) + .options() + .last_callback(|c| c.int_macro(&name, value)) .unwrap_or_else(|| { default_macro_constant_type(ctx, value) }); @@ -297,7 +272,20 @@ impl ClangSubItemParser for Var { )) } CXCursor_VarDecl => { - let name = cursor.spelling(); + 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); @@ -308,18 +296,19 @@ impl ClangSubItemParser for Var { // TODO(emilio): do we have to special-case constant arrays in // some other places? let is_const = ty.is_const() || - (ty.kind() == CXType_ConstantArray && + ([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_eq!( - ty.kind(), - CXType_Auto, + assert!( + matches!(ty.kind(), CXType_Auto | CXType_Unexposed), "Couldn't resolve constant type, and it \ - wasn't an nondeductible auto type!" + wasn't an nondeductible auto type or unexposed \ + type!" ); return Err(e); } @@ -348,8 +337,7 @@ impl ClangSubItemParser for Var { let mut val = cursor.evaluate().and_then(|v| v.as_int()); if val.is_none() || !kind.signedness_matches(val.unwrap()) { - let tu = ctx.translation_unit(); - val = get_integer_literal_from_cursor(&cursor, tu); + val = get_integer_literal_from_cursor(&cursor); } val.map(|val| { @@ -387,29 +375,14 @@ impl ClangSubItemParser for Var { /// Try and parse a macro using all the macros parsed until now. fn parse_macro( ctx: &BindgenContext, - tokens: &[ClangToken], + cursor: &clang::Cursor, ) -> Option<(Vec<u8>, cexpr::expr::EvalResult)> { use cexpr::expr; - let mut cexpr_tokens: Vec<_> = tokens - .iter() - .filter_map(ClangToken::as_cexpr_token) - .collect(); + let cexpr_tokens = cursor.cexpr_tokens(); let parser = expr::IdentifierParser::new(ctx.parsed_macros()); - if let Ok((_, (id, val))) = parser.macro_definition(&cexpr_tokens) { - return Some((id.into(), val)); - } - - // Try without the last token, to workaround a libclang bug in versions - // previous to 4.0. - // - // See: - // https://bugs.llvm.org//show_bug.cgi?id=9069 - // https://reviews.llvm.org/D26446 - cexpr_tokens.pop()?; - match parser.macro_definition(&cexpr_tokens) { Ok((_, (id, val))) => Some((id.into(), val)), _ => None, @@ -429,10 +402,7 @@ fn parse_int_literal_tokens(cursor: &clang::Cursor) -> Option<i64> { } } -fn get_integer_literal_from_cursor( - cursor: &clang::Cursor, - unit: &clang::TranslationUnit, -) -> Option<i64> { +fn get_integer_literal_from_cursor(cursor: &clang::Cursor) -> Option<i64> { use clang_sys::*; let mut value = None; cursor.visit(|c| { @@ -441,7 +411,7 @@ fn get_integer_literal_from_cursor( value = parse_int_literal_tokens(&c); } CXCursor_UnexposedExpr => { - value = get_integer_literal_from_cursor(&c, unit); + value = get_integer_literal_from_cursor(&c); } _ => (), } @@ -49,11 +49,23 @@ macro_rules! doc_mod { }; } +macro_rules! fn_with_regex_arg { + ($(#[$attrs:meta])* pub fn $($tokens:tt)*) => { + $(#[$attrs])* + /// Check the [regular expression arguments] section and the [regex] crate + /// documentation for further information. + /// + /// [regular expression arguments]: ./struct.Builder.html#regular-expression-arguments + /// [regex]: <https://docs.rs/regex> + pub fn $($tokens)* + }; +} + mod clang; mod codegen; mod deps; mod features; -mod ir; +pub mod ir; mod parse; mod regex_set; mod time; @@ -66,22 +78,29 @@ doc_mod!(ir, ir_docs); doc_mod!(parse, parse_docs); doc_mod!(regex_set, regex_set_docs); -pub use crate::codegen::{AliasVariation, EnumVariation, MacroTypeVariation}; +use codegen::CodegenError; +use ir::comment; + +pub use crate::codegen::{ + AliasVariation, EnumVariation, MacroTypeVariation, NonCopyUnionStyle, +}; use crate::features::RustFeatures; pub use crate::features::{ RustTarget, LATEST_STABLE_RUST, RUST_TARGET_STRINGS, }; use crate::ir::context::{BindgenContext, ItemId}; +pub use crate::ir::function::Abi; use crate::ir::item::Item; -use crate::parse::{ClangItemParser, ParseError}; -use crate::regex_set::RegexSet; +use crate::parse::ParseError; +pub use crate::regex_set::RegexSet; use std::borrow::Cow; +use std::env; use std::fs::{File, OpenOptions}; use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; -use std::{env, iter}; +use std::rc::Rc; // Some convenient typedefs for a fast hash map and hash set. type HashMap<K, V> = ::rustc_hash::FxHashMap<K, V>; @@ -90,6 +109,7 @@ pub(crate) use std::collections::hash_map::Entry; /// Default prefix for the anon fields. pub const DEFAULT_ANON_FIELDS_PREFIX: &str = "__bindgen_anon_"; +const DEFAULT_NON_EXTERN_FNS_SUFFIX: &str = "__extern"; fn file_is_cpp(name_file: &str) -> bool { name_file.ends_with(".hpp") || @@ -219,12 +239,20 @@ impl Default for CodegenConfig { /// End-users of the crate may need to set the `BINDGEN_EXTRA_CLANG_ARGS` environment variable to /// add additional arguments. For example, to build against a different sysroot a user could set /// `BINDGEN_EXTRA_CLANG_ARGS` to `--sysroot=/path/to/sysroot`. -#[derive(Debug, Default)] +/// +/// # Regular expression arguments +/// +/// Some [`Builder`] methods like the `allowlist_*` and `blocklist_*` family of methods allow +/// regular expressions as arguments. These regular expressions will be parenthesized and wrapped +/// in `^` and `$`. So if `<regex>` is passed as argument, the regular expression to be stored will +/// be `^(<regex>)$`. +/// +/// Releases of `bindgen` with a version lesser or equal to `0.62.0` used to accept the wildcard +/// pattern `*` as a valid regular expression. This behavior has been deprecated and the `.*` +/// pattern must be used instead. +#[derive(Debug, Default, Clone)] pub struct Builder { options: BindgenOptions, - input_headers: Vec<String>, - // Tuples of unsaved file contents of the form (name, contents). - input_header_contents: Vec<(String, String)>, } /// Construct a new [`Builder`](./struct.Builder.html). @@ -232,12 +260,26 @@ pub fn builder() -> Builder { Default::default() } +fn get_extra_clang_args() -> Vec<String> { + // Add any extra arguments from the environment to the clang command line. + let extra_clang_args = + match get_target_dependent_env_var("BINDGEN_EXTRA_CLANG_ARGS") { + None => return vec![], + Some(s) => s, + }; + // Try to parse it with shell quoting. If we fail, make it one single big argument. + if let Some(strings) = shlex::split(&extra_clang_args) { + return strings; + } + vec![extra_clang_args] +} + impl Builder { /// Generates the command line flags use for creating `Builder`. pub fn command_line_flags(&self) -> Vec<String> { let mut output_vector: Vec<String> = Vec::new(); - if let Some(header) = self.input_headers.last().cloned() { + if let Some(header) = self.options.input_headers.last().cloned() { // Positional argument 'header' output_vector.push(header); } @@ -264,11 +306,19 @@ impl Builder { codegen::EnumVariation::Rust { non_exhaustive: true, } => "rust_non_exhaustive", - codegen::EnumVariation::NewType { is_bitfield: true } => { - "bitfield" - } - codegen::EnumVariation::NewType { is_bitfield: false } => { - "newtype" + codegen::EnumVariation::NewType { + is_bitfield: true, + .. + } => "bitfield", + codegen::EnumVariation::NewType { + is_bitfield: false, + is_global, + } => { + if is_global { + "newtype_global" + } else { + "newtype" + } } codegen::EnumVariation::Consts => "consts", codegen::EnumVariation::ModuleConsts => "moduleconsts", @@ -289,9 +339,17 @@ impl Builder { .push(self.options.default_alias_style.as_str().into()); } + if self.options.default_non_copy_union_style != Default::default() { + output_vector.push("--default-non-copy-union-style".into()); + output_vector.push( + self.options.default_non_copy_union_style.as_str().into(), + ); + } + let regex_sets = &[ (&self.options.bitfield_enums, "--bitfield-enum"), (&self.options.newtype_enums, "--newtype-enum"), + (&self.options.newtype_global_enums, "--newtype-global-enum"), (&self.options.rustified_enums, "--rustified-enum"), ( &self.options.rustified_non_exhaustive_enums, @@ -305,6 +363,11 @@ impl Builder { (&self.options.type_alias, "--type-alias"), (&self.options.new_type_alias, "--new-type-alias"), (&self.options.new_type_alias_deref, "--new-type-alias-deref"), + ( + &self.options.bindgen_wrapper_union, + "--bindgen-wrapper-union", + ), + (&self.options.manually_drop_union, "--manually-drop-union"), (&self.options.blocklisted_types, "--blocklist-type"), (&self.options.blocklisted_functions, "--blocklist-function"), (&self.options.blocklisted_items, "--blocklist-item"), @@ -313,6 +376,7 @@ impl Builder { (&self.options.allowlisted_functions, "--allowlist-function"), (&self.options.allowlisted_types, "--allowlist-type"), (&self.options.allowlisted_vars, "--allowlist-var"), + (&self.options.allowlisted_files, "--allowlist-file"), (&self.options.no_partialeq_types, "--no-partialeq"), (&self.options.no_copy_types, "--no-copy"), (&self.options.no_debug_types, "--no-debug"), @@ -328,6 +392,13 @@ impl Builder { } } + for (abi, set) in &self.options.abi_overrides { + for item in set.get_items() { + output_vector.push("--override-abi".to_owned()); + output_vector.push(format!("{}={}", item, abi)); + } + } + if !self.options.layout_tests { output_vector.push("--no-layout-tests".into()); } @@ -525,8 +596,8 @@ impl Builder { output_vector.push("--no-record-matches".into()); } - if self.options.size_t_is_usize { - output_vector.push("--size_t-is-usize".into()); + if !self.options.size_t_is_usize { + output_vector.push("--no-size_t-is-usize".into()); } if !self.options.rustfmt_bindings { @@ -568,6 +639,44 @@ impl Builder { output_vector.push("--explicit-padding".into()); } + if self.options.vtable_generation { + output_vector.push("--vtable-generation".into()); + } + + if self.options.sort_semantically { + output_vector.push("--sort-semantically".into()); + } + + if self.options.merge_extern_blocks { + output_vector.push("--merge-extern-blocks".into()); + } + + if self.options.wrap_unsafe_ops { + output_vector.push("--wrap-unsafe-ops".into()); + } + + #[cfg(feature = "cli")] + for callbacks in &self.options.parse_callbacks { + output_vector.extend(callbacks.cli_args()); + } + if self.options.wrap_static_fns { + output_vector.push("--wrap-static-fns".into()) + } + + if let Some(ref path) = self.options.wrap_static_fns_path { + output_vector.push("--wrap-static-fns-path".into()); + output_vector.push(path.display().to_string()); + } + + if let Some(ref suffix) = self.options.wrap_static_fns_suffix { + output_vector.push("--wrap-static-fns-suffix".into()); + output_vector.push(suffix.clone()); + } + + if cfg!(feature = "experimental") { + output_vector.push("--experimental".into()); + } + // Add clang arguments output_vector.push("--".into()); @@ -576,13 +685,13 @@ impl Builder { output_vector.extend(self.options.clang_args.iter().cloned()); } - if self.input_headers.len() > 1 { - // To pass more than one header, we need to pass all but the last - // header via the `-include` clang arg - for header in &self.input_headers[..self.input_headers.len() - 1] { - output_vector.push("-include".to_string()); - output_vector.push(header.clone()); - } + // To pass more than one header, we need to pass all but the last + // header via the `-include` clang arg + for header in &self.options.input_headers + [..self.options.input_headers.len().saturating_sub(1)] + { + output_vector.push("-include".to_string()); + output_vector.push(header.clone()); } output_vector @@ -611,7 +720,7 @@ impl Builder { /// .unwrap(); /// ``` pub fn header<T: Into<String>>(mut self, header: T) -> Builder { - self.input_headers.push(header.into()); + self.options.input_headers.push(header.into()); self } @@ -640,7 +749,8 @@ impl Builder { .to_str() .expect("Cannot convert current directory name to string") .to_owned(); - self.input_header_contents + self.options + .input_header_contents .push((absolute_path, contents.into())); self } @@ -649,6 +759,13 @@ impl Builder { /// /// The default is the latest stable Rust version pub fn rust_target(mut self, rust_target: RustTarget) -> Self { + #[allow(deprecated)] + if rust_target <= RustTarget::Stable_1_30 { + warn!( + "The {} rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues", + String::from(rust_target) + ); + } self.options.set_rust_target(rust_target); self } @@ -720,12 +837,6 @@ impl Builder { self } - /// Deprecated alias for allowlist_recursively. - #[deprecated(note = "Use allowlist_recursively instead")] - pub fn whitelist_recursively(self, doit: bool) -> Self { - self.allowlist_recursively(doit) - } - /// Generate `#[macro_use] extern crate objc;` instead of `use objc;` /// in the prologue of the files generated from objective-c files pub fn objc_extern_crate(mut self, doit: bool) -> Self { @@ -758,168 +869,110 @@ impl Builder { self } - /// Hide the given type from the generated bindings. Regular expressions are - /// supported. - #[deprecated(note = "Use blocklist_type instead")] - pub fn hide_type<T: AsRef<str>>(self, arg: T) -> Builder { - self.blocklist_type(arg) - } - - /// Hide the given type from the generated bindings. Regular expressions are - /// supported. - #[deprecated(note = "Use blocklist_type instead")] - pub fn blacklist_type<T: AsRef<str>>(self, arg: T) -> Builder { - self.blocklist_type(arg) - } - - /// Hide the given type from the generated bindings. Regular expressions are - /// supported. - /// - /// To blocklist types prefixed with "mylib" use `"mylib_.*"`. - /// For more complicated expressions check - /// [regex](https://docs.rs/regex/*/regex/) docs - pub fn blocklist_type<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.blocklisted_types.insert(arg); - self - } - - /// Hide the given function from the generated bindings. Regular expressions - /// are supported. - #[deprecated(note = "Use blocklist_function instead")] - pub fn blacklist_function<T: AsRef<str>>(self, arg: T) -> Builder { - self.blocklist_function(arg) - } - - /// Hide the given function from the generated bindings. Regular expressions - /// are supported. - /// - /// To blocklist functions prefixed with "mylib" use `"mylib_.*"`. - /// For more complicated expressions check - /// [regex](https://docs.rs/regex/*/regex/) docs - pub fn blocklist_function<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.blocklisted_functions.insert(arg); - self - } - - /// Hide the given item from the generated bindings, regardless of - /// whether it's a type, function, module, etc. Regular - /// expressions are supported. - #[deprecated(note = "Use blocklist_item instead")] - pub fn blacklist_item<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.blocklisted_items.insert(arg); - self - } - - /// Hide the given item from the generated bindings, regardless of - /// whether it's a type, function, module, etc. Regular - /// expressions are supported. - /// - /// To blocklist items prefixed with "mylib" use `"mylib_.*"`. - /// For more complicated expressions check - /// [regex](https://docs.rs/regex/*/regex/) docs - pub fn blocklist_item<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.blocklisted_items.insert(arg); - self - } - - /// Hide any contents of the given file from the generated bindings, - /// regardless of whether it's a type, function, module etc. - pub fn blocklist_file<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.blocklisted_files.insert(arg); - self - } - - /// Treat the given type as opaque in the generated bindings. Regular - /// expressions are supported. - /// - /// To change types prefixed with "mylib" into opaque, use `"mylib_.*"`. - /// For more complicated expressions check - /// [regex](https://docs.rs/regex/*/regex/) docs - pub fn opaque_type<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.opaque_types.insert(arg); - self - } - - /// Allowlist the given type so that it (and all types that it transitively - /// refers to) appears in the generated bindings. Regular expressions are - /// supported. - #[deprecated(note = "use allowlist_type instead")] - pub fn whitelisted_type<T: AsRef<str>>(self, arg: T) -> Builder { - self.allowlist_type(arg) + fn_with_regex_arg! { + /// Hide the given type from the generated bindings. Regular expressions are + /// supported. + /// + /// To blocklist types prefixed with "mylib" use `"mylib_.*"`. + pub fn blocklist_type<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.blocklisted_types.insert(arg); + self + } } - /// Allowlist the given type so that it (and all types that it transitively - /// refers to) appears in the generated bindings. Regular expressions are - /// supported. - #[deprecated(note = "use allowlist_type instead")] - pub fn whitelist_type<T: AsRef<str>>(self, arg: T) -> Builder { - self.allowlist_type(arg) + fn_with_regex_arg! { + /// Hide the given function from the generated bindings. Regular expressions + /// are supported. + /// + /// Methods can be blocklisted by prefixing the name of the type implementing + /// them followed by an underscore. So if `Foo` has a method `bar`, it can + /// be blocklisted as `Foo_bar`. + /// + /// To blocklist functions prefixed with "mylib" use `"mylib_.*"`. + pub fn blocklist_function<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.blocklisted_functions.insert(arg); + self + } } - /// Allowlist the given type so that it (and all types that it transitively - /// refers to) appears in the generated bindings. Regular expressions are - /// supported. - /// - /// To allowlist types prefixed with "mylib" use `"mylib_.*"`. - /// For more complicated expressions check - /// [regex](https://docs.rs/regex/*/regex/) docs - pub fn allowlist_type<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.allowlisted_types.insert(arg); - self + fn_with_regex_arg! { + /// Hide the given item from the generated bindings, regardless of + /// whether it's a type, function, module, etc. Regular + /// expressions are supported. + /// + /// To blocklist items prefixed with "mylib" use `"mylib_.*"`. + pub fn blocklist_item<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.blocklisted_items.insert(arg); + self + } } - /// Allowlist the given function so that it (and all types that it - /// transitively refers to) appears in the generated bindings. Regular - /// expressions are supported. - /// - /// To allowlist functions prefixed with "mylib" use `"mylib_.*"`. - /// For more complicated expressions check - /// [regex](https://docs.rs/regex/*/regex/) docs - pub fn allowlist_function<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.allowlisted_functions.insert(arg); - self + fn_with_regex_arg! { + /// Hide any contents of the given file from the generated bindings, + /// regardless of whether it's a type, function, module etc. + pub fn blocklist_file<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.blocklisted_files.insert(arg); + self + } } - /// Allowlist the given function. - /// - /// Deprecated: use allowlist_function instead. - #[deprecated(note = "use allowlist_function instead")] - pub fn whitelist_function<T: AsRef<str>>(self, arg: T) -> Builder { - self.allowlist_function(arg) + fn_with_regex_arg! { + /// Treat the given type as opaque in the generated bindings. Regular + /// expressions are supported. + /// + /// To change types prefixed with "mylib" into opaque, use `"mylib_.*"`. + pub fn opaque_type<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.opaque_types.insert(arg); + self + } } - /// Allowlist the given function. - /// - /// Deprecated: use allowlist_function instead. - #[deprecated(note = "use allowlist_function instead")] - pub fn whitelisted_function<T: AsRef<str>>(self, arg: T) -> Builder { - self.allowlist_function(arg) + fn_with_regex_arg! { + /// Allowlist the given type so that it (and all types that it transitively + /// refers to) appears in the generated bindings. Regular expressions are + /// supported. + /// + /// To allowlist types prefixed with "mylib" use `"mylib_.*"`. + pub fn allowlist_type<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.allowlisted_types.insert(arg); + self + } } - /// Allowlist the given variable so that it (and all types that it - /// transitively refers to) appears in the generated bindings. Regular - /// expressions are supported. - /// - /// To allowlist variables prefixed with "mylib" use `"mylib_.*"`. - /// For more complicated expressions check - /// [regex](https://docs.rs/regex/*/regex/) docs - pub fn allowlist_var<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.allowlisted_vars.insert(arg); - self + fn_with_regex_arg! { + /// Allowlist the given function so that it (and all types that it + /// transitively refers to) appears in the generated bindings. Regular + /// expressions are supported. + /// + /// Methods can be allowlisted by prefixing the name of the type + /// implementing them followed by an underscore. So if `Foo` has a method + /// `bar`, it can be allowlisted as `Foo_bar`. + /// + /// To allowlist functions prefixed with "mylib" use `"mylib_.*"`. + pub fn allowlist_function<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.allowlisted_functions.insert(arg); + self + } } - /// Deprecated: use allowlist_var instead. - #[deprecated(note = "use allowlist_var instead")] - pub fn whitelist_var<T: AsRef<str>>(self, arg: T) -> Builder { - self.allowlist_var(arg) + fn_with_regex_arg! { + /// Allowlist the given variable so that it (and all types that it + /// transitively refers to) appears in the generated bindings. Regular + /// expressions are supported. + /// + /// To allowlist variables prefixed with "mylib" use `"mylib_.*"`. + pub fn allowlist_var<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.allowlisted_vars.insert(arg); + self + } } - /// Allowlist the given variable. - /// - /// Deprecated: use allowlist_var instead. - #[deprecated(note = "use allowlist_var instead")] - pub fn whitelisted_var<T: AsRef<str>>(self, arg: T) -> Builder { - self.allowlist_var(arg) + fn_with_regex_arg! { + /// Allowlist the given file so that its contents appear in the generated bindings. + pub fn allowlist_file<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.allowlisted_files.insert(arg); + self + } } /// Set the default style of code to generate for enums @@ -931,75 +984,101 @@ impl Builder { self } - /// Mark the given enum (or set of enums, if using a pattern) as being - /// bitfield-like. Regular expressions are supported. - /// - /// This makes bindgen generate a type that isn't a rust `enum`. Regular - /// expressions are supported. - /// - /// This is similar to the newtype enum style, but with the bitwise - /// operators implemented. - pub fn bitfield_enum<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.bitfield_enums.insert(arg); - self - } - - /// Mark the given enum (or set of enums, if using a pattern) as a newtype. - /// Regular expressions are supported. - /// - /// This makes bindgen generate a type that isn't a Rust `enum`. Regular - /// expressions are supported. - pub fn newtype_enum<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.newtype_enums.insert(arg); - self - } - - /// Mark the given enum (or set of enums, if using a pattern) as a Rust - /// enum. - /// - /// This makes bindgen generate enums instead of constants. Regular - /// expressions are supported. - /// - /// **Use this with caution**, creating this in unsafe code - /// (including FFI) with an invalid value will invoke undefined behaviour. - /// You may want to use the newtype enum style instead. - pub fn rustified_enum<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.rustified_enums.insert(arg); - self - } - - /// Mark the given enum (or set of enums, if using a pattern) as a Rust - /// enum with the `#[non_exhaustive]` attribute. - /// - /// This makes bindgen generate enums instead of constants. Regular - /// expressions are supported. - /// - /// **Use this with caution**, creating this in unsafe code - /// (including FFI) with an invalid value will invoke undefined behaviour. - /// You may want to use the newtype enum style instead. - pub fn rustified_non_exhaustive_enum<T: AsRef<str>>( - mut self, - arg: T, - ) -> Builder { - self.options.rustified_non_exhaustive_enums.insert(arg); - self - } - - /// Mark the given enum (or set of enums, if using a pattern) as a set of - /// constants that are not to be put into a module. - pub fn constified_enum<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.constified_enums.insert(arg); - self - } - - /// Mark the given enum (or set of enums, if using a pattern) as a set of - /// constants that should be put into a module. - /// - /// This makes bindgen generate modules containing constants instead of - /// just constants. Regular expressions are supported. - pub fn constified_enum_module<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.constified_enum_modules.insert(arg); - self + fn_with_regex_arg! { + /// Mark the given enum (or set of enums, if using a pattern) as being + /// bitfield-like. Regular expressions are supported. + /// + /// This makes bindgen generate a type that isn't a rust `enum`. Regular + /// expressions are supported. + /// + /// This is similar to the newtype enum style, but with the bitwise + /// operators implemented. + pub fn bitfield_enum<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.bitfield_enums.insert(arg); + self + } + } + + fn_with_regex_arg! { + /// Mark the given enum (or set of enums, if using a pattern) as a newtype. + /// Regular expressions are supported. + /// + /// This makes bindgen generate a type that isn't a Rust `enum`. Regular + /// expressions are supported. + pub fn newtype_enum<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.newtype_enums.insert(arg); + self + } + } + + fn_with_regex_arg! { + /// Mark the given enum (or set of enums, if using a pattern) as a newtype + /// whose variants are exposed as global constants. + /// + /// Regular expressions are supported. + /// + /// This makes bindgen generate a type that isn't a Rust `enum`. Regular + /// expressions are supported. + pub fn newtype_global_enum<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.newtype_global_enums.insert(arg); + self + } + } + + fn_with_regex_arg! { + /// Mark the given enum (or set of enums, if using a pattern) as a Rust + /// enum. + /// + /// This makes bindgen generate enums instead of constants. Regular + /// expressions are supported. + /// + /// **Use this with caution**, creating this in unsafe code + /// (including FFI) with an invalid value will invoke undefined behaviour. + /// You may want to use the newtype enum style instead. + pub fn rustified_enum<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.rustified_enums.insert(arg); + self + } + } + + fn_with_regex_arg! { + /// Mark the given enum (or set of enums, if using a pattern) as a Rust + /// enum with the `#[non_exhaustive]` attribute. + /// + /// This makes bindgen generate enums instead of constants. Regular + /// expressions are supported. + /// + /// **Use this with caution**, creating this in unsafe code + /// (including FFI) with an invalid value will invoke undefined behaviour. + /// You may want to use the newtype enum style instead. + pub fn rustified_non_exhaustive_enum<T: AsRef<str>>( + mut self, + arg: T, + ) -> Builder { + self.options.rustified_non_exhaustive_enums.insert(arg); + self + } + } + + fn_with_regex_arg! { + /// Mark the given enum (or set of enums, if using a pattern) as a set of + /// constants that are not to be put into a module. + pub fn constified_enum<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.constified_enums.insert(arg); + self + } + } + + fn_with_regex_arg! { + /// Mark the given enum (or set of enums, if using a pattern) as a set of + /// constants that should be put into a module. + /// + /// This makes bindgen generate modules containing constants instead of + /// just constants. Regular expressions are supported. + pub fn constified_enum_module<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.constified_enum_modules.insert(arg); + self + } } /// Set the default type for macro constants @@ -1020,43 +1099,81 @@ impl Builder { self } - /// Mark the given typedef alias (or set of aliases, if using a pattern) to - /// use regular Rust type aliasing. - /// - /// This is the default behavior and should be used if `default_alias_style` - /// was set to NewType or NewTypeDeref and you want to override it for a - /// set of typedefs. - pub fn type_alias<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.type_alias.insert(arg); - self + fn_with_regex_arg! { + /// Mark the given typedef alias (or set of aliases, if using a pattern) to + /// use regular Rust type aliasing. + /// + /// This is the default behavior and should be used if `default_alias_style` + /// was set to NewType or NewTypeDeref and you want to override it for a + /// set of typedefs. + pub fn type_alias<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.type_alias.insert(arg); + self + } } - /// Mark the given typedef alias (or set of aliases, if using a pattern) to - /// be generated as a new type by having the aliased type be wrapped in a - /// #[repr(transparent)] struct. - /// - /// Used to enforce stricter type checking. - pub fn new_type_alias<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.new_type_alias.insert(arg); - self + fn_with_regex_arg! { + /// Mark the given typedef alias (or set of aliases, if using a pattern) to + /// be generated as a new type by having the aliased type be wrapped in a + /// #[repr(transparent)] struct. + /// + /// Used to enforce stricter type checking. + pub fn new_type_alias<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.new_type_alias.insert(arg); + self + } } - /// Mark the given typedef alias (or set of aliases, if using a pattern) to - /// be generated as a new type by having the aliased type be wrapped in a - /// #[repr(transparent)] struct and also have an automatically generated - /// impl's of `Deref` and `DerefMut` to their aliased type. - pub fn new_type_alias_deref<T: AsRef<str>>(mut self, arg: T) -> Builder { - self.options.new_type_alias_deref.insert(arg); - self + fn_with_regex_arg! { + /// Mark the given typedef alias (or set of aliases, if using a pattern) to + /// be generated as a new type by having the aliased type be wrapped in a + /// #[repr(transparent)] struct and also have an automatically generated + /// impl's of `Deref` and `DerefMut` to their aliased type. + pub fn new_type_alias_deref<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.new_type_alias_deref.insert(arg); + self + } } - /// Add a string to prepend to the generated bindings. The string is passed - /// through without any modification. - pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Self { - self.options.raw_lines.push(arg.into()); + /// Set the default style of code to generate for unions with a non-Copy member. + pub fn default_non_copy_union_style( + mut self, + arg: codegen::NonCopyUnionStyle, + ) -> Self { + self.options.default_non_copy_union_style = arg; self } + fn_with_regex_arg! { + /// Mark the given union (or set of union, if using a pattern) to use + /// a bindgen-generated wrapper for its members if at least one is non-Copy. + pub fn bindgen_wrapper_union<T: AsRef<str>>(mut self, arg: T) -> Self { + self.options.bindgen_wrapper_union.insert(arg); + self + } + } + + fn_with_regex_arg! { + /// Mark the given union (or set of union, if using a pattern) to use + /// [`::core::mem::ManuallyDrop`] for its members if at least one is non-Copy. + /// + /// Note: `ManuallyDrop` was stabilized in Rust 1.20.0, do not use it if your + /// MSRV is lower. + pub fn manually_drop_union<T: AsRef<str>>(mut self, arg: T) -> Self { + self.options.manually_drop_union.insert(arg); + self + } + } + + fn_with_regex_arg! { + /// Add a string to prepend to the generated bindings. The string is passed + /// through without any modification. + pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Self { + self.options.raw_lines.push(arg.into()); + self + } + } + /// Add a given line to the beginning of module `mod`. pub fn module_raw_line<T, U>(mut self, mod_: T, line: U) -> Self where @@ -1336,17 +1453,6 @@ impl Builder { self } - /// Avoid generating any unstable Rust, such as Rust unions, in the generated bindings. - #[deprecated(note = "please use `rust_target` instead")] - pub fn unstable_rust(self, doit: bool) -> Self { - let rust_target = if doit { - RustTarget::Nightly - } else { - LATEST_STABLE_RUST - }; - self.rust_target(rust_target) - } - /// Use core instead of libstd in the generated bindings. pub fn use_core(mut self) -> Builder { self.options.use_core = true; @@ -1371,7 +1477,7 @@ impl Builder { mut self, cb: Box<dyn callbacks::ParseCallbacks>, ) -> Self { - self.options.parse_callbacks = Some(cb); + self.options.parse_callbacks.push(Rc::from(cb)); self } @@ -1443,39 +1549,48 @@ impl Builder { self } + /// If true, enables experimental support to generate vtable functions. + /// + /// Should mostly work, though some edge cases are likely to be broken. + pub fn vtable_generation(mut self, doit: bool) -> Self { + self.options.vtable_generation = doit; + self + } + + /// If true, enables the sorting of the output in a predefined manner. + /// + /// TODO: Perhaps move the sorting order out into a config + pub fn sort_semantically(mut self, doit: bool) -> Self { + self.options.sort_semantically = doit; + self + } + + /// If true, merges extern blocks. + pub fn merge_extern_blocks(mut self, doit: bool) -> Self { + self.options.merge_extern_blocks = doit; + self + } + /// Generate the Rust bindings using the options built up thus far. - pub fn generate(mut self) -> Result<Bindings, ()> { + pub fn generate(mut self) -> Result<Bindings, BindgenError> { // Add any extra arguments from the environment to the clang command line. - if let Some(extra_clang_args) = - get_target_dependent_env_var("BINDGEN_EXTRA_CLANG_ARGS") - { - // Try to parse it with shell quoting. If we fail, make it one single big argument. - if let Some(strings) = shlex::split(&extra_clang_args) { - self.options.clang_args.extend(strings); - } else { - self.options.clang_args.push(extra_clang_args); - }; - } + self.options.clang_args.extend(get_extra_clang_args()); // Transform input headers to arguments on the clang command line. - self.options.input_header = self.input_headers.pop(); - self.options.extra_input_headers = self.input_headers; self.options.clang_args.extend( - self.options.extra_input_headers.iter().flat_map(|header| { - iter::once("-include".into()) - .chain(iter::once(header.to_string())) - }), + self.options.input_headers + [..self.options.input_headers.len().saturating_sub(1)] + .iter() + .flat_map(|header| ["-include".into(), header.to_string()]), ); - self.options.input_unsaved_files.extend( - self.input_header_contents - .drain(..) - .map(|(name, contents)| { - clang::UnsavedFile::new(&name, &contents) - }), - ); + let input_unsaved_files = + std::mem::take(&mut self.options.input_header_contents) + .into_iter() + .map(|(name, contents)| clang::UnsavedFile::new(name, contents)) + .collect::<Vec<_>>(); - Bindings::generate(self.options) + Bindings::generate(self.options, input_unsaved_files) } /// Preprocess and dump the input header files to disk. @@ -1500,7 +1615,7 @@ impl Builder { let mut is_cpp = args_are_cpp(&self.options.clang_args); // For each input header, add `#include "$header"`. - for header in &self.input_headers { + for header in &self.options.input_headers { is_cpp |= file_is_cpp(header); wrapper_contents.push_str("#include \""); @@ -1510,7 +1625,7 @@ impl Builder { // For each input header content, add a prefix line of `#line 0 "$name"` // followed by the contents. - for &(ref name, ref contents) in &self.input_header_contents { + for (name, contents) in &self.options.input_header_contents { is_cpp |= file_is_cpp(name); wrapper_contents.push_str("#line 0 \""); @@ -1530,7 +1645,7 @@ impl Builder { wrapper_file.write_all(wrapper_contents.as_bytes())?; } - let mut cmd = Command::new(&clang.path); + let mut cmd = Command::new(clang.path); cmd.arg("-save-temps") .arg("-E") .arg("-C") @@ -1542,6 +1657,10 @@ impl Builder { cmd.arg(a); } + for a in get_extra_clang_args() { + cmd.arg(a); + } + let mut child = cmd.spawn()?; let mut preprocessed = child.stdout.take().unwrap(); @@ -1562,46 +1681,58 @@ impl Builder { } } - /// Don't derive `PartialEq` for a given type. Regular - /// expressions are supported. - pub fn no_partialeq<T: Into<String>>(mut self, arg: T) -> Builder { - self.options.no_partialeq_types.insert(arg.into()); - self + fn_with_regex_arg! { + /// Don't derive `PartialEq` for a given type. Regular + /// expressions are supported. + pub fn no_partialeq<T: Into<String>>(mut self, arg: T) -> Builder { + self.options.no_partialeq_types.insert(arg.into()); + self + } } - /// Don't derive `Copy` for a given type. Regular - /// expressions are supported. - pub fn no_copy<T: Into<String>>(mut self, arg: T) -> Self { - self.options.no_copy_types.insert(arg.into()); - self + fn_with_regex_arg! { + /// Don't derive `Copy` for a given type. Regular + /// expressions are supported. + pub fn no_copy<T: Into<String>>(mut self, arg: T) -> Self { + self.options.no_copy_types.insert(arg.into()); + self + } } - /// Don't derive `Debug` for a given type. Regular - /// expressions are supported. - pub fn no_debug<T: Into<String>>(mut self, arg: T) -> Self { - self.options.no_debug_types.insert(arg.into()); - self + fn_with_regex_arg! { + /// Don't derive `Debug` for a given type. Regular + /// expressions are supported. + pub fn no_debug<T: Into<String>>(mut self, arg: T) -> Self { + self.options.no_debug_types.insert(arg.into()); + self + } } - /// Don't derive/impl `Default` for a given type. Regular - /// expressions are supported. - pub fn no_default<T: Into<String>>(mut self, arg: T) -> Self { - self.options.no_default_types.insert(arg.into()); - self + fn_with_regex_arg! { + /// Don't derive/impl `Default` for a given type. Regular + /// expressions are supported. + pub fn no_default<T: Into<String>>(mut self, arg: T) -> Self { + self.options.no_default_types.insert(arg.into()); + self + } } - /// Don't derive `Hash` for a given type. Regular - /// expressions are supported. - pub fn no_hash<T: Into<String>>(mut self, arg: T) -> Builder { - self.options.no_hash_types.insert(arg.into()); - self + fn_with_regex_arg! { + /// Don't derive `Hash` for a given type. Regular + /// expressions are supported. + pub fn no_hash<T: Into<String>>(mut self, arg: T) -> Builder { + self.options.no_hash_types.insert(arg.into()); + self + } } - /// Add `#[must_use]` for the given type. Regular - /// expressions are supported. - pub fn must_use_type<T: Into<String>>(mut self, arg: T) -> Builder { - self.options.must_use_types.insert(arg.into()); - self + fn_with_regex_arg! { + /// Add `#[must_use]` for the given type. Regular + /// expressions are supported. + pub fn must_use_type<T: Into<String>>(mut self, arg: T) -> Builder { + self.options.must_use_types.insert(arg.into()); + self + } } /// Set whether `arr[size]` should be treated as `*mut T` or `*mut [T; size]` (same for mut) @@ -1660,10 +1791,52 @@ impl Builder { self.options.c_naming = doit; self } + + /// Override the ABI of a given function. Regular expressions are supported. + pub fn override_abi<T: Into<String>>(mut self, abi: Abi, arg: T) -> Self { + self.options + .abi_overrides + .entry(abi) + .or_default() + .insert(arg.into()); + self + } + + /// If true, wraps unsafe operations in unsafe blocks. + pub fn wrap_unsafe_ops(mut self, doit: bool) -> Self { + self.options.wrap_unsafe_ops = doit; + self + } + + #[cfg(feature = "experimental")] + /// Whether to generate extern wrappers for `static` and `static inline` functions. Defaults to + /// false. + pub fn wrap_static_fns(mut self, doit: bool) -> Self { + self.options.wrap_static_fns = doit; + self + } + + #[cfg(feature = "experimental")] + /// Set the path for the source code file that would be created if any wrapper functions must + /// be generated due to the presence of static functions. + /// + /// Bindgen will automatically add the right extension to the header and source code files. + pub fn wrap_static_fns_path<T: AsRef<Path>>(mut self, path: T) -> Self { + self.options.wrap_static_fns_path = Some(path.as_ref().to_owned()); + self + } + + #[cfg(feature = "experimental")] + /// Set the suffix added to the extern wrapper functions generated for `static` and `static + /// inline` functions. + pub fn wrap_static_fns_suffix<T: AsRef<str>>(mut self, suffix: T) -> Self { + self.options.wrap_static_fns_suffix = Some(suffix.as_ref().to_owned()); + self + } } /// Configuration options for generated bindings. -#[derive(Debug)] +#[derive(Clone, Debug)] struct BindgenOptions { /// The set of types that have been blocklisted and should not appear /// anywhere in the generated code. @@ -1705,6 +1878,9 @@ struct BindgenOptions { /// Allowlisted variables. See docs for `allowlisted_types` for more. allowlisted_vars: RegexSet, + /// The set of files whose contents should be allowlisted. + allowlisted_files: RegexSet, + /// The default style of code to generate for enums default_enum_style: codegen::EnumVariation, @@ -1715,6 +1891,9 @@ struct BindgenOptions { /// The enum patterns to mark an enum as a newtype. newtype_enums: RegexSet, + /// The enum patterns to mark an enum as a global newtype. + newtype_global_enums: RegexSet, + /// The enum patterns to mark an enum as a Rust enum. rustified_enums: RegexSet, @@ -1743,6 +1922,18 @@ struct BindgenOptions { /// Deref and Deref to their aliased type. new_type_alias_deref: RegexSet, + /// The default style of code to generate for union containing non-Copy + /// members. + default_non_copy_union_style: codegen::NonCopyUnionStyle, + + /// The union patterns to mark an non-Copy union as using the bindgen + /// generated wrapper. + bindgen_wrapper_union: RegexSet, + + /// The union patterns to mark an non-Copy union as using the + /// `::core::mem::ManuallyDrop` wrapper. + manually_drop_union: RegexSet, + /// Whether we should generate builtins or not. builtins: bool, @@ -1827,13 +2018,6 @@ struct BindgenOptions { /// Whether to time the bindgen phases. time_phases: bool, - /// True if we should generate constant names that are **directly** under - /// namespaces. - namespaced_constants: bool, - - /// True if we should use MSVC name mangling rules. - msvc_mangling: bool, - /// Whether we should convert float types to f32/f64 types. convert_floats: bool, @@ -1849,18 +2033,15 @@ struct BindgenOptions { /// The set of arguments to pass straight through to Clang. clang_args: Vec<String>, - /// The input header file. - input_header: Option<String>, - - /// Any additional input header files. - extra_input_headers: Vec<String>, + /// The input header files. + input_headers: Vec<String>, - /// Unsaved files for input. - input_unsaved_files: Vec<clang::UnsavedFile>, + /// Tuples of unsaved file contents of the form (name, contents). + input_header_contents: Vec<(String, String)>, /// A user-provided visitor to allow customizing different kinds of /// situations. - parse_callbacks: Option<Box<dyn callbacks::ParseCallbacks>>, + parse_callbacks: Vec<Rc<dyn callbacks::ParseCallbacks>>, /// Which kind of items should we generate? By default, we'll generate all /// of them. @@ -1978,19 +2159,35 @@ struct BindgenOptions { /// Always output explicit padding fields force_explicit_padding: bool, -} -/// TODO(emilio): This is sort of a lie (see the error message that results from -/// removing this), but since we don't share references across panic boundaries -/// it's ok. -impl ::std::panic::UnwindSafe for BindgenOptions {} + /// Emit vtable functions. + vtable_generation: bool, + + /// Sort the code generation. + sort_semantically: bool, + + /// Deduplicate `extern` blocks. + merge_extern_blocks: bool, + + abi_overrides: HashMap<Abi, RegexSet>, + + /// Whether to wrap unsafe operations in unsafe blocks or not. + wrap_unsafe_ops: bool, + + wrap_static_fns: bool, + + wrap_static_fns_suffix: Option<String>, + + wrap_static_fns_path: Option<PathBuf>, +} impl BindgenOptions { fn build(&mut self) { - let mut regex_sets = [ + let regex_sets = [ &mut self.allowlisted_vars, &mut self.allowlisted_types, &mut self.allowlisted_functions, + &mut self.allowlisted_files, &mut self.blocklisted_types, &mut self.blocklisted_functions, &mut self.blocklisted_items, @@ -2000,11 +2197,14 @@ impl BindgenOptions { &mut self.constified_enums, &mut self.constified_enum_modules, &mut self.newtype_enums, + &mut self.newtype_global_enums, &mut self.rustified_enums, &mut self.rustified_non_exhaustive_enums, &mut self.type_alias, &mut self.new_type_alias, &mut self.new_type_alias_deref, + &mut self.bindgen_wrapper_union, + &mut self.manually_drop_union, &mut self.no_partialeq_types, &mut self.no_copy_types, &mut self.no_debug_types, @@ -2013,7 +2213,7 @@ impl BindgenOptions { &mut self.must_use_types, ]; let record_matches = self.record_matches; - for regex_set in &mut regex_sets { + for regex_set in self.abi_overrides.values_mut().chain(regex_sets) { regex_set.build(record_matches); } } @@ -2030,101 +2230,150 @@ impl BindgenOptions { pub fn rust_features(&self) -> RustFeatures { self.rust_features } + + fn last_callback<T>( + &self, + f: impl Fn(&dyn callbacks::ParseCallbacks) -> Option<T>, + ) -> Option<T> { + self.parse_callbacks + .iter() + .filter_map(|cb| f(cb.as_ref())) + .last() + } + + fn all_callbacks<T>( + &self, + f: impl Fn(&dyn callbacks::ParseCallbacks) -> Vec<T>, + ) -> Vec<T> { + self.parse_callbacks + .iter() + .flat_map(|cb| f(cb.as_ref())) + .collect() + } + + fn process_comment(&self, comment: &str) -> String { + let comment = comment::preprocess(comment); + self.parse_callbacks + .last() + .and_then(|cb| cb.process_comment(&comment)) + .unwrap_or(comment) + } } impl Default for BindgenOptions { fn default() -> BindgenOptions { + macro_rules! options { + ($($field:ident $(: $value:expr)?,)* --default-fields-- $($default_field:ident,)*) => { + BindgenOptions { + $($field $(: $value)*,)* + $($default_field: Default::default(),)* + } + }; + } + let rust_target = RustTarget::default(); - BindgenOptions { + options! { rust_target, rust_features: rust_target.into(), - blocklisted_types: Default::default(), - blocklisted_functions: Default::default(), - blocklisted_items: Default::default(), - blocklisted_files: Default::default(), - opaque_types: Default::default(), - rustfmt_path: Default::default(), - depfile: Default::default(), - allowlisted_types: Default::default(), - allowlisted_functions: Default::default(), - allowlisted_vars: Default::default(), - default_enum_style: Default::default(), - bitfield_enums: Default::default(), - newtype_enums: Default::default(), - rustified_enums: Default::default(), - rustified_non_exhaustive_enums: Default::default(), - constified_enums: Default::default(), - constified_enum_modules: Default::default(), - default_macro_constant_type: Default::default(), - default_alias_style: Default::default(), - type_alias: Default::default(), - new_type_alias: Default::default(), - new_type_alias_deref: Default::default(), - builtins: false, - emit_ast: false, - emit_ir: false, - emit_ir_graphviz: None, layout_tests: true, - impl_debug: false, - impl_partialeq: false, derive_copy: true, derive_debug: true, - derive_default: false, - derive_hash: false, - derive_partialord: false, - derive_ord: false, - derive_partialeq: false, - derive_eq: false, - enable_cxx_namespaces: false, - enable_function_attribute_detection: false, - disable_name_namespacing: false, - disable_nested_struct_naming: false, - disable_header_comment: false, - use_core: false, - ctypes_prefix: None, anon_fields_prefix: DEFAULT_ANON_FIELDS_PREFIX.into(), - namespaced_constants: true, - msvc_mangling: false, convert_floats: true, - raw_lines: vec![], - module_lines: HashMap::default(), - clang_args: vec![], - input_header: None, - extra_input_headers: vec![], - input_unsaved_files: vec![], - parse_callbacks: None, codegen_config: CodegenConfig::all(), - conservative_inline_namespaces: false, generate_comments: true, - generate_inline_functions: false, allowlist_recursively: true, - generate_block: false, - objc_extern_crate: false, - block_extern_crate: false, enable_mangling: true, detect_include_paths: true, - fit_macro_constants: false, prepend_enum_name: true, - time_phases: false, record_matches: true, rustfmt_bindings: true, - size_t_is_usize: false, - rustfmt_configuration_file: None, - no_partialeq_types: Default::default(), - no_copy_types: Default::default(), - no_debug_types: Default::default(), - no_default_types: Default::default(), - no_hash_types: Default::default(), - must_use_types: Default::default(), - array_pointers_in_arguments: false, - wasm_import_module_name: None, - dynamic_library_name: None, - dynamic_link_require_all: false, - respect_cxx_access_specs: false, - translate_enum_integer_types: false, - c_naming: false, - force_explicit_padding: false, + size_t_is_usize: true, + + --default-fields-- + blocklisted_types, + blocklisted_functions, + blocklisted_items, + blocklisted_files, + opaque_types, + rustfmt_path, + depfile, + allowlisted_types, + allowlisted_functions, + allowlisted_vars, + allowlisted_files, + default_enum_style, + bitfield_enums, + newtype_enums, + newtype_global_enums, + rustified_enums, + rustified_non_exhaustive_enums, + constified_enums, + constified_enum_modules, + default_macro_constant_type, + default_alias_style, + type_alias, + new_type_alias, + new_type_alias_deref, + default_non_copy_union_style, + bindgen_wrapper_union, + manually_drop_union, + builtins, + emit_ast, + emit_ir, + emit_ir_graphviz, + impl_debug, + impl_partialeq, + derive_default, + derive_hash, + derive_partialord, + derive_ord, + derive_partialeq, + derive_eq, + enable_cxx_namespaces, + enable_function_attribute_detection, + disable_name_namespacing, + disable_nested_struct_naming, + disable_header_comment, + use_core, + ctypes_prefix, + raw_lines, + module_lines, + clang_args, + input_headers, + input_header_contents, + parse_callbacks, + conservative_inline_namespaces, + generate_inline_functions, + generate_block, + objc_extern_crate, + block_extern_crate, + fit_macro_constants, + time_phases, + rustfmt_configuration_file, + no_partialeq_types, + no_copy_types, + no_debug_types, + no_default_types, + no_hash_types, + must_use_types, + array_pointers_in_arguments, + wasm_import_module_name, + dynamic_library_name, + dynamic_link_require_all, + respect_cxx_access_specs, + translate_enum_integer_types, + c_naming, + force_explicit_padding, + vtable_generation, + sort_semantically, + merge_extern_blocks, + abi_overrides, + wrap_unsafe_ops, + wrap_static_fns, + wrap_static_fns_suffix, + wrap_static_fns_path, } } } @@ -2155,10 +2404,51 @@ fn ensure_libclang_is_loaded() { #[cfg(not(feature = "runtime"))] fn ensure_libclang_is_loaded() {} +/// Error type for rust-bindgen. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum BindgenError { + /// The header was a folder. + FolderAsHeader(PathBuf), + /// Permissions to read the header is insufficient. + InsufficientPermissions(PathBuf), + /// The header does not exist. + NotExist(PathBuf), + /// Clang diagnosed an error. + ClangDiagnostic(String), + /// Code generation reported an error. + Codegen(CodegenError), +} + +impl std::fmt::Display for BindgenError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BindgenError::FolderAsHeader(h) => { + write!(f, "'{}' is a folder", h.display()) + } + BindgenError::InsufficientPermissions(h) => { + write!(f, "insufficient permissions to read '{}'", h.display()) + } + BindgenError::NotExist(h) => { + write!(f, "header '{}' does not exist.", h.display()) + } + BindgenError::ClangDiagnostic(message) => { + write!(f, "clang diagnosed error: {}", message) + } + BindgenError::Codegen(err) => { + write!(f, "codegen error: {}", err) + } + } + } +} + +impl std::error::Error for BindgenError {} + /// Generated Rust bindings. #[derive(Debug)] pub struct Bindings { options: BindgenOptions, + warnings: Vec<String>, module: proc_macro2::TokenStream, } @@ -2173,6 +2463,19 @@ fn rust_to_clang_target(rust_target: &str) -> String { clang_target .push_str(rust_target.strip_prefix("aarch64-apple-").unwrap()); return clang_target; + } else if rust_target.starts_with("riscv64gc-") { + let mut clang_target = "riscv64-".to_owned(); + clang_target.push_str(rust_target.strip_prefix("riscv64gc-").unwrap()); + return clang_target; + } else if rust_target.ends_with("-espidf") { + let mut clang_target = + rust_target.strip_suffix("-espidf").unwrap().to_owned(); + clang_target.push_str("-elf"); + if clang_target.starts_with("riscv32imc-") { + clang_target = "riscv32-".to_owned() + + clang_target.strip_prefix("riscv32imc-").unwrap(); + } + return clang_target; } rust_target.to_owned() } @@ -2207,7 +2510,8 @@ impl Bindings { /// Generate bindings for the given options. pub(crate) fn generate( mut options: BindgenOptions, - ) -> Result<Bindings, ()> { + input_unsaved_files: Vec<clang::UnsavedFile>, + ) -> Result<Bindings, BindgenError> { ensure_libclang_is_loaded(); #[cfg(feature = "runtime")] @@ -2293,7 +2597,7 @@ impl Bindings { // Whether we are working with C or C++ inputs. let is_cpp = args_are_cpp(&options.clang_args) || - options.input_header.as_deref().map_or(false, file_is_cpp); + options.input_headers.iter().any(|h| file_is_cpp(h)); let search_paths = if is_cpp { clang.cpp_search_paths @@ -2324,28 +2628,26 @@ impl Bindings { true } - if let Some(h) = options.input_header.as_ref() { - if let Ok(md) = std::fs::metadata(h) { + if let Some(h) = options.input_headers.last() { + let path = Path::new(h); + if let Ok(md) = std::fs::metadata(path) { if md.is_dir() { - eprintln!("error: '{}' is a folder", h); - return Err(()); + return Err(BindgenError::FolderAsHeader(path.into())); } if !can_read(&md.permissions()) { - eprintln!( - "error: insufficient permissions to read '{}'", - h - ); - return Err(()); + return Err(BindgenError::InsufficientPermissions( + path.into(), + )); } - options.clang_args.push(h.clone()) + let h = h.clone(); + options.clang_args.push(h); } else { - eprintln!("error: header '{}' does not exist.", h); - return Err(()); + return Err(BindgenError::NotExist(path.into())); } } - for (idx, f) in options.input_unsaved_files.iter().enumerate() { - if idx != 0 || options.input_header.is_some() { + for (idx, f) in input_unsaved_files.iter().enumerate() { + if idx != 0 || !options.input_headers.is_empty() { options.clang_args.push("-include".to_owned()); } options.clang_args.push(f.name.to_str().unwrap().to_owned()) @@ -2354,7 +2656,7 @@ impl Bindings { debug!("Fixed-up options: {:?}", options); let time_phases = options.time_phases; - let mut context = BindgenContext::new(options); + let mut context = BindgenContext::new(options, &input_unsaved_files); if is_host_build { debug_assert_eq!( @@ -2371,13 +2673,13 @@ impl Bindings { parse(&mut context)?; } - let (items, options) = codegen::codegen(context); + let (module, options, warnings) = + codegen::codegen(context).map_err(BindgenError::Codegen)?; Ok(Bindings { options, - module: quote! { - #( #items )* - }, + warnings, + module, }) } @@ -2395,7 +2697,7 @@ impl Bindings { /// Write these bindings as source text to the given `Write`able. pub fn write<'a>(&self, mut writer: Box<dyn Write + 'a>) -> io::Result<()> { if !self.options.disable_header_comment { - let version = Some("0.59.2"); + let version = Some("0.64.0"); let header = format!( "/* automatically generated by rust-bindgen {} */\n\n", version.unwrap_or("(unknown version)") @@ -2474,7 +2776,7 @@ impl Bindings { .as_ref() .and_then(|f| f.to_str()) { - cmd.args(&["--config-path", path]); + cmd.args(["--config-path", path]); } let mut child = cmd.spawn()?; @@ -2519,6 +2821,23 @@ impl Bindings { _ => Ok(Cow::Owned(source)), } } + + /// Emit all the warning messages raised while generating the bindings in a build script. + /// + /// If you are using `bindgen` outside of a build script you should use [`Bindings::warnings`] + /// and handle the messages accordingly instead. + #[inline] + pub fn emit_warnings(&self) { + for message in &self.warnings { + println!("cargo:warning={}", message); + } + } + + /// Return all the warning messages raised while generating the bindings. + #[inline] + pub fn warnings(&self) -> &[String] { + &self.warnings + } } impl std::fmt::Display for Bindings { @@ -2561,19 +2880,24 @@ fn parse_one( } /// Parse the Clang AST into our `Item` internal representation. -fn parse(context: &mut BindgenContext) -> Result<(), ()> { +fn parse(context: &mut BindgenContext) -> Result<(), BindgenError> { use clang_sys::*; - let mut any_error = false; + let mut error = None; for d in context.translation_unit().diags().iter() { let msg = d.format(); let is_err = d.severity() >= CXDiagnostic_Error; - eprintln!("{}, err: {}", msg, is_err); - any_error |= is_err; + if is_err { + let error = error.get_or_insert_with(String::new); + error.push_str(&msg); + error.push('\n'); + } else { + eprintln!("clang diag: {}", msg); + } } - if any_error { - return Err(()); + if let Some(message) = error { + return Err(BindgenError::ClangDiagnostic(message)); } let cursor = context.translation_unit().cursor(); @@ -2641,11 +2965,10 @@ pub fn clang_version() -> ClangVersion { /// Looks for the env var `var_${TARGET}`, and falls back to just `var` when it is not found. fn get_target_dependent_env_var(var: &str) -> Option<String> { if let Ok(target) = env::var("TARGET") { - if let Ok(v) = env::var(&format!("{}_{}", var, target)) { + if let Ok(v) = env::var(format!("{}_{}", var, target)) { return Some(v); } - if let Ok(v) = - env::var(&format!("{}_{}", var, target.replace("-", "_"))) + if let Ok(v) = env::var(format!("{}_{}", var, target.replace('-', "_"))) { return Some(v); } @@ -2691,9 +3014,7 @@ fn commandline_flag_unit_test_function() { .map(|&x| x.into()) .collect::<Vec<String>>(); - assert!(test_cases - .iter() - .all(|ref x| command_line_flags.contains(x),)); + assert!(test_cases.iter().all(|x| command_line_flags.contains(x))); //Test 2 let bindings = crate::builder() @@ -2718,12 +3039,30 @@ fn commandline_flag_unit_test_function() { .collect::<Vec<String>>(); println!("{:?}", command_line_flags); - assert!(test_cases - .iter() - .all(|ref x| command_line_flags.contains(x),)); + assert!(test_cases.iter().all(|x| command_line_flags.contains(x))); } #[test] fn test_rust_to_clang_target() { assert_eq!(rust_to_clang_target("aarch64-apple-ios"), "arm64-apple-ios"); } + +#[test] +fn test_rust_to_clang_target_riscv() { + assert_eq!( + rust_to_clang_target("riscv64gc-unknown-linux-gnu"), + "riscv64-unknown-linux-gnu" + ) +} + +#[test] +fn test_rust_to_clang_target_espidf() { + assert_eq!( + rust_to_clang_target("riscv32imc-esp-espidf"), + "riscv32-esp-elf" + ); + assert_eq!( + rust_to_clang_target("xtensa-esp32-espidf"), + "xtensa-esp32-elf" + ); +} diff --git a/src/log_stubs.rs b/log_stubs.rs index 8315983..8315983 100644 --- a/src/log_stubs.rs +++ b/log_stubs.rs diff --git a/out/tests.rs b/out/tests.rs deleted file mode 100644 index e69de29..0000000 --- a/out/tests.rs +++ /dev/null diff --git a/parse.rs b/parse.rs new file mode 100644 index 0000000..1fd83cd --- /dev/null +++ b/parse.rs @@ -0,0 +1,40 @@ +//! Common traits and types related to parsing our IR from Clang cursors. + +use crate::clang; +use crate::ir::context::{BindgenContext, ItemId}; + +/// Not so much an error in the traditional sense, but a control flow message +/// when walking over Clang's AST with a cursor. +#[derive(Debug)] +pub enum ParseError { + /// Recurse down the current AST node's children. + Recurse, + /// Continue on to the next sibling AST node, or back up to the parent's + /// siblings if we've exhausted all of this node's siblings (and so on). + Continue, +} + +/// The result of parsing a Clang AST node. +#[derive(Debug)] +pub enum ParseResult<T> { + /// We've already resolved this item before, here is the extant `ItemId` for + /// it. + AlreadyResolved(ItemId), + + /// This is a newly parsed item. If the cursor is `Some`, it points to the + /// AST node where the new `T` was declared. + New(T, Option<clang::Cursor>), +} + +/// An intermediate representation "sub-item" (i.e. one of the types contained +/// inside an `ItemKind` variant) that can be parsed from a Clang cursor. +pub trait ClangSubItemParser: Sized { + /// Attempt to parse this type from the given cursor. + /// + /// The fact that is a reference guarantees it's held by the context, and + /// allow returning already existing types. + fn parse( + cursor: clang::Cursor, + context: &mut BindgenContext, + ) -> Result<ParseResult<Self>, ParseError>; +} diff --git a/patches/Android.bp.diff b/patches/Android.bp.diff new file mode 100644 index 0000000..0e9e66e --- /dev/null +++ b/patches/Android.bp.diff @@ -0,0 +1,13 @@ +diff --git a/Android.bp b/Android.bp +index fd57489..86a6c71 100644 +--- a/Android.bp ++++ b/Android.bp +@@ -56,7 +56,7 @@ rust_library_host { + "which-rustfmt", + ], + rustlibs: [ +- "libbitflags", ++ "libbitflags-1.3.2", + "libcexpr", + "libclang_sys", + "liblazy_static", diff --git a/patches/bindgen_cmd.diff b/patches/bindgen_cmd.diff new file mode 100644 index 0000000..9b28ff7 --- /dev/null +++ b/patches/bindgen_cmd.diff @@ -0,0 +1,90 @@ +diff --git a/android/bindgen_cmd/Android.bp b/android/bindgen_cmd/Android.bp +new file mode 100644 +index 0000000..689e7ae +--- /dev/null ++++ b/android/bindgen_cmd/Android.bp +@@ -0,0 +1,28 @@ ++package { ++ // See: http://go/android-license-faq ++ // A large-scale-change added 'default_applicable_licenses' to import ++ // all of the 'license_kinds' from "external_rust_crates_bindgen_license" ++ // to get the below license kinds: ++ // SPDX-license-identifier-Apache-2.0 ++ default_applicable_licenses: ["external_rust_crates_bindgen_license"], ++} ++ ++rust_library_host { ++ name: "libbindgen_cmd", ++ crate_name: "bindgen_cmd", ++ srcs: ["src/lib.rs"], ++ edition: "2018", ++ features: [ ++ "clap", ++ "runtime", ++ "which", ++ "which-rustfmt", ++ ], ++ rustlibs: [ ++ "libbindgen", ++ "libbindgen_cli", ++ "libclap", ++ "libenv_logger", ++ ], ++ compile_multilib: "first", ++} +diff --git a/android/bindgen_cmd/src/lib.rs b/android/bindgen_cmd/src/lib.rs +new file mode 100644 +index 0000000..d33da7f +--- /dev/null ++++ b/android/bindgen_cmd/src/lib.rs +@@ -0,0 +1,50 @@ ++// Copyright 2020, The Android Open Source Project ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++ ++//! This is a helper crate for using bindgen as a library from within ++//! Android's build system. Some functionality (such as the type selection ++//! heuristic) is not available on the command line, and so the library ++//! interface may be necessary. Bindgen also needs to receive configuration ++//! from Soong however to find appropriate headers, set global cflags, etc. ++//! ++//! This crate provides the ability to run a hooked version of the command ++//! line bindgen tool, with the ability to call a user-provided transformation ++//! on the the builder before it is used. ++ ++use bindgen; ++use bindgen_cli; ++use std::env; ++ ++/// Takes in a function describing adjustments to make to a builder ++/// initialized by the command line. `build(|x| x)` is equivalent to ++/// running bindgen. When converting a build.rs, you will want to convert the ++/// additional configuration they do into a function, then pass it to `build` ++/// inside your main function. ++pub fn build<C: FnOnce(bindgen::Builder) -> bindgen::Builder>(configure: C) { ++ env_logger::init(); ++ ++ match bindgen_cli::builder_from_flags(env::args()) { ++ Ok((builder, output, _)) => { ++ configure(builder) ++ .generate() ++ .expect("Unable to generate bindings") ++ .write(output) ++ .expect("Unable to write output"); ++ } ++ Err(error) => { ++ eprintln!("{}", error); ++ std::process::exit(1); ++ } ++ }; ++} diff --git a/post_update.sh b/post_update.sh deleted file mode 100755 index 52614aa..0000000 --- a/post_update.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -# $1 Path to the new version. -# $2 Path to the old version. - -set -x -set -e - -cp -a -n -r $2/android $1/ diff --git a/src/regex_set.rs b/regex_set.rs index 127c001..6246dd2 100644 --- a/src/regex_set.rs +++ b/regex_set.rs @@ -4,7 +4,7 @@ use regex::RegexSet as RxSet; use std::cell::Cell; /// A dynamic set of regular expressions. -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct RegexSet { items: Vec<String>, /// Whether any of the items in the set was ever matched. The length of this @@ -16,6 +16,13 @@ pub struct RegexSet { } impl RegexSet { + /// Create a new RegexSet + pub fn new() -> RegexSet { + RegexSet { + ..Default::default() + } + } + /// Is this set empty? pub fn is_empty(&self) -> bool { self.items.is_empty() @@ -26,7 +33,11 @@ impl RegexSet { where S: AsRef<str>, { - self.items.push(string.as_ref().to_owned()); + let string = string.as_ref().to_owned(); + if string == "*" { + warn!("using wildcard patterns (`*`) is no longer considered valid. Use `.*` instead"); + } + self.items.push(string); self.matched.push(Cell::new(false)); self.set = None; } @@ -53,7 +64,7 @@ impl RegexSet { /// Must be called before calling `matches()`, or it will always return /// false. pub fn build(&mut self, record_matches: bool) { - let items = self.items.iter().map(|item| format!("^{}$", item)); + let items = self.items.iter().map(|item| format!("^({})$", item)); self.record_matches = record_matches; self.set = match RxSet::new(items) { Ok(x) => Some(x), diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index f3398db..0000000 --- a/src/main.rs +++ /dev/null @@ -1,113 +0,0 @@ -extern crate bindgen; -#[cfg(feature = "logging")] -extern crate env_logger; -#[macro_use] -#[cfg(feature = "logging")] -extern crate log; -extern crate clap; - -use bindgen::clang_version; -use std::env; -use std::panic; - -#[macro_use] -#[cfg(not(feature = "logging"))] -mod log_stubs; - -mod options; -use crate::options::builder_from_flags; - -fn clang_version_check() { - let version = clang_version(); - let expected_version = if cfg!(feature = "testing_only_libclang_9") { - Some((9, 0)) - } else if cfg!(feature = "testing_only_libclang_5") { - Some((5, 0)) - } else if cfg!(feature = "testing_only_libclang_4") { - Some((4, 0)) - } else if cfg!(feature = "testing_only_libclang_3_9") { - Some((3, 9)) - } else { - None - }; - - info!( - "Clang Version: {}, parsed: {:?}", - version.full, version.parsed - ); - - if expected_version.is_some() { - // assert_eq!(version.parsed, version.parsed); - } -} - -pub fn main() { - #[cfg(feature = "logging")] - env_logger::init(); - - match builder_from_flags(env::args()) { - Ok((builder, output, verbose)) => { - clang_version_check(); - let builder_result = panic::catch_unwind(|| { - builder.generate().expect("Unable to generate bindings") - }); - - if builder_result.is_err() { - if verbose { - print_verbose_err(); - } - std::process::exit(1); - } - - let bindings = builder_result.unwrap(); - bindings.write(output).expect("Unable to write output"); - } - Err(error) => { - println!("{}", error); - std::process::exit(1); - } - }; -} - -fn print_verbose_err() { - println!("Bindgen unexpectedly panicked"); - println!( - "This may be caused by one of the known-unsupported \ - things (https://rust-lang.github.io/rust-bindgen/cpp.html), \ - please modify the bindgen flags to work around it as \ - described in https://rust-lang.github.io/rust-bindgen/cpp.html" - ); - println!( - "Otherwise, please file an issue at \ - https://github.com/rust-lang/rust-bindgen/issues/new" - ); -} - -#[cfg(test)] -mod test { - fn build_flags_output_helper(builder: &bindgen::Builder) { - let mut command_line_flags = builder.command_line_flags(); - command_line_flags.insert(0, "bindgen".to_string()); - - let flags_quoted: Vec<String> = command_line_flags - .iter() - .map(|x| format!("{}", shlex::quote(x))) - .collect(); - let flags_str = flags_quoted.join(" "); - println!("{}", flags_str); - - let (builder, _output, _verbose) = - crate::options::builder_from_flags(command_line_flags.into_iter()) - .unwrap(); - builder.generate().expect("failed to generate bindings"); - } - - #[test] - fn commandline_multiple_headers() { - let bindings = bindgen::Builder::default() - .header("tests/headers/char.h") - .header("tests/headers/func_ptr.h") - .header("tests/headers/16-byte-alignment.h"); - build_flags_output_helper(&bindings); - } -} diff --git a/src/options.rs b/src/options.rs deleted file mode 100644 index 94f3047..0000000 --- a/src/options.rs +++ /dev/null @@ -1,1000 +0,0 @@ -use bindgen::{ - builder, AliasVariation, Builder, CodegenConfig, EnumVariation, - MacroTypeVariation, RustTarget, DEFAULT_ANON_FIELDS_PREFIX, - RUST_TARGET_STRINGS, -}; -use clap::{App, Arg}; -use std::fs::File; -use std::io::{self, stderr, Error, ErrorKind, Write}; -use std::path::PathBuf; -use std::str::FromStr; - -/// Construct a new [`Builder`](./struct.Builder.html) from command line flags. -pub fn builder_from_flags<I>( - args: I, -) -> Result<(Builder, Box<dyn io::Write>, bool), io::Error> -where - I: Iterator<Item = String>, -{ - let rust_target_help = format!( - "Version of the Rust compiler to target. Valid options are: {:?}. Defaults to {:?}.", - RUST_TARGET_STRINGS, - String::from(RustTarget::default()) - ); - - let matches = App::new("bindgen") - .version(Some("0.59.2").unwrap_or("unknown")) - .about("Generates Rust bindings from C/C++ headers.") - .usage("bindgen [FLAGS] [OPTIONS] <header> -- <clang-args>...") - .args(&[ - Arg::with_name("header") - .help("C or C++ header file") - .required(true), - Arg::with_name("depfile") - .long("depfile") - .takes_value(true) - .help("Path to write depfile to"), - Arg::with_name("default-enum-style") - .long("default-enum-style") - .help("The default style of code used to generate enums.") - .value_name("variant") - .default_value("consts") - .possible_values(&[ - "consts", - "moduleconsts", - "bitfield", - "newtype", - "rust", - "rust_non_exhaustive", - ]) - .multiple(false), - Arg::with_name("bitfield-enum") - .long("bitfield-enum") - .help( - "Mark any enum whose name matches <regex> as a set of \ - bitfield flags.", - ) - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("newtype-enum") - .long("newtype-enum") - .help("Mark any enum whose name matches <regex> as a newtype.") - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("rustified-enum") - .long("rustified-enum") - .help("Mark any enum whose name matches <regex> as a Rust enum.") - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("constified-enum") - .long("constified-enum") - .help( - "Mark any enum whose name matches <regex> as a series of \ - constants.", - ) - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("constified-enum-module") - .long("constified-enum-module") - .help( - "Mark any enum whose name matches <regex> as a module of \ - constants.", - ) - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("default-macro-constant-type") - .long("default-macro-constant-type") - .help("The default signed/unsigned type for C macro constants.") - .value_name("variant") - .default_value("unsigned") - .possible_values(&["signed", "unsigned"]) - .multiple(false), - Arg::with_name("default-alias-style") - .long("default-alias-style") - .help("The default style of code used to generate typedefs.") - .value_name("variant") - .default_value("type_alias") - .possible_values(&[ - "type_alias", - "new_type", - "new_type_deref", - ]) - .multiple(false), - Arg::with_name("normal-alias") - .long("normal-alias") - .help( - "Mark any typedef alias whose name matches <regex> to use \ - normal type aliasing.", - ) - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("new-type-alias") - .long("new-type-alias") - .help( - "Mark any typedef alias whose name matches <regex> to have \ - a new type generated for it.", - ) - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("new-type-alias-deref") - .long("new-type-alias-deref") - .help( - "Mark any typedef alias whose name matches <regex> to have \ - a new type with Deref and DerefMut to the inner type.", - ) - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("blocklist-type") - .alias("blacklist-type") - .long("blocklist-type") - .help("Mark <type> as hidden.") - .value_name("type") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("blocklist-function") - .alias("blacklist-function") - .long("blocklist-function") - .help("Mark <function> as hidden.") - .value_name("function") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("blocklist-item") - .alias("blacklist-item") - .long("blocklist-item") - .help("Mark <item> as hidden.") - .value_name("item") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("blocklist-file") - .alias("blacklist-file") - .long("blocklist-file") - .help("Mark all contents of <path> as hidden.") - .value_name("path") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("no-layout-tests") - .long("no-layout-tests") - .help("Avoid generating layout tests for any type."), - Arg::with_name("no-derive-copy") - .long("no-derive-copy") - .help("Avoid deriving Copy on any type."), - Arg::with_name("no-derive-debug") - .long("no-derive-debug") - .help("Avoid deriving Debug on any type."), - Arg::with_name("no-derive-default") - .long("no-derive-default") - .hidden(true) - .help("Avoid deriving Default on any type."), - Arg::with_name("impl-debug").long("impl-debug").help( - "Create Debug implementation, if it can not be derived \ - automatically.", - ), - Arg::with_name("impl-partialeq") - .long("impl-partialeq") - .help( - "Create PartialEq implementation, if it can not be derived \ - automatically.", - ), - Arg::with_name("with-derive-default") - .long("with-derive-default") - .help("Derive Default on any type."), - Arg::with_name("with-derive-hash") - .long("with-derive-hash") - .help("Derive hash on any type."), - Arg::with_name("with-derive-partialeq") - .long("with-derive-partialeq") - .help("Derive partialeq on any type."), - Arg::with_name("with-derive-partialord") - .long("with-derive-partialord") - .help("Derive partialord on any type."), - Arg::with_name("with-derive-eq") - .long("with-derive-eq") - .help( - "Derive eq on any type. Enable this option also \ - enables --with-derive-partialeq", - ), - Arg::with_name("with-derive-ord") - .long("with-derive-ord") - .help( - "Derive ord on any type. Enable this option also \ - enables --with-derive-partialord", - ), - Arg::with_name("no-doc-comments") - .long("no-doc-comments") - .help( - "Avoid including doc comments in the output, see: \ - https://github.com/rust-lang/rust-bindgen/issues/426", - ), - Arg::with_name("no-recursive-allowlist") - .long("no-recursive-allowlist") - .alias("no-recursive-whitelist") - .help( - "Disable allowlisting types recursively. This will cause \ - bindgen to emit Rust code that won't compile! See the \ - `bindgen::Builder::allowlist_recursively` method's \ - documentation for details.", - ), - Arg::with_name("objc-extern-crate") - .long("objc-extern-crate") - .help("Use extern crate instead of use for objc."), - Arg::with_name("generate-block") - .long("generate-block") - .help("Generate block signatures instead of void pointers."), - Arg::with_name("block-extern-crate") - .long("block-extern-crate") - .help("Use extern crate instead of use for block."), - Arg::with_name("distrust-clang-mangling") - .long("distrust-clang-mangling") - .help("Do not trust the libclang-provided mangling"), - Arg::with_name("builtins").long("builtins").help( - "Output bindings for builtin definitions, e.g. \ - __builtin_va_list.", - ), - Arg::with_name("ctypes-prefix") - .long("ctypes-prefix") - .help( - "Use the given prefix before raw types instead of \ - ::std::os::raw.", - ) - .value_name("prefix") - .takes_value(true), - Arg::with_name("anon-fields-prefix") - .long("anon-fields-prefix") - .help("Use the given prefix for the anon fields.") - .value_name("prefix") - .default_value(DEFAULT_ANON_FIELDS_PREFIX) - .takes_value(true), - Arg::with_name("time-phases") - .long("time-phases") - .help("Time the different bindgen phases and print to stderr"), - // All positional arguments after the end of options marker, `--` - Arg::with_name("clang-args").last(true).multiple(true), - Arg::with_name("emit-clang-ast") - .long("emit-clang-ast") - .help("Output the Clang AST for debugging purposes."), - Arg::with_name("emit-ir") - .long("emit-ir") - .help("Output our internal IR for debugging purposes."), - Arg::with_name("emit-ir-graphviz") - .long("emit-ir-graphviz") - .help("Dump graphviz dot file.") - .value_name("path") - .takes_value(true), - Arg::with_name("enable-cxx-namespaces") - .long("enable-cxx-namespaces") - .help("Enable support for C++ namespaces."), - Arg::with_name("disable-name-namespacing") - .long("disable-name-namespacing") - .help( - "Disable namespacing via mangling, causing bindgen to \ - generate names like \"Baz\" instead of \"foo_bar_Baz\" \ - for an input name \"foo::bar::Baz\".", - ), - Arg::with_name("disable-nested-struct-naming") - .long("disable-nested-struct-naming") - .help( - "Disable nested struct naming, causing bindgen to generate \ - names like \"bar\" instead of \"foo_bar\" for a nested \ - definition \"struct foo { struct bar { } b; };\"." - ), - Arg::with_name("disable-untagged-union") - .long("disable-untagged-union") - .help( - "Disable support for native Rust unions.", - ), - Arg::with_name("disable-header-comment") - .long("disable-header-comment") - .help("Suppress insertion of bindgen's version identifier into generated bindings.") - .multiple(true), - Arg::with_name("ignore-functions") - .long("ignore-functions") - .help( - "Do not generate bindings for functions or methods. This \ - is useful when you only care about struct layouts.", - ), - Arg::with_name("generate") - .long("generate") - .help( - "Generate only given items, split by commas. \ - Valid values are \"functions\",\"types\", \"vars\", \ - \"methods\", \"constructors\" and \"destructors\".", - ) - .takes_value(true), - Arg::with_name("ignore-methods") - .long("ignore-methods") - .help("Do not generate bindings for methods."), - Arg::with_name("no-convert-floats") - .long("no-convert-floats") - .help("Do not automatically convert floats to f32/f64."), - Arg::with_name("no-prepend-enum-name") - .long("no-prepend-enum-name") - .help("Do not prepend the enum name to constant or newtype variants."), - Arg::with_name("no-include-path-detection") - .long("no-include-path-detection") - .help("Do not try to detect default include paths"), - Arg::with_name("fit-macro-constant-types") - .long("fit-macro-constant-types") - .help("Try to fit macro constants into types smaller than u32/i32"), - Arg::with_name("unstable-rust") - .long("unstable-rust") - .help("Generate unstable Rust code (deprecated; use --rust-target instead).") - .multiple(true), // FIXME: Pass legacy test suite - Arg::with_name("opaque-type") - .long("opaque-type") - .help("Mark <type> as opaque.") - .value_name("type") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("output") - .short("o") - .long("output") - .help("Write Rust bindings to <output>.") - .takes_value(true), - Arg::with_name("raw-line") - .long("raw-line") - .help("Add a raw line of Rust code at the beginning of output.") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("module-raw-line") - .long("module-raw-line") - .help("Add a raw line of Rust code to a given module.") - .takes_value(true) - .multiple(true) - .number_of_values(2) - .value_names(&["module-name", "raw-line"]), - Arg::with_name("rust-target") - .long("rust-target") - .help(&rust_target_help) - .takes_value(true), - Arg::with_name("use-core") - .long("use-core") - .help("Use types from Rust core instead of std."), - Arg::with_name("conservative-inline-namespaces") - .long("conservative-inline-namespaces") - .help( - "Conservatively generate inline namespaces to avoid name \ - conflicts.", - ), - Arg::with_name("use-msvc-mangling") - .long("use-msvc-mangling") - .help("MSVC C++ ABI mangling. DEPRECATED: Has no effect."), - Arg::with_name("allowlist-function") - .long("allowlist-function") - .alias("whitelist-function") - .help( - "Allowlist all the free-standing functions matching \ - <regex>. Other non-allowlisted functions will not be \ - generated.", - ) - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("generate-inline-functions") - .long("generate-inline-functions") - .help("Generate inline functions."), - Arg::with_name("allowlist-type") - .long("allowlist-type") - .alias("whitelist-type") - .help( - "Only generate types matching <regex>. Other non-allowlisted types will \ - not be generated.", - ) - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("allowlist-var") - .long("allowlist-var") - .alias("whitelist-var") - .help( - "Allowlist all the free-standing variables matching \ - <regex>. Other non-allowlisted variables will not be \ - generated.", - ) - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("verbose") - .long("verbose") - .help("Print verbose error messages."), - Arg::with_name("dump-preprocessed-input") - .long("dump-preprocessed-input") - .help( - "Preprocess and dump the input header files to disk. \ - Useful when debugging bindgen, using C-Reduce, or when \ - filing issues. The resulting file will be named \ - something like `__bindgen.i` or `__bindgen.ii`.", - ), - Arg::with_name("no-record-matches") - .long("no-record-matches") - .help( - "Do not record matching items in the regex sets. \ - This disables reporting of unused items.", - ), - Arg::with_name("size_t-is-usize") - .long("size_t-is-usize") - .help("Translate size_t to usize."), - Arg::with_name("no-rustfmt-bindings") - .long("no-rustfmt-bindings") - .help("Do not format the generated bindings with rustfmt."), - Arg::with_name("rustfmt-bindings") - .long("rustfmt-bindings") - .help( - "Format the generated bindings with rustfmt. DEPRECATED: \ - --rustfmt-bindings is now enabled by default. Disable \ - with --no-rustfmt-bindings.", - ), - Arg::with_name("rustfmt-configuration-file") - .long("rustfmt-configuration-file") - .help( - "The absolute path to the rustfmt configuration file. \ - The configuration file will be used for formatting the bindings. \ - This parameter is incompatible with --no-rustfmt-bindings.", - ) - .value_name("path") - .takes_value(true) - .multiple(false) - .number_of_values(1), - Arg::with_name("no-partialeq") - .long("no-partialeq") - .help("Avoid deriving PartialEq for types matching <regex>.") - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("no-copy") - .long("no-copy") - .help("Avoid deriving Copy for types matching <regex>.") - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("no-debug") - .long("no-debug") - .help("Avoid deriving Debug for types matching <regex>.") - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("no-default") - .long("no-default") - .help("Avoid deriving/implement Default for types matching <regex>.") - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("no-hash") - .long("no-hash") - .help("Avoid deriving Hash for types matching <regex>.") - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("must-use-type") - .long("must-use-type") - .help("Add #[must_use] annotation to types matching <regex>.") - .value_name("regex") - .takes_value(true) - .multiple(true) - .number_of_values(1), - Arg::with_name("enable-function-attribute-detection") - .long("enable-function-attribute-detection") - .help( - "Enables detecting unexposed attributes in functions (slow). - Used to generate #[must_use] annotations.", - ), - Arg::with_name("use-array-pointers-in-arguments") - .long("use-array-pointers-in-arguments") - .help("Use `*const [T; size]` instead of `*const T` for C arrays"), - Arg::with_name("wasm-import-module-name") - .long("wasm-import-module-name") - .value_name("name") - .takes_value(true) - .help("The name to be used in a #[link(wasm_import_module = ...)] statement"), - Arg::with_name("dynamic-loading") - .long("dynamic-loading") - .takes_value(true) - .help("Use dynamic loading mode with the given library name."), - Arg::with_name("dynamic-link-require-all") - .long("dynamic-link-require-all") - .help("Require successful linkage to all functions in the library."), - Arg::with_name("respect-cxx-access-specs") - .long("respect-cxx-access-specs") - .help("Makes generated bindings `pub` only for items if the items are publically accessible in C++."), - Arg::with_name("translate-enum-integer-types") - .long("translate-enum-integer-types") - .help("Always translate enum integer types to native Rust integer types."), - Arg::with_name("c-naming") - .long("c-naming") - .help("Generate types with C style naming."), - Arg::with_name("explicit-padding") - .long("explicit-padding") - .help("Always output explicit padding fields."), - ]) // .args() - .get_matches_from(args); - - let mut builder = builder(); - - if let Some(header) = matches.value_of("header") { - builder = builder.header(header); - } else { - return Err(Error::new(ErrorKind::Other, "Header not found")); - } - - if matches.is_present("unstable-rust") { - builder = builder.rust_target(RustTarget::Nightly); - writeln!( - &mut stderr(), - "warning: the `--unstable-rust` option is deprecated" - ) - .expect("Unable to write error message"); - } - - if let Some(rust_target) = matches.value_of("rust-target") { - builder = builder.rust_target(RustTarget::from_str(rust_target)?); - } - - if let Some(variant) = matches.value_of("default-enum-style") { - builder = builder.default_enum_style(EnumVariation::from_str(variant)?) - } - - if let Some(bitfields) = matches.values_of("bitfield-enum") { - for regex in bitfields { - builder = builder.bitfield_enum(regex); - } - } - - if let Some(newtypes) = matches.values_of("newtype-enum") { - for regex in newtypes { - builder = builder.newtype_enum(regex); - } - } - - if let Some(rustifieds) = matches.values_of("rustified-enum") { - for regex in rustifieds { - builder = builder.rustified_enum(regex); - } - } - - if let Some(const_enums) = matches.values_of("constified-enum") { - for regex in const_enums { - builder = builder.constified_enum(regex); - } - } - - if let Some(constified_mods) = matches.values_of("constified-enum-module") { - for regex in constified_mods { - builder = builder.constified_enum_module(regex); - } - } - - if let Some(variant) = matches.value_of("default-macro-constant-type") { - builder = builder - .default_macro_constant_type(MacroTypeVariation::from_str(variant)?) - } - - if let Some(variant) = matches.value_of("default-alias-style") { - builder = - builder.default_alias_style(AliasVariation::from_str(variant)?); - } - - if let Some(type_alias) = matches.values_of("normal-alias") { - for regex in type_alias { - builder = builder.type_alias(regex); - } - } - - if let Some(new_type) = matches.values_of("new-type-alias") { - for regex in new_type { - builder = builder.new_type_alias(regex); - } - } - - if let Some(new_type_deref) = matches.values_of("new-type-alias-deref") { - for regex in new_type_deref { - builder = builder.new_type_alias_deref(regex); - } - } - - if let Some(hidden_types) = matches.values_of("blocklist-type") { - for ty in hidden_types { - builder = builder.blocklist_type(ty); - } - } - - if let Some(hidden_functions) = matches.values_of("blocklist-function") { - for fun in hidden_functions { - builder = builder.blocklist_function(fun); - } - } - - if let Some(hidden_identifiers) = matches.values_of("blocklist-item") { - for id in hidden_identifiers { - builder = builder.blocklist_item(id); - } - } - - if let Some(hidden_files) = matches.values_of("blocklist-file") { - for file in hidden_files { - builder = builder.blocklist_file(file); - } - } - - if matches.is_present("builtins") { - builder = builder.emit_builtins(); - } - - if matches.is_present("no-layout-tests") { - builder = builder.layout_tests(false); - } - - if matches.is_present("no-derive-copy") { - builder = builder.derive_copy(false); - } - - if matches.is_present("no-derive-debug") { - builder = builder.derive_debug(false); - } - - if matches.is_present("impl-debug") { - builder = builder.impl_debug(true); - } - - if matches.is_present("impl-partialeq") { - builder = builder.impl_partialeq(true); - } - - if matches.is_present("with-derive-default") { - builder = builder.derive_default(true); - } - - if matches.is_present("with-derive-hash") { - builder = builder.derive_hash(true); - } - - if matches.is_present("with-derive-partialeq") { - builder = builder.derive_partialeq(true); - } - - if matches.is_present("with-derive-partialord") { - builder = builder.derive_partialord(true); - } - - if matches.is_present("with-derive-eq") { - builder = builder.derive_eq(true); - } - - if matches.is_present("with-derive-ord") { - builder = builder.derive_ord(true); - } - - if matches.is_present("no-derive-default") { - builder = builder.derive_default(false); - } - - if matches.is_present("no-prepend-enum-name") { - builder = builder.prepend_enum_name(false); - } - - if matches.is_present("no-include-path-detection") { - builder = builder.detect_include_paths(false); - } - - if matches.is_present("fit-macro-constant-types") { - builder = builder.fit_macro_constants(true); - } - - if matches.is_present("time-phases") { - builder = builder.time_phases(true); - } - - if matches.is_present("use-array-pointers-in-arguments") { - builder = builder.array_pointers_in_arguments(true); - } - - if let Some(wasm_import_name) = matches.value_of("wasm-import-module-name") - { - builder = builder.wasm_import_module_name(wasm_import_name); - } - - if let Some(prefix) = matches.value_of("ctypes-prefix") { - builder = builder.ctypes_prefix(prefix); - } - - if let Some(prefix) = matches.value_of("anon-fields-prefix") { - builder = builder.anon_fields_prefix(prefix); - } - - if let Some(what_to_generate) = matches.value_of("generate") { - let mut config = CodegenConfig::empty(); - for what in what_to_generate.split(',') { - match what { - "functions" => config.insert(CodegenConfig::FUNCTIONS), - "types" => config.insert(CodegenConfig::TYPES), - "vars" => config.insert(CodegenConfig::VARS), - "methods" => config.insert(CodegenConfig::METHODS), - "constructors" => config.insert(CodegenConfig::CONSTRUCTORS), - "destructors" => config.insert(CodegenConfig::DESTRUCTORS), - otherwise => { - return Err(Error::new( - ErrorKind::Other, - format!("Unknown generate item: {}", otherwise), - )); - } - } - } - builder = builder.with_codegen_config(config); - } - - if matches.is_present("emit-clang-ast") { - builder = builder.emit_clang_ast(); - } - - if matches.is_present("emit-ir") { - builder = builder.emit_ir(); - } - - if let Some(path) = matches.value_of("emit-ir-graphviz") { - builder = builder.emit_ir_graphviz(path); - } - - if matches.is_present("enable-cxx-namespaces") { - builder = builder.enable_cxx_namespaces(); - } - - if matches.is_present("enable-function-attribute-detection") { - builder = builder.enable_function_attribute_detection(); - } - - if matches.is_present("disable-name-namespacing") { - builder = builder.disable_name_namespacing(); - } - - if matches.is_present("disable-nested-struct-naming") { - builder = builder.disable_nested_struct_naming(); - } - - if matches.is_present("disable-untagged-union") { - builder = builder.disable_untagged_union(); - } - - if matches.is_present("disable-header-comment") { - builder = builder.disable_header_comment(); - } - - if matches.is_present("ignore-functions") { - builder = builder.ignore_functions(); - } - - if matches.is_present("ignore-methods") { - builder = builder.ignore_methods(); - } - - if matches.is_present("no-convert-floats") { - builder = builder.no_convert_floats(); - } - - if matches.is_present("no-doc-comments") { - builder = builder.generate_comments(false); - } - - if matches.is_present("no-recursive-allowlist") { - builder = builder.allowlist_recursively(false); - } - - if matches.is_present("objc-extern-crate") { - builder = builder.objc_extern_crate(true); - } - - if matches.is_present("generate-block") { - builder = builder.generate_block(true); - } - - if matches.is_present("block-extern-crate") { - builder = builder.block_extern_crate(true); - } - - if let Some(opaque_types) = matches.values_of("opaque-type") { - for ty in opaque_types { - builder = builder.opaque_type(ty); - } - } - - if let Some(lines) = matches.values_of("raw-line") { - for line in lines { - builder = builder.raw_line(line); - } - } - - if let Some(mut values) = matches.values_of("module-raw-line") { - while let Some(module) = values.next() { - let line = values.next().unwrap(); - builder = builder.module_raw_line(module, line); - } - } - - if matches.is_present("use-core") { - builder = builder.use_core(); - } - - if matches.is_present("distrust-clang-mangling") { - builder = builder.trust_clang_mangling(false); - } - - if matches.is_present("conservative-inline-namespaces") { - builder = builder.conservative_inline_namespaces(); - } - - if matches.is_present("generate-inline-functions") { - builder = builder.generate_inline_functions(true); - } - - if let Some(allowlist) = matches.values_of("allowlist-function") { - for regex in allowlist { - builder = builder.allowlist_function(regex); - } - } - - if let Some(allowlist) = matches.values_of("allowlist-type") { - for regex in allowlist { - builder = builder.allowlist_type(regex); - } - } - - if let Some(allowlist) = matches.values_of("allowlist-var") { - for regex in allowlist { - builder = builder.allowlist_var(regex); - } - } - - if let Some(args) = matches.values_of("clang-args") { - for arg in args { - builder = builder.clang_arg(arg); - } - } - - let output = if let Some(path) = matches.value_of("output") { - let file = File::create(path)?; - if let Some(depfile) = matches.value_of("depfile") { - builder = builder.depfile(path, depfile); - } - Box::new(io::BufWriter::new(file)) as Box<dyn io::Write> - } else { - if let Some(depfile) = matches.value_of("depfile") { - builder = builder.depfile("-", depfile); - } - Box::new(io::BufWriter::new(io::stdout())) as Box<dyn io::Write> - }; - - if matches.is_present("dump-preprocessed-input") { - builder.dump_preprocessed_input()?; - } - - if matches.is_present("no-record-matches") { - builder = builder.record_matches(false); - } - - if matches.is_present("size_t-is-usize") { - builder = builder.size_t_is_usize(true); - } - - let no_rustfmt_bindings = matches.is_present("no-rustfmt-bindings"); - if no_rustfmt_bindings { - builder = builder.rustfmt_bindings(false); - } - - if let Some(path_str) = matches.value_of("rustfmt-configuration-file") { - let path = PathBuf::from(path_str); - - if no_rustfmt_bindings { - return Err(Error::new( - ErrorKind::Other, - "Cannot supply both --rustfmt-configuration-file and --no-rustfmt-bindings", - )); - } - - if !path.is_absolute() { - return Err(Error::new( - ErrorKind::Other, - "--rustfmt-configuration--file needs to be an absolute path!", - )); - } - - if path.to_str().is_none() { - return Err(Error::new( - ErrorKind::Other, - "--rustfmt-configuration-file contains non-valid UTF8 characters.", - )); - } - - builder = builder.rustfmt_configuration_file(Some(path)); - } - - if let Some(no_partialeq) = matches.values_of("no-partialeq") { - for regex in no_partialeq { - builder = builder.no_partialeq(regex); - } - } - - if let Some(no_copy) = matches.values_of("no-copy") { - for regex in no_copy { - builder = builder.no_copy(regex); - } - } - - if let Some(no_debug) = matches.values_of("no-debug") { - for regex in no_debug { - builder = builder.no_debug(regex); - } - } - - if let Some(no_default) = matches.values_of("no-default") { - for regex in no_default { - builder = builder.no_default(regex); - } - } - - if let Some(no_hash) = matches.values_of("no-hash") { - for regex in no_hash { - builder = builder.no_hash(regex); - } - } - - if let Some(must_use_type) = matches.values_of("must-use-type") { - for regex in must_use_type { - builder = builder.must_use_type(regex); - } - } - - if let Some(dynamic_library_name) = matches.value_of("dynamic-loading") { - builder = builder.dynamic_library_name(dynamic_library_name); - } - - if matches.is_present("dynamic-link-require-all") { - builder = builder.dynamic_link_require_all(true); - } - - if matches.is_present("respect-cxx-access-specs") { - builder = builder.respect_cxx_access_specs(true); - } - - if matches.is_present("translate-enum-integer-types") { - builder = builder.translate_enum_integer_types(true); - } - - if matches.is_present("c-naming") { - builder = builder.c_naming(true); - } - - if matches.is_present("explicit-padding") { - builder = builder.explicit_padding(true); - } - - let verbose = matches.is_present("verbose"); - - Ok((builder, output, verbose)) -} diff --git a/src/parse.rs b/src/parse.rs deleted file mode 100644 index f60de43..0000000 --- a/src/parse.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! Common traits and types related to parsing our IR from Clang cursors. - -use crate::clang; -use crate::ir::context::{BindgenContext, ItemId, TypeId}; -use crate::ir::ty::TypeKind; - -/// Not so much an error in the traditional sense, but a control flow message -/// when walking over Clang's AST with a cursor. -#[derive(Debug)] -pub enum ParseError { - /// Recurse down the current AST node's children. - Recurse, - /// Continue on to the next sibling AST node, or back up to the parent's - /// siblings if we've exhausted all of this node's siblings (and so on). - Continue, -} - -/// The result of parsing a Clang AST node. -#[derive(Debug)] -pub enum ParseResult<T> { - /// We've already resolved this item before, here is the extant `ItemId` for - /// it. - AlreadyResolved(ItemId), - - /// This is a newly parsed item. If the cursor is `Some`, it points to the - /// AST node where the new `T` was declared. - New(T, Option<clang::Cursor>), -} - -/// An intermediate representation "sub-item" (i.e. one of the types contained -/// inside an `ItemKind` variant) that can be parsed from a Clang cursor. -pub trait ClangSubItemParser: Sized { - /// Attempt to parse this type from the given cursor. - /// - /// The fact that is a reference guarantees it's held by the context, and - /// allow returning already existing types. - fn parse( - cursor: clang::Cursor, - context: &mut BindgenContext, - ) -> Result<ParseResult<Self>, ParseError>; -} - -/// An intermediate representation item that can be parsed from a Clang cursor. -pub trait ClangItemParser: Sized { - /// Parse this item from the given Clang cursor. - fn parse( - cursor: clang::Cursor, - parent: Option<ItemId>, - context: &mut BindgenContext, - ) -> Result<ItemId, ParseError>; - - /// Parse this item from the given Clang type. - fn from_ty( - ty: &clang::Type, - location: clang::Cursor, - parent: Option<ItemId>, - ctx: &mut BindgenContext, - ) -> Result<TypeId, ParseError>; - - /// Identical to `from_ty`, but use the given `id` as the `ItemId` for the - /// newly parsed item. - fn from_ty_with_id( - id: ItemId, - ty: &clang::Type, - location: clang::Cursor, - parent: Option<ItemId>, - ctx: &mut BindgenContext, - ) -> Result<TypeId, ParseError>; - - /// Parse this item from the given Clang type, or if we haven't resolved all - /// the other items this one depends on, an unresolved reference. - fn from_ty_or_ref( - ty: clang::Type, - location: clang::Cursor, - parent_id: Option<ItemId>, - context: &mut BindgenContext, - ) -> TypeId; - - /// Identical to `from_ty_or_ref`, but use the given `potential_id` as the - /// `ItemId` for the newly parsed item. - fn from_ty_or_ref_with_id( - potential_id: ItemId, - ty: clang::Type, - location: clang::Cursor, - parent_id: Option<ItemId>, - context: &mut BindgenContext, - ) -> TypeId; - - /// Create a named template type. - fn type_param( - with_id: Option<ItemId>, - location: clang::Cursor, - ctx: &mut BindgenContext, - ) -> Option<TypeId>; - - /// Create a builtin type. - fn builtin_type( - kind: TypeKind, - is_const: bool, - context: &mut BindgenContext, - ) -> TypeId; -} |