cbindgen-0.27.0/.cargo_vcs_info.json0000644000000001360000000000100126730ustar { "git": { "sha1": "58c6156b0d91e82abb03c26187b8d18fa4345ce0" }, "path_in_vcs": "" }cbindgen-0.27.0/.clippy.toml000064400000000000000000000000731046102023000137360ustar 00000000000000# Specify the minimum supported Rust version msrv = "1.70" cbindgen-0.27.0/.gitattributes000064400000000000000000000002471046102023000143610ustar 00000000000000# Running test suite will update test expectations to use LF line endings, # so they need to be checked out using LF as well. * text=auto eol=lf linestyle_crlf* -text cbindgen-0.27.0/.github/workflows/cbindgen.yml000064400000000000000000000035711046102023000173620ustar 00000000000000name: cbindgen on: push: branches: - master pull_request: branches: - master merge_group: types: - checks_requested jobs: rustfmt-clippy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install stable uses: dtolnay/rust-toolchain@stable with: components: "clippy, rustfmt" - name: Run rustfmt run: | cargo +stable fmt --check - name: Run clippy run: | cargo +stable clippy --workspace -- -D warnings - name: Install minimum supported Rust version id: msrv uses: dtolnay/rust-toolchain@1.74 - name: Build with minimum supported Rust version run: | cargo +${{steps.msrv.outputs.name}} test nonexistent-test --verbose build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install stable Rust uses: dtolnay/rust-toolchain@stable - name: Install Python uses: actions/setup-python@v4 with: python-version: '3.8' - name: Install Cython run: | python -m pip install --upgrade pip wheel pip install Cython==3.0.2 - name: Build run: | cargo +stable build --verbose - name: Build no-default-features run: | cargo +stable build --verbose --no-default-features - name: Test package env: CBINDGEN_TEST_VERIFY: 1 run: | cargo +stable package --verbose (cd target/package/cbindgen-$(cargo +stable run -- --version | cut -d ' ' -f 2) && cargo +stable test --verbose) - name: Install nightly Rust uses: dtolnay/rust-toolchain@nightly - name: Test env: CBINDGEN_TEST_VERIFY: 1 run: | cargo +nightly test --verbose - name: Test minimal-versions run: | cargo +nightly update -Zminimal-versions cargo +nightly test cbindgen-0.27.0/.github/workflows/deploy.yml000064400000000000000000000031071046102023000171000ustar 00000000000000name: deploy on: push: tags: - 'v*.*.*' jobs: linux-binaries: permissions: # Grant the GITHUB_TOKEN additional permissions necessary for creating a release. # We only run this action for tags, so any code has already been reviewed by # someone with permissions to create a tag. contents: write runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 - name: Install stable uses: dtolnay/rust-toolchain@stable - name: semver run: | cargo +stable install cargo-semver-checks --locked cargo +stable semver-checks check-release - name: Build cbindgen run: | cargo +stable build --release - name: Strip cbindgen run: | strip target/release/cbindgen - name: Handle release data and files id: tagName run: | VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2) echo "version=$VERSION" >> "$GITHUB_OUTPUT" # Steps to extract the last release notes from CHANGES: # 1. Remove the first three lines # 2. Stop at the next heading level # 3. Remove the last line # 4. Deindent the bullet points to avoid a markdown code block tail -n +3 CHANGES | sed '/^##/q' | sed '$ d' | awk '{$1=$1};1' > CHANGES.txt - name: Create a release run: | TAG=${{ steps.tagName.outputs.version }} gh release create ${TAG} --title "${TAG}" --notes-file "CHANGES.txt" --draft 'target/release/cbindgen#cbindgen-ubuntu20.04' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} cbindgen-0.27.0/.gitignore000064400000000000000000000000251046102023000134500ustar 00000000000000target .idea .vscode cbindgen-0.27.0/CHANGES000064400000000000000000000327051046102023000124650ustar 00000000000000# unreleased # 0.27.0 * Revert: The `Config` struct now has a private member. * Allow users to specify a crate version for bindings generation (#901). * Update MSRV to 1.74 (#912, #987). * Support #[deprecated] on enum variants (#933). * Support integrating the package_version information in a header file comment (#939). * Add a language backend (#942). * Support generics with defaulted args (#959). * Add `VaList` compatibility (#970). # 0.26.0 * Fix swapping of `>>=` and `<<=` in constants. * Add support for #[deprecated] (#860). * Built-in support for bitflags 2.0. * Support for "C-unwind" ABI. * Generate bindings for non-public extern items if they are #[no_mangle]. ## 0.25.0 * Re-release of yanked 0.24.6 as a major release * Update MSRV to 1.57 * Support variadic arguments (`...`) (#805) * Add --depfile option (#820) * Breaking changes: The `Config` struct now has a private member. ## 0.24.6 (YANKED: depfile option was breaking, see #841) * Update MSRV to 1.57 * Support variadic arguments (`...`) (#805) * Add --depfile option (#820) ## 0.24.5 * Don't enforce tempfile version. ## 0.24.4 * Move expand infinite recursion fix (#799) * Add with_cpp_compat to the builder (#796) * Handle never type in return position consistently (#780) * Fix warnings (#816, #819) * Updated documentation (#788, #791, #792, #810, #823) ## 0.24.3 * Make struct expressions correctly generated through typedefs (#768). ## 0.24.2 * Make bitfield operators use explicit constructors. ## 0.24.1 * Add support for unary negation (#765). * Make more bitfield operators constexpr (#765). ## 0.24.0 * Basic const generic support (#759, #760 #762). * Suffixes on integer literals are now honored to avoid narrowing (#764). ## 0.23.0 * Better support for constexpr. (#756) * constexpr is now enabled by default in C++ mode. You can use const.allow_constexpr=false to revert to previous behavior. (#756) * Minimum syn version no longer parses old rust code. (#754) ## 0.22.0 * Support rename rule for union body members (#751). * constant: Add support for associated constant expressions (#752). * Fix regression in CamelCase rename rule (should be lowerCamelCase) (#750). * enumeration: simplify standard types in variants (#749). * Avoid generating and writing bindings when called recursively (#747). * Cython: Omit per-variant tags in unions generated for Rust enums (#748). * Update various dependencies. ## 0.21.0 * Update MSRV to 1.54.0 * Update clap to 3.1. * Update heck to 0.4.0 * unraw identifiers * Honor documentation_length in Cython. * Add documentation_style to with short and full options * Map RawFd to Int * Respect remove_underscores config when prefixing name to enum ## 0.20.0 * Add Builder::with_using_namespaces. (#688) * Ignore PhantomPinned. (#695) * Simplify Pin to T. (#697) * Update --pretty=expanded to -Zunpretty=expanded. (#706) ## 0.19.0 * Simplify types in generics (#663) * Use --profile=check for macro expansion (#671) * Use exported name to prefix enum variants (#672) * Fix path attribute handling in inline submodules (#679) * Fix a stack overflow with some recursive typedefs (#680) ## 0.18.0 * Simplify types in nested types such as pointed-to types and function signatures (#661) ## 0.17.0 * Add with_parse_extra_bindings to builder. (#645) * Support NonZero and fix incorrect simplification of Option into ptr. (#647) * Deal with name conflicts correctly in declaration type resolution. (#651) * Support pointers to ZSTs. (#656) ## 0.16.0 * Remove artificial restriction on lifetime parameters on enums (#604) * Add an option for converting usize/isize into size_t/ptrdiff_t. (#606) * Allow controlling the cargo profile used for expansion. (#607) * Support wider range of expressions in enum discriminants (#614) * Support generation of Cython bindings (#590) * Fixed some issues with style=tag and recursive structs (#615) * Default C style to Both (as specified in docs) (#615) * Fix resolution of path dependencies from certain modules. (#629) * Support inlined definitions for tuple variants with a single field in C (#631) Thanks to all the awesome contributors that contributed to this release. ## 0.15.0 * Allow customizing mangling of generic parameters in C (#575) * Box simplifies to T* in C (4ce324c) * ManuallyDrop and MaybeUninit simplify to T in C, and are opaque in C++ (0076a17) * C++ supports a derive-ostream annotation to derive serialization of structs, unions and enums (#582) * Large character constants have been fixed on Windows (#586) * Constants are now generated for typedefs, etc (#589) * The `sort_by` configuration option has been made to work for constants (#587) * Default sort order is source order now (sort_by = "None"), and can be changed by the above option (#587) ## 0.14.6 * Fixed the builds with older versions of rustc. ## 0.14.5 * Add support to specify line ending style (#568) * Add cbindgen:ptrs-as-arrays annotation to allow making function arguments C/C++ arrays. ## 0.14.4 * Allow to override the mangling separator (#502) * cbindgen now handles better having ZSTs in template parameters, and default template parameters (#563). * Support for annotating nonnull pointers (#558) * Fixed bitflags that overflow a signed integer (#556) * Support for wildcard argument names (#550) * Support for the never return type, with configurable annotation (#549) * Properly reject arrays as function arguments (#540) ## 0.14.3 * Introduce cbindgen:ignore comment annotation, to allow ignoring items or modules. (#519) * Support for casts in constant expressions. (#526) * Make a non-fatal error a warning message. (#535) * Add a --metadata option to the CLI, to allow passing pre-computed cargo metadata. (#538) ## 0.14.2 * Fixed minimal dependency versions. (#507) * Add an option to write pragma once. (#511) * Fix submodule scanning for implicit Rust 2018 modules. (#512) * Fix dependency parsing / scanning to handle target-specific versions. (#513) * Use heck for case conversion. (#514) * Add support for verbatim content after includes. (#416) * Allow to add attributes to most generated functions. (#515) ## 0.14.1 * Handle mangling pointers. (#508) * Unconditionally generate a return statement in partialeq implementations. (#509) ## 0.14.0 * Minor tweak at how [export.exclude] is handled to allow excluding generic instantiations in C mode. (#501) * Documented cpp_compat option. (#496) * Fixed a panic when parsing associated constants for a built-in type. (#494) ## 0.13.2 * Constants now have suitable documentation. (#471) * Fixed some C warnings by emitting void when there are no arguments. (#470) * Avoids reading cargo.toml when not needed, which can cause panics in workspace situations. * Only write `default` cases if the switch is not exhaustive. (#475) * Some warnings have been refined. (#477) * Code generation for static arrays has been fixed. (#479) * Opt-in support for constexpr in constants. (#481) * Fix C code generation and some warnings when extremely large constants are used. (#490) * Proper escaping of enum variants and fields. (#483) * Added support for RefCell (as an opaque type) and Cell. (#489) ## 0.13.1 * Support `#[cfg]` on individual enum variants. (#469) ## 0.13.0 * Support 'swift_name' attributes on generated functions (#449) * Add [export.pre_body] to config (#452) * Handle new line in doc attribute (#454) * Add support for `Self` in tagged enums, structs and unions (#455, #455, #456) * Make sentinel variant respect regular config (#459) * Fix layout of tagged enums with size under some configurations (#463) * Add an option to allow configuring the order of function names in generated headers (#466) ## 0.12.2 * Fixed version detection with lockfile v2. https://github.com/mozilla/cbindgen/pull/446 * Added support for export_name on functions. https://github.com/mozilla/cbindgen/pull/447 ## 0.12.1 * Added support for #[repr*64)] on enums. https://github.com/mozilla/cbindgen/pull/441 * Added support to generate plain enums instead of enum classes for C++. https://github.com/mozilla/cbindgen/pull/443 * Fixed dependency resolution with lockfile v2. https://github.com/mozilla/cbindgen/pull/438 ## 0.12.0 * Added support for #[repr(align)] and #[repr(packed)] on structs and unions. https://github.com/mozilla/cbindgen/pull/431 * Added support to generate copy-assignment operators for enums. https://github.com/mozilla/cbindgen/pull/434 ## 0.11.1 * More binary operators and expressions are supported. https://github.com/mozilla/cbindgen/pull/425 * More built-in bitflags operators. https://github.com/mozilla/cbindgen/pull/426 ## 0.11.0 * Made rust char map to uint32_t. https://github.com/mozilla/cbindgen/pull/424 ## 0.10.1 * Improved error message for missing config file. https://github.com/mozilla/cbindgen/pull/422 * Add missing header for char32_t. https://github.com/mozilla/cbindgen/pull/414 ## 0.10.0 * Initialize struct literal with list-initializer for C++11 standard. https://github.com/mozilla/cbindgen/pull/401 * Surround namespace with __cplusplus ifdef in cpp_compat mode. https://github.com/mozilla/cbindgen/pull/407 * Add support for --quiet flag. https://github.com/mozilla/cbindgen/pull/400 * Map char to char32_t. https://github.com/mozilla/cbindgen/pull/396 * Improve binding_crate_ref() error message. https://github.com/mozilla/cbindgen/pull/395 * avoid prematurely returning during expansion. https://github.com/mozilla/cbindgen/pull/389 * Add support for adding "using namespace" statements. https://github.com/mozilla/cbindgen/pull/387 ## 0.9.1 * Various improvements to comment output. https://github.com/mozilla/cbindgen/pull/370 / https://github.com/mozilla/cbindgen/pull/375. * Fixed expand when ran from build.rs. https://github.com/mozilla/cbindgen/pull/371 * More debugging output for expansion. https://github.com/mozilla/cbindgen/pull/383 * New option to add a default private constructor in C++ tagged enums. https://github.com/mozilla/cbindgen/pull/377 * Syn and related dependencies updated to 1.0. https://github.com/mozilla/cbindgen/pull/379 ## 0.9.0 * Support to generate C headers with C++ compatibility. https://github.com/mozilla/cbindgen/pull/349 * Fix include guard generation when no_includes is set. https://github.com/mozilla/cbindgen/pull/352 * Fix crate parsing order so that types from the binding crate are preferred in presence of conflicting names. https://github.com/mozilla/cbindgen/pull/355 * Add extra_bindings option to generate bindings for dependencies. https://github.com/mozilla/cbindgen/pull/362 * Clap dependency is optional now so it's feasible to avoid building it if cbindgen is used as a library. https://github.com/mozilla/cbindgen/pull/363 ## 0.8.7 * Require C++11 to run the test-suite (#341, test-only) * Improve mangling error message (#340) * Add the ability to automatically derive copy-constructors for tagged enums (#339) * Use placement new for constructing in tagged unions' helper methods (#333) ## 0.8.6 * Fixed a panic when missing a lock file ## 0.8.5 * Improved support for Rust 2018 modules * Add possibility to autogenerate tagged union destructors ## 0.8.4 * Support for package renaming in Cargo.toml * Improved error messages for |cargo metadata| failures * Replaced 'test.py' harness with |cargo test| * Char constants will now be escaped properly * Visibility of constants will now be respected * Added a C99 doc comment style ## 0.8.2 * Improvements to bitflags parsing ## 0.8.1 * Support for manual parsing and expanding of bitflags macros * Support for optional tagged enum casts with asserts ## 0.8.0 * support for 'includes' without default includes * removed dependency on ancient serde_derive! ## 0.7.1 * Fix for crash when unwrapping unsupported type with associated constants ## 0.7.0 * support for libc::ssize_t * fixed some warnings on nightly * fix for transparent associated constants * switched more API's to use AsRef * impl std::error::Error for cbindgen::Error * now support putting user-defined content in item bodies ## 0.6.8 * ptrdiff_t is now a recognized primitive type * named function type arguments are now propagated * fixes for struct literals in constants * reserved C/C++ keywords are now escaped automatically * negative enum discriminants are now supported * fix for prefixing and formatting variant names * fix for snake_case formatter * syn update * doxygen style comments are now output by default * VaList is now a recognized primitive type * fix for comment processing * constant is propagated into array types * references are now supported in IR, and used for some helper method generation * assignment helper functions will generate array copies if necessary cbindgen-0.27.0/Cargo.lock0000644000000355100000000000100106520ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "anstream" version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", ] [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "cbindgen" version = "0.27.0" dependencies = [ "clap", "heck", "indexmap", "log", "pretty_assertions", "proc-macro2", "quote", "serde", "serde_json", "serial_test", "syn", "tempfile", "toml", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d8838454fda655dafd3accb2b6e2bea645b9e4078abe84a22ceb947235c5cc" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", ] [[package]] name = "clap_lex" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "dashmap" version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", "hashbrown", "lock_api", "once_cell", "parking_lot_core", ] [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "fastrand" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "indexmap" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "linux-raw-sys" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "parking_lot" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-targets", ] [[package]] name = "pretty_assertions" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ "diff", "yansi", ] [[package]] name = "proc-macro2" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ "bitflags", ] [[package]] name = "rustix" version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] [[package]] name = "serial_test" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" dependencies = [ "dashmap", "lazy_static", "parking_lot", "serial_test_derive", ] [[package]] name = "serial_test_derive" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", "once_cell", "rustix", "windows-sys 0.59.0", ] [[package]] name = "toml" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit", ] [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] [[package]] name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" cbindgen-0.27.0/Cargo.toml0000644000000042230000000000100106720ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.74" name = "cbindgen" version = "0.27.0" authors = [ "Emilio Cobos Álvarez ", "Jeff Muizelaar ", "Kartikaya Gupta ", "Ryan Hunt ", ] build = "build.rs" exclude = ["tests/profile.rs"] autobins = false autoexamples = false autotests = false autobenches = false description = "A tool for generating C bindings to Rust code." readme = "README.md" keywords = [ "bindings", "ffi", "code-generation", ] categories = [ "external-ffi-bindings", "development-tools::ffi", ] license = "MPL-2.0" repository = "https://github.com/mozilla/cbindgen" [lib] name = "cbindgen" path = "src/lib.rs" [[bin]] name = "cbindgen" path = "src/main.rs" doc = false required-features = ["clap"] [[test]] name = "depfile" path = "tests/depfile.rs" [[test]] name = "tests" path = "tests/tests.rs" [dependencies.clap] version = "4.3" optional = true [dependencies.heck] version = "0.4" [dependencies.indexmap] version = "2.1.0" [dependencies.log] version = "0.4" [dependencies.proc-macro2] version = "1.0.60" [dependencies.quote] version = "1" [dependencies.serde] version = "1.0.103" features = ["derive"] default-features = false [dependencies.serde_json] version = "1.0" [dependencies.syn] version = "2.0.64" features = [ "clone-impls", "extra-traits", "fold", "full", "parsing", "printing", ] default-features = false [dependencies.tempfile] version = "3" [dependencies.toml] version = "0.8.8" [dev-dependencies.pretty_assertions] version = "1.4.0" [dev-dependencies.serial_test] version = "2.0.0" default-features = false [features] default = ["clap"] cbindgen-0.27.0/Cargo.toml.orig0000644000000043350000000000100116350ustar [package] name = "cbindgen" version = "0.27.0" authors = [ "Emilio Cobos Álvarez ", "Jeff Muizelaar ", "Kartikaya Gupta ", "Ryan Hunt " ] license = "MPL-2.0" description = "A tool for generating C bindings to Rust code." keywords = ["bindings", "ffi", "code-generation"] categories = ["external-ffi-bindings", "development-tools::ffi"] repository = "https://github.com/mozilla/cbindgen" edition = "2018" rust-version = "1.74" exclude = [ "tests/profile.rs", # Test relies in a sub-crate, see https://github.com/rust-lang/cargo/issues/9017 ] [dependencies] clap = { version = "4.3", optional = true } indexmap = "2.1.0" log = "0.4" serde = { version = "1.0.103", default-features = false, features = ["derive"] } serde_json = "1.0" tempfile = "3" toml = "0.8.8" proc-macro2 = "1.0.60" quote = "1" heck = "0.4" [dependencies.syn] version = "2.0.64" default-features = false features = ["clone-impls", "extra-traits", "fold", "full", "parsing", "printing"] [dev-dependencies] serial_test = { version = "2.0.0", default-features = false } pretty_assertions = "1.4.0" [features] default = ["clap"] [[bin]] name = "cbindgen" path = "src/main.rs" doc = false required-features = ["clap"] [lib] name = "cbindgen" path = "src/lib.rs" [workspace] exclude = [ "tests/depfile/single_crate_config", "tests/depfile/single_crate_default_config", "tests/depfile/single_crate", "tests/rust/bitfield", "tests/rust/dep_v2", "tests/rust/dep_v2/dep", "tests/rust/derive_eq", "tests/rust/expand_default_features", "tests/rust/expand_dep_v2", "tests/rust/expand_dep_v2/dep_v2", "tests/rust/expand_dep_v2/dep", "tests/rust/expand_dep", "tests/rust/expand_dep/dep", "tests/rust/expand_features", "tests/rust/expand_no_default_features", "tests/rust/expand", "tests/rust/external_workspace_child", "tests/rust/literal_target", "tests/rust/mod_2015", "tests/rust/mod_2018", "tests/rust/mod_attr", "tests/rust/mod_path", "tests/rust/package_version", "tests/rust/rename_crate", "tests/rust/rename_crate/dependency", "tests/rust/rename_crate/no_extern", "tests/rust/rename_crate/old_dep", "tests/rust/workspace", "tests/rust/workspace/dep", ] cbindgen-0.27.0/Cargo.toml.orig000064400000000000000000000043351046102023000143570ustar 00000000000000[package] name = "cbindgen" version = "0.27.0" authors = [ "Emilio Cobos Álvarez ", "Jeff Muizelaar ", "Kartikaya Gupta ", "Ryan Hunt " ] license = "MPL-2.0" description = "A tool for generating C bindings to Rust code." keywords = ["bindings", "ffi", "code-generation"] categories = ["external-ffi-bindings", "development-tools::ffi"] repository = "https://github.com/mozilla/cbindgen" edition = "2018" rust-version = "1.74" exclude = [ "tests/profile.rs", # Test relies in a sub-crate, see https://github.com/rust-lang/cargo/issues/9017 ] [dependencies] clap = { version = "4.3", optional = true } indexmap = "2.1.0" log = "0.4" serde = { version = "1.0.103", default-features = false, features = ["derive"] } serde_json = "1.0" tempfile = "3" toml = "0.8.8" proc-macro2 = "1.0.60" quote = "1" heck = "0.4" [dependencies.syn] version = "2.0.64" default-features = false features = ["clone-impls", "extra-traits", "fold", "full", "parsing", "printing"] [dev-dependencies] serial_test = { version = "2.0.0", default-features = false } pretty_assertions = "1.4.0" [features] default = ["clap"] [[bin]] name = "cbindgen" path = "src/main.rs" doc = false required-features = ["clap"] [lib] name = "cbindgen" path = "src/lib.rs" [workspace] exclude = [ "tests/depfile/single_crate_config", "tests/depfile/single_crate_default_config", "tests/depfile/single_crate", "tests/rust/bitfield", "tests/rust/dep_v2", "tests/rust/dep_v2/dep", "tests/rust/derive_eq", "tests/rust/expand_default_features", "tests/rust/expand_dep_v2", "tests/rust/expand_dep_v2/dep_v2", "tests/rust/expand_dep_v2/dep", "tests/rust/expand_dep", "tests/rust/expand_dep/dep", "tests/rust/expand_features", "tests/rust/expand_no_default_features", "tests/rust/expand", "tests/rust/external_workspace_child", "tests/rust/literal_target", "tests/rust/mod_2015", "tests/rust/mod_2018", "tests/rust/mod_attr", "tests/rust/mod_path", "tests/rust/package_version", "tests/rust/rename_crate", "tests/rust/rename_crate/dependency", "tests/rust/rename_crate/no_extern", "tests/rust/rename_crate/old_dep", "tests/rust/workspace", "tests/rust/workspace/dep", ] cbindgen-0.27.0/LICENSE000064400000000000000000000405251046102023000124760ustar 00000000000000Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. cbindgen-0.27.0/README.md000064400000000000000000000102771046102023000127510ustar 00000000000000# `cbindgen`   [![Build Status]][actions] [![Latest Version]][crates.io] [![Api Rustdoc]][rustdoc] [![Rust](https://img.shields.io/badge/rust-1.70%2B-blue.svg?maxAge=3600)](https://github.com/mozilla/cbindgen) [Build Status]: https://github.com/mozilla/cbindgen/workflows/cbindgen/badge.svg [actions]: https://github.com/mozilla/cbindgen/actions [Latest Version]: https://img.shields.io/crates/v/cbindgen.svg [crates.io]: https://crates.io/crates/cbindgen [Api Rustdoc]: https://img.shields.io/badge/api-rustdoc-blue.svg [rustdoc]: https://docs.rs/cbindgen [Read the full user docs here!](https://github.com/mozilla/cbindgen/blob/master/docs.md) cbindgen creates C/C++11 headers for Rust libraries which expose a public C API. While you could do this by hand, it's not a particularly good use of your time. It's also much more likely to be error-prone than machine-generated headers that are based on your actual Rust code. The cbindgen developers have also worked closely with the developers of Rust to ensure that the headers we generate reflect actual guarantees about Rust's type layout and ABI. C++ headers are nice because we can use operator overloads, constructors, enum classes, and templates to make the API more ergonomic and Rust-like. C headers are nice because you can be more confident that whoever you're interoperating with can handle them. With cbindgen *you don't need to choose*! You can just tell it to emit both from the same Rust library. There are two ways to use cbindgen: as a standalone program, or as a library (presumably in your build.rs). There isn't really much practical difference, because cbindgen is a simple rust library with no interesting dependencies. Using it as a program means people building your software will need it installed. Using it in your library means people may have to build cbindgen more frequently (e.g. every time they update their rust compiler). It's worth noting that the development of cbindgen has been largely adhoc, as features have been added to support the usecases of the maintainers. This means cbindgen may randomly fail to support some particular situation simply because no one has put in the effort to handle it yet. [Please file an issue if you run into such a situation](https://github.com/mozilla/cbindgen/issues/new). Although since we all have other jobs, you might need to do the implementation work too :) # Quick Start To install cbindgen, you just need to run ```text cargo install --force cbindgen ``` (--force just makes it update to the latest cbindgen if it's already installed) Or with Homebrew, run ```text brew install cbindgen ``` To use cbindgen you need two things: * A configuration (cbindgen.toml, which can be empty to start) * A Rust crate with a public C API Then all you need to do is run it: ```text cbindgen --config cbindgen.toml --crate my_rust_library --output my_header.h ``` This produces a header file for C++. For C, add the `--lang c` switch. See `cbindgen --help` for more options. [Read the full user docs here!](docs.md) [Get a template cbindgen.toml here.](template.toml) # Examples We don't currently have a nice tailored example application, but [the tests](tests/rust/) contain plenty of interesting examples of our features. You may also find it interesting to browse the projects that are using cbindgen in production: * [milksnake](https://github.com/getsentry/milksnake) * [webrender](https://searchfox.org/mozilla-central/source/gfx/webrender_bindings) ([generated header](https://searchfox.org/mozilla-central/source/__GENERATED__/gfx/webrender_bindings/webrender_ffi_generated.h)) * [stylo](https://searchfox.org/mozilla-central/source/layout/style) ([generated header](https://searchfox.org/mozilla-central/source/__GENERATED__/layout/style/ServoStyleConsts.h)) * [maturin](https://github.com/PyO3/maturin) * [tquic](https://github.com/Tencent/tquic) ([generated header](https://github.com/Tencent/tquic/blob/develop/include/tquic.h)) If you're using `cbindgen` and would like to be added to this list, please open a pull request! # Releases cbindgen doesn't have a fixed release calendar, please file an issue requesting a release if there's something fixed in trunk that you need released. Ping `@emilio` for increased effect. cbindgen-0.27.0/build.rs000064400000000000000000000052461046102023000131370ustar 00000000000000fn generate_tests() { use std::env; use std::ffi::OsStr; use std::fs::{self, File}; use std::io::Write; use std::path::{Path, PathBuf}; 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 tests_dir = manifest_dir.join("tests").join("rust"); let tests = fs::read_dir(&tests_dir).unwrap(); let entries = tests.map(|t| t.expect("Couldn't read test file")); println!("cargo:rerun-if-changed={}", tests_dir.display()); for entry in entries { let path_segment = if entry.file_type().unwrap().is_file() { match entry.path().extension().and_then(OsStr::to_str) { Some("rs") => {} _ => continue, }; entry .path() .file_stem() .unwrap() .to_str() .unwrap() .to_owned() } else { entry.file_name().to_str().unwrap().to_owned() }; let identifier = path_segment .replace(|c| !char::is_alphanumeric(c), "_") .replace("__", "_"); writeln!( dst, "test_file!(test_{}, {:?}, {:?});", identifier, path_segment, entry.path(), ) .unwrap(); } dst.flush().unwrap(); } fn generate_depfile_tests() { use std::env; use std::fs::{self, File}; use std::io::Write; use std::path::{Path, PathBuf}; let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); let mut dst = File::create(Path::new(&out_dir).join("depfile_tests.rs")).unwrap(); let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); let tests_dir = manifest_dir.join("tests").join("depfile"); let tests = fs::read_dir(&tests_dir).unwrap(); let entries = tests.map(|t| t.expect("Couldn't read test file")); println!("cargo:rerun-if-changed={}", tests_dir.display()); for entry in entries { if entry.file_type().unwrap().is_file() { continue; }; let path_segment = entry.file_name().to_str().unwrap().to_owned(); let identifier = path_segment .replace(|c| !char::is_alphanumeric(c), "_") .replace("__", "_"); writeln!( dst, "test_file!(test_depfile_{}, {:?}, {:?});", identifier, path_segment, entry.path(), ) .unwrap(); } dst.flush().unwrap(); } fn main() { generate_tests(); generate_depfile_tests(); } cbindgen-0.27.0/contributing.md000064400000000000000000000030221046102023000145110ustar 00000000000000# Contributing Thanks for wanting to contribute! If you want help or mentorship, please file a GitHub issue and I'll be sure to provide guidance to the best of my ability. Otherwise be sure to check out `internals.md` for an overview on the internals. ## Filing a pull request Check out [Servo's GitHub workflow](https://github.com/servo/servo/wiki/Github-workflow) for an overview on creating a pull request. Don't worry about requesting code review, as there is nothing formally setup for this repository. I try and review each pull request as soon as I can. There is continuous integration setup for `cbindgen` using [GitHub Actions](https://github.com/mozilla/cbindgen/actions). It automatically runs `cargo test` which runs `cbindgen` against a series of Rust files from `tests/rust/` and checks that the output compiles using `gcc` or `g++`. In addition to a C/C++ compiler `cargo test` requires Python and Cython (`python -m pip install Cython`) for checking Cython bindings generated from tests (`.pyx` files). Note that the tests will be failed with Cython 3.x or later. Please run `cargo test` before filing a pull request to be sure that all tests pass. This will also update the test expectations. Rustfmt is also enforced by GitHub Actions. To format your code install `rustfmt-preview` using `rustup component add rustfmt-preview` and then `cargo fmt`. GitHub Actions runs with Rust nightly, so use `rustup run nightly -- cargo fmt` to guarantee consistent results. Writing new tests with your pull requests is also appreciated. cbindgen-0.27.0/docs.md000064400000000000000000001206221046102023000127400ustar 00000000000000# cbindgen User Guide cbindgen creates C/C++11 headers for Rust libraries which expose a public C API. While you could do this by hand, it's not a particularly good use of your time. It's also much more likely to be error-prone than machine-generated headers that are based on your actual Rust code. The cbindgen developers have also worked closely with the developers of Rust to ensure that the headers we generate reflect actual guarantees about Rust's type layout and ABI. C++ headers are nice because we can use operator overloads, constructors, enum classes, and templates to make the API more ergonomic and Rust-like. C headers are nice because you can be more confident that whoever you're interoperating with can handle them. With cbindgen *you don't need to choose*! You can just tell it to emit both from the same Rust library. There are two ways to use cbindgen: as a standalone program, or as a library (presumably in your build.rs). There isn't really much practical difference, because cbindgen is a simple rust library with no interesting dependencies. Using it as a program means people building your software will need it installed. Using it in your library means people may have to build cbindgen more frequently (e.g. every time they update their rust compiler). It's worth noting that the development of cbindgen has been largely adhoc, as features have been added to support the usecases of the maintainers. This means cbindgen may randomly fail to support some particular situation simply because no one has put in the effort to handle it yet. [Please file an issue if you run into such a situation][file-it]. Although since we all have other jobs, you might need to do the implementation work too :) # Quick Start To install cbindgen, you just need to run ```text cargo install --force cbindgen ``` (--force just makes it update to the latest cbindgen if it's already installed) To use cbindgen you need two things: * A configuration (cbindgen.toml, which can be empty to start) * A Rust crate with a public C API Then all you need to do is run it: ```text cbindgen --config cbindgen.toml --crate my_rust_library --output my_header.h ``` This produces a header file for C++. For C, add the `--lang c` switch. \ `cbindgen` also supports generation of [Cython](https://cython.org) bindings, use `--lang cython` for that. See `cbindgen --help` for more options. [Get a template cbindgen.toml here.](template.toml) ## build.rs If you don't want to use cbindgen as an application, here's an example build.rs script: ```rust extern crate cbindgen; use std::env; fn main() { let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); cbindgen::Builder::new() .with_crate(crate_dir) .generate() .expect("Unable to generate bindings") .write_to_file("bindings.h"); } ``` You can add configuration options using the [`Builder`](https://docs.rs/cbindgen/*/cbindgen/struct.Builder.html#methods) interface. When actively working on code, you likely don't want cbindgen to fail the entire build. Instead of expect-ing the result of the header generation, you could [ignore parse errors](https://github.com/mozilla/cbindgen/issues/472#issuecomment-831439826) and let rustc or your code analysis bring up: ```rust // ... .generate() .map_or_else( |error| match error { cbindgen::Error::ParseSyntaxError { .. } => {} e => panic!("{:?}", e), }, |bindings| { bindings.write_to_file("target/include/bindings.h"); }, ); } ``` Be sure to add the following section to your Cargo.toml: ``` [build-dependencies] cbindgen = "0.24.0" ``` If you'd like to use a `build.rs` script with a `cbindgen.toml`, consider using [`cbindgen::generate()`](https://docs.rs/cbindgen/*/cbindgen/fn.generate.html) instead. # Writing Your C API cbindgen has a simple but effective strategy. It walks through your crate looking for: * `#[no_mangle] pub extern fn` ("functions") * `#[no_mangle] pub static` ("globals") * `pub const` ("constants") and generates a header declaring those items. But to declare those items, it needs to also be able to describe the layout and ABI of the types that appear in their signatures. So it will also spider through your crate (and optionally its dependencies) to try to find the definitions of every type used in your public API. > 🚨 NOTE: A major limitation of cbindgen is that it does not understand Rust's module system or namespacing. This means that if cbindgen sees that it needs the definition for `MyType` and there exists two things in your project with the type name `MyType`, it won't know what to do. Currently, cbindgen's behaviour is unspecified if this happens. However this may be ok if they have [different cfgs][section-cfgs]. If a type is determined to have a guaranteed layout, a full definition will be emitted in the header. If the type doesn't have a guaranteed layout, only a forward declaration will be emitted. This may be fine if the type is intended to be passed around opaquely and by reference. # Examples 🚧 🏗 It would be really nice to have some curated and clean examples, but we don't have those yet. [The README has some useful links though](README.md#examples). # Supported Types Most things in Rust don't have a guaranteed layout by default. In most cases this is nice because it enables layout to be optimized in the majority of cases where type layout isn't that interesting. However this is problematic for our purposes. Thankfully Rust lets us opt into guaranteed layouts with the `repr` attribute. You can learn about all of the different repr attributes [by reading Rust's reference][reference], but here's a quick summary: * `#[repr(C)]`: give this struct/union/enum the same layout and ABI C would * `#[repr(u8, u16, ... etc)]`: give this enum the same layout and ABI as the given integer type * `#[repr(transparent)]`: give this single-field struct the same ABI as its field (useful for newtyping integers but keeping the integer ABI) cbindgen supports the `#[repr(align(N))]` and `#[repr(packed)]` attributes, but currently does not support `#[repr(packed(N))]`. cbindgen also supports using `repr(C)`/`repr(u8)` on non-C-like enums (enums with fields). This gives a C-compatible tagged union layout, as [defined by this RFC 2195][really-tagged-unions]. `repr(C)` will give a simpler layout that is perhaps more intuitive, while `repr(u8)` will produce a more compact layout. If you ensure everything has a guaranteed repr, then cbindgen will generate definitions for: * struct (named-style or tuple-style) * enum (fieldless or with fields) * union * type * `[T; n]` (arrays always have a guaranteed C-compatible layout) * `&T`, `&mut T`, `*const T`, `*mut T`, `Option<&T>`, `Option<&mut T>` (all have the same pointer ABI) * `fn()` (as an actual function pointer) * `bitflags! { ... }` (if macro_expansion.bitflags is enabled) structs, enums, unions, and type aliases may be generic, although certain generic substitutions may fail to resolve under certain configurations. In C mode generics are resolved through monomorphization and mangling, while in C++ mode generics are resolved with templates. cbindgen cannot support generic functions, as they do not actually have a single defined symbol. cbindgen sadly cannot ever support anonymous tuples `(A, B, ...)`, as there is no way to guarantee their layout. You must use a tuple struct. cbindgen also cannot support wide pointers like `&dyn Trait` or `&[T]`, as their layout and ABI is not guaranteed. In the case of slices you can at least decompose them into a pointer and length, and reconstruct them with `slice::from_raw_parts`. If cbindgen determines that a type is zero-sized, it will erase all references to that type (so fields of that type simply won't be emitted). This won't work if that type appears as a function argument because C, C++, and Rust all have different definitions of what it means for a type to be empty. Don't use the `[u64; 0]` trick to over-align a struct, we don't support this. cbindgen contains the following hardcoded mappings (again completely ignoring namespacing, literally just looking at the name of the type): ## std types * bool => bool * char => uint32_t * u8 => uint8_t * u16 => uint16_t * u32 => uint32_t * u64 => uint64_t * usize => uintptr_t * i8 => int8_t * i16 => int16_t * i32 => int32_t * i64 => int64_t * isize => intptr_t * f32 => float * f64 => double * VaList => va_list * RawFd => int * PhantomData => *evaporates*, can only appear as the field of a type * PhantomPinned => *evaporates*, can only appear as the field of a type * () => *evaporates*, can only appear as the field of a type * MaybeUninit, ManuallyDrop, and Pin => T ## libc types * c_void => void * c_char => char * c_schar => signed char * c_uchar => unsigned char * c_float => float * c_double => double * c_short => short * c_int => int * c_long => long * c_longlong => long long * c_ushort => unsigned short * c_uint => unsigned int * c_ulong => unsigned long * c_ulonglong => unsigned long long ## stdint types * uint8_t => uint8_t * uint16_t => uint16_t * uint32_t => uint32_t * uint64_t => uint64_t * uintptr_t => uintptr_t * size_t => size_t * int8_t => int8_t * int16_t => int16_t * int32_t => int32_t * int64_t => int64_t * intptr_t => intptr_t * ssize_t => ssize_t * ptrdiff_t => ptrdiff_t # Configuring Your Header cbindgen supports several different options for configuring the output of your header, including target language, styling, mangling, prefixing, includes, and defines. ## Defines and Cfgs As cbindgen spiders through your crate, it will make note of all the cfgs it found on the path to every item. If it finds multiple declarations that share a single name but have different cfgs, it will then try to emit every version it found wrapped in defines that correspond to those cfgs. In this way platform-specific APIs or representations can be properly supported. However cbindgen has no way of knowing how you want to map those cfgs to defines. You will need to use the `[defines]` section in your cbindgen.toml to specify all the different mappings. It natively understands concepts like any() and all(), so you only need to tell it how you want to translate base concepts like `target_os = "freebsd"` or `feature = "serde"`. Note that because cbindgen just parses the source of your crate, you mostly don't need to worry about what crate features or what platform you're targetting. Every possible configuration should be visible to the parser. Our primitive mappings should also be completely platform agnostic (i32 is int32_t regardless of your target). While modules within a crate form a tree with uniquely defined paths to each item, and therefore uniquely defined cfgs for those items, dependencies do not. If you depend on a crate in multiple ways, and those ways produce different cfgs, one of them will be arbitrarily chosen for any types found in that crate. ## Annotations While output configuration is primarily done through the cbindgen.toml, in some cases you need to manually override your global settings. In those cases you can add inline annotations to your types, which are doc comments that start with `cbindgen:`. Here's an example of using annotations to rename a struct's fields and opt into overloading `operator==`: ```rust /// cbindgen:field-names=[x, y] /// cbindgen:derive-eq #[repr(C)] pub struct Point(pub f32, pub f32); ``` An annotation may be a bool, string (no quotes), or list of strings. If just the annotation's name is provided, `=true` is assumed. The annotation parser is currently fairly naive and lacks any capacity for escaping, so don't try to make any strings with `=`, `,`, `[` or `]`. Most annotations are just local overrides for identical settings in the cbindgen.toml, but a few are unique because they don't make sense in a global context. The set of supported annotation are as follows: ### Ignore annotation cbindgen will automatically ignore any `#[test]` or `#[cfg(test)]` item it finds. You can manually ignore other stuff with the `ignore` annotation attribute: ```rust pub mod my_interesting_mod; /// cbindgen:ignore pub mod my_uninteresting_mod; // This won't be scanned by cbindgen. ``` ### No export annotation cbindgen will usually emit all items it finds, as instructed by the parse and export config sections. This annotation will make cbindgen skip this item from the output, while still being aware of it. This is useful for a) suppressing "Can't find" errors and b) emitting `struct my_struct` for types in a different header (rather than a bare `my_struct`). There is no equivalent config for this annotation - by comparison, the export exclude config will cause cbindgen to not be aware of the item at all. Note that cbindgen will still traverse `no-export` structs that are `repr(C)` to emit types present in the fields. You will need to manually exclude those types in your config if desired. ``` /// cbindgen:no-export #[repr(C)] pub struct Foo { .. }; // This won't be emitted by cbindgen in the header #[repr(C)] fn bar() -> Foo { .. } // Will be emitted as `struct foo bar();` ``` ### Struct Annotations * field-names=\[field1, field2, ...\] -- sets the names of all the fields in the output struct. These names will be output verbatim, and are not eligible for renaming. The rest are just local overrides for the same options found in the cbindgen.toml: * rename-all=RenameRule * derive-constructor * derive-eq * derive-neq * derive-lt * derive-lte * derive-gt * derive-gte * {eq,neq,lt,lte,gt,gte}-attributes: Takes a single identifier which will be emitted before the signature of the auto-generated `operator==` / `operator!=` / etc(if any). The idea is for this to be used to annotate the operator with attributes, for example: ```rust /// cbindgen:eq-attributes=MY_ATTRIBUTES #[repr(C)] pub struct Foo { .. } ``` Will generate something like: ``` MY_ATTRIBUTES bool operator==(const Foo& other) const { ... } ``` Combined with something like: ``` #define MY_ATTRIBUTES [[nodiscard]] ``` for example. ### Enum Annotations * enum-trailing-values=\[variant1, variant2, ...\] -- add the following fieldless enum variants to the end of the enum's definition. These variant names *will* have the enum's renaming rules applied. WARNING: if any of these values are ever passed into Rust, behaviour will be Undefined. Rust does not know about them, and will assume they cannot happen. The rest are just local overrides for the same options found in the cbindgen.toml: * rename-all=RenameRule * add-sentinel * derive-helper-methods * derive-const-casts * derive-mut-casts * derive-tagged-enum-destructor * derive-tagged-enum-copy-constructor * enum-class * prefix-with-name * private-default-tagged-enum-constructor * {destructor,copy-constructor,copy-assignment}-attributes: See the description of the struct attributes, these do the same for the respective generated code. ### Enum variant annotations These apply to both tagged and untagged enum _variants_. * variant-{constructor,const-cast,mut-cast,is}-attributes: See the description of the struct attributes. These do the same for the respective functions. TODO: We should allow to override the `derive-{const,mut}-casts`, helper methods et al. with per-variant annotations, probably. ### Union Annotations * field-names=\[field1, field2, ...\] -- sets the names of all the fields in the output union. These names will be output verbatim, and are not eligible for renaming. The rest are just local overrides for the same options found in the cbindgen.toml: * rename-all=RenameRule ### Function Annotations All function attributes are just local overrides for the same options found in the cbindgen.toml: * rename-all=RenameRule * prefix * postfix * ptrs-as-arrays=\[[ptr\_name1; array\_length1], [ptr\_name2; array\_length2], ...\] -- represents the pointer arguments of a function as arrays. Below how the mappings are performed: ``` arg: *const T --> const T arg[array_length] arg: *mut T ---> T arg[array_length] ``` If `array_length` is not specified: ``` arg: *const T --> const T arg[] arg: *mut T --> T arg[] ``` ## Generating Swift Bindings In addition to parsing function names in C/C++ header files, the Swift compiler can make use of the `swift_name` attribute on functions to generate more idiomatic names for imported functions and methods. This attribute is commonly used in Objective-C/C/C++ via the `NS_SWIFT_NAME` and `CF_SWIFT_NAME` macros. Given configuration in the cbindgen.toml, `cbindgen` can generate these attributes for you by guessing an appropriate method signature based on the existing function name (and type, if it is a method in an `impl` block). This is controlled by the `swift_name_macro` option in the cbindgen.toml. ## cbindgen.toml Most configuration happens through your cbindgen.toml file. Every value has a default (that is usually reasonable), so you can start with an empty cbindgen.toml and tweak it until you like the output you're getting. Note that many options defined here only apply for one of C or C++. Usually it's an option specifying whether we should try to make use of a feature in C++'s type system or generate a helper method. ```toml # The language to output bindings in # # possible values: "C", "C++", "Cython" # # default: "C++" language = "C" # Options for wrapping the contents of the header: # An optional string of text to output at the beginning of the generated file # default: doesn't emit anything header = "/* Text to put at the beginning of the generated file. Probably a license. */" # An optional string of text to output at the end of the generated file # default: doesn't emit anything trailer = "/* Text to put at the end of the generated file */" # An optional name to use as an include guard # default: doesn't emit an include guard include_guard = "mozilla_wr_bindings_h" # Whether to add a `#pragma once` guard # default: doesn't emit a `#pragma once` pragma_once = true # An optional string of text to output between major sections of the generated # file as a warning against manual editing # # default: doesn't emit anything autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" # Whether to include a comment with the version of cbindgen used to generate the file # default: false include_version = true # An optional namespace to output around the generated bindings # default: doesn't emit a namespace namespace = "ffi" # An optional list of namespaces to output around the generated bindings # default: [] namespaces = ["mozilla", "wr"] # An optional list of namespaces to declare as using with "using namespace" # default: [] using_namespaces = ["mozilla", "wr"] # A list of sys headers to #include (with angle brackets) # default: [] sys_includes = ["stdio", "string"] # A list of headers to #include (with quotes) # default: [] includes = ["my_great_lib.h"] # Whether cbindgen's default C/C++ standard imports should be suppressed. These # imports are included by default because our generated headers tend to require # them (e.g. for uint32_t). Currently, the generated imports are: # # * for C: , , , , # # * for C++: , , , , (depending on config) # # default: false no_includes = false # Whether to make a C header C++ compatible. # These will wrap generated functions into a `extern "C"` block, e.g. # # #ifdef __cplusplus # extern "C" { # #endif // __cplusplus # # // Generated functions. # # #ifdef __cplusplus # } // extern "C" # #endif // __cplusplus # # If the language is not C this option won't have any effect. # # default: false cpp_compat = false # A list of lines to add verbatim after the includes block after_includes = "#define VERSION 1" # Code Style Options # The style to use for curly braces # # possible values: "SameLine", "NextLine" # # default: "SameLine" braces = "SameLine" # The desired length of a line to use when formatting lines # default: 100 line_length = 80 # The amount of spaces to indent by # default: 2 tab_width = 3 # Include doc comments from Rust as documentation documentation = true # How the generated documentation should be commented. # # possible values: # * "c": /* like this */ # * "c99": // like this # * "c++": /// like this # * "doxy": like C, but with leading *'s on each line # * "auto": "c++" if that's the language, "doxy" otherwise # # default: "auto" documentation_style = "doxy" # How much of the documentation for each item is output. # # possible values: # * "short": Only the first line. # * "full": The full documentation. # # default: "full" documentation_length = "short" # Codegen Options # When generating a C header, the kind of declaration style to use for structs # or enums. # # possible values: # * "type": typedef struct { ... } MyType; # * "tag": struct MyType { ... }; # * "both": typedef struct MyType { ... } MyType; # # default: "both" style = "both" # If this option is true `usize` and `isize` will be converted into `size_t` and `ptrdiff_t` # instead of `uintptr_t` and `intptr_t` respectively. usize_is_size_t = true # A list of substitutions for converting cfg's to ifdefs. cfgs which aren't # defined here will just be discarded. # # e.g. # `#[cfg(target = "freebsd")] ...` # becomes # `#if defined(DEFINE_FREEBSD) ... #endif` [defines] "target_os = freebsd" = "DEFINE_FREEBSD" "feature = serde" = "DEFINE_SERDE" [export] # A list of additional items to always include in the generated bindings if they're # found but otherwise don't appear to be used by the public API. # # default: [] include = ["MyOrphanStruct", "MyGreatTypeRename"] # A list of items to not include in the generated bindings # default: [] exclude = ["Bad"] # A prefix to add before the name of every item # default: no prefix is added prefix = "CAPI_" # Types of items that we'll generate. If empty, then all types of item are emitted. # # possible items: (TODO: explain these in detail) # * "constants": # * "globals": # * "enums": # * "structs": # * "unions": # * "typedefs": # * "opaque": # * "functions": # # default: [] item_types = ["enums", "structs", "opaque", "functions"] # Whether applying rules in export.rename prevents export.prefix from applying. # # e.g. given this toml: # # [export] # prefix = "capi_" # [export.rename] # "MyType" = "my_cool_type" # # You get the following results: # # renaming_overrides_prefixing = true: # "MyType" => "my_cool_type" # # renaming_overrides_prefixing = false: # "MyType => capi_my_cool_type" # # default: false renaming_overrides_prefixing = true # Table of name conversions to apply to item names (lhs becomes rhs) [export.rename] "MyType" = "my_cool_type" "my_function" = "BetterFunctionName" # Table of things to prepend to the body of any struct, union, or enum that has the # given name. This can be used to add things like methods which don't change ABI, # mark fields private, etc [export.pre_body] "MyType" = """ MyType() = delete; private: """ # Table of things to append to the body of any struct, union, or enum that has the # given name. This can be used to add things like methods which don't change ABI. [export.body] "MyType" = """ void cppMethod() const; """ # Configuration for name mangling [export.mangle] # Whether the types should be renamed during mangling, for example # c_char -> CChar, etc. rename_types = "PascalCase" # Whether the underscores from the mangled name should be omitted. remove_underscores = false [layout] # A string that should come before the name of any type which has been marked # as `#[repr(packed)]`. For instance, "__attribute__((packed))" would be a # reasonable value if targeting gcc/clang. A more portable solution would # involve emitting the name of a macro which you define in a platform-specific # way. e.g. "PACKED" # # default: `#[repr(packed)]` types will be treated as opaque, since it would # be unsafe for C callers to use a incorrectly laid-out union. packed = "PACKED" # A string that should come before the name of any type which has been marked # as `#[repr(align(n))]`. This string must be a function-like macro which takes # a single argument (the requested alignment, `n`). For instance, a macro # `#define`d as `ALIGNED(n)` in `header` which translates to # `__attribute__((aligned(n)))` would be a reasonable value if targeting # gcc/clang. # # default: `#[repr(align(n))]` types will be treated as opaque, since it # could be unsafe for C callers to use a incorrectly-aligned union. aligned_n = "ALIGNED" [fn] # An optional prefix to put before every function declaration # default: no prefix added prefix = "WR_START_FUNC" # An optional postfix to put after any function declaration # default: no postix added postfix = "WR_END_FUNC" # How to format function arguments # # possible values: # * "horizontal": place all arguments on the same line # * "vertical": place each argument on its own line # * "auto": only use vertical if horizontal would exceed line_length # # default: "auto" args = "horizontal" # An optional string that should prefix function declarations which have been # marked as `#[must_use]`. For instance, "__attribute__((warn_unused_result))" # would be a reasonable value if targeting gcc/clang. A more portable solution # would involve emitting the name of a macro which you define in a # platform-specific way. e.g. "MUST_USE_FUNC" # default: nothing is emitted for must_use functions must_use = "MUST_USE_FUNC" # An optional string that should prefix function declarations which have been # marked as `#[deprecated]` without note. For instance, "__attribute__((deprecated))" # would be a reasonable value if targeting gcc/clang. A more portable solution # would involve emitting the name of a macro which you define in a # platform-specific way. e.g. "DEPRECATED_FUNC" # default: nothing is emitted for deprecated functions deprecated = "DEPRECATED_FUNC" # An optional string that should prefix function declarations which have been # marked as `#[deprecated(note = "reason")]`. `{}` will be replaced with the # double-quoted string. For instance, "__attribute__((deprecated({})))" # would be a reasonable value if targeting gcc/clang. A more portable solution # would involve emitting the name of a macro which you define in a # platform-specific way. e.g. "DEPRECATED_FUNC_WITH_NOTE(note)" # default: nothing is emitted for deprecated functions deprecated_with_notes = "DEPRECATED_FUNC_WITH_NOTE" # An optional string that will be used in the attribute position for functions # that don't return (that return `!` in Rust). # # For instance, `__attribute__((noreturn))` would be a reasonable value if # targeting gcc/clang. no_return = "NO_RETURN" # An optional string that, if present, will be used to generate Swift function # and method signatures for generated functions, for example "CF_SWIFT_NAME". # If no such macro is available in your toolchain, you can define one using the # `header` option in cbindgen.toml # default: no swift_name function attributes are generated swift_name_macro = "CF_SWIFT_NAME" # A rule to use to rename function argument names. The renaming assumes the input # is the Rust standard snake_case, however it accepts all the different rename_args # inputs. This means many options here are no-ops or redundant. # # possible values (that actually do something): # * "CamelCase": my_arg => myArg # * "PascalCase": my_arg => MyArg # * "GeckoCase": my_arg => aMyArg # * "ScreamingSnakeCase": my_arg => MY_ARG # * "None": apply no renaming # # technically possible values (that shouldn't have a purpose here): # * "SnakeCase": apply no renaming # * "LowerCase": apply no renaming (actually applies to_lowercase, is this bug?) # * "UpperCase": same as ScreamingSnakeCase in this context # * "QualifiedScreamingSnakeCase" => same as ScreamingSnakeCase in this context # # default: "None" rename_args = "PascalCase" # This rule specifies the order in which functions will be sorted. # # "Name": sort by the name of the function # "None": keep order in which the functions have been parsed # # default: "None" sort_by = "Name" [struct] # A rule to use to rename struct field names. The renaming assumes the input is # the Rust standard snake_case, however it acccepts all the different rename_args # inputs. This means many options here are no-ops or redundant. # # possible values (that actually do something): # * "CamelCase": my_arg => myArg # * "PascalCase": my_arg => MyArg # * "GeckoCase": my_arg => mMyArg # * "ScreamingSnakeCase": my_arg => MY_ARG # * "None": apply no renaming # # technically possible values (that shouldn't have a purpose here): # * "SnakeCase": apply no renaming # * "LowerCase": apply no renaming (actually applies to_lowercase, is this bug?) # * "UpperCase": same as ScreamingSnakeCase in this context # * "QualifiedScreamingSnakeCase" => same as ScreamingSnakeCase in this context # # default: "None" rename_fields = "PascalCase" # An optional string that should come before the name of any struct which has been # marked as `#[must_use]`. For instance, "__attribute__((warn_unused))" # would be a reasonable value if targeting gcc/clang. A more portable solution # would involve emitting the name of a macro which you define in a # platform-specific way. e.g. "MUST_USE_STRUCT" # # default: nothing is emitted for must_use structs must_use = "MUST_USE_STRUCT" # An optional string that should come before the name of any struct which has been # marked as `#[deprecated]` without note. For instance, "__attribute__((deprecated))" # would be a reasonable value if targeting gcc/clang. A more portable solution # would involve emitting the name of a macro which you define in a # platform-specific way. e.g. "DEPRECATED_STRUCT" # default: nothing is emitted for deprecated structs deprecated = "DEPRECATED_STRUCT" # An optional string that should come before the name of any struct which has been # marked as `#[deprecated(note = "reason")]`. `{}` will be replaced with the # double-quoted string. For instance, "__attribute__((deprecated({})))" # would be a reasonable value if targeting gcc/clang. A more portable solution # would involve emitting the name of a macro which you define in a # platform-specific way. e.g. "DEPRECATED_STRUCT_WITH_NOTE(note)" # default: nothing is emitted for deprecated structs deprecated_with_notes = "DEPRECATED_STRUCT_WITH_NOTE" # Whether a Rust type with associated consts should emit those consts inside the # type's body. Otherwise they will be emitted trailing and with the type's name # prefixed. This does nothing if the target is C, or if # [const]allow_static_const = false # # default: false # associated_constants_in_body: false # Whether to derive a simple constructor that takes a value for every field. # default: false derive_constructor = true # Whether to derive an operator== for all structs # default: false derive_eq = false # Whether to derive an operator!= for all structs # default: false derive_neq = false # Whether to derive an operator< for all structs # default: false derive_lt = false # Whether to derive an operator<= for all structs # default: false derive_lte = false # Whether to derive an operator> for all structs # default: false derive_gt = false # Whether to derive an operator>= for all structs # default: false derive_gte = false [enum] # A rule to use to rename enum variants, and the names of any fields those # variants have. This should probably be split up into two separate options, but # for now, they're the same! See the documentation for `[struct]rename_fields` # for how this applies to fields. Renaming of the variant assumes that the input # is the Rust standard PascalCase. In the case of QualifiedScreamingSnakeCase, # it also assumed that the enum's name is PascalCase. # # possible values (that actually do something): # * "CamelCase": MyVariant => myVariant # * "SnakeCase": MyVariant => my_variant # * "ScreamingSnakeCase": MyVariant => MY_VARIANT # * "QualifiedScreamingSnakeCase": MyVariant => ENUM_NAME_MY_VARIANT # * "LowerCase": MyVariant => myvariant # * "UpperCase": MyVariant => MYVARIANT # * "None": apply no renaming # # technically possible values (that shouldn't have a purpose for the variants): # * "PascalCase": apply no renaming # * "GeckoCase": apply no renaming # # default: "None" rename_variants = "None" # Whether an extra "sentinel" enum variant should be added to all generated enums. # Firefox uses this for their IPC serialization library. # # WARNING: if the sentinel is ever passed into Rust, behaviour will be Undefined. # Rust does not know about this value, and will assume it cannot happen. # # default: false add_sentinel = false # Whether enum variant names should be prefixed with the name of the enum. # default: false prefix_with_name = false # Whether to emit enums using "enum class" when targeting C++. # default: true enum_class = true # Whether to generate static `::MyVariant(..)` constructors and `bool IsMyVariant()` # methods for enums with fields. # # default: false derive_helper_methods = false # Whether to generate `const MyVariant& AsMyVariant() const` methods for enums with fields. # default: false derive_const_casts = false # Whether to generate `MyVariant& AsMyVariant()` methods for enums with fields # default: false derive_mut_casts = false # The name of the macro/function to use for asserting `IsMyVariant()` in the body of # derived `AsMyVariant()` cast methods. # # default: "assert" (but also causes `` to be included by default) cast_assert_name = "MOZ_RELEASE_ASSERT" # An optional string that should come before the name of any enum which has been # marked as `#[must_use]`. For instance, "__attribute__((warn_unused))" # would be a reasonable value if targeting gcc/clang. A more portable solution # would involve emitting the name of a macro which you define in a # platform-specific way. e.g. "MUST_USE_ENUM" # # Note that this refers to the *output* type. That means this will not apply to an enum # with fields, as it will be emitted as a struct. `[struct]must_use` will apply there. # # default: nothing is emitted for must_use enums must_use = "MUST_USE_ENUM" # An optional string that should come before the name of any enum which has been # marked as `#[deprecated]` without note. For instance, "__attribute__((deprecated))" # would be a reasonable value if targeting gcc/clang. A more portable solution # would involve emitting the name of a macro which you define in a # platform-specific way. e.g. "DEPRECATED_ENUM" # default: nothing is emitted for deprecated enums deprecated = "DEPRECATED_ENUM" # An optional string that should come before the name of any enum which has been # marked as `#[deprecated(note = "reason")]`. `{}` will be replaced with the # double-quoted string. For instance, "__attribute__((deprecated({})))" # would be a reasonable value if targeting gcc/clang. A more portable solution # would involve emitting the name of a macro which you define in a # platform-specific way. e.g. "DEPRECATED_ENUM_WITH_NOTE(note)" # default: nothing is emitted for deprecated enums deprecated_with_notes = "DEPRECATED_ENUM_WITH_NOTE" # An optional string that should come after the name of any enum variant which has been # marked as `#[deprecated]` without note. For instance, "__attribute__((deprecated))" # would be a reasonable value if targeting gcc/clang. A more portable solution would # involve emitting the name of a macro which you define in a platform-specific # way. e.g. "DEPRECATED_ENUM_VARIANT" # default: nothing is emitted for deprecated enum variants deprecated_variant = "DEPRECATED_ENUM_VARIANT" # An optional string that should come after the name of any enum variant which has been # marked as `#[deprecated(note = "reason")]`. `{}` will be replaced with the # double-quoted string. For instance, "__attribute__((deprecated({})))" would be a # reasonable value if targeting gcc/clang. A more portable solution would involve # emitting the name of a macro which you define in a platform-specific # way. e.g. "DEPRECATED_ENUM_WITH_NOTE(note)" # default: nothing is emitted for deprecated enum variants deprecated_variant_with_notes = "DEPRECATED_ENUM_VARIANT_WITH_NOTE({})" # Whether enums with fields should generate destructors. This exists so that generic # enums can be properly instantiated with payloads that are C++ types with # destructors. This isn't necessary for structs because C++ has rules to # automatically derive the correct constructors and destructors for those types. # # Care should be taken with this option, as Rust and C++ cannot # properly interoperate with eachother's notions of destructors. Also, this may # change the ABI for the type. Either your destructor-full enums must live # exclusively within C++, or they must only be passed by-reference between # C++ and Rust. # # default: false derive_tagged_enum_destructor = false # Whether enums with fields should generate copy-constructor. See the discussion on # derive_tagged_enum_destructor for why this is both useful and very dangerous. # # default: false derive_tagged_enum_copy_constructor = false # Whether enums with fields should generate copy-assignment operators. # # This depends on also deriving copy-constructors, and it is highly encouraged # for this to be set to true. # # default: false derive_tagged_enum_copy_assignment = false # Whether enums with fields should generate an empty, private destructor. # This allows the auto-generated constructor functions to compile, if there are # non-trivially constructible members. This falls in the same family of # dangerousness as `derive_tagged_enum_copy_constructor` and co. # # default: false private_default_tagged_enum_constructor = false [const] # Whether a generated constant can be a static const in C++ mode. I have no # idea why you would turn this off. # # default: true allow_static_const = true # Whether a generated constant can be constexpr in C++ mode. # # default: true allow_constexpr = false # This rule specifies the order in which constants will be sorted. # # "Name": sort by the name of the constant # "None": keep order in which the constants have been parsed # # default: "None" sort_by = "Name" [macro_expansion] # Whether bindings should be generated for instances of the bitflags! macro. # default: false bitflags = true # Options for how your Rust library should be parsed [parse] # Whether to parse dependent crates and include their types in the output # default: false parse_deps = true # A white list of crate names that are allowed to be parsed. If this is defined, # only crates found in this list will ever be parsed. # # default: there is no whitelist (NOTE: this is the opposite of []) include = ["webrender", "webrender_traits"] # A black list of crate names that are not allowed to be parsed. # default: [] exclude = ["libc"] # Whether to use a new temporary target directory when running `rustc -Zunpretty=expanded`. # This may be required for some build processes. # # default: false clean = false # Which crates other than the top-level binding crate we should generate # bindings for. # # default: [] extra_bindings = ["my_awesome_dep"] [parse.expand] # A list of crate names that should be run through `cargo expand` before # parsing to expand any macros. Note that if a crate is named here, it # will always be parsed, even if the blacklist/whitelist says it shouldn't be. # # default: [] crates = ["euclid"] # If enabled, use the `--all-features` option when expanding. Ignored when # `features` is set. For backwards-compatibility, this is forced on if # `expand = ["euclid"]` shorthand is used. # # default: false all_features = false # When `all_features` is disabled and this is also disabled, use the # `--no-default-features` option when expanding. # # default: true default_features = true # A list of feature names that should be used when running `cargo expand`. This # combines with `default_features` like in your `Cargo.toml`. Note that the features # listed here are features for the current crate being built, *not* the crates # being expanded. The crate's `Cargo.toml` must take care of enabling the # appropriate features in its dependencies # # default: [] features = ["cbindgen"] [ptr] # An optional string to decorate all pointers that are # required to be non null. Nullability is inferred from the Rust type: `&T`, # `&mut T` and `NonNull` all require a valid pointer value. non_null_attribute = "_Nonnull" # Options specific to Cython bindings. [cython] # Header specified in the top level `cdef extern from header:` declaration. # # default: * header = '"my_header.h"' # `from module cimport name1, name2` declarations added in the same place # where you'd get includes in C. [cython.cimports] module = ["name1", "name2"] ``` [reference]: https://doc.rust-lang.org/nightly/reference/type-layout.html#representations [really-tagged-unions]: https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md [section-cfgs]: #defines-and-cfgs [file-it]: https://github.com/mozilla/cbindgen/issues/new cbindgen-0.27.0/internals.md000064400000000000000000000044671046102023000140170ustar 00000000000000## Overview `cbindgen` works in four phases: 1. *Parsing* - Crate information is gathered from `cargo`, and `rust` source files are read using `syn` 1. *Loading* - `syn` AST nodes are converted into an IR of `Item`s that loosely correspond to the C types that will be output 1. *Transformation* - Several passes are run that transform the IR. Some examples: - Generic `type` aliases are used to specialize the type they refer to - Annotations are transferred from `type` aliases to the item they refer to - `Option<&T>` is converted to `*const T` - `Option<&mut T>` is converted to `*mut T` - Generic paths in struct fields, union variants, and static globals are collected and used to generate monomorphs of the structs or unions they refer to - The items are sorted by dependencies and type and unused items are filtered out 1. *Writing* - The IR is pretty printed to a file or `stdout` ## Process Flow The main interface for `cbindgen` is `bindgen::Builder` which accepts configuration options and either a crate directory to parse or a list of source files. If a list of source files is given, then `bindgen::Builder` will parse them using `parser::parse_src` which will use `syn` to parse a specific file. No `extern crate` items will be followed for dependencies, but `mod` items will be attempted to be followed. If a crate directory is given, then `bindgen::Builder` will use `cargo::Cargo` to load a dependency graph from `Cargo.toml`, `Cargo.lock`, and `cargo metadata`. Then `parser::parse_lib` will parse each crate, following `extern crate` items when `ParseConfig::parse_deps` is enabled and the crate is not in the whitelist or blacklist of crates. In addition `bindgen::Parser` may use `cargo expand` on a crate to expand macro definitions. Once the `syn` nodes are collected by either method, they are given to `bindgen::Parse` which will perform *Loading* by creating a `ir::Item` for each `syn` node as appropriate. `bindgen::Builder` will then convert the resulting `bindgen::Parse`'s into a `bindgen::Library` which is the driver of all of the *Transformation* passes. // TODO - Talk more about passes Then finally the `bindgen::Library` will create a `bindgen::Bindings` which contains the `ir::Item`'s that are ready to be written. The `bindgen::Bindings` can then be written to `stdout` or a specific file. cbindgen-0.27.0/rust-toolchain.toml000064400000000000000000000000371046102023000153330ustar 00000000000000[toolchain] channel = "nightly"cbindgen-0.27.0/src/bindgen/bindings.rs000064400000000000000000000170451046102023000160320ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::borrow::Cow; use std::cell::RefCell; use std::collections::HashMap; use std::fs; use std::fs::File; use std::io::{Read, Write}; use std::path; use std::rc::Rc; use crate::bindgen::config::{Config, Language}; use crate::bindgen::ir::{ Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Type, Typedef, }; use crate::bindgen::language_backend::{ CLikeLanguageBackend, CythonLanguageBackend, LanguageBackend, }; use crate::bindgen::writer::SourceWriter; /// A bindings header that can be written. pub struct Bindings { pub config: Config, /// The map from path to struct, used to lookup whether a given type is a /// transparent struct. This is needed to generate code for constants. struct_map: ItemMap, typedef_map: ItemMap, struct_fileds_memo: RefCell>>>, pub globals: Vec, pub constants: Vec, pub items: Vec, pub functions: Vec, source_files: Vec, /// Bindings are generated by a recursive call to cbindgen /// and shouldn't do anything when written anywhere. noop: bool, pub package_version: String, } impl Bindings { #[allow(clippy::too_many_arguments)] pub(crate) fn new( config: Config, struct_map: ItemMap, typedef_map: ItemMap, constants: Vec, globals: Vec, items: Vec, functions: Vec, source_files: Vec, noop: bool, package_version: String, ) -> Bindings { Bindings { config, struct_map, typedef_map, struct_fileds_memo: Default::default(), globals, constants, items, functions, source_files, noop, package_version, } } // FIXME(emilio): What to do when the configuration doesn't match? pub fn struct_is_transparent(&self, path: &BindgenPath) -> bool { let mut any = false; self.struct_map.for_items(path, |s| any |= s.is_transparent); any } /// Peels through typedefs to allow resolving structs. fn resolved_struct_path<'a>(&self, path: &'a BindgenPath) -> Cow<'a, BindgenPath> { let mut resolved_path = Cow::Borrowed(path); loop { let mut found = None; self.typedef_map.for_items(&resolved_path, |item| { if let Type::Path(ref p) = item.aliased { found = Some(p.path().clone()); } }); resolved_path = match found { Some(p) => Cow::Owned(p), None => break, } } resolved_path } pub fn struct_exists(&self, path: &BindgenPath) -> bool { let mut any = false; self.struct_map .for_items(&self.resolved_struct_path(path), |_| any = true); any } pub fn struct_field_names(&self, path: &BindgenPath) -> Rc> { let mut memos = self.struct_fileds_memo.borrow_mut(); if let Some(memo) = memos.get(path) { return memo.clone(); } let resolved_path = self.resolved_struct_path(path); let mut fields = Vec::::new(); self.struct_map.for_items(&resolved_path, |st| { let mut pos: usize = 0; for field in &st.fields { if let Some(found_pos) = fields.iter().position(|v| *v == field.name) { pos = found_pos + 1; } else { fields.insert(pos, field.name.clone()); pos += 1; } } }); let fields = Rc::new(fields); memos.insert(path.clone(), fields.clone()); if let Cow::Owned(p) = resolved_path { memos.insert(p, fields.clone()); } fields } pub fn generate_depfile>(&self, header_path: P, depfile_path: P) { if let Some(dir) = depfile_path.as_ref().parent() { if !dir.exists() { std::fs::create_dir_all(dir).unwrap() } } let canon_header_path = header_path.as_ref().canonicalize().unwrap(); let mut canon_source_files: Vec<_> = self .source_files .iter() .chain(self.config.config_path.as_ref()) .map(|p| p.canonicalize().unwrap()) .collect(); // Sorting makes testing easier by ensuring the output is ordered. canon_source_files.sort_unstable(); // When writing the depfile we must escape whitespace in paths to avoid it being interpreted // as a seperator. // It is not clear how to otherwise _correctly_ replace whitespace in a non-unicode // compliant slice, without knowing the encoding, so we lossy convert such cases, // to avoid panics. let mut depfile = File::create(depfile_path).unwrap(); write!( &mut depfile, "{}:", canon_header_path.to_string_lossy().replace(' ', "\\ ") ) .expect("Writing header name to depfile failed"); canon_source_files.into_iter().for_each(|source_file| { // Add line-continue and line-break and then indent with 4 spaces. // This makes the output more human-readable. depfile.write_all(b" \\\n ").unwrap(); let escaped_path = source_file.to_string_lossy().replace(' ', "\\ "); depfile.write_all(escaped_path.as_bytes()).unwrap(); }); writeln!(&mut depfile).unwrap(); depfile.flush().unwrap(); } pub fn write_to_file>(&self, path: P) -> bool { if self.noop { return false; } // Don't compare files if we've never written this file before if !path.as_ref().is_file() { if let Some(parent) = path::Path::new(path.as_ref()).parent() { fs::create_dir_all(parent).unwrap(); } self.write(File::create(path).unwrap()); return true; } let mut new_file_contents = Vec::new(); self.write(&mut new_file_contents); let mut old_file_contents = Vec::new(); { let mut old_file = File::open(&path).unwrap(); old_file.read_to_end(&mut old_file_contents).unwrap(); } if old_file_contents != new_file_contents { let mut new_file = File::create(&path).unwrap(); new_file.write_all(&new_file_contents).unwrap(); true } else { false } } pub fn write(&self, file: F) { match self.config.language { Language::Cxx | Language::C => { self.write_with_backend(file, &mut CLikeLanguageBackend::new(&self.config)) } Language::Cython => { self.write_with_backend(file, &mut CythonLanguageBackend::new(&self.config)) } } } fn write_with_backend( &self, file: F, language_backend: &mut LB, ) { if self.noop { return; } let mut out = SourceWriter::new(file, self); language_backend.write_bindings(&mut out, self); } } cbindgen-0.27.0/src/bindgen/bitflags.rs000064400000000000000000000202051046102023000160200ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use proc_macro2::TokenStream; use std::collections::HashSet; use syn::fold::Fold; use syn::parse::{Parse, ParseStream, Parser, Result as ParseResult}; // $(#[$outer:meta])* // ($($vis:tt)*) $BitFlags:ident: $T:ty { // $( // $(#[$inner:ident $($args:tt)*])* // const $Flag:ident = $value:expr; // )+ // } #[derive(Debug)] pub struct BitflagsStruct { attrs: Vec, vis: syn::Visibility, #[allow(dead_code)] struct_token: Token![struct], name: syn::Ident, #[allow(dead_code)] colon_token: Token![:], repr: syn::Type, flags: Flags, } // impl $BitFlags:ident: $T:ty { // $( // $(#[$inner:ident $($args:tt)*])* // const $Flag:ident = $value:expr; // )+ // } #[derive(Debug)] pub struct BitflagsImpl { #[allow(dead_code)] impl_token: Token![impl], name: syn::Ident, #[allow(dead_code)] colon_token: Token![:], repr: syn::Type, flags: Flags, } #[derive(Debug)] pub enum Bitflags { Struct(BitflagsStruct), Impl(BitflagsImpl), } impl Bitflags { pub fn expand(&self) -> (Option, syn::ItemImpl) { match self { Bitflags::Struct(BitflagsStruct { attrs, vis, name, repr, flags, .. }) => { let struct_ = parse_quote! { #(#attrs)* #vis struct #name { bits: #repr, } }; let consts = flags.expand(name, repr, false); let impl_ = parse_quote! { impl #name { #consts } }; (Some(struct_), impl_) } Bitflags::Impl(BitflagsImpl { name, repr, flags, .. }) => { let consts = flags.expand(name, repr, true); let impl_: syn::ItemImpl = parse_quote! { impl #name { #consts } }; (None, impl_) } } } } impl Parse for Bitflags { fn parse(input: ParseStream) -> ParseResult { Ok(if input.peek(Token![impl]) { Self::Impl(BitflagsImpl { impl_token: input.parse()?, name: input.parse()?, colon_token: input.parse()?, repr: input.parse()?, flags: input.parse()?, }) } else { Self::Struct(BitflagsStruct { attrs: input.call(syn::Attribute::parse_outer)?, vis: input.parse()?, struct_token: input.parse()?, name: input.parse()?, colon_token: input.parse()?, repr: input.parse()?, flags: input.parse()?, }) }) } } // $(#[$inner:ident $($args:tt)*])* // const $Flag:ident = $value:expr; #[derive(Debug)] struct Flag { attrs: Vec, #[allow(dead_code)] const_token: Token![const], name: syn::Ident, #[allow(dead_code)] equals_token: Token![=], value: syn::Expr, #[allow(dead_code)] semicolon_token: Token![;], } struct FlagValueFold<'a> { struct_name: &'a syn::Ident, flag_names: &'a HashSet, out_of_line: bool, } impl<'a> FlagValueFold<'a> { fn is_self(&self, ident: &syn::Ident) -> bool { ident == self.struct_name || ident == "Self" } } impl<'a> Fold for FlagValueFold<'a> { fn fold_expr(&mut self, node: syn::Expr) -> syn::Expr { // bitflags 2 doesn't expose `bits` publically anymore, and the documented way to // combine flags is using the `bits` method, e.g. // ``` // bitflags! { // struct Flags: u8 { // const A = 1; // const B = 1 << 1; // const AB = Flags::A.bits() | Flags::B.bits(); // } // } // ``` // As we're transforming the struct definition into `struct StructName { bits: T }` // as far as our bindings generation is concerned, `bits` is available as a field, // so by replacing `StructName::FLAG.bits()` with `StructName::FLAG.bits`, we make // e.g. `Flags::AB` available in the generated bindings. // For out-of-line definitions of the struct(*), where the struct is defined as a // newtype, we replace it with `StructName::FLAGS.0`. // * definitions like: // ``` // struct Flags(u8); // bitflags! { // impl Flags: u8 { // const A = 1; // const B = 1 << 1; // const AB = Flags::A.bits() | Flags::B.bits(); // } // } // ``` match node { syn::Expr::MethodCall(syn::ExprMethodCall { attrs, receiver, dot_token, method, args, .. }) if method == "bits" && args.is_empty() && matches!(&*receiver, syn::Expr::Path(syn::ExprPath { path, .. }) if path.segments.len() == 2 && self.is_self(&path.segments.first().unwrap().ident) && self .flag_names .contains(&path.segments.last().unwrap().ident.to_string())) => { return syn::Expr::Field(syn::ExprField { attrs, base: receiver, dot_token, member: if self.out_of_line { syn::Member::Unnamed(parse_quote! {0}) } else { syn::Member::Named(method) }, }); } _ => {} } syn::fold::fold_expr(self, node) } } impl Flag { fn expand( &self, struct_name: &syn::Ident, repr: &syn::Type, flag_names: &HashSet, out_of_line: bool, ) -> TokenStream { let Flag { ref attrs, ref name, ref value, .. } = *self; let folded_value = FlagValueFold { struct_name, flag_names, out_of_line, } .fold_expr(value.clone()); let value = if out_of_line { quote! { ((#folded_value) as #repr) } } else { quote! { { bits: (#folded_value) as #repr } } }; quote! { #(#attrs)* pub const #name : #struct_name = #struct_name #value; } } } impl Parse for Flag { fn parse(input: ParseStream) -> ParseResult { Ok(Self { attrs: input.call(syn::Attribute::parse_outer)?, const_token: input.parse()?, name: input.parse()?, equals_token: input.parse()?, value: input.parse()?, semicolon_token: input.parse()?, }) } } #[derive(Debug)] struct Flags(Vec); impl Parse for Flags { fn parse(input: ParseStream) -> ParseResult { let content; let _ = braced!(content in input); let mut flags = vec![]; while !content.is_empty() { flags.push(content.parse()?); } Ok(Flags(flags)) } } impl Flags { fn expand(&self, struct_name: &syn::Ident, repr: &syn::Type, out_of_line: bool) -> TokenStream { let mut ts = quote! {}; let flag_names = self .0 .iter() .map(|flag| flag.name.to_string()) .collect::>(); for flag in &self.0 { ts.extend(flag.expand(struct_name, repr, &flag_names, out_of_line)); } ts } } pub fn parse(tokens: TokenStream) -> ParseResult { let parser = Bitflags::parse; parser.parse2(tokens) } cbindgen-0.27.0/src/bindgen/builder.rs000064400000000000000000000306101046102023000156540ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::path; use crate::bindgen::bindings::Bindings; use crate::bindgen::cargo::Cargo; use crate::bindgen::config::{Braces, Config, Language, Profile, Style}; use crate::bindgen::error::Error; use crate::bindgen::library::Library; use crate::bindgen::parser::{self, Parse}; /// A builder for generating a bindings header. #[derive(Debug, Clone)] pub struct Builder { config: Config, srcs: Vec, lib: Option<(path::PathBuf, Option)>, lib_cargo: Option, std_types: bool, lockfile: Option, } impl Builder { #[allow(clippy::new_without_default)] pub fn new() -> Builder { Builder { config: Config::default(), srcs: Vec::new(), lib: None, lib_cargo: None, std_types: true, lockfile: None, } } #[allow(unused)] pub fn with_header>(mut self, header: S) -> Builder { self.config.header = Some(String::from(header.as_ref())); self } #[allow(unused)] pub fn with_no_includes(mut self) -> Builder { self.config.no_includes = true; self } #[allow(unused)] pub fn with_include>(mut self, include: S) -> Builder { self.config.includes.push(String::from(include.as_ref())); self } #[allow(unused)] pub fn with_sys_include>(mut self, include: S) -> Builder { self.config .sys_includes .push(String::from(include.as_ref())); self } #[allow(unused)] pub fn with_after_include>(mut self, line: S) -> Builder { self.config.after_includes = Some(String::from(line.as_ref())); self } #[allow(unused)] pub fn with_trailer>(mut self, trailer: S) -> Builder { self.config.trailer = Some(String::from(trailer.as_ref())); self } #[allow(unused)] pub fn with_include_guard>(mut self, include_guard: S) -> Builder { self.config.include_guard = Some(String::from(include_guard.as_ref())); self } #[allow(unused)] pub fn with_pragma_once(mut self, pragma_once: bool) -> Builder { self.config.pragma_once = pragma_once; self } #[allow(unused)] pub fn with_autogen_warning>(mut self, autogen_warning: S) -> Builder { self.config.autogen_warning = Some(String::from(autogen_warning.as_ref())); self } #[allow(unused)] pub fn with_include_version(mut self, include_version: bool) -> Builder { self.config.include_version = include_version; self } #[allow(unused)] pub fn with_namespace>(mut self, namespace: S) -> Builder { self.config.namespace = Some(String::from(namespace.as_ref())); self } #[allow(unused)] pub fn with_namespaces>(mut self, namespaces: &[S]) -> Builder { self.config.namespaces = Some( namespaces .iter() .map(|x| String::from(x.as_ref())) .collect(), ); self } #[allow(unused)] pub fn with_using_namespaces>(mut self, namespaces: &[S]) -> Builder { self.config.using_namespaces = Some( namespaces .iter() .map(|x| String::from(x.as_ref())) .collect(), ); self } #[allow(unused)] pub fn with_braces(mut self, braces: Braces) -> Builder { self.config.braces = braces; self } #[allow(unused)] pub fn with_line_length(mut self, line_length: usize) -> Builder { self.config.line_length = line_length; self } #[allow(unused)] pub fn with_tab_width(mut self, tab_width: usize) -> Builder { self.config.tab_width = tab_width; self } #[allow(unused)] pub fn with_language(mut self, language: Language) -> Builder { self.config.language = language; self } #[allow(unused)] pub fn with_cpp_compat(mut self, cpp_compat: bool) -> Builder { self.config.cpp_compat = cpp_compat; self } #[allow(unused)] pub fn with_style(mut self, style: Style) -> Builder { self.config.style = style; self } #[allow(unused)] pub fn include_item>(mut self, item_name: S) -> Builder { self.config .export .include .push(String::from(item_name.as_ref())); self } #[allow(unused)] pub fn exclude_item>(mut self, item_name: S) -> Builder { self.config .export .exclude .push(String::from(item_name.as_ref())); self } #[allow(unused)] pub fn rename_item>(mut self, from: S, to: S) -> Builder { self.config .export .rename .insert(String::from(from.as_ref()), String::from(to.as_ref())); self } #[allow(unused)] pub fn with_item_prefix>(mut self, prefix: S) -> Builder { self.config.export.prefix = Some(String::from(prefix.as_ref())); self } #[allow(unused)] pub fn with_parse_deps(mut self, parse_deps: bool) -> Builder { self.config.parse.parse_deps = parse_deps; self } #[allow(unused)] pub fn with_parse_include>(mut self, include: &[S]) -> Builder { self.config.parse.include = Some(include.iter().map(|x| String::from(x.as_ref())).collect()); self } #[allow(unused)] pub fn with_parse_exclude>(mut self, exclude: &[S]) -> Builder { self.config.parse.exclude = exclude.iter().map(|x| String::from(x.as_ref())).collect(); self } #[allow(unused)] pub fn with_parse_expand>(mut self, expand: &[S]) -> Builder { self.config.parse.expand.crates = expand.iter().map(|x| String::from(x.as_ref())).collect(); self } #[allow(unused)] pub fn with_parse_expand_all_features(mut self, expand_all_features: bool) -> Builder { self.config.parse.expand.all_features = expand_all_features; self } #[allow(unused)] pub fn with_parse_expand_default_features(mut self, expand_default_features: bool) -> Builder { self.config.parse.expand.default_features = expand_default_features; self } #[allow(unused)] pub fn with_parse_expand_features>(mut self, expand_features: &[S]) -> Builder { self.config.parse.expand.features = Some( expand_features .iter() .map(|x| String::from(x.as_ref())) .collect(), ); self } #[allow(unused)] pub fn with_parse_expand_profile(mut self, profile: Profile) -> Builder { self.config.parse.expand.profile = profile; self } #[allow(unused)] pub fn with_parse_extra_bindings>(mut self, extra_bindings: &[S]) -> Builder { self.config.parse.extra_bindings = extra_bindings .iter() .map(|x| String::from(x.as_ref())) .collect(); self } #[allow(unused)] pub fn with_only_target_dependencies(mut self, only_target_dependencies: bool) -> Builder { self.config.only_target_dependencies = only_target_dependencies; self } #[allow(unused)] pub fn with_documentation(mut self, documentation: bool) -> Builder { self.config.documentation = documentation; self } #[allow(unused)] pub fn with_target_os_define(mut self, platform: &str, preprocessor_define: &str) -> Builder { self.config.defines.insert( format!("target_os = {}", platform), preprocessor_define.to_owned(), ); self } #[allow(unused)] pub fn with_define(mut self, key: &str, value: &str, preprocessor_define: &str) -> Builder { self.config.defines.insert( format!("{} = {}", key, value), preprocessor_define.to_owned(), ); self } #[allow(unused)] pub fn with_config(mut self, config: Config) -> Builder { self.config = config; self } #[allow(unused)] pub fn with_std_types(mut self, std_types: bool) -> Builder { self.std_types = std_types; self } #[allow(unused)] pub fn with_src>(mut self, src: P) -> Builder { self.srcs.push(src.as_ref().to_owned()); self } #[allow(unused)] pub fn with_crate>(mut self, lib_dir: P) -> Builder { debug_assert!(self.lib.is_none()); debug_assert!(self.lib_cargo.is_none()); self.lib = Some((path::PathBuf::from(lib_dir.as_ref()), None)); self } #[allow(unused)] pub fn with_crate_and_name, S: AsRef>( mut self, lib_dir: P, binding_lib_name: S, ) -> Builder { debug_assert!(self.lib.is_none()); debug_assert!(self.lib_cargo.is_none()); self.lib = Some(( path::PathBuf::from(lib_dir.as_ref()), Some(String::from(binding_lib_name.as_ref())), )); self } #[allow(unused)] pub(crate) fn with_cargo(mut self, lib: Cargo) -> Builder { debug_assert!(self.lib.is_none()); debug_assert!(self.lib_cargo.is_none()); self.lib_cargo = Some(lib); self } #[allow(unused)] pub fn with_lockfile>(mut self, lockfile: P) -> Builder { debug_assert!(self.lockfile.is_none()); debug_assert!(self.lib_cargo.is_none()); self.lockfile = Some(path::PathBuf::from(lockfile.as_ref())); self } pub fn generate(self) -> Result { // If macro expansion is enabled, then cbindgen will attempt to build the crate // and will run its build script which may run cbindgen again. That second run may start // infinite recursion, or overwrite previously written files with bindings. // So if we are called recursively, we are skipping the whole generation // and produce "noop" bindings that won't be able to overwrite anything. if std::env::var("_CBINDGEN_IS_RUNNING").is_ok() { return Ok(Bindings::new( self.config, Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), true, String::new(), )); } let mut result = Parse::new(); if self.std_types { result.add_std_types(); } for x in &self.srcs { result.extend_with(&parser::parse_src(x, &self.config)?); } if let Some((lib_dir, binding_lib_name)) = self.lib.clone() { let lockfile = self.lockfile.as_deref(); let cargo = Cargo::load( &lib_dir, lockfile, binding_lib_name.as_deref(), self.config.parse.parse_deps, self.config.parse.clean, self.config.only_target_dependencies, /* existing_metadata = */ None, )?; result.extend_with(&parser::parse_lib(cargo, &self.config)?); } else if let Some(cargo) = self.lib_cargo.clone() { result.extend_with(&parser::parse_lib(cargo, &self.config)?); } result.source_files.extend_from_slice(self.srcs.as_slice()); Library::new( self.config, result.constants, result.globals, result.enums, result.structs, result.unions, result.opaque_items, result.typedefs, result.functions, result.source_files, result.package_version, ) .generate() } } #[cfg(test)] mod tests { use super::*; #[test] fn with_style() { assert_eq!( Style::Tag, Builder::new().with_style(Style::Tag).config.style ); } } cbindgen-0.27.0/src/bindgen/cargo/cargo.rs000064400000000000000000000231531046102023000164200ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::path::{Path, PathBuf}; use crate::bindgen::cargo::cargo_expand; use crate::bindgen::cargo::cargo_lock::{self, Lock}; pub(crate) use crate::bindgen::cargo::cargo_metadata::PackageRef; use crate::bindgen::cargo::cargo_metadata::{self, Metadata}; use crate::bindgen::cargo::cargo_toml; use crate::bindgen::config::Profile; use crate::bindgen::error::Error; use crate::bindgen::ir::Cfg; /// Parse a dependency string used in Cargo.lock fn parse_dep_string(dep_string: &str) -> (&str, Option<&str>) { let split: Vec<&str> = dep_string.split_whitespace().collect(); (split[0], split.get(1).cloned()) } /// A collection of metadata for a library from cargo. #[derive(Clone, Debug)] pub(crate) struct Cargo { manifest_path: PathBuf, binding_crate_name: String, lock: Option, metadata: Metadata, clean: bool, } impl Cargo { /// Gather metadata from cargo for a specific library and binding crate /// name. If dependency finding isn't needed then Cargo.lock files don't /// need to be parsed. pub(crate) fn load( crate_dir: &Path, lock_file: Option<&Path>, binding_crate_name: Option<&str>, use_cargo_lock: bool, clean: bool, only_target_dependencies: bool, existing_metadata_file: Option<&Path>, ) -> Result { let toml_path = crate_dir.join("Cargo.toml"); let metadata = cargo_metadata::metadata(&toml_path, existing_metadata_file, only_target_dependencies) .map_err(|x| Error::CargoMetadata(toml_path.to_str().unwrap().to_owned(), x))?; let lock_path = lock_file .map(PathBuf::from) .unwrap_or_else(|| Path::new(&metadata.workspace_root).join("Cargo.lock")); let lock = if use_cargo_lock { match cargo_lock::lock(&lock_path) { Ok(lock) => Some(lock), Err(x) => { warn!("Couldn't load lock file {:?}: {:?}", lock_path, x); None } } } else { None }; // Use the specified binding crate name or infer it from the manifest let binding_crate_name = match binding_crate_name { Some(s) => s.to_owned(), None => { let manifest = cargo_toml::manifest(&toml_path) .map_err(|x| Error::CargoToml(toml_path.to_str().unwrap().to_owned(), x))?; manifest.package.name } }; Ok(Cargo { manifest_path: toml_path, binding_crate_name, lock, metadata, clean, }) } pub(crate) fn binding_crate_name(&self) -> &str { &self.binding_crate_name } pub(crate) fn binding_crate_ref(&self) -> PackageRef { match self.find_pkg_to_generate_bindings_ref(&self.binding_crate_name) { Some(pkg_ref) => pkg_ref, None => panic!( "Unable to find {} for {:?}", self.binding_crate_name, self.manifest_path ), } } pub(crate) fn dependencies(&self, package: &PackageRef) -> Vec<(PackageRef, Option)> { let lock = match self.lock { Some(ref lock) => lock, None => return vec![], }; let mut dependencies = None; // Find the dependencies listing in the lockfile if let Some(ref root) = lock.root { // If the version is not on the lockfile then it shouldn't be // ambiguous. if root.name == package.name && package .version .as_ref() .map_or(true, |v| *v == root.version) { dependencies = root.dependencies.as_ref(); } } if dependencies.is_none() { if let Some(ref lock_packages) = lock.package { for lock_package in lock_packages { if lock_package.name == package.name && package .version .as_ref() .map_or(true, |v| *v == lock_package.version) { dependencies = lock_package.dependencies.as_ref(); break; } } } } if dependencies.is_none() { return vec![]; } dependencies .unwrap() .iter() .map(|dep| { let (dep_name, dep_version) = parse_dep_string(dep); // If a version was not specified find the only package with the name of the dependency let dep_version = dep_version.or_else(|| { let mut versions = self.metadata.packages.iter().filter_map(|package| { if package.name_and_version.name != dep_name { return None; } package.name_and_version.version.as_deref() }); // If the iterator contains more items, meaning multiple versions of the same // package are present, warn! amd abort. let version = versions.next(); if versions.next().is_none() { version } else { warn!("when looking for a version for package {}, multiple versions where found", dep_name); None } }); // Try to find the cfgs in the Cargo.toml let cfg = self .metadata .packages .get(package) .and_then(|meta_package| meta_package.dependencies.get(dep_name)) .and_then(Cfg::load_metadata); let package_ref = PackageRef { name: dep_name.to_owned(), version: dep_version.map(|v| v.to_owned()), }; (package_ref, cfg) }) .collect() } /// Finds the package reference for which we want to generate bindings in `cargo metadata` /// matching on `package_name` and verifying the manifest path matches so that we don't get a /// a dependency with the same name (fix for https://github.com/mozilla/cbindgen/issues/900) fn find_pkg_to_generate_bindings_ref(&self, package_name: &str) -> Option { // Keep a list of candidates in case the manifest check fails, so that the old behavior // still applies, returning the first package that was found let mut candidates = vec![]; for package in &self.metadata.packages { if package.name_and_version.name == package_name { // If we are sure it is the right package return it if Path::new(package.manifest_path.as_str()) == self.manifest_path { return Some(package.name_and_version.clone()); } // Otherwise note that a package was found candidates.push(package.name_and_version.clone()); } } // If we could not verify the manifest path but we found candidates return the first one if // any, this is the old behavior which did not check for manifest path, kept for backwards // compatibility candidates.into_iter().next() } /// Finds the directory for a specified package reference. #[allow(unused)] pub(crate) fn find_crate_dir(&self, package: &PackageRef) -> Option { self.metadata .packages .get(package) .and_then(|meta_package| { Path::new(&meta_package.manifest_path) .parent() .map(|x| x.to_owned()) }) } /// Finds `src/lib.rs` for a specified package reference. pub(crate) fn find_crate_src(&self, package: &PackageRef) -> Option { let kind_lib = String::from("lib"); let kind_staticlib = String::from("staticlib"); let kind_rlib = String::from("rlib"); let kind_cdylib = String::from("cdylib"); let kind_dylib = String::from("dylib"); self.metadata .packages .get(package) .and_then(|meta_package| { for target in &meta_package.targets { if target.kind.contains(&kind_lib) || target.kind.contains(&kind_staticlib) || target.kind.contains(&kind_rlib) || target.kind.contains(&kind_cdylib) || target.kind.contains(&kind_dylib) { return Some(PathBuf::from(&target.src_path)); } } None }) } pub(crate) fn expand_crate( &self, package: &PackageRef, expand_all_features: bool, expand_default_features: bool, expand_features: &Option>, profile: Profile, ) -> Result { cargo_expand::expand( &self.manifest_path, &package.name, package.version.as_deref(), self.clean, expand_all_features, expand_default_features, expand_features, profile, ) } } cbindgen-0.27.0/src/bindgen/cargo/cargo_expand.rs000064400000000000000000000106101046102023000177510ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::bindgen::config::Profile; use std::env; use std::error; use std::fmt; use std::io; use std::path::{Path, PathBuf}; use std::process::Command; use std::str::{from_utf8, Utf8Error}; extern crate tempfile; use self::tempfile::Builder; #[derive(Debug)] /// Possible errors that can occur during `rustc -Zunpretty=expanded`. pub enum Error { /// Error during creation of temporary directory Io(io::Error), /// Output of `cargo metadata` was not valid utf8 Utf8(Utf8Error), /// Error during execution of `cargo rustc -Zunpretty=expanded` Compile(String), } impl From for Error { fn from(err: io::Error) -> Self { Error::Io(err) } } impl From for Error { fn from(err: Utf8Error) -> Self { Error::Utf8(err) } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::Io(ref err) => err.fmt(f), Error::Utf8(ref err) => err.fmt(f), Error::Compile(ref err) => write!(f, "{}", err), } } } impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Error::Io(ref err) => Some(err), Error::Utf8(ref err) => Some(err), Error::Compile(..) => None, } } } /// Use rustc to expand and pretty print the crate into a single file, /// removing any macros in the process. #[allow(clippy::too_many_arguments)] pub fn expand( manifest_path: &Path, crate_name: &str, version: Option<&str>, use_tempdir: bool, expand_all_features: bool, expand_default_features: bool, expand_features: &Option>, profile: Profile, ) -> Result { let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo")); let mut cmd = Command::new(cargo); let mut _temp_dir = None; // drop guard if use_tempdir { _temp_dir = Some(Builder::new().prefix("cbindgen-expand").tempdir()?); cmd.env("CARGO_TARGET_DIR", _temp_dir.unwrap().path()); } else if let Ok(ref path) = env::var("CARGO_EXPAND_TARGET_DIR") { cmd.env("CARGO_TARGET_DIR", path); } else if let Ok(ref path) = env::var("OUT_DIR") { // When cbindgen was started programatically from a build.rs file, Cargo is running and // locking the default target directory. In this case we need to use another directory, // else we would end up in a deadlock. If Cargo is running `OUT_DIR` will be set, so we // can use a directory relative to that. cmd.env("CARGO_TARGET_DIR", PathBuf::from(path).join("expanded")); } // Set this variable so that we don't call it recursively if we expand a crate that is using // cbindgen cmd.env("_CBINDGEN_IS_RUNNING", "1"); cmd.arg("rustc"); cmd.arg("--lib"); // When build with the release profile we can't choose the `check` profile. if profile != Profile::Release { cmd.arg("--profile=check"); } cmd.arg("--manifest-path"); cmd.arg(manifest_path); if let Some(features) = expand_features { cmd.arg("--features"); let mut features_str = String::new(); for (index, feature) in features.iter().enumerate() { if index != 0 { features_str.push(' '); } features_str.push_str(feature); } cmd.arg(features_str); } if expand_all_features { cmd.arg("--all-features"); } if !expand_default_features { cmd.arg("--no-default-features"); } match profile { Profile::Debug => {} Profile::Release => { cmd.arg("--release"); } } cmd.arg("-p"); let mut package = crate_name.to_owned(); if let Some(version) = version { package.push(':'); package.push_str(version); } cmd.arg(&package); cmd.arg("--verbose"); cmd.arg("--"); cmd.arg("-Zunpretty=expanded"); info!("Command: {:?}", cmd); let output = cmd.output()?; let src = from_utf8(&output.stdout)?.to_owned(); let error = from_utf8(&output.stderr)?.to_owned(); if src.is_empty() { Err(Error::Compile(error)) } else { Ok(src) } } cbindgen-0.27.0/src/bindgen/cargo/cargo_lock.rs000064400000000000000000000025501046102023000174260ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::fs::File; use std::io; use std::io::Read; use std::path::Path; #[derive(Debug)] /// Possible errors that can occur during Cargo.toml parsing. pub enum Error { /// Error during reading of Cargo.toml #[allow(dead_code)] Io(io::Error), /// Deserialization error #[allow(dead_code)] Toml(toml::de::Error), } impl From for Error { fn from(err: io::Error) -> Self { Error::Io(err) } } impl From for Error { fn from(err: toml::de::Error) -> Self { Error::Toml(err) } } #[derive(Clone, Deserialize, Debug)] pub struct Lock { pub root: Option, pub package: Option>, } #[derive(Clone, Deserialize, Debug)] pub struct Package { pub name: String, pub version: String, /// A list of dependencies formatted like "NAME VERSION-OPT REGISTRY-OPT" pub dependencies: Option>, } /// Parse the Cargo.toml for a given path pub fn lock(manifest_path: &Path) -> Result { let mut s = String::new(); let mut f = File::open(manifest_path)?; f.read_to_string(&mut s)?; toml::from_str::(&s).map_err(|x| x.into()) } cbindgen-0.27.0/src/bindgen/cargo/cargo_metadata.rs000064400000000000000000000167331046102023000202660ustar 00000000000000#![deny(missing_docs)] #![allow(dead_code)] //! Structured access to the output of `cargo metadata` //! Usually used from within a `cargo-*` executable // Forked from `https://github.com/oli-obk/cargo_metadata` // Modifications: // 1. Remove `resolve` from Metadata because it was causing parse failures // 2. Fix the `manifest-path` argument // 3. Add `--all-features` argument // 4. Remove the `--no-deps` argument use std::borrow::{Borrow, Cow}; use std::collections::{HashMap, HashSet}; use std::env; use std::error; use std::fmt; use std::hash::{Hash, Hasher}; use std::io; use std::path::Path; use std::process::{Command, Output}; use std::str::Utf8Error; #[derive(Clone, Deserialize, Debug)] /// Starting point for metadata returned by `cargo metadata` pub struct Metadata { /// A list of all crates referenced by this crate (and the crate itself) pub packages: HashSet, version: usize, /// path to the workspace containing the `Cargo.lock` pub workspace_root: String, } /// A reference to a package including it's name and the specific version. #[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] pub struct PackageRef { pub name: String, pub version: Option, } #[derive(Clone, Deserialize, Debug)] /// A crate pub struct Package { #[serde(flatten)] pub name_and_version: PackageRef, id: String, source: Option, /// List of dependencies of this particular package pub dependencies: HashSet, /// Targets provided by the crate (lib, bin, example, test, ...) pub targets: Vec, features: HashMap>, /// path containing the `Cargo.toml` pub manifest_path: String, } #[derive(Clone, Deserialize, Debug)] /// A dependency of the main crate pub struct Dependency { /// Name as given in the `Cargo.toml` pub name: String, source: Option, /// Whether this is required or optional pub req: String, kind: Option, optional: bool, uses_default_features: bool, features: Vec, pub target: Option, } #[derive(Clone, Deserialize, Debug)] /// A single target (lib, bin, example, ...) provided by a crate pub struct Target { /// Name as given in the `Cargo.toml` or generated from the file name pub name: String, /// Kind of target ("bin", "example", "test", "bench", "lib") pub kind: Vec, /// Almost the same as `kind`, except when an example is a library instad of an executable. /// In that case `crate_types` contains things like `rlib` and `dylib` while `kind` is `example` #[serde(default)] pub crate_types: Vec, /// Path to the main source file of the target pub src_path: String, } #[derive(Debug)] /// Possible errors that can occur during metadata parsing. pub enum Error { /// Error during execution of `cargo metadata` Io(io::Error), /// Metadata extraction failure Metadata(Output), /// Output of `cargo metadata` was not valid utf8 Utf8(Utf8Error), /// Deserialization error (structure of json did not match expected structure) Json(serde_json::Error), } impl From for Error { fn from(err: io::Error) -> Self { Error::Io(err) } } impl From for Error { fn from(err: Utf8Error) -> Self { Error::Utf8(err) } } impl From for Error { fn from(err: serde_json::Error) -> Self { Error::Json(err) } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::Io(ref err) => err.fmt(f), Error::Metadata(_) => write!(f, "Metadata error"), Error::Utf8(ref err) => err.fmt(f), Error::Json(ref err) => err.fmt(f), } } } impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Error::Io(ref err) => Some(err), Error::Metadata(_) => None, Error::Utf8(ref err) => Some(err), Error::Json(ref err) => Some(err), } } } // Implementations that let us lookup Packages and Dependencies by name (string) impl Borrow for Package { fn borrow(&self) -> &PackageRef { &self.name_and_version } } impl Hash for Package { fn hash(&self, state: &mut H) { self.name_and_version.hash(state); } } impl PartialEq for Package { fn eq(&self, other: &Self) -> bool { self.name_and_version == other.name_and_version } } impl Eq for Package {} impl Borrow for Dependency { fn borrow(&self) -> &str { &self.name } } impl Hash for Dependency { fn hash(&self, state: &mut H) { self.name.hash(state); } } impl PartialEq for Dependency { fn eq(&self, other: &Self) -> bool { self.name == other.name } } impl Eq for Dependency {} fn discover_target(manifest_path: &Path) -> Option { if let Ok(target) = std::env::var("TARGET") { return Some(target); } // We must be running as a standalone script, not under cargo. // Let's use the host platform instead. // We figure out the host platform through rustc and use that. // We unfortunatelly cannot go through cargo, since cargo rustc _also_ builds. // If `rustc` fails to run, we just fall back to not passing --filter-platforms. // // NOTE: We set the current directory in case of rustup shenanigans. let rustc = env::var("RUSTC").unwrap_or_else(|_| String::from("rustc")); debug!("Discovering host platform by {:?}", rustc); let rustc_output = Command::new(rustc) .current_dir(manifest_path.parent().unwrap()) .arg("-vV") .output(); let rustc_output = match rustc_output { Ok(ref out) => String::from_utf8_lossy(&out.stdout), Err(..) => return None, }; let field = "host: "; rustc_output .lines() .find_map(|l| l.strip_prefix(field).map(|stripped| stripped.to_string())) } /// The main entry point to obtaining metadata pub fn metadata( manifest_path: &Path, existing_metadata_file: Option<&Path>, only_target: bool, ) -> Result { let output; let metadata = match existing_metadata_file { Some(path) => Cow::Owned(std::fs::read_to_string(path)?), None => { let target = if only_target { let target = discover_target(manifest_path); if target.is_none() { warn!( "Failed to discover host platform for cargo metadata; \ will fetch dependencies for all platforms." ); } target } else { None }; let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo")); let mut cmd = Command::new(cargo); cmd.arg("metadata"); cmd.arg("--all-features"); cmd.arg("--format-version").arg("1"); if let Some(target) = target { cmd.arg("--filter-platform").arg(target); } cmd.arg("--manifest-path"); cmd.arg(manifest_path); output = cmd.output()?; if !output.status.success() { return Err(Error::Metadata(output)); } Cow::Borrowed(std::str::from_utf8(&output.stdout)?) } }; let meta: Metadata = serde_json::from_str(&metadata)?; Ok(meta) } cbindgen-0.27.0/src/bindgen/cargo/cargo_toml.rs000064400000000000000000000031511046102023000174470ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::error; use std::fmt; use std::fs::File; use std::io; use std::io::Read; use std::path::Path; #[derive(Debug)] /// Possible errors that can occur during Cargo.toml parsing. pub enum Error { /// Error during reading of Cargo.toml Io(io::Error), /// Deserialization error Toml(toml::de::Error), } impl From for Error { fn from(err: io::Error) -> Self { Error::Io(err) } } impl From for Error { fn from(err: toml::de::Error) -> Self { Error::Toml(err) } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::Io(ref err) => err.fmt(f), Error::Toml(ref err) => err.fmt(f), } } } impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Error::Io(ref err) => Some(err), Error::Toml(ref err) => Some(err), } } } #[derive(Clone, Deserialize, Debug)] pub struct Manifest { pub package: Package, } #[derive(Clone, Deserialize, Debug)] pub struct Package { pub name: String, } /// Parse the Cargo.toml for a given path pub fn manifest(manifest_path: &Path) -> Result { let mut s = String::new(); let mut f = File::open(manifest_path)?; f.read_to_string(&mut s)?; toml::from_str::(&s).map_err(|x| x.into()) } cbindgen-0.27.0/src/bindgen/cargo/mod.rs000064400000000000000000000006161046102023000161030ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #[allow(clippy::module_inception)] mod cargo; pub(crate) mod cargo_expand; pub(crate) mod cargo_lock; pub(crate) mod cargo_metadata; pub(crate) mod cargo_toml; pub(crate) use self::cargo::*; cbindgen-0.27.0/src/bindgen/cdecl.rs000064400000000000000000000330061046102023000153020ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::io::Write; use crate::bindgen::config::Layout; use crate::bindgen::declarationtyperesolver::DeclarationType; use crate::bindgen::ir::{ConstExpr, Function, GenericArgument, Type}; use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::writer::{ListType, SourceWriter}; use crate::bindgen::{Config, Language}; // This code is for translating Rust types into C declarations. // See Section 6.7, Declarations, in the C standard for background. // http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf enum CDeclarator { Ptr { is_const: bool, is_nullable: bool, is_ref: bool, }, Array(String), Func { args: Vec<(Option, CDecl)>, layout: Layout, never_return: bool, }, } impl CDeclarator { fn is_ptr(&self) -> bool { matches!(self, CDeclarator::Ptr { .. } | CDeclarator::Func { .. }) } } struct CDecl { type_qualifers: String, type_name: String, type_generic_args: Vec, declarators: Vec, type_ctype: Option, deprecated: Option, } impl CDecl { fn new() -> CDecl { CDecl { type_qualifers: String::new(), type_name: String::new(), type_generic_args: Vec::new(), declarators: Vec::new(), type_ctype: None, deprecated: None, } } fn from_type(t: &Type, config: &Config) -> CDecl { let mut cdecl = CDecl::new(); cdecl.build_type(t, false, config); cdecl } fn from_func_arg(t: &Type, array_length: Option<&str>, config: &Config) -> CDecl { let mut cdecl = CDecl::new(); let length = match array_length { Some(l) => l, None => return CDecl::from_type(t, config), }; let (ty, is_const) = match t { Type::Ptr { ty, is_const, .. } => (ty, is_const), _ => unreachable!( "Should never have an array length for a non pointer type {:?}", t ), }; let ptr_as_array = Type::Array(ty.clone(), ConstExpr::Value(length.to_string())); cdecl.build_type(&ptr_as_array, *is_const, config); cdecl } fn from_func(f: &Function, layout: Layout, config: &Config) -> CDecl { let mut cdecl = CDecl::new(); cdecl.build_func(f, layout, config); cdecl } fn build_func(&mut self, f: &Function, layout: Layout, config: &Config) { let args = f .args .iter() .map(|arg| { ( arg.name.clone(), CDecl::from_func_arg(&arg.ty, arg.array_length.as_deref(), config), ) }) .collect(); self.declarators.push(CDeclarator::Func { args, layout, never_return: f.never_return, }); self.deprecated.clone_from(&f.annotations.deprecated); self.build_type(&f.ret, false, config); } fn build_type(&mut self, t: &Type, is_const: bool, config: &Config) { match t { Type::Path(ref generic) => { if is_const { assert!( self.type_qualifers.is_empty(), "error generating cdecl for {:?}", t ); "const".clone_into(&mut self.type_qualifers); } assert!( self.type_name.is_empty(), "error generating cdecl for {:?}", t ); generic.export_name().clone_into(&mut self.type_name); assert!( self.type_generic_args.is_empty(), "error generating cdecl for {:?}", t ); generic.generics().clone_into(&mut self.type_generic_args); self.type_ctype = generic.ctype().cloned(); } Type::Primitive(ref p) => { if is_const { assert!( self.type_qualifers.is_empty(), "error generating cdecl for {:?}", t ); "const".clone_into(&mut self.type_qualifers); } assert!( self.type_name.is_empty(), "error generating cdecl for {:?}", t ); self.type_name = p.to_repr_c(config).to_string(); } Type::Ptr { ref ty, is_nullable, is_const: ptr_is_const, is_ref, } => { self.declarators.push(CDeclarator::Ptr { is_const, is_nullable: *is_nullable, is_ref: *is_ref, }); self.build_type(ty, *ptr_is_const, config); } Type::Array(ref t, ref constant) => { let len = constant.as_str().to_owned(); self.declarators.push(CDeclarator::Array(len)); self.build_type(t, is_const, config); } Type::FuncPtr { ref ret, ref args, is_nullable: _, never_return, } => { let args = args .iter() .map(|(ref name, ref ty)| (name.clone(), CDecl::from_type(ty, config))) .collect(); self.declarators.push(CDeclarator::Ptr { is_const: false, is_nullable: true, is_ref: false, }); self.declarators.push(CDeclarator::Func { args, layout: config.function.args, never_return: *never_return, }); self.build_type(ret, false, config); } } } fn write( &self, language_backend: &mut LB, out: &mut SourceWriter, ident: Option<&str>, config: &Config, ) { // Write the type-specifier and type-qualifier first if !self.type_qualifers.is_empty() { write!(out, "{} ", self.type_qualifers); } if config.language != Language::Cython { if let Some(ref ctype) = self.type_ctype { write!(out, "{} ", ctype.to_str()); } } write!(out, "{}", self.type_name); if !self.type_generic_args.is_empty() { out.write("<"); out.write_horizontal_source_list( language_backend, &self.type_generic_args, ListType::Join(", "), |language_backend, out, g| match *g { GenericArgument::Type(ref ty) => language_backend.write_type(out, ty), GenericArgument::Const(ref expr) => write!(out, "{}", expr.as_str()), }, ); out.write(">"); } // When we have an identifier, put a space between the type and the declarators if ident.is_some() { out.write(" "); } // Write the left part of declarators before the identifier let mut iter_rev = self.declarators.iter().rev().peekable(); #[allow(clippy::while_let_on_iterator)] while let Some(declarator) = iter_rev.next() { let next_is_pointer = iter_rev.peek().map_or(false, |x| x.is_ptr()); match *declarator { CDeclarator::Ptr { is_const, is_nullable, is_ref, } => { out.write(if is_ref { "&" } else { "*" }); if is_const { out.write("const "); } if !is_nullable && !is_ref && config.language != Language::Cython { if let Some(attr) = &config.pointer.non_null_attribute { write!(out, "{} ", attr); } } } CDeclarator::Array(..) => { if next_is_pointer { out.write("("); } } CDeclarator::Func { .. } => { if next_is_pointer { out.write("("); } } } } // Write the identifier if let Some(ident) = ident { write!(out, "{}", ident); } // Write the right part of declarators after the identifier let mut iter = self.declarators.iter(); let mut last_was_pointer = false; #[allow(clippy::while_let_on_iterator)] while let Some(declarator) = iter.next() { match *declarator { CDeclarator::Ptr { .. } => { last_was_pointer = true; } CDeclarator::Array(ref constant) => { if last_was_pointer { out.write(")"); } write!(out, "[{}]", constant); last_was_pointer = false; } CDeclarator::Func { ref args, ref layout, never_return, } => { if last_was_pointer { out.write(")"); } out.write("("); if args.is_empty() && config.language == Language::C { out.write("void"); } fn write_vertical( language_backend: &mut LB, out: &mut SourceWriter, config: &Config, args: &[(Option, CDecl)], ) { let align_length = out.line_length_for_align(); out.push_set_spaces(align_length); for (i, (arg_ident, arg_ty)) in args.iter().enumerate() { if i != 0 { out.write(","); out.new_line(); } // Convert &Option to Option<&str> let arg_ident = arg_ident.as_ref().map(|x| x.as_ref()); arg_ty.write(language_backend, out, arg_ident, config); } out.pop_tab(); } fn write_horizontal( language_backend: &mut LB, out: &mut SourceWriter, config: &Config, args: &[(Option, CDecl)], ) { for (i, (arg_ident, arg_ty)) in args.iter().enumerate() { if i != 0 { out.write(", "); } // Convert &Option to Option<&str> let arg_ident = arg_ident.as_ref().map(|x| x.as_ref()); arg_ty.write(language_backend, out, arg_ident, config); } } match layout { Layout::Vertical => write_vertical(language_backend, out, config, args), Layout::Horizontal => write_horizontal(language_backend, out, config, args), Layout::Auto => { if !out.try_write( |out| write_horizontal(language_backend, out, config, args), config.line_length, ) { write_vertical(language_backend, out, config, args) } } } out.write(")"); if never_return && config.language != Language::Cython { if let Some(ref no_return_attr) = config.function.no_return { out.write_fmt(format_args!(" {}", no_return_attr)); } } last_was_pointer = true; } } } } } pub fn write_func( language_backend: &mut LB, out: &mut SourceWriter, f: &Function, layout: Layout, config: &Config, ) { CDecl::from_func(f, layout, config).write(language_backend, out, Some(f.path().name()), config); } pub fn write_field( language_backend: &mut LB, out: &mut SourceWriter, t: &Type, ident: &str, config: &Config, ) { CDecl::from_type(t, config).write(language_backend, out, Some(ident), config); } pub fn write_type( language_backend: &mut LB, out: &mut SourceWriter, t: &Type, config: &Config, ) { CDecl::from_type(t, config).write(language_backend, out, None, config); } cbindgen-0.27.0/src/bindgen/config.rs000064400000000000000000001142621046102023000155010ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::collections::{BTreeMap, HashMap}; use std::default::Default; use std::str::FromStr; use std::{fmt, fs, path::Path as StdPath, path::PathBuf as StdPathBuf}; use serde::de::value::{MapAccessDeserializer, SeqAccessDeserializer}; use serde::de::{Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; use crate::bindgen::ir::annotation::AnnotationSet; use crate::bindgen::ir::path::Path; use crate::bindgen::ir::repr::ReprAlign; pub use crate::bindgen::rename::RenameRule; pub const VERSION: &str = env!("CARGO_PKG_VERSION"); /// A language type to generate bindings for. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Language { Cxx, C, Cython, } impl FromStr for Language { type Err = String; fn from_str(s: &str) -> Result { match s { "cxx" => Ok(Language::Cxx), "Cxx" => Ok(Language::Cxx), "CXX" => Ok(Language::Cxx), "cpp" => Ok(Language::Cxx), "Cpp" => Ok(Language::Cxx), "CPP" => Ok(Language::Cxx), "c++" => Ok(Language::Cxx), "C++" => Ok(Language::Cxx), "c" => Ok(Language::C), "C" => Ok(Language::C), "cython" => Ok(Language::Cython), "Cython" => Ok(Language::Cython), _ => Err(format!("Unrecognized Language: '{}'.", s)), } } } deserialize_enum_str!(Language); impl Language { pub(crate) fn typedef(self) -> &'static str { match self { Language::Cxx | Language::C => "typedef", Language::Cython => "ctypedef", } } } /// Controls what type of line endings are used in the generated code. #[derive(Debug, Clone, Copy)] #[allow(clippy::upper_case_acronyms)] #[derive(Default)] pub enum LineEndingStyle { /// Use Unix-style linefeed characters #[default] LF, /// Use classic Mac-style carriage-return characters CR, /// Use Windows-style carriage-return and linefeed characters CRLF, /// Use the native mode for the platform: CRLF on Windows, LF everywhere else. Native, } impl LineEndingStyle { pub fn as_str(&self) -> &'static str { match self { Self::LF => "\n", Self::CR => "\r", Self::CRLF => "\r\n", Self::Native => { #[cfg(target_os = "windows")] { Self::CRLF.as_str() } #[cfg(not(target_os = "windows"))] { Self::LF.as_str() } } } } } impl FromStr for LineEndingStyle { type Err = String; fn from_str(s: &str) -> Result { match s.to_lowercase().as_ref() { "native" => Ok(Self::Native), "lf" => Ok(Self::LF), "crlf" => Ok(Self::CRLF), "cr" => Ok(Self::CR), _ => Err(format!("Unrecognized line ending style: '{}'.", s)), } } } deserialize_enum_str!(LineEndingStyle); /// A style of braces to use for generating code. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Braces { SameLine, NextLine, } impl FromStr for Braces { type Err = String; fn from_str(s: &str) -> Result { match s { "SameLine" => Ok(Braces::SameLine), "same_line" => Ok(Braces::SameLine), "NextLine" => Ok(Braces::NextLine), "next_line" => Ok(Braces::NextLine), _ => Err(format!("Unrecognized Braces: '{}'.", s)), } } } deserialize_enum_str!(Braces); /// A type of layout to use when generating long lines of code. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Layout { Horizontal, Vertical, Auto, } impl FromStr for Layout { type Err = String; fn from_str(s: &str) -> Result { match s { "Horizontal" => Ok(Layout::Horizontal), "horizontal" => Ok(Layout::Horizontal), "Vertical" => Ok(Layout::Vertical), "vertical" => Ok(Layout::Vertical), "Auto" => Ok(Layout::Auto), "auto" => Ok(Layout::Auto), _ => Err(format!("Unrecognized Layout: '{}'.", s)), } } } deserialize_enum_str!(Layout); /// How the comments containing documentation should be styled. #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub enum DocumentationStyle { C, C99, Doxy, Cxx, Auto, } impl FromStr for DocumentationStyle { type Err = String; fn from_str(s: &str) -> Result { match s.to_lowercase().as_ref() { "c" => Ok(DocumentationStyle::C), "c99" => Ok(DocumentationStyle::C99), "cxx" => Ok(DocumentationStyle::Cxx), "c++" => Ok(DocumentationStyle::Cxx), "doxy" => Ok(DocumentationStyle::Doxy), "auto" => Ok(DocumentationStyle::Auto), _ => Err(format!("Unrecognized documentation style: '{}'.", s)), } } } deserialize_enum_str!(DocumentationStyle); /// How much of the documentation to include in the header file. #[derive(Debug, Clone, Copy)] pub enum DocumentationLength { Short, Full, } impl FromStr for DocumentationLength { type Err = String; fn from_str(s: &str) -> Result { match s.to_lowercase().as_ref() { "short" => Ok(DocumentationLength::Short), "full" => Ok(DocumentationLength::Full), _ => Err(format!("Unrecognized documentation style: '{}'.", s)), } } } deserialize_enum_str!(DocumentationLength); /// A style of Style to use when generating structs and enums. #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] pub enum Style { #[default] Both, Tag, Type, } impl Style { pub fn generate_tag(self) -> bool { match self { Style::Both | Style::Tag => true, Style::Type => false, } } pub fn generate_typedef(self) -> bool { match self { Style::Both | Style::Type => true, Style::Tag => false, } } // https://cython.readthedocs.io/en/latest/src/userguide/external_C_code.html#styles-of-struct-union-and-enum-declaration pub fn cython_def(self) -> &'static str { if self.generate_tag() { "cdef " } else { "ctypedef " } } } impl FromStr for Style { type Err = String; fn from_str(s: &str) -> Result { match s { "Both" => Ok(Style::Both), "both" => Ok(Style::Both), "Tag" => Ok(Style::Tag), "tag" => Ok(Style::Tag), "Type" => Ok(Style::Type), "type" => Ok(Style::Type), _ => Err(format!("Unrecognized Style: '{}'.", s)), } } } deserialize_enum_str!(Style); /// Different item types that we can generate and filter. #[derive(Debug, Clone, PartialEq, Eq)] pub enum ItemType { Constants, Globals, Enums, Structs, Unions, Typedefs, OpaqueItems, Functions, } impl FromStr for ItemType { type Err = String; fn from_str(s: &str) -> Result { use self::ItemType::*; Ok(match &*s.to_lowercase() { "constants" => Constants, "globals" => Globals, "enums" => Enums, "structs" => Structs, "unions" => Unions, "typedefs" => Typedefs, "opaque" => OpaqueItems, "functions" => Functions, _ => return Err(format!("Unrecognized Style: '{}'.", s)), }) } } deserialize_enum_str!(ItemType); /// Type which specifies the sort order of functions #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SortKey { Name, None, } impl FromStr for SortKey { type Err = String; fn from_str(s: &str) -> Result { use self::SortKey::*; Ok(match &*s.to_lowercase() { "name" => Name, "none" => None, _ => return Err(format!("Unrecognized sort option: '{}'.", s)), }) } } deserialize_enum_str!(SortKey); /// Settings to apply when exporting items. #[derive(Debug, Clone, Deserialize, Default)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct ExportConfig { /// A list of additional items not used by exported functions to include in /// the generated bindings pub include: Vec, /// A list of items to not include in the generated bindings pub exclude: Vec, /// Table of name conversions to apply to item names pub rename: HashMap, /// Table of raw strings to prepend to the body of items. pub pre_body: HashMap, /// Table of raw strings to append to the body of items. pub body: HashMap, /// A prefix to add before the name of every item pub prefix: Option, /// Types of items to generate. pub item_types: Vec, /// Whether renaming overrides or extends prefixing. pub renaming_overrides_prefixing: bool, /// Mangling configuration. pub mangle: MangleConfig, } /// Mangling-specific configuration. #[derive(Debug, Clone, Deserialize, Default)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct MangleConfig { /// The rename rule to apply to the type names mangled. pub rename_types: RenameRule, /// Remove the underscores used for name mangling. pub remove_underscores: bool, } impl ExportConfig { pub(crate) fn should_generate(&self, item_type: ItemType) -> bool { self.item_types.is_empty() || self.item_types.contains(&item_type) } pub(crate) fn pre_body(&self, path: &Path) -> Option<&str> { self.pre_body.get(path.name()).map(|s| s.trim_matches('\n')) } pub(crate) fn post_body(&self, path: &Path) -> Option<&str> { self.body.get(path.name()).map(|s| s.trim_matches('\n')) } pub(crate) fn rename(&self, item_name: &mut String) { if let Some(name) = self.rename.get(item_name) { item_name.clone_from(name); if self.renaming_overrides_prefixing { return; } } if let Some(ref prefix) = self.prefix { item_name.insert_str(0, prefix); } } } /// Settings to apply to generated types with layout modifiers. #[derive(Debug, Default, Clone, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct LayoutConfig { /// The way to annotate C types as #[repr(packed)]. pub packed: Option, /// The way to annotate C types as #[repr(align(...))]. This is assumed to be a functional /// macro which takes a single argument (the alignment). pub aligned_n: Option, } impl LayoutConfig { pub(crate) fn ensure_safe_to_represent(&self, align: &ReprAlign) -> Result<(), String> { match (align, &self.packed, &self.aligned_n) { (ReprAlign::Packed, None, _) => Err("Cannot safely represent #[repr(packed)] type without configured 'packed' annotation.".to_string()), (ReprAlign::Align(_), _, None) => Err("Cannot safely represent #[repr(aligned(...))] type without configured 'aligned_n' annotation.".to_string()), _ => Ok(()), } } } /// Settings to apply to generated functions. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct FunctionConfig { /// Optional text to output before each function declaration pub prefix: Option, /// Optional text to output after each function declaration pub postfix: Option, /// The way to annotation this function as #[must_use] pub must_use: Option, /// The way to annotation this function as #[deprecated] without notes pub deprecated: Option, /// The way to annotation this function as #[deprecated] with notes pub deprecated_with_note: Option, /// The style to layout the args pub args: Layout, /// The rename rule to apply to function args pub rename_args: RenameRule, /// An optional macro to use when generating Swift function name attributes pub swift_name_macro: Option, /// Sort key for functions pub sort_by: Option, /// Optional text to output after functions which return `!`. pub no_return: Option, } impl Default for FunctionConfig { fn default() -> FunctionConfig { FunctionConfig { prefix: None, postfix: None, must_use: None, deprecated: None, deprecated_with_note: None, args: Layout::Auto, rename_args: RenameRule::None, swift_name_macro: None, sort_by: None, no_return: None, } } } impl FunctionConfig { pub(crate) fn prefix(&self, annotations: &AnnotationSet) -> Option { if let Some(x) = annotations.atom("prefix") { return x; } self.prefix.clone() } pub(crate) fn postfix(&self, annotations: &AnnotationSet) -> Option { if let Some(x) = annotations.atom("postfix") { return x; } self.postfix.clone() } } /// Settings to apply to generated structs. #[derive(Debug, Default, Clone, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct StructConfig { /// The rename rule to apply to the name of struct fields pub rename_fields: RenameRule, /// Whether to generate a constructor for the struct (which takes /// arguments to initialize all the members) pub derive_constructor: bool, /// Whether to generate a piecewise equality operator pub derive_eq: bool, /// Whether to generate a piecewise inequality operator pub derive_neq: bool, /// Whether to generate a less than operator on structs with one field pub derive_lt: bool, /// Whether to generate a less than or equal to operator on structs with one field pub derive_lte: bool, /// Whether to generate a greater than operator on structs with one field pub derive_gt: bool, /// Whether to generate a greater than or equal to operator on structs with one field pub derive_gte: bool, /// Whether to generate a ostream serializer for the struct pub derive_ostream: bool, /// Whether associated constants should be in the body. Only applicable to /// non-transparent structs, and in C++-only. pub associated_constants_in_body: bool, /// The way to annotate this struct as #[must_use]. pub must_use: Option, /// The way to annotation this function as #[deprecated] without notes pub deprecated: Option, /// The way to annotation this function as #[deprecated] with notes pub deprecated_with_note: Option, } impl StructConfig { pub(crate) fn derive_constructor(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-constructor") { return x; } self.derive_constructor } pub(crate) fn derive_eq(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-eq") { return x; } self.derive_eq } pub(crate) fn derive_neq(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-neq") { return x; } self.derive_neq } pub(crate) fn derive_lt(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-lt") { return x; } self.derive_lt } pub(crate) fn derive_lte(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-lte") { return x; } self.derive_lte } pub(crate) fn derive_gt(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-gt") { return x; } self.derive_gt } pub(crate) fn derive_gte(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-gte") { return x; } self.derive_gte } pub(crate) fn derive_ostream(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-ostream") { return x; } self.derive_ostream } } /// Settings to apply to generated enums. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct EnumConfig { /// The rename rule to apply to the name of enum variants pub rename_variants: RenameRule, /// The rename rule to apply to the names of the union fields in C/C++ /// generated from the Rust enum. Applied before rename_variants /// rename rule. Defaults to SnakeCase. pub rename_variant_name_fields: RenameRule, /// Whether to add a `Sentinel` value at the end of every enum /// This is useful in Gecko for IPC serialization pub add_sentinel: bool, /// Whether the enum variants should be prefixed with the enum name pub prefix_with_name: bool, /// Whether to generate static `::X(..)` constructors and `IsX()` /// methods for tagged enums. pub derive_helper_methods: bool, /// Whether to generate `AsX() const` methods for tagged enums. pub derive_const_casts: bool, /// Whether to generate `AsX()` methods for tagged enums. pub derive_mut_casts: bool, /// The name of the macro to use for `derive_{const,mut}casts`. If custom, you're /// responsible to provide the necessary header, otherwise `assert` will be /// used, and `` will be included. pub cast_assert_name: Option, /// The way to annotation this enum as #[must_use]. pub must_use: Option, /// The way to annotation this function as #[deprecated] without notes pub deprecated: Option, /// The way to annotation this function as #[deprecated] with notes pub deprecated_with_note: Option, /// The way to annotate this enum variant as #[deprecated] without notes pub deprecated_variant: Option, /// The way to annotate this enum variant as #[deprecated] with notes pub deprecated_variant_with_note: Option, /// Whether to generate destructors of tagged enums. pub derive_tagged_enum_destructor: bool, /// Whether to generate copy-constructors of tagged enums. pub derive_tagged_enum_copy_constructor: bool, /// Whether to generate copy-assignment operators of tagged enums. /// /// This is only generated if a copy constructor for the same tagged enum is /// generated as well. pub derive_tagged_enum_copy_assignment: bool, /// Whether to generate a ostream serializer for the struct pub derive_ostream: bool, /// Declare the enum as an enum class. /// Only relevant when targeting C++. pub enum_class: bool, /// Whether to generate empty, private default-constructors for tagged /// enums. pub private_default_tagged_enum_constructor: bool, } impl Default for EnumConfig { fn default() -> EnumConfig { EnumConfig { rename_variants: RenameRule::None, rename_variant_name_fields: RenameRule::SnakeCase, add_sentinel: false, prefix_with_name: false, derive_helper_methods: false, derive_const_casts: false, derive_mut_casts: false, cast_assert_name: None, must_use: None, deprecated: None, deprecated_with_note: None, deprecated_variant: None, deprecated_variant_with_note: None, derive_tagged_enum_destructor: false, derive_tagged_enum_copy_constructor: false, derive_tagged_enum_copy_assignment: false, derive_ostream: false, enum_class: true, private_default_tagged_enum_constructor: false, } } } impl EnumConfig { pub(crate) fn add_sentinel(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("add-sentinel") { return x; } self.add_sentinel } pub(crate) fn derive_helper_methods(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-helper-methods") { return x; } self.derive_helper_methods } pub(crate) fn derive_const_casts(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-const-casts") { return x; } self.derive_const_casts } pub(crate) fn derive_mut_casts(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-mut-casts") { return x; } self.derive_mut_casts } pub(crate) fn derive_tagged_enum_destructor(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-tagged-enum-destructor") { return x; } self.derive_tagged_enum_destructor } pub(crate) fn derive_tagged_enum_copy_constructor(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-tagged-enum-copy-constructor") { return x; } self.derive_tagged_enum_copy_constructor } pub(crate) fn derive_tagged_enum_copy_assignment(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-tagged-enum-copy-assignment") { return x; } self.derive_tagged_enum_copy_assignment } pub(crate) fn derive_ostream(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-ostream") { return x; } self.derive_ostream } pub(crate) fn enum_class(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("enum-class") { return x; } self.enum_class } pub(crate) fn private_default_tagged_enum_constructor( &self, annotations: &AnnotationSet, ) -> bool { if let Some(x) = annotations.bool("private-default-tagged-enum-constructor") { return x; } self.private_default_tagged_enum_constructor } } /// Settings to apply to generated constants. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct ConstantConfig { /// Whether a generated constant can be a static const in C++ mode. pub allow_static_const: bool, /// Whether a generated constant should be constexpr in C++ mode. pub allow_constexpr: bool, /// Sort key for constants pub sort_by: Option, } impl Default for ConstantConfig { fn default() -> ConstantConfig { ConstantConfig { allow_static_const: true, allow_constexpr: true, sort_by: None, } } } /// Settings for custom macro expansion. #[derive(Debug, Clone, Deserialize, Default)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct MacroExpansionConfig { /// Whether the `bitflags` macro should be expanded. pub bitflags: bool, } /// Controls which Cargo profile is used for macro expansion. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Profile { Debug, Release, } impl FromStr for Profile { type Err = String; fn from_str(s: &str) -> Result { match s { "debug" | "Debug" => Ok(Profile::Debug), "release" | "Release" => Ok(Profile::Release), _ => Err(format!("Unrecognized Profile: '{}'.", s)), } } } deserialize_enum_str!(Profile); /// Settings to apply when running `rustc -Zunpretty=expanded` #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct ParseExpandConfig { /// The names of crates to parse with `rustc -Zunpretty=expanded` pub crates: Vec, /// Whether to enable all the features when expanding. pub all_features: bool, /// Whether to use the default feature set when expanding. pub default_features: bool, /// List of features to use when expanding. Combines with `default_features` like in /// `Cargo.toml`. pub features: Option>, /// Controls whether or not to pass `--release` when expanding. pub profile: Profile, } impl Default for ParseExpandConfig { fn default() -> ParseExpandConfig { ParseExpandConfig { crates: Vec::new(), all_features: false, default_features: true, features: None, profile: Profile::Debug, } } } // Backwards-compatibility deserializer for ParseExpandConfig. This allows accepting both the // simple `expand = ["crate"]` and the more complex `expand = {"crates": ["crate"], // "default_features": false}` format for the `expand` key. // // Note that one (major) difference between the two forms is that, for backwards-compatibility // reasons, the `expand = ["crate"]` form will enable the `--all-features` flag by default while // the `expand = {"crates": ["crate"]}` form will use the default feature set by default. fn retrocomp_parse_expand_config_deserialize<'de, D: Deserializer<'de>>( deserializer: D, ) -> Result { struct ParseExpandVisitor; impl<'de> Visitor<'de> for ParseExpandVisitor { type Value = ParseExpandConfig; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a map or sequence of string") } fn visit_seq>(self, seq: A) -> Result { let crates = as Deserialize>::deserialize(SeqAccessDeserializer::new(seq))?; Ok(ParseExpandConfig { crates, all_features: true, default_features: true, features: None, profile: Profile::Debug, }) } fn visit_map>(self, map: A) -> Result { ::deserialize(MapAccessDeserializer::new(map)) } } deserializer.deserialize_any(ParseExpandVisitor) } /// Settings to apply when parsing. #[derive(Debug, Default, Clone, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct ParseConfig { /// Whether to parse dependencies when generating bindings. When this is true, /// each dependent crate is found using a combination of `cargo metadata` and /// `Cargo.lock`. To further control this behavior, crates can be whitelisted or /// blacklisted using `include` and `exclude` respectively. Additionally in cases /// where crates have types to expose in bindings hidden in macros, a crate can /// be marked in `expand` and `cargo expand` will be used to expand the macros /// before parsing. A crate marked in `expand` doesn't need to be added to any /// whitelist. pub parse_deps: bool, /// An optional whitelist of names of crates to parse pub include: Option>, /// The names of crates to not parse pub exclude: Vec, /// The configuration options for `rustc -Zunpretty=expanded` #[serde(deserialize_with = "retrocomp_parse_expand_config_deserialize")] pub expand: ParseExpandConfig, /// Whether to use a new temporary target directory when running `rustc -Zunpretty=expanded`. /// This may be required for some build processes. pub clean: bool, /// List of crate names which generate consts, statics, and fns. By default /// no dependent crates generate them. pub extra_bindings: Vec, } impl ParseConfig { pub(crate) fn should_generate_top_level_item( &self, crate_name: &str, binding_crate_name: &str, ) -> bool { if crate_name == binding_crate_name { // Always generate items for the binding crate. return true; } self.extra_bindings.iter().any(|dep| dep == crate_name) } } /// Settings to apply to pointers #[derive(Debug, Clone, Default, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct PtrConfig { /// Optional attribute to apply to pointers that are required to not be null pub non_null_attribute: Option, } /// Settings specific to Cython bindings. #[derive(Debug, Clone, Default, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct CythonConfig { /// Header specified in the top level `cdef extern from header:` declaration. pub header: Option, /// `from module cimport name1, name2, ...` declarations added in the same place /// where you'd get includes in C. pub cimports: BTreeMap>, } /// A collection of settings to customize the generated bindings. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct Config { /// Optional text to output at the beginning of the file pub header: Option, /// A list of additional includes to put at the beginning of the generated header pub includes: Vec, /// A list of additional system includes to put at the beginning of the generated header pub sys_includes: Vec, /// Optional verbatim code added after the include blocks pub after_includes: Option, /// Optional text to output at the end of the file pub trailer: Option, /// Optional name to use for an include guard pub include_guard: Option, /// Add a `#pragma once` guard pub pragma_once: bool, /// Generates no includes at all. Overrides all other include options /// /// This option is useful when using cbindgen with tools such as python's cffi which /// doesn't understand include directives pub no_includes: bool, // Package version: True if the package version should appear as a comment in the .h file pub package_version: bool, /// Optional text to output at major sections to deter manual editing pub autogen_warning: Option, /// Include a comment with the version of cbindgen used to generate the file pub include_version: bool, /// An optional name for the root namespace. Only applicable when language="C++" pub namespace: Option, /// An optional list of namespaces. Only applicable when language="C++" pub namespaces: Option>, /// An optional list of namespaces to declare as using. Only applicable when language="C++" pub using_namespaces: Option>, /// The style to use for braces pub braces: Braces, /// The preferred length of a line, used for auto breaking function arguments pub line_length: usize, /// The amount of spaces in a tab pub tab_width: usize, /// The type of line endings to generate pub line_endings: LineEndingStyle, /// The language to output bindings for pub language: Language, /// Include preprocessor defines in C bindings to ensure C++ compatibility pub cpp_compat: bool, /// The style to declare structs, enums and unions in for C pub style: Style, /// Default sort key for functions and constants. pub sort_by: SortKey, /// If this option is true `usize` and `isize` will be converted into `size_t` and `ptrdiff_t` /// instead of `uintptr_t` and `intptr_t` respectively. pub usize_is_size_t: bool, /// The configuration options for parsing pub parse: ParseConfig, /// The configuration options for exporting pub export: ExportConfig, /// The configuration options for macros. pub macro_expansion: MacroExpansionConfig, /// The configuration options for type layouts. pub layout: LayoutConfig, /// The configuration options for functions #[serde(rename = "fn")] pub function: FunctionConfig, /// The configuration options for structs #[serde(rename = "struct")] pub structure: StructConfig, /// The configuration options for enums #[serde(rename = "enum")] pub enumeration: EnumConfig, /// The configuration options for constants #[serde(rename = "const")] pub constant: ConstantConfig, /// Preprocessor defines to use when generating #ifdef's for #[cfg] pub defines: HashMap, /// Include doc comments from Rust as documentation pub documentation: bool, /// How documentation comments should be styled. pub documentation_style: DocumentationStyle, /// How much of the documentation should be output for each item. pub documentation_length: DocumentationLength, /// Configuration options for pointers #[serde(rename = "ptr")] pub pointer: PtrConfig, /// Only download sources for dependencies needed for the target platform. /// /// By default, cbindgen will fetch sources for dependencies used on any platform so that if a /// type is defined in terms of a type from a dependency on another target (probably behind a /// `#[cfg]`), cbindgen will be able to generate the appropriate binding as it can see the /// nested type's definition. However, this makes calling cbindgen slower, as it may have to /// download a number of additional dependencies. /// /// As an example, consider this Cargo.toml: /// /// ```toml /// [target.'cfg(windows)'.dependencies] /// windows = "0.7" /// ``` /// /// with this declaration in one of the `.rs` files that cbindgen is asked to generate bindings /// for: /// /// ```rust,ignore /// #[cfg(windows)] /// pub struct Error(windows::ErrorCode); /// ``` /// /// With the default value (`false`), cbindgen will download the `windows` dependency even when /// not compiling for Windows, and will thus be able to generate the binding for `Error` /// (behind a `#define`). /// /// If this value is instead to `true`, cbindgen will _not_ download the `windows` dependency /// if it's not compiling for Windows, but will also fail to generate a Windows binding for /// `Error` as it does not know the definition for `ErrorCode`. /// /// The target can be chosen via the `TARGET` environment variable (if used /// via the CLI, when ran from a build script cargo sets this variable /// appropriately). pub only_target_dependencies: bool, /// Configuration options specific to Cython. pub cython: CythonConfig, #[doc(hidden)] #[serde(skip)] /// Internal field for tracking from which file the config was loaded. /// /// Users should not set this field explicitly. Making the field private /// prevents users from filling the struct with `..Default::default()`, /// and creating a new InternalConfig struct would require more breaking /// changes to our public API. pub config_path: Option, } impl Default for Config { fn default() -> Config { Config { header: None, includes: Vec::new(), sys_includes: Vec::new(), after_includes: None, trailer: None, include_guard: None, pragma_once: false, autogen_warning: None, include_version: false, no_includes: false, package_version: false, namespace: None, namespaces: None, using_namespaces: None, braces: Braces::SameLine, line_length: 100, tab_width: 2, line_endings: LineEndingStyle::default(), language: Language::Cxx, cpp_compat: false, style: Style::default(), usize_is_size_t: false, sort_by: SortKey::None, macro_expansion: Default::default(), parse: ParseConfig::default(), export: ExportConfig::default(), layout: LayoutConfig::default(), function: FunctionConfig::default(), structure: StructConfig::default(), enumeration: EnumConfig::default(), constant: ConstantConfig::default(), defines: HashMap::new(), documentation: true, documentation_style: DocumentationStyle::Auto, documentation_length: DocumentationLength::Full, pointer: PtrConfig::default(), only_target_dependencies: false, cython: CythonConfig::default(), config_path: None, } } } impl Config { pub(crate) fn cpp_compatible_c(&self) -> bool { self.language == Language::C && self.cpp_compat } pub(crate) fn include_guard(&self) -> Option<&str> { if self.language == Language::Cython { None } else { self.include_guard.as_deref() } } pub(crate) fn includes(&self) -> &[String] { if self.language == Language::Cython { &[] } else { &self.includes } } pub(crate) fn sys_includes(&self) -> &[String] { if self.language == Language::Cython { &[] } else { &self.sys_includes } } pub fn from_file>(file_name: P) -> Result { let config_text = fs::read_to_string(file_name.as_ref()).map_err(|_| { format!( "Couldn't open config file: {}.", file_name.as_ref().display() ) })?; let mut config = toml::from_str::(&config_text) .map_err(|e| format!("Couldn't parse config file: {}.", e))?; config.config_path = Some(StdPathBuf::from(file_name.as_ref())); Ok(config) } pub fn from_root_or_default>(root: P) -> Config { let c = root.as_ref().join("cbindgen.toml"); if c.exists() { Config::from_file(c).unwrap() } else { Config::default() } } } cbindgen-0.27.0/src/bindgen/declarationtyperesolver.rs000064400000000000000000000030211046102023000211730ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::bindgen::ir::Path; use std::collections::hash_map::Entry; use std::collections::HashMap; impl DeclarationType { pub fn to_str(self) -> &'static str { match self { DeclarationType::Struct => "struct", DeclarationType::Enum => "enum", DeclarationType::Union => "union", } } } #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] pub enum DeclarationType { Struct, Enum, Union, } #[derive(Default)] pub struct DeclarationTypeResolver { types: HashMap>, } impl DeclarationTypeResolver { fn insert(&mut self, path: &Path, ty: Option) { if let Entry::Vacant(vacant_entry) = self.types.entry(path.clone()) { vacant_entry.insert(ty); } } pub fn add_enum(&mut self, path: &Path) { self.insert(path, Some(DeclarationType::Enum)); } pub fn add_struct(&mut self, path: &Path) { self.insert(path, Some(DeclarationType::Struct)); } pub fn add_union(&mut self, path: &Path) { self.insert(path, Some(DeclarationType::Union)); } pub fn add_none(&mut self, path: &Path) { self.insert(path, None); } pub fn type_for(&self, path: &Path) -> Option { *self.types.get(path)? } } cbindgen-0.27.0/src/bindgen/dependencies.rs000064400000000000000000000030461046102023000166570ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::cmp::Ordering; use std::collections::HashSet; use crate::bindgen::ir::{ItemContainer, Path}; /// A dependency list is used for gathering what order to output the types. #[derive(Default)] pub struct Dependencies { pub order: Vec, pub items: HashSet, } impl Dependencies { pub fn new() -> Dependencies { Dependencies { order: Vec::new(), items: HashSet::new(), } } pub fn sort(&mut self) { // Sort untagged enums and opaque structs into their own layers because they don't // depend on each other or anything else. let ordering = |a: &ItemContainer, b: &ItemContainer| match (a, b) { (ItemContainer::Enum(x), ItemContainer::Enum(y)) if x.tag.is_none() && y.tag.is_none() => { x.path.cmp(&y.path) } (ItemContainer::Enum(x), _) if x.tag.is_none() => Ordering::Less, (_, ItemContainer::Enum(x)) if x.tag.is_none() => Ordering::Greater, (ItemContainer::OpaqueItem(x), ItemContainer::OpaqueItem(y)) => x.path.cmp(&y.path), (&ItemContainer::OpaqueItem(_), _) => Ordering::Less, (_, &ItemContainer::OpaqueItem(_)) => Ordering::Greater, _ => Ordering::Equal, }; self.order.sort_by(ordering); } } cbindgen-0.27.0/src/bindgen/error.rs000064400000000000000000000055641046102023000153710ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::error; use std::fmt; pub use crate::bindgen::cargo::cargo_expand::Error as CargoExpandError; pub use crate::bindgen::cargo::cargo_metadata::Error as CargoMetadataError; pub use crate::bindgen::cargo::cargo_toml::Error as CargoTomlError; pub use syn::parse::Error as ParseError; #[derive(Debug)] #[allow(clippy::enum_variant_names)] pub enum Error { CargoMetadata(String, CargoMetadataError), CargoToml(String, CargoTomlError), CargoExpand(String, CargoExpandError), ParseSyntaxError { crate_name: String, src_path: String, error: ParseError, }, ParseCannotOpenFile { crate_name: String, src_path: String, }, } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Error::CargoMetadata(ref path, ref error) => write!( f, "Couldn't execute `cargo metadata` with manifest {:?}: {:?}", path, error ), Error::CargoToml(ref path, ref error) => { write!(f, "Couldn't load manifest file {:?}: {:?}", path, error) } Error::CargoExpand(ref crate_name, ref error) => write!( f, "Parsing crate `{}`: couldn't run `cargo rustc -Zunpretty=expanded`: {:?}", crate_name, error ), Error::ParseSyntaxError { ref crate_name, ref src_path, ref error, } => { write!( f, "Parsing crate `{}`:`{}`:\n{:?}", crate_name, src_path, error )?; if !src_path.is_empty() { write!( f, "\nTry running `rustc -Z parse-only {}` to see a nicer error message", src_path, )? } Ok(()) } Error::ParseCannotOpenFile { ref crate_name, ref src_path, } => write!( f, "Parsing crate `{}`: cannot open file `{}`.", crate_name, src_path ), } } } impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Error::CargoMetadata(_, ref error) => Some(error), Error::CargoToml(_, ref error) => Some(error), Error::CargoExpand(_, ref error) => Some(error), Error::ParseSyntaxError { ref error, .. } => Some(error), Error::ParseCannotOpenFile { .. } => None, } } } cbindgen-0.27.0/src/bindgen/ir/annotation.rs000064400000000000000000000154311046102023000170160ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::borrow::Cow; use std::collections::hash_map::Entry; use std::collections::HashMap; use std::str::FromStr; use crate::bindgen::config::{Config, Language}; use crate::bindgen::utilities::SynAttributeHelpers; // A system for specifying properties on items. Annotations are // given through document comments and parsed by this code. // // An annotation is in the form cbindgen:PROPERTY=VALUE // Where PROPERTY depends on the item // Where VALUE can be // * list - [Item1, Item2, Item3, ...] // * atom - Foo // * bool - true,false // Examples: // * cbindgen:field-names=[mHandle, mNamespace] // * cbindgen:function-postfix=WR_DESTRUCTOR_SAFE /// A value specified by an annotation. #[derive(Debug, Clone)] pub enum AnnotationValue { List(Vec), Atom(Option), Bool(bool), } /// A set of annotations specified by a document comment. #[derive(Debug, Default, Clone)] pub struct AnnotationSet { annotations: HashMap, pub must_use: bool, pub deprecated: Option, } pub enum DeprecatedNoteKind { Function, Struct, Enum, EnumVariant, } impl AnnotationSet { pub fn new() -> AnnotationSet { AnnotationSet { annotations: HashMap::new(), must_use: false, deprecated: None, } } pub fn is_empty(&self) -> bool { self.annotations.is_empty() && !self.must_use } pub(crate) fn must_use(&self, config: &Config) -> bool { self.must_use && config.language != Language::Cython } pub(crate) fn deprecated_note<'c>( &self, config: &'c Config, kind: DeprecatedNoteKind, ) -> Option> { let note = self.deprecated.as_deref()?; if config.language == Language::Cython { return None; } if note.is_empty() { return Some(Cow::Borrowed(match kind { DeprecatedNoteKind::Enum => config.enumeration.deprecated.as_deref()?, DeprecatedNoteKind::EnumVariant => { config.enumeration.deprecated_variant.as_deref()? } DeprecatedNoteKind::Function => config.function.deprecated.as_deref()?, DeprecatedNoteKind::Struct => config.structure.deprecated.as_deref()?, })); } let format = match kind { DeprecatedNoteKind::Enum => &config.enumeration.deprecated_with_note, DeprecatedNoteKind::EnumVariant => &config.enumeration.deprecated_variant_with_note, DeprecatedNoteKind::Function => &config.function.deprecated_with_note, DeprecatedNoteKind::Struct => &config.structure.deprecated_with_note, } .as_ref()?; Some(Cow::Owned(format.replace("{}", &format!("{:?}", note)))) } pub fn load(attrs: &[syn::Attribute]) -> Result { let lines = attrs.get_comment_lines(); let lines: Vec<&str> = lines .iter() .filter_map(|line| { let line = line.trim_start(); if !line.starts_with("cbindgen:") { return None; } Some(line) }) .collect(); let must_use = attrs.has_attr_word("must_use"); let deprecated = attrs.find_deprecated_note(); let mut annotations = HashMap::new(); // Look at each line for an annotation for line in lines { debug_assert!(line.starts_with("cbindgen:")); // Remove the "cbindgen:" prefix let annotation = &line[9..]; // Split the annotation in two let parts: Vec<&str> = annotation.split('=').map(|x| x.trim()).collect(); if parts.len() > 2 { return Err(format!("Couldn't parse {}.", line)); } // Grab the name that this annotation is modifying let name = parts[0]; // If the annotation only has a name, assume it's setting a bool flag if parts.len() == 1 { annotations.insert(name.to_string(), AnnotationValue::Bool(true)); continue; } // Parse the value we're setting the name to let value = parts[1]; if let Some(x) = parse_list(value) { annotations.insert(name.to_string(), AnnotationValue::List(x)); continue; } if let Ok(x) = value.parse::() { annotations.insert(name.to_string(), AnnotationValue::Bool(x)); continue; } annotations.insert( name.to_string(), if value.is_empty() { AnnotationValue::Atom(None) } else { AnnotationValue::Atom(Some(value.to_string())) }, ); } Ok(AnnotationSet { annotations, must_use, deprecated, }) } /// Adds an annotation value if none is specified. pub fn add_default(&mut self, name: &str, value: AnnotationValue) { if let Entry::Vacant(e) = self.annotations.entry(name.to_string()) { e.insert(value); } } pub fn list(&self, name: &str) -> Option> { match self.annotations.get(name) { Some(AnnotationValue::List(x)) => Some(x.clone()), _ => None, } } pub fn atom(&self, name: &str) -> Option> { match self.annotations.get(name) { Some(AnnotationValue::Atom(x)) => Some(x.clone()), _ => None, } } pub fn bool(&self, name: &str) -> Option { match self.annotations.get(name) { Some(AnnotationValue::Bool(x)) => Some(*x), _ => None, } } pub fn parse_atom(&self, name: &str) -> Option where T: Default + FromStr, { match self.annotations.get(name) { Some(AnnotationValue::Atom(x)) => Some( x.as_ref() .map_or(T::default(), |y| y.parse::().ok().unwrap()), ), _ => None, } } } /// Parse lists like "[x, y, z]". This is not implemented efficiently or well. fn parse_list(list: &str) -> Option> { if list.len() < 2 { return None; } match (list.chars().next(), list.chars().last()) { (Some('['), Some(']')) => Some( list[1..list.len() - 1] .split(',') .map(|x| x.trim().to_string()) .collect(), ), _ => None, } } cbindgen-0.27.0/src/bindgen/ir/cfg.rs000064400000000000000000000265421046102023000154100ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::fmt; use std::io::Write; use crate::bindgen::cargo::cargo_metadata::Dependency; use crate::bindgen::config::{Config, Language}; use crate::bindgen::writer::SourceWriter; #[derive(PartialEq, Eq)] enum DefineKey<'a> { Boolean(&'a str), Named(&'a str, &'a str), } impl<'a> DefineKey<'a> { fn load(key: &str) -> DefineKey { // TODO: dirty parser if !key.contains('=') { return DefineKey::Boolean(key); } let mut splits = key.trim().split('='); let name = match splits.next() { Some(n) => n.trim(), None => return DefineKey::Boolean(key), }; let value = match splits.next() { Some(v) => v.trim(), None => return DefineKey::Boolean(key), }; if splits.next().is_some() { return DefineKey::Boolean(key); } DefineKey::Named(name, value) } } #[derive(Debug, Clone)] pub enum Cfg { Boolean(String), Named(String, String), Any(Vec), All(Vec), Not(Box), } impl fmt::Display for Cfg { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Cfg::Boolean(key) => write!(f, "{}", key), Cfg::Named(key, value) => write!(f, "{} = {:?}", key, value), Cfg::Any(cfgs) => { write!(f, "any(")?; for (index, cfg) in cfgs.iter().enumerate() { if index > 0 { write!(f, ", ")?; } write!(f, "{}", cfg)?; } write!(f, ")") } Cfg::All(cfgs) => { write!(f, "all(")?; for (index, cfg) in cfgs.iter().enumerate() { if index > 0 { write!(f, ", ")?; } write!(f, "{}", cfg)?; } write!(f, ")") } Cfg::Not(cfg) => write!(f, "not({})", cfg), } } } impl syn::parse::Parse for Cfg { fn parse(input: syn::parse::ParseStream) -> syn::Result { let arg: syn::Meta = input.parse()?; match arg { syn::Meta::Path(path) => path .get_ident() .map(|ident| Cfg::Boolean(ident.to_string())) .ok_or_else(|| input.error("path must be identifier")), syn::Meta::NameValue(syn::MetaNameValue { path, value: syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit), .. }), .. }) => path .get_ident() .map(|ident| Cfg::Named(ident.to_string(), lit.value())) .ok_or_else(|| input.error("path must be identifier")), syn::Meta::List(meta) => { if meta.path.is_ident("not") { let cfg = meta.parse_args()?; Ok(Cfg::Not(Box::new(cfg))) } else if meta.path.is_ident("all") { let cfgs = meta.parse_args_with( syn::punctuated::Punctuated::::parse_terminated, )?; Ok(Cfg::All(cfgs.into_iter().collect())) } else if meta.path.is_ident("any") { let cfgs = meta.parse_args_with( syn::punctuated::Punctuated::::parse_terminated, )?; Ok(Cfg::Any(cfgs.into_iter().collect())) } else { Err(input.error("invalid list argument")) } } _ => Err(input.error("Failed to parse cfg")), } } } impl Cfg { pub fn join(cfgs: &[Cfg]) -> Option { if cfgs.is_empty() { None } else { Some(Cfg::All(cfgs.to_owned())) } } pub fn append(parent: Option<&Cfg>, child: Option) -> Option { match (parent, child) { (None, None) => None, (None, Some(child)) => Some(child), (Some(parent), None) => Some(parent.clone()), (Some(parent), Some(child)) => Some(Cfg::All(vec![parent.clone(), child])), } } pub fn load(attrs: &[syn::Attribute]) -> Option { let mut configs = Vec::new(); for attr in attrs { if let syn::Meta::List(meta @ syn::MetaList { path, .. }) = &attr.meta { if !path.is_ident("cfg") { continue; } let cfg = meta.parse_args().ok(); if let Some(config) = cfg { configs.push(config); } } } match configs.len() { 0 => None, 1 => Some(configs.pop().unwrap()), _ => Some(Cfg::All(configs)), } } pub fn load_metadata(dependency: &Dependency) -> Option { let target = dependency.target.as_ref()?; match syn::parse_str::(target) { Ok(target) => { // Parsing succeeded using the #[cfg] syntax if let syn::Meta::List(meta) = target { if !meta.path.is_ident("cfg") { return None; } meta.parse_args().ok() } else { None } } Err(_) => { // Parsing failed using #[cfg], this may be a literal target // name Some(Cfg::Boolean(target.clone())) } } } } pub trait ToCondition: Sized { fn to_condition(&self, config: &Config) -> Option; } impl ToCondition for Option { fn to_condition(&self, config: &Config) -> Option { self.as_ref()?.to_condition(config) } } impl ToCondition for Cfg { fn to_condition(&self, config: &Config) -> Option { match *self { Cfg::Boolean(ref cfg_name) => { let define = config .defines .iter() .find(|(key, ..)| DefineKey::Boolean(cfg_name) == DefineKey::load(key)); if let Some((_, define)) = define { Some(Condition::Define(define.to_owned())) } else { warn!( "Missing `[defines]` entry for `{}` in cbindgen config.", self, ); None } } Cfg::Named(ref cfg_name, ref cfg_value) => { let define = config.defines.iter().find(|(key, ..)| { DefineKey::Named(cfg_name, cfg_value) == DefineKey::load(key) }); if let Some((_, define)) = define { Some(Condition::Define(define.to_owned())) } else { warn!( "Missing `[defines]` entry for `{}` in cbindgen config.", self, ); None } } Cfg::Any(ref children) => { let conditions: Vec<_> = children .iter() .filter_map(|x| x.to_condition(config)) .collect(); match conditions.len() { 0 => None, 1 => conditions.into_iter().next(), _ => Some(Condition::Any(conditions)), } } Cfg::All(ref children) => { let cfgs: Vec<_> = children .iter() .filter_map(|x| x.to_condition(config)) .collect(); match cfgs.len() { 0 => None, 1 => cfgs.into_iter().next(), _ => Some(Condition::All(cfgs)), } } Cfg::Not(ref child) => child .to_condition(config) .map(|cfg| Condition::Not(Box::new(cfg))), } } } #[derive(Debug, Clone)] pub enum Condition { Define(String), Any(Vec), All(Vec), Not(Box), } impl Condition { fn write(&self, config: &Config, out: &mut SourceWriter) { match *self { Condition::Define(ref define) => { if config.language == Language::Cython { write!(out, "{}", define); } else { out.write("defined("); write!(out, "{}", define); out.write(")"); } } Condition::Any(ref conditions) => { out.write("("); for (i, condition) in conditions.iter().enumerate() { if i != 0 { out.write(if config.language == Language::Cython { " or " } else { " || " }); } condition.write(config, out); } out.write(")"); } Condition::All(ref conditions) => { out.write("("); for (i, condition) in conditions.iter().enumerate() { if i != 0 { out.write(if config.language == Language::Cython { " and " } else { " && " }); } condition.write(config, out); } out.write(")"); } Condition::Not(ref condition) => { out.write(if config.language == Language::Cython { "not " } else { "!" }); condition.write(config, out); } } } } pub trait ConditionWrite { fn write_before(&self, config: &Config, out: &mut SourceWriter); fn write_after(&self, config: &Config, out: &mut SourceWriter); } impl ConditionWrite for Option { fn write_before(&self, config: &Config, out: &mut SourceWriter) { if let Some(ref cfg) = *self { if config.language == Language::Cython { out.write("IF "); cfg.write(config, out); out.open_brace(); } else { out.push_set_spaces(0); out.write("#if "); cfg.write(config, out); out.pop_set_spaces(); out.new_line(); } } } fn write_after(&self, config: &Config, out: &mut SourceWriter) { if self.is_some() { if config.language == Language::Cython { out.close_brace(false); } else { out.new_line(); out.push_set_spaces(0); out.write("#endif"); out.pop_set_spaces(); } } } } cbindgen-0.27.0/src/bindgen/ir/constant.rs000064400000000000000000000577611046102023000165110ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::borrow::Cow; use std::collections::HashMap; use std::io::Write; use syn::ext::IdentExt; use syn::UnOp; use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path, Struct, ToCondition, Type, }; use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::library::Library; use crate::bindgen::writer::SourceWriter; use crate::bindgen::Bindings; fn member_to_ident(member: &syn::Member) -> String { match member { syn::Member::Named(ref name) => name.unraw().to_string(), syn::Member::Unnamed(ref index) => format!("_{}", index.index), } } // TODO: Maybe add support to more std associated constants. pub(crate) fn to_known_assoc_constant(associated_to: &Path, name: &str) -> Option { use crate::bindgen::ir::{IntKind, PrimitiveType}; if name != "MAX" && name != "MIN" { return None; } let prim = PrimitiveType::maybe(associated_to.name())?; let prefix = match prim { PrimitiveType::Integer { kind, signed, zeroable: _, } => match kind { IntKind::B8 => { if signed { "INT8" } else { "UINT8" } } IntKind::B16 => { if signed { "INT16" } else { "UINT16" } } IntKind::B32 => { if signed { "INT32" } else { "UINT32" } } IntKind::B64 => { if signed { "INT64" } else { "UINT64" } } _ => return None, }, _ => return None, }; Some(format!("{}_{}", prefix, name)) } #[derive(Debug, Clone)] pub enum Literal { Expr(String), Path { associated_to: Option<(Path, String)>, name: String, }, PostfixUnaryOp { op: &'static str, value: Box, }, BinOp { left: Box, op: &'static str, right: Box, }, FieldAccess { base: Box, field: String, }, Struct { path: Path, export_name: String, fields: HashMap, }, Cast { ty: Type, value: Box, }, } impl Literal { fn replace_self_with(&mut self, self_ty: &Path) { match *self { Literal::PostfixUnaryOp { ref mut value, .. } => { value.replace_self_with(self_ty); } Literal::BinOp { ref mut left, ref mut right, .. } => { left.replace_self_with(self_ty); right.replace_self_with(self_ty); } Literal::FieldAccess { ref mut base, .. } => { base.replace_self_with(self_ty); } Literal::Struct { ref mut path, ref mut export_name, ref mut fields, } => { if path.replace_self_with(self_ty) { self_ty.name().clone_into(export_name); } for ref mut expr in fields.values_mut() { expr.replace_self_with(self_ty); } } Literal::Cast { ref mut ty, ref mut value, } => { ty.replace_self_with(self_ty); value.replace_self_with(self_ty); } Literal::Path { ref mut associated_to, .. } => { if let Some((ref mut path, ref mut export_name)) = *associated_to { if path.replace_self_with(self_ty) { self_ty.name().clone_into(export_name); } } } Literal::Expr(..) => {} } } fn is_valid(&self, bindings: &Bindings) -> bool { match *self { Literal::Expr(..) => true, Literal::Path { ref associated_to, ref name, } => { if let Some((ref path, _export_name)) = associated_to { return bindings.struct_exists(path) || to_known_assoc_constant(path, name).is_some(); } true } Literal::PostfixUnaryOp { ref value, .. } => value.is_valid(bindings), Literal::BinOp { ref left, ref right, .. } => left.is_valid(bindings) && right.is_valid(bindings), Literal::FieldAccess { ref base, .. } => base.is_valid(bindings), Literal::Struct { ref path, .. } => bindings.struct_exists(path), Literal::Cast { ref value, .. } => value.is_valid(bindings), } } fn can_be_constexpr(&self) -> bool { !self.has_pointer_casts() } fn visit(&self, visitor: &mut impl FnMut(&Self) -> bool) -> bool { if !visitor(self) { return false; } match self { Literal::Expr(..) | Literal::Path { .. } => true, Literal::PostfixUnaryOp { ref value, .. } => value.visit(visitor), Literal::BinOp { ref left, ref right, .. } => left.visit(visitor) && right.visit(visitor), Literal::FieldAccess { ref base, .. } => base.visit(visitor), Literal::Struct { ref fields, .. } => { for (_name, field) in fields.iter() { if !field.visit(visitor) { return false; } } true } Literal::Cast { ref value, .. } => value.visit(visitor), } } fn has_pointer_casts(&self) -> bool { let mut has_pointer_casts = false; self.visit(&mut |lit| { if let Literal::Cast { ref ty, .. } = *lit { has_pointer_casts = has_pointer_casts || ty.is_ptr(); } !has_pointer_casts }); has_pointer_casts } pub fn uses_only_primitive_types(&self) -> bool { let mut uses_only_primitive_types = true; self.visit(&mut |lit| { // XXX This is a bit sketchy, but alas. uses_only_primitive_types = uses_only_primitive_types && match *lit { Literal::Struct { .. } => false, Literal::Cast { ref ty, .. } => ty.is_primitive_or_ptr_primitive(), _ => true, }; uses_only_primitive_types }); uses_only_primitive_types } } impl Literal { pub fn rename_for_config(&mut self, config: &Config) { match self { Literal::Struct { ref mut export_name, fields, .. } => { config.export.rename(export_name); for lit in fields.values_mut() { lit.rename_for_config(config); } } Literal::FieldAccess { ref mut base, .. } => { base.rename_for_config(config); } Literal::Path { ref mut associated_to, ref mut name, } => { if let Some((_path, ref mut export_name)) = associated_to { config.export.rename(export_name); } else { config.export.rename(name); } } Literal::PostfixUnaryOp { ref mut value, .. } => { value.rename_for_config(config); } Literal::BinOp { ref mut left, ref mut right, .. } => { left.rename_for_config(config); right.rename_for_config(config); } Literal::Expr(_) => {} Literal::Cast { ref mut ty, ref mut value, } => { ty.rename_for_config(config, &GenericParams::default()); value.rename_for_config(config); } } } // Translate from full blown `syn::Expr` into a simpler `Literal` type pub fn load(expr: &syn::Expr) -> Result { match *expr { // Match binary expressions of the form `a * b` syn::Expr::Binary(ref bin_expr) => { let l = Self::load(&bin_expr.left)?; let r = Self::load(&bin_expr.right)?; let op = match bin_expr.op { syn::BinOp::Add(..) => "+", syn::BinOp::Sub(..) => "-", syn::BinOp::Mul(..) => "*", syn::BinOp::Div(..) => "/", syn::BinOp::Rem(..) => "%", syn::BinOp::And(..) => "&&", syn::BinOp::Or(..) => "||", syn::BinOp::BitXor(..) => "^", syn::BinOp::BitAnd(..) => "&", syn::BinOp::BitOr(..) => "|", syn::BinOp::Shl(..) => "<<", syn::BinOp::Shr(..) => ">>", syn::BinOp::Eq(..) => "==", syn::BinOp::Lt(..) => "<", syn::BinOp::Le(..) => "<=", syn::BinOp::Ne(..) => "!=", syn::BinOp::Ge(..) => ">=", syn::BinOp::Gt(..) => ">", syn::BinOp::AddAssign(..) => "+=", syn::BinOp::SubAssign(..) => "-=", syn::BinOp::MulAssign(..) => "*=", syn::BinOp::DivAssign(..) => "/=", syn::BinOp::RemAssign(..) => "%=", syn::BinOp::BitXorAssign(..) => "^=", syn::BinOp::BitAndAssign(..) => "&=", syn::BinOp::BitOrAssign(..) => "|=", syn::BinOp::ShlAssign(..) => "<<=", syn::BinOp::ShrAssign(..) => ">>=", currently_unknown => { return Err(format!( "unsupported binary operator: {:?}", currently_unknown )) } }; Ok(Literal::BinOp { left: Box::new(l), op, right: Box::new(r), }) } // Match literals like true, 'a', 32 etc syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => { match lit { syn::Lit::Byte(ref value) => Ok(Literal::Expr(format!("{}", value.value()))), syn::Lit::Char(ref value) => Ok(Literal::Expr(match value.value() as u32 { 0..=255 => format!("'{}'", value.value().escape_default()), other_code => format!(r"U'\U{:08X}'", other_code), })), syn::Lit::Int(ref value) => { let suffix = match value.suffix() { "u64" => "ull", "i64" => "ll", "u32" => "u", _ if value.base10_parse::().is_err() => "ull", _ => "", }; Ok(Literal::Expr(format!( "{}{}", value.base10_digits(), suffix ))) } syn::Lit::Float(ref value) => { Ok(Literal::Expr(value.base10_digits().to_string())) } syn::Lit::Bool(ref value) => Ok(Literal::Expr(format!("{}", value.value))), // TODO: Add support for byte string and Verbatim _ => Err(format!("Unsupported literal expression. {:?}", *lit)), } } syn::Expr::Field(syn::ExprField { ref base, ref member, .. }) => Ok(Literal::FieldAccess { base: Box::new(Literal::load(base)?), field: member_to_ident(member), }), syn::Expr::Call(syn::ExprCall { ref func, ref args, .. }) => { let struct_name = match Literal::load(func)? { Literal::Path { associated_to: None, name, } => name, _ => return Err(format!("Unsupported call expression. {:?}", *expr)), }; let mut fields = HashMap::::default(); for (index, arg) in args.iter().enumerate() { let ident = member_to_ident(&syn::Member::Unnamed(syn::Index::from(index))).to_string(); let value = Literal::load(arg)?; fields.insert(ident, value); } Ok(Literal::Struct { path: Path::new(struct_name.clone()), export_name: struct_name, fields, }) } syn::Expr::Struct(syn::ExprStruct { ref path, ref fields, .. }) => { let struct_name = path.segments[0].ident.unraw().to_string(); let mut field_map = HashMap::::default(); for field in fields { let ident = member_to_ident(&field.member).to_string(); let value = Literal::load(&field.expr)?; field_map.insert(ident, value); } Ok(Literal::Struct { path: Path::new(struct_name.clone()), export_name: struct_name, fields: field_map, }) } syn::Expr::Unary(syn::ExprUnary { ref op, ref expr, .. }) => match *op { UnOp::Not(_) => { let val = Self::load(expr)?; Ok(Literal::PostfixUnaryOp { op: "~", value: Box::new(val), }) } UnOp::Neg(_) => { let val = Self::load(expr)?; Ok(Literal::PostfixUnaryOp { op: "-", value: Box::new(val), }) } _ => Err(format!("Unsupported Unary expression. {:?}", *op)), }, // Match identifiers, like `5 << SHIFT` syn::Expr::Path(syn::ExprPath { ref path, .. }) => { // Handle only the simplest identifiers and Associated::IDENT // kind of syntax. Ok(match path.segments.len() { 1 => Literal::Path { associated_to: None, name: path.segments[0].ident.to_string(), }, 2 => { let struct_name = path.segments[0].ident.to_string(); Literal::Path { associated_to: Some((Path::new(&struct_name), struct_name)), name: path.segments[1].ident.to_string(), } } _ => return Err(format!("Unsupported path expression. {:?}", path)), }) } syn::Expr::Paren(syn::ExprParen { ref expr, .. }) => Self::load(expr), syn::Expr::Cast(syn::ExprCast { ref expr, ref ty, .. }) => { let val = Self::load(expr)?; match Type::load(ty)? { Some(ty) => Ok(Literal::Cast { ty, value: Box::new(val), }), None => Err("Cannot cast to zero sized type.".to_owned()), } } _ => Err(format!("Unsupported expression. {:?}", *expr)), } } } #[derive(Debug, Clone)] pub struct Constant { pub path: Path, pub export_name: String, pub ty: Type, pub value: Literal, pub cfg: Option, pub annotations: AnnotationSet, pub documentation: Documentation, pub associated_to: Option, } impl Constant { pub fn load( path: Path, mod_cfg: Option<&Cfg>, ty: &syn::Type, expr: &syn::Expr, attrs: &[syn::Attribute], associated_to: Option, ) -> Result { let ty = Type::load(ty)?; let mut ty = match ty { Some(ty) => ty, None => { return Err("Cannot have a zero sized const definition.".to_owned()); } }; let mut lit = Literal::load(expr)?; if let Some(ref associated_to) = associated_to { ty.replace_self_with(associated_to); lit.replace_self_with(associated_to); } Ok(Constant::new( path, ty, lit, Cfg::append(mod_cfg, Cfg::load(attrs)), AnnotationSet::load(attrs)?, Documentation::load(attrs), associated_to, )) } pub fn new( path: Path, ty: Type, value: Literal, cfg: Option, annotations: AnnotationSet, documentation: Documentation, associated_to: Option, ) -> Self { let export_name = path.name().to_owned(); Self { path, export_name, ty, value, cfg, annotations, documentation, associated_to, } } pub fn uses_only_primitive_types(&self) -> bool { self.value.uses_only_primitive_types() && self.ty.is_primitive_or_ptr_primitive() } } impl Item for Constant { fn path(&self) -> &Path { &self.path } fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { self.ty.add_dependencies(library, out); } fn export_name(&self) -> &str { &self.export_name } fn cfg(&self) -> Option<&Cfg> { self.cfg.as_ref() } fn annotations(&self) -> &AnnotationSet { &self.annotations } fn annotations_mut(&mut self) -> &mut AnnotationSet { &mut self.annotations } fn documentation(&self) -> &Documentation { &self.documentation } fn container(&self) -> ItemContainer { ItemContainer::Constant(self.clone()) } fn rename_for_config(&mut self, config: &Config) { if self.associated_to.is_none() { config.export.rename(&mut self.export_name); } self.value.rename_for_config(config); self.ty.rename_for_config(config, &GenericParams::default()); // FIXME: should probably propagate something here } fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) { self.ty.resolve_declaration_types(resolver); } fn generic_params(&self) -> &GenericParams { GenericParams::empty() } } impl Constant { pub fn write_declaration( &self, config: &Config, language_backend: &mut LB, out: &mut SourceWriter, associated_to_struct: &Struct, ) { debug_assert!(self.associated_to.is_some()); debug_assert!(config.language == Language::Cxx); debug_assert!(!associated_to_struct.is_transparent); debug_assert!(config.structure.associated_constants_in_body); debug_assert!(config.constant.allow_static_const); if let Type::Ptr { is_const: true, .. } = self.ty { out.write("static "); } else { out.write("static const "); } language_backend.write_type(out, &self.ty); write!(out, " {};", self.export_name()) } pub fn write( &self, config: &Config, language_backend: &mut LB, out: &mut SourceWriter, associated_to_struct: Option<&Struct>, ) { if let Some(assoc) = associated_to_struct { if assoc.is_generic() { return; // Not tested / implemented yet, so bail out. } } if !self.value.is_valid(out.bindings()) { return; } let associated_to_transparent = associated_to_struct.map_or(false, |s| s.is_transparent); let in_body = associated_to_struct.is_some() && config.language == Language::Cxx && config.structure.associated_constants_in_body && config.constant.allow_static_const && !associated_to_transparent; let condition = self.cfg.to_condition(config); condition.write_before(config, out); let name = if in_body { Cow::Owned(format!( "{}::{}", associated_to_struct.unwrap().export_name(), self.export_name(), )) } else if self.associated_to.is_none() { Cow::Borrowed(self.export_name()) } else { let associated_name = match associated_to_struct { Some(s) => Cow::Borrowed(s.export_name()), None => { let mut name = self.associated_to.as_ref().unwrap().name().to_owned(); config.export.rename(&mut name); Cow::Owned(name) } }; Cow::Owned(format!("{}_{}", associated_name, self.export_name())) }; let mut value = &self.value; while let Literal::Struct { path, fields, .. } = value { if !out.bindings().struct_is_transparent(path) { break; } value = fields.iter().next().unwrap().1 } language_backend.write_documentation(out, self.documentation()); let allow_constexpr = config.constant.allow_constexpr && self.value.can_be_constexpr(); match config.language { Language::Cxx if config.constant.allow_static_const || allow_constexpr => { if allow_constexpr { out.write("constexpr ") } if config.constant.allow_static_const { out.write(if in_body { "inline " } else { "static " }); } if let Type::Ptr { is_const: true, .. } = self.ty { // Nothing. } else { out.write("const "); } language_backend.write_type(out, &self.ty); write!(out, " {} = ", name); language_backend.write_literal(out, value); write!(out, ";"); } Language::Cxx | Language::C => { write!(out, "#define {} ", name); language_backend.write_literal(out, value); } Language::Cython => { out.write("const "); language_backend.write_type(out, &self.ty); // For extern Cython declarations the initializer is ignored, // but still useful as documentation, so we write it as a comment. write!(out, " {} # = ", name); language_backend.write_literal(out, value); } } condition.write_after(config, out); } } cbindgen-0.27.0/src/bindgen/ir/documentation.rs000064400000000000000000000015571046102023000175210ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::bindgen::utilities::SynAttributeHelpers; #[derive(Debug, Clone)] pub struct Documentation { pub doc_comment: Vec, } impl Documentation { pub fn load(attrs: &[syn::Attribute]) -> Self { let doc = attrs .get_comment_lines() .into_iter() .filter(|x| !x.trim_start().starts_with("cbindgen:")) .collect(); Documentation { doc_comment: doc } } pub fn simple(line: &str) -> Self { Documentation { doc_comment: vec![line.to_owned()], } } pub fn none() -> Self { Documentation { doc_comment: Vec::new(), } } } cbindgen-0.27.0/src/bindgen/ir/enumeration.rs000064400000000000000000001472061046102023000172000ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::io::Write; use syn::ext::IdentExt; use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ AnnotationSet, AnnotationValue, Cfg, ConditionWrite, DeprecatedNoteKind, Documentation, Field, GenericArgument, GenericParams, GenericPath, Item, ItemContainer, Literal, Path, Repr, ReprStyle, Struct, ToCondition, Type, }; use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::library::Library; use crate::bindgen::mangle; use crate::bindgen::monomorph::Monomorphs; use crate::bindgen::rename::{IdentifierType, RenameRule}; use crate::bindgen::reserved; use crate::bindgen::writer::{ListType, SourceWriter}; #[allow(clippy::large_enum_variant)] #[derive(Debug, Clone)] pub enum VariantBody { Empty(AnnotationSet), Body { /// The variant field / export name. name: String, /// The struct with all the items. body: Struct, /// A separate named struct is not created for this variant, /// an unnamed struct is inlined at the point of use instead. /// This is a reasonable thing to do only for tuple variants with a single field. inline: bool, /// Generated cast methods return the variant's only field instead of the variant itself. /// For backward compatibility casts are inlined in a slightly /// larger set of cases than whole variants. inline_casts: bool, }, } impl VariantBody { fn empty() -> Self { Self::Empty(AnnotationSet::new()) } pub fn annotations(&self) -> &AnnotationSet { match *self { Self::Empty(ref anno) => anno, Self::Body { ref body, .. } => &body.annotations, } } fn is_empty(&self) -> bool { match *self { Self::Empty(..) => true, Self::Body { .. } => false, } } fn specialize( &self, generic_values: &[GenericArgument], mappings: &[(&Path, &GenericArgument)], config: &Config, ) -> Self { match *self { Self::Empty(ref annos) => Self::Empty(annos.clone()), Self::Body { ref name, ref body, inline, inline_casts, } => Self::Body { name: name.clone(), body: body.specialize(generic_values, mappings, config), inline, inline_casts, }, } } } #[derive(Debug, Clone)] pub struct EnumVariant { pub name: String, pub export_name: String, pub discriminant: Option, pub body: VariantBody, pub cfg: Option, pub documentation: Documentation, } impl EnumVariant { fn load( inline_tag_field: bool, variant: &syn::Variant, generic_params: GenericParams, mod_cfg: Option<&Cfg>, self_path: &Path, enum_annotations: &AnnotationSet, config: &Config, ) -> Result { let discriminant = match variant.discriminant { Some((_, ref expr)) => Some(Literal::load(expr)?), None => None, }; fn parse_fields( inline_tag_field: bool, fields: &syn::punctuated::Punctuated, self_path: &Path, inline_name: Option<&str>, ) -> Result, String> { let mut res = Vec::new(); if inline_tag_field { res.push(Field::from_name_and_type( inline_name.map_or_else(|| "tag".to_string(), |name| format!("{}_tag", name)), Type::Path(GenericPath::new(Path::new("Tag"), vec![])), )); } for (i, field) in fields.iter().enumerate() { if let Some(mut ty) = Type::load(&field.ty)? { ty.replace_self_with(self_path); res.push(Field { name: inline_name.map_or_else( || match field.ident { Some(ref ident) => ident.unraw().to_string(), None => i.to_string(), }, |name| name.to_string(), ), ty, cfg: Cfg::load(&field.attrs), annotations: AnnotationSet::load(&field.attrs)?, documentation: Documentation::load(&field.attrs), }); } } Ok(res) } let variant_cfg = Cfg::append(mod_cfg, Cfg::load(&variant.attrs)); let mut annotations = AnnotationSet::load(&variant.attrs)?; if let Some(b) = enum_annotations.bool("derive-ostream") { annotations.add_default("derive-ostream", AnnotationValue::Bool(b)); } let body_rule = enum_annotations .parse_atom::("rename-variant-name-fields") .unwrap_or(config.enumeration.rename_variant_name_fields); let body = match variant.fields { syn::Fields::Unit => VariantBody::Empty(annotations), syn::Fields::Named(ref fields) => { let path = Path::new(format!("{}_Body", variant.ident)); let name = body_rule .apply( &variant.ident.unraw().to_string(), IdentifierType::StructMember, ) .into_owned(); VariantBody::Body { body: Struct::new( path, generic_params, parse_fields(inline_tag_field, &fields.named, self_path, None)?, inline_tag_field, true, None, false, None, annotations, Documentation::none(), ), name, inline: false, inline_casts: false, } } syn::Fields::Unnamed(ref fields) => { let path = Path::new(format!("{}_Body", variant.ident)); let name = body_rule .apply( &variant.ident.unraw().to_string(), IdentifierType::StructMember, ) .into_owned(); let inline_casts = fields.unnamed.len() == 1; // In C++ types with destructors cannot be put into unnamed structs like the // inlining requires, and it's hard to detect such types. // Besides that for C++ we generate casts/getters that can be used instead of // direct field accesses and also have a benefit of being checked. // As a result we don't currently inline variant definitions in C++ mode at all. let inline = inline_casts && config.language != Language::Cxx; let inline_name = if inline { Some(&*name) } else { None }; VariantBody::Body { body: Struct::new( path, generic_params, parse_fields(inline_tag_field, &fields.unnamed, self_path, inline_name)?, inline_tag_field, true, None, false, None, annotations, Documentation::none(), ), name, inline, inline_casts, } } }; Ok(EnumVariant::new( variant.ident.unraw().to_string(), discriminant, body, variant_cfg, Documentation::load(&variant.attrs), )) } pub fn new( name: String, discriminant: Option, body: VariantBody, cfg: Option, documentation: Documentation, ) -> Self { let export_name = name.clone(); Self { name, export_name, discriminant, body, cfg, documentation, } } fn simplify_standard_types(&mut self, config: &Config) { if let VariantBody::Body { ref mut body, .. } = self.body { body.simplify_standard_types(config); } } fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { if let VariantBody::Body { ref body, .. } = self.body { body.add_dependencies(library, out); } } fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) { if let VariantBody::Body { ref mut body, .. } = self.body { body.resolve_declaration_types(resolver); } } fn specialize( &self, generic_values: &[GenericArgument], mappings: &[(&Path, &GenericArgument)], config: &Config, ) -> Self { Self::new( mangle::mangle_name(&self.name, generic_values, &config.export.mangle), self.discriminant.clone(), self.body.specialize(generic_values, mappings, config), self.cfg.clone(), self.documentation.clone(), ) } fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) { if let VariantBody::Body { ref body, .. } = self.body { body.add_monomorphs(library, out); } } fn mangle_paths(&mut self, monomorphs: &Monomorphs) { if let VariantBody::Body { ref mut body, .. } = self.body { body.mangle_paths(monomorphs); } } } #[derive(Debug, Clone)] pub struct Enum { pub path: Path, pub export_name: String, pub generic_params: GenericParams, pub repr: Repr, pub variants: Vec, pub tag: Option, pub cfg: Option, pub annotations: AnnotationSet, pub documentation: Documentation, } impl Enum { /// Name of the generated tag enum. pub(crate) fn tag_name(&self) -> &str { self.tag.as_deref().unwrap_or_else(|| self.export_name()) } /// Enum with data turns into a union of structs with each struct having its own tag field. pub(crate) fn inline_tag_field(repr: &Repr) -> bool { repr.style != ReprStyle::C } pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) { if self.is_generic() { return; } for v in &self.variants { v.add_monomorphs(library, out); } } fn can_derive_eq(&self) -> bool { if self.tag.is_none() { return false; } self.variants.iter().all(|variant| match variant.body { VariantBody::Empty(..) => true, VariantBody::Body { ref body, .. } => body.can_derive_eq(), }) } pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) { for variant in &mut self.variants { variant.mangle_paths(monomorphs); } } pub fn load( item: &syn::ItemEnum, mod_cfg: Option<&Cfg>, config: &Config, ) -> Result { let repr = Repr::load(&item.attrs)?; if repr.style == ReprStyle::Rust && repr.ty.is_none() { return Err("Enum is not marked with a valid #[repr(prim)] or #[repr(C)].".to_owned()); } // TODO: Implement translation of aligned enums. if repr.align.is_some() { return Err("Enum is marked with #[repr(align(...))] or #[repr(packed)].".to_owned()); } let path = Path::new(item.ident.unraw().to_string()); let generic_params = GenericParams::load(&item.generics)?; let mut variants = Vec::new(); let mut has_data = false; let annotations = AnnotationSet::load(&item.attrs)?; for variant in item.variants.iter() { let variant = EnumVariant::load( Self::inline_tag_field(&repr), variant, generic_params.clone(), mod_cfg, &path, &annotations, config, )?; has_data = has_data || !variant.body.is_empty(); variants.push(variant); } if let Some(names) = annotations.list("enum-trailing-values") { for name in names { variants.push(EnumVariant::new( name, None, VariantBody::empty(), None, Documentation::none(), )); } } if config.enumeration.add_sentinel(&annotations) { variants.push(EnumVariant::new( "Sentinel".to_owned(), None, VariantBody::empty(), None, Documentation::simple(" Must be last for serialization purposes"), )); } let tag = if has_data { Some("Tag".to_string()) } else { None }; Ok(Enum::new( path, generic_params, repr, variants, tag, Cfg::append(mod_cfg, Cfg::load(&item.attrs)), annotations, Documentation::load(&item.attrs), )) } #[allow(clippy::too_many_arguments)] pub fn new( path: Path, generic_params: GenericParams, repr: Repr, variants: Vec, tag: Option, cfg: Option, annotations: AnnotationSet, documentation: Documentation, ) -> Self { let export_name = path.name().to_owned(); Self { path, export_name, generic_params, repr, variants, tag, cfg, annotations, documentation, } } } impl Item for Enum { fn path(&self) -> &Path { &self.path } fn export_name(&self) -> &str { &self.export_name } fn cfg(&self) -> Option<&Cfg> { self.cfg.as_ref() } fn annotations(&self) -> &AnnotationSet { &self.annotations } fn annotations_mut(&mut self) -> &mut AnnotationSet { &mut self.annotations } fn documentation(&self) -> &Documentation { &self.documentation } fn container(&self) -> ItemContainer { ItemContainer::Enum(self.clone()) } fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) { if self.tag.is_some() { if self.repr.style == ReprStyle::C { resolver.add_struct(&self.path); } else { resolver.add_union(&self.path); } } else if self.repr.style == ReprStyle::C { resolver.add_enum(&self.path); } else { // This is important to handle conflicting names with opaque items. resolver.add_none(&self.path); } } fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) { for &mut ref mut var in &mut self.variants { var.resolve_declaration_types(resolver); } } fn generic_params(&self) -> &GenericParams { &self.generic_params } fn rename_for_config(&mut self, config: &Config) { config.export.rename(&mut self.export_name); if config.language != Language::Cxx && self.tag.is_some() { // it makes sense to always prefix Tag with type name in C let new_tag = format!("{}_Tag", self.export_name); if self.repr.style == ReprStyle::Rust { for variant in &mut self.variants { if let VariantBody::Body { ref mut body, .. } = variant.body { let path = Path::new(new_tag.clone()); let generic_path = GenericPath::new(path, vec![]); body.fields[0].ty = Type::Path(generic_path); } } } self.tag = Some(new_tag); } for variant in &mut self.variants { reserved::escape(&mut variant.export_name); if let Some(discriminant) = &mut variant.discriminant { discriminant.rename_for_config(config); } if let VariantBody::Body { ref mut name, ref mut body, .. } = variant.body { body.rename_for_config(config); reserved::escape(name); } } if config.enumeration.prefix_with_name || self.annotations.bool("prefix-with-name").unwrap_or(false) { let separator = if config.export.mangle.remove_underscores { "" } else { "_" }; for variant in &mut self.variants { variant.export_name = format!("{}{}{}", self.export_name, separator, variant.export_name); if let VariantBody::Body { ref mut body, .. } = variant.body { body.export_name = format!("{}{}{}", self.export_name, separator, body.export_name()); } } } let rules = self .annotations .parse_atom::("rename-all") .unwrap_or(config.enumeration.rename_variants); if let Some(r) = rules.not_none() { self.variants = self .variants .iter() .map(|variant| { EnumVariant::new( r.apply( &variant.export_name, IdentifierType::EnumVariant { prefix: &self.export_name, }, ) .into_owned(), variant.discriminant.clone(), match variant.body { VariantBody::Empty(..) => variant.body.clone(), VariantBody::Body { ref name, ref body, inline, inline_casts, } => VariantBody::Body { name: r.apply(name, IdentifierType::StructMember).into_owned(), body: body.clone(), inline, inline_casts, }, }, variant.cfg.clone(), variant.documentation.clone(), ) }) .collect(); } } fn instantiate_monomorph( &self, generic_values: &[GenericArgument], library: &Library, out: &mut Monomorphs, ) { let mappings = self.generic_params.call(self.path.name(), generic_values); for variant in &self.variants { if let VariantBody::Body { ref body, .. } = variant.body { body.instantiate_monomorph(generic_values, library, out); } } let mangled_path = mangle::mangle_path( &self.path, generic_values, &library.get_config().export.mangle, ); let monomorph = Enum::new( mangled_path, GenericParams::default(), self.repr, self.variants .iter() .map(|v| v.specialize(generic_values, &mappings, library.get_config())) .collect(), self.tag.clone(), self.cfg.clone(), self.annotations.clone(), self.documentation.clone(), ); out.insert_enum(library, self, monomorph, generic_values.to_owned()); } fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { for variant in &self.variants { variant.add_dependencies(library, out); } } } impl Enum { /// Emit the tag enum and convenience methods for it. /// For enums with data this is only a part of the output, /// but for enums without data it's the whole output (modulo doc comments etc.). pub(crate) fn write_tag_enum< F: Write, LB: LanguageBackend, WV: Fn(&mut LB, &mut SourceWriter, &EnumVariant), >( &self, config: &Config, language_backend: &mut LB, out: &mut SourceWriter, size: Option<&str>, write_variant: WV, ) { let tag_name = self.tag_name(); // Open the tag enum. match config.language { Language::C => { if let Some(prim) = size { // If we need to specify size, then we have no choice but to create a typedef, // so `config.style` is not respected. write!(out, "enum"); if let Some(note) = self .annotations .deprecated_note(config, DeprecatedNoteKind::Enum) { write!(out, " {}", note); } write!(out, " {}", tag_name); if config.cpp_compatible_c() { out.new_line(); out.write("#ifdef __cplusplus"); out.new_line(); write!(out, " : {}", prim); out.new_line(); out.write("#endif // __cplusplus"); out.new_line(); } } else { if config.style.generate_typedef() { out.write("typedef "); } out.write("enum"); if let Some(note) = self .annotations .deprecated_note(config, DeprecatedNoteKind::Enum) { write!(out, " {}", note); } if config.style.generate_tag() { write!(out, " {}", tag_name); } } } Language::Cxx => { if config.enumeration.enum_class(&self.annotations) { out.write("enum class"); } else { out.write("enum"); } if self.annotations.must_use(config) { if let Some(ref anno) = config.enumeration.must_use { write!(out, " {}", anno) } } if let Some(note) = self .annotations .deprecated_note(config, DeprecatedNoteKind::Enum) { write!(out, " {}", note); } write!(out, " {}", tag_name); if let Some(prim) = size { write!(out, " : {}", prim); } } Language::Cython => { if size.is_some() { // If we need to specify size, then we have no choice but to create a typedef, // so `config.style` is not respected. write!(out, "cdef enum"); } else { write!(out, "{}enum {}", config.style.cython_def(), tag_name); } } } out.open_brace(); // Emit enumerators for the tag enum. for (i, variant) in self.variants.iter().enumerate() { if i != 0 { out.new_line() } write_variant(language_backend, out, variant); } // Close the tag enum. if config.language == Language::C && size.is_none() && config.style.generate_typedef() { out.close_brace(false); write!(out, " {};", tag_name); } else { out.close_brace(true); } // Emit typedef specifying the tag enum's size if necessary. // In C++ enums can "inherit" from numeric types (`enum E: uint8_t { ... }`), // but in C `typedef uint8_t E` is the only way to give a fixed size to `E`. if let Some(prim) = size { if config.cpp_compatible_c() { out.new_line_if_not_start(); out.write("#ifndef __cplusplus"); } if config.language != Language::Cxx { out.new_line(); write!(out, "{} {} {};", config.language.typedef(), prim, tag_name); } if config.cpp_compatible_c() { out.new_line_if_not_start(); out.write("#endif // __cplusplus"); } } // Emit convenience methods for the tag enum. self.write_derived_functions_enum(config, language_backend, out); } /// The code here mirrors the beginning of `Struct::write` and `Union::write`. pub(crate) fn open_struct_or_union( &self, config: &Config, out: &mut SourceWriter, inline_tag_field: bool, ) { match config.language { Language::C if config.style.generate_typedef() => out.write("typedef "), Language::C | Language::Cxx => {} Language::Cython => out.write(config.style.cython_def()), } out.write(if inline_tag_field { "union" } else { "struct" }); if self.annotations.must_use(config) { if let Some(ref anno) = config.structure.must_use { write!(out, " {}", anno); } } if let Some(note) = self .annotations .deprecated_note(config, DeprecatedNoteKind::Struct) { write!(out, " {} ", note); } if config.language != Language::C || config.style.generate_tag() { write!(out, " {}", self.export_name()); } out.open_brace(); // Emit the pre_body section, if relevant. if let Some(body) = config.export.pre_body(&self.path) { out.write_raw_block(body); out.new_line(); } } /// Emit struct definitions for variants having data. pub(crate) fn write_variant_defs( &self, config: &Config, language_backend: &mut LB, // TODO probably need only one of Config/LanguageBackend out: &mut SourceWriter, ) { for variant in &self.variants { if let VariantBody::Body { ref body, inline: false, .. } = variant.body { out.new_line(); out.new_line(); let condition = variant.cfg.to_condition(config); // Cython doesn't support conditional enum variants. if config.language != Language::Cython { condition.write_before(config, out); } language_backend.write_struct(out, body); if config.language != Language::Cython { condition.write_after(config, out); } } } } /// Emit tag field that is separate from all variants. /// For non-inline tag scenario this is *the* tag field, and it does not exist in the variants. /// For the inline tag scenario this is just a convenience and another way /// to refer to the same tag that exist in all the variants. pub(crate) fn write_tag_field( &self, config: &Config, out: &mut SourceWriter, size: Option<&str>, inline_tag_field: bool, tag_name: &str, ) { // C++ allows accessing only common initial sequence of union // fields so we have to wrap the tag field into an anonymous struct. let wrap_tag = inline_tag_field && config.language == Language::Cxx; if wrap_tag { out.write("struct"); out.open_brace(); } if config.language == Language::C && size.is_none() && !config.style.generate_typedef() { out.write("enum "); } write!(out, "{} tag;", tag_name); if wrap_tag { out.close_brace(true); } } /// Emit fields for all variants with data. pub(crate) fn write_variant_fields< F: Write, LB: LanguageBackend, WF: Fn(&mut LB, &mut SourceWriter, &Field), >( &self, config: &Config, language_backend: &mut LB, out: &mut SourceWriter, inline_tag_field: bool, write_field: WF, ) { let mut first = true; for variant in &self.variants { if let VariantBody::Body { name, body, inline, .. } = &variant.body { if !first { out.new_line(); } first = false; let condition = variant.cfg.to_condition(config); // Cython doesn't support conditional enum variants. if config.language != Language::Cython { condition.write_before(config, out); } if *inline { // Write definition of an inlined variant with data. // Cython extern declarations don't manage layouts, layouts are defined entierly // by the corresponding C code. So we can inline the unnamed struct and get the // same observable result. Moreother we have to do it because Cython doesn't // support unnamed structs. // For the same reason with Cython we can omit per-variant tags (the first // field) to avoid extra noise, the main `tag` is enough in this case. if config.language != Language::Cython { out.write("struct"); out.open_brace(); } let start_field = usize::from(inline_tag_field && config.language == Language::Cython); out.write_vertical_source_list( language_backend, &body.fields[start_field..], ListType::Cap(";"), &write_field, ); if config.language != Language::Cython { out.close_brace(true); } } else if config.style.generate_typedef() || config.language == Language::Cython { write!(out, "{} {};", body.export_name(), name); } else { write!(out, "struct {} {};", body.export_name(), name); } if config.language != Language::Cython { condition.write_after(config, out); } } } } // Emit convenience methods for enums themselves. fn write_derived_functions_enum( &self, config: &Config, language_backend: &mut LB, out: &mut SourceWriter, ) { let has_data = self.tag.is_some(); let tag_name = self.tag_name(); if config.language != Language::Cxx { return; } // Emit an ostream function if required. if config.enumeration.derive_ostream(&self.annotations) { // For enums without data, this emits the serializer function for the // enum. For enums with data, this emits the serializer function for // the tag enum. In the latter case we need a couple of minor changes // due to the function living inside the top-level struct or enum. let stream = config .function .rename_args .apply("stream", IdentifierType::FunctionArg); let instance = config .function .rename_args .apply("instance", IdentifierType::FunctionArg); out.new_line(); out.new_line(); // For enums without data, we mark the function inline because the // header might get included into multiple compilation units that // get linked together, and not marking it inline would result in // multiply-defined symbol errors. For enums with data we don't have // the same problem, but mark it as a friend function of the // containing union/struct. // Note also that for enums with data, the case labels for switch // statements apparently need to be qualified to the top-level // generated struct or union. This is why the generated case labels // below use the A::B::C format for enums with data, with A being // self.export_name(). Failure to have that qualification results // in a surprising compilation failure for the generated header. write!( out, "{} std::ostream& operator<<(std::ostream& {}, const {}& {})", if has_data { "friend" } else { "inline" }, stream, tag_name, instance, ); out.open_brace(); if has_data { // C++ name resolution rules are weird. write!( out, "using {} = {}::{};", tag_name, self.export_name(), tag_name ); out.new_line(); } write!(out, "switch ({})", instance); out.open_brace(); let vec: Vec<_> = self .variants .iter() .map(|x| { format!( "case {}::{}: {} << \"{}\"; break;", tag_name, x.export_name, stream, x.export_name ) }) .collect(); out.write_vertical_source_list( language_backend, &vec[..], ListType::Join(""), |_, out, s| write!(out, "{}", s), ); out.close_brace(false); out.new_line(); write!(out, "return {};", stream); out.close_brace(false); if has_data { // For enums with data, this emits the serializer function for // the top-level union or struct. out.new_line(); out.new_line(); write!( out, "friend std::ostream& operator<<(std::ostream& {}, const {}& {})", stream, self.export_name(), instance, ); out.open_brace(); // C++ name resolution rules are weird. write!( out, "using {} = {}::{};", tag_name, self.export_name(), tag_name ); out.new_line(); write!(out, "switch ({}.tag)", instance); out.open_brace(); let vec: Vec<_> = self .variants .iter() .map(|x| { let tag_str = format!("\"{}\"", x.export_name); if let VariantBody::Body { ref name, ref body, .. } = x.body { format!( "case {}::{}: {} << {}{}{}.{}; break;", tag_name, x.export_name, stream, if body.has_tag_field { "" } else { &tag_str }, if body.has_tag_field { "" } else { " << " }, instance, name, ) } else { format!( "case {}::{}: {} << {}; break;", tag_name, x.export_name, stream, tag_str, ) } }) .collect(); out.write_vertical_source_list( language_backend, &vec[..], ListType::Join(""), |_, out, s| write!(out, "{}", s), ); out.close_brace(false); out.new_line(); write!(out, "return {};", stream); out.close_brace(false); } } } // Emit convenience methods for structs or unions produced for enums with data. pub(crate) fn write_derived_functions_data< F: Write, LB: LanguageBackend, WF: Fn(&mut LB, &mut SourceWriter, &Field), >( &self, config: &Config, language_backend: &mut LB, out: &mut SourceWriter, tag_name: &str, write_field: WF, ) { if config.language != Language::Cxx { return; } if config.enumeration.derive_helper_methods(&self.annotations) { for variant in &self.variants { out.new_line(); out.new_line(); let condition = variant.cfg.to_condition(config); condition.write_before(config, out); let arg_renamer = |name: &str| { config .function .rename_args .apply(name, IdentifierType::FunctionArg) .into_owned() }; macro_rules! write_attrs { ($op:expr) => {{ if let Some(Some(attrs)) = variant .body .annotations() .atom(concat!("variant-", $op, "-attributes")) { write!(out, "{} ", attrs); } }}; } write_attrs!("constructor"); write!(out, "static {} {}(", self.export_name, variant.export_name); if let VariantBody::Body { ref body, .. } = variant.body { let skip_fields = body.has_tag_field as usize; let vec: Vec<_> = body .fields .iter() .skip(skip_fields) .map(|field| { Field::from_name_and_type( // const-ref args to constructor arg_renamer(&field.name), Type::const_ref_to(&field.ty), ) }) .collect(); out.write_vertical_source_list( language_backend, &vec[..], ListType::Join(","), &write_field, ); } write!(out, ")"); out.open_brace(); write!(out, "{} result;", self.export_name); if let VariantBody::Body { name: ref variant_name, ref body, .. } = variant.body { let skip_fields = body.has_tag_field as usize; for field in body.fields.iter().skip(skip_fields) { out.new_line(); match field.ty { Type::Array(ref ty, ref length) => { // arrays are not assignable in C++ so we // need to manually copy the elements write!(out, "for (int i = 0; i < {}; i++)", length.as_str()); out.open_brace(); write!(out, "::new (&result.{}.{}[i]) (", variant_name, field.name); language_backend.write_type(out, ty); write!(out, ")({}[i]);", arg_renamer(&field.name)); out.close_brace(false); } ref ty => { write!(out, "::new (&result.{}.{}) (", variant_name, field.name); language_backend.write_type(out, ty); write!(out, ")({});", arg_renamer(&field.name)); } } } } out.new_line(); write!(out, "result.tag = {}::{};", tag_name, variant.export_name); out.new_line(); write!(out, "return result;"); out.close_brace(false); out.new_line(); out.new_line(); write_attrs!("is"); // FIXME: create a config for method case write!(out, "bool Is{}() const", variant.export_name); out.open_brace(); write!(out, "return tag == {}::{};", tag_name, variant.export_name); out.close_brace(false); let assert_name = match config.enumeration.cast_assert_name { Some(ref n) => &**n, None => "assert", }; let mut derive_casts = |const_casts: bool| { let (member_name, body, inline_casts) = match variant.body { VariantBody::Body { ref name, ref body, inline_casts, .. } => (name, body, inline_casts), VariantBody::Empty(..) => return, }; let skip_fields = body.has_tag_field as usize; let field_count = body.fields.len() - skip_fields; if field_count == 0 { return; } out.new_line(); out.new_line(); if const_casts { write_attrs!("const-cast"); } else { write_attrs!("mut-cast"); } if inline_casts { let field = body.fields.last().unwrap(); let return_type = field.ty.clone(); let return_type = Type::Ptr { ty: Box::new(return_type), is_const: const_casts, is_ref: true, is_nullable: false, }; language_backend.write_type(out, &return_type); } else if const_casts { write!(out, "const {}&", body.export_name()); } else { write!(out, "{}&", body.export_name()); } write!(out, " As{}()", variant.export_name); if const_casts { write!(out, " const"); } out.open_brace(); write!(out, "{}(Is{}());", assert_name, variant.export_name); out.new_line(); write!(out, "return {}", member_name); if inline_casts { write!(out, "._0"); } write!(out, ";"); out.close_brace(false); }; if config.enumeration.derive_const_casts(&self.annotations) { derive_casts(true) } if config.enumeration.derive_mut_casts(&self.annotations) { derive_casts(false) } condition.write_after(config, out); } } let other = config .function .rename_args .apply("other", IdentifierType::FunctionArg); macro_rules! write_attrs { ($op:expr) => {{ if let Some(Some(attrs)) = self.annotations.atom(concat!($op, "-attributes")) { write!(out, "{} ", attrs); } }}; } if self.can_derive_eq() && config.structure.derive_eq(&self.annotations) { out.new_line(); out.new_line(); write_attrs!("eq"); write!( out, "bool operator==(const {}& {}) const", self.export_name, other ); out.open_brace(); write!(out, "if (tag != {}.tag)", other); out.open_brace(); write!(out, "return false;"); out.close_brace(false); out.new_line(); write!(out, "switch (tag)"); out.open_brace(); let mut exhaustive = true; for variant in &self.variants { if let VariantBody::Body { name: ref variant_name, .. } = variant.body { let condition = variant.cfg.to_condition(config); condition.write_before(config, out); write!( out, "case {}::{}: return {} == {}.{};", self.tag.as_ref().unwrap(), variant.export_name, variant_name, other, variant_name ); condition.write_after(config, out); out.new_line(); } else { exhaustive = false; } } if !exhaustive { write!(out, "default: break;"); } out.close_brace(false); out.new_line(); write!(out, "return true;"); out.close_brace(false); if config.structure.derive_neq(&self.annotations) { out.new_line(); out.new_line(); write_attrs!("neq"); write!( out, "bool operator!=(const {}& {}) const", self.export_name, other ); out.open_brace(); write!(out, "return !(*this == {});", other); out.close_brace(false); } } if config .enumeration .private_default_tagged_enum_constructor(&self.annotations) { out.new_line(); out.new_line(); write!(out, "private:"); out.new_line(); write!(out, "{}()", self.export_name); out.open_brace(); out.close_brace(false); out.new_line(); write!(out, "public:"); out.new_line(); } if config .enumeration .derive_tagged_enum_destructor(&self.annotations) { out.new_line(); out.new_line(); write_attrs!("destructor"); write!(out, "~{}()", self.export_name); out.open_brace(); write!(out, "switch (tag)"); out.open_brace(); let mut exhaustive = true; for variant in &self.variants { if let VariantBody::Body { ref name, ref body, .. } = variant.body { let condition = variant.cfg.to_condition(config); condition.write_before(config, out); write!( out, "case {}::{}: {}.~{}(); break;", self.tag.as_ref().unwrap(), variant.export_name, name, body.export_name(), ); condition.write_after(config, out); out.new_line(); } else { exhaustive = false; } } if !exhaustive { write!(out, "default: break;"); } out.close_brace(false); out.close_brace(false); } if config .enumeration .derive_tagged_enum_copy_constructor(&self.annotations) { out.new_line(); out.new_line(); write_attrs!("copy-constructor"); write!( out, "{}(const {}& {})", self.export_name, self.export_name, other ); out.new_line(); write!(out, " : tag({}.tag)", other); out.open_brace(); write!(out, "switch (tag)"); out.open_brace(); let mut exhaustive = true; for variant in &self.variants { if let VariantBody::Body { ref name, ref body, .. } = variant.body { let condition = variant.cfg.to_condition(config); condition.write_before(config, out); write!( out, "case {}::{}: ::new (&{}) ({})({}.{}); break;", self.tag.as_ref().unwrap(), variant.export_name, name, body.export_name(), other, name, ); condition.write_after(config, out); out.new_line(); } else { exhaustive = false; } } if !exhaustive { write!(out, "default: break;"); } out.close_brace(false); out.close_brace(false); if config .enumeration .derive_tagged_enum_copy_assignment(&self.annotations) { out.new_line(); write_attrs!("copy-assignment"); write!( out, "{}& operator=(const {}& {})", self.export_name, self.export_name, other ); out.open_brace(); write!(out, "if (this != &{})", other); out.open_brace(); write!(out, "this->~{}();", self.export_name); out.new_line(); write!(out, "new (this) {}({});", self.export_name, other); out.close_brace(false); out.new_line(); write!(out, "return *this;"); out.close_brace(false); } } } pub fn simplify_standard_types(&mut self, config: &Config) { for variant in &mut self.variants { variant.simplify_standard_types(config); } } } cbindgen-0.27.0/src/bindgen/ir/field.rs000064400000000000000000000024051046102023000157240ustar 00000000000000use syn::ext::IdentExt; use crate::bindgen::ir::{AnnotationSet, Cfg}; use crate::bindgen::ir::{Documentation, Path, Type}; #[derive(Debug, Clone)] pub struct Field { pub name: String, pub ty: Type, pub cfg: Option, pub annotations: AnnotationSet, pub documentation: Documentation, } impl Field { pub fn from_name_and_type(name: String, ty: Type) -> Field { Field { name, ty, cfg: None, annotations: AnnotationSet::new(), documentation: Documentation::none(), } } pub fn load(field: &syn::Field, self_path: &Path) -> Result, String> { Ok(if let Some(mut ty) = Type::load(&field.ty)? { ty.replace_self_with(self_path); Some(Field { name: field .ident .as_ref() .ok_or_else(|| "field is missing identifier".to_string())? .unraw() .to_string(), ty, cfg: Cfg::load(&field.attrs), annotations: AnnotationSet::load(&field.attrs)?, documentation: Documentation::load(&field.attrs), }) } else { None }) } } cbindgen-0.27.0/src/bindgen/ir/function.rs000064400000000000000000000230661046102023000164740ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::collections::HashMap; use syn::ext::IdentExt; use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{AnnotationSet, Cfg, Documentation, GenericPath, Path, Type}; use crate::bindgen::library::Library; use crate::bindgen::monomorph::Monomorphs; use crate::bindgen::rename::{IdentifierType, RenameRule}; use crate::bindgen::reserved; use crate::bindgen::utilities::IterHelpers; #[derive(Debug, Clone)] pub struct FunctionArgument { pub name: Option, pub ty: Type, pub array_length: Option, } #[derive(Debug, Clone)] pub struct Function { pub path: Path, /// Path to the self-type of the function /// If the function is a method, this will contain the path of the type in the impl block pub self_type_path: Option, pub ret: Type, pub args: Vec, pub extern_decl: bool, pub cfg: Option, pub annotations: AnnotationSet, pub documentation: Documentation, pub never_return: bool, } impl Function { pub fn load( path: Path, self_type_path: Option<&Path>, sig: &syn::Signature, extern_decl: bool, attrs: &[syn::Attribute], mod_cfg: Option<&Cfg>, ) -> Result { let mut args = sig.inputs.iter().try_skip_map(|x| x.as_argument())?; if sig.variadic.is_some() { args.push(FunctionArgument { name: None, ty: Type::Primitive(super::PrimitiveType::VaList), array_length: None, }) } let (mut ret, never_return) = Type::load_from_output(&sig.output)?; if let Some(self_path) = self_type_path { for arg in &mut args { arg.ty.replace_self_with(self_path); } ret.replace_self_with(self_path); } Ok(Function { path, self_type_path: self_type_path.cloned(), ret, args, extern_decl, cfg: Cfg::append(mod_cfg, Cfg::load(attrs)), annotations: AnnotationSet::load(attrs)?, documentation: Documentation::load(attrs), never_return, }) } pub fn swift_name(&self, config: &Config) -> Option { if config.language == Language::Cython { return None; } // If the symbol name starts with the type name, separate the two components with '.' // so that Swift recognises the association between the method and the type let (ref type_prefix, ref type_name) = match self.self_type_path { Some(ref type_name) => { let type_name = type_name.to_string(); if !self.path.name().starts_with(&type_name) { return Some(self.path.to_string()); } (format!("{}.", type_name), type_name) } None => ("".to_string(), "".to_string()), }; let item_name = self .path .name() .trim_start_matches(type_name) .trim_start_matches('_'); let item_args = { let mut items = Vec::with_capacity(self.args.len()); for arg in self.args.iter() { items.push(format!("{}:", arg.name.as_ref()?.as_str())); } items.join("") }; Some(format!("{}{}({})", type_prefix, item_name, item_args)) } pub fn path(&self) -> &Path { &self.path } pub fn simplify_standard_types(&mut self, config: &Config) { self.ret.simplify_standard_types(config); for arg in &mut self.args { arg.ty.simplify_standard_types(config); } } pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { self.ret.add_dependencies(library, out); for arg in &self.args { arg.ty.add_dependencies(library, out); } } pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) { self.ret.add_monomorphs(library, out); for arg in &self.args { arg.ty.add_monomorphs(library, out); } } pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) { self.ret.mangle_paths(monomorphs); for arg in &mut self.args { arg.ty.mangle_paths(monomorphs); } } pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) { self.ret.resolve_declaration_types(resolver); for arg in &mut self.args { arg.ty.resolve_declaration_types(resolver); } } pub fn rename_for_config(&mut self, config: &Config) { // Rename the types used in arguments let generic_params = Default::default(); self.ret.rename_for_config(config, &generic_params); // Apply rename rules to argument names let rules = self .annotations .parse_atom::("rename-all") .unwrap_or(config.function.rename_args); if let Some(r) = rules.not_none() { let args = std::mem::take(&mut self.args); self.args = args .into_iter() .map(|arg| { let name = arg .name .map(|n| r.apply(&n, IdentifierType::FunctionArg).into_owned()); FunctionArgument { name, ty: arg.ty, array_length: None, } }) .collect() } // Escape C/C++ reserved keywords used in argument names, and // recursively rename argument types. for arg in &mut self.args { arg.ty.rename_for_config(config, &generic_params); if let Some(ref mut name) = arg.name { reserved::escape(name); } } // Save the array length of the pointer arguments which need to use // the C-array notation if let Some(tuples) = self.annotations.list("ptrs-as-arrays") { let mut ptrs_as_arrays: HashMap = HashMap::new(); for str_tuple in tuples { let parts: Vec<&str> = str_tuple[1..str_tuple.len() - 1] .split(';') .map(|x| x.trim()) .collect(); if parts.len() != 2 { warn!( "{:?} does not follow the correct syntax, so the annotation is being ignored", parts ); continue; } ptrs_as_arrays.insert(parts[0].to_string(), parts[1].to_string()); } for arg in &mut self.args { match arg.ty { Type::Ptr { .. } => {} _ => continue, } let name = match arg.name { Some(ref name) => name, None => continue, }; arg.array_length = ptrs_as_arrays.get(name).cloned(); } } } } trait SynFnArgHelpers { fn as_argument(&self) -> Result, String>; } fn gen_self_type(receiver: &syn::Receiver) -> Result { let mut self_ty = Type::Path(GenericPath::self_path()); // Custom self type if receiver.colon_token.is_some() { self_ty = Type::load(receiver.ty.as_ref())?.unwrap_or(self_ty); } if receiver.reference.is_none() { return Ok(self_ty); } let is_const = receiver.mutability.is_none(); Ok(Type::Ptr { ty: Box::new(self_ty), is_const, is_nullable: false, is_ref: false, }) } impl SynFnArgHelpers for syn::FnArg { fn as_argument(&self) -> Result, String> { match *self { syn::FnArg::Typed(syn::PatType { ref pat, ref ty, .. }) => { let ty = match Type::load(ty)? { Some(x) => x, None => return Ok(None), }; let name = match **pat { syn::Pat::Wild(..) => None, syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => { if ty == Type::Primitive(super::PrimitiveType::VaList) { None } else { Some(ident.unraw().to_string()) } } _ => { return Err(format!( "Parameter has an unsupported argument name: {:?}", pat )) } }; if let Type::Array(..) = ty { return Err("Array as function arguments are not supported".to_owned()); } Ok(Some(FunctionArgument { name, ty, array_length: None, })) } syn::FnArg::Receiver(ref receiver) => Ok(Some(FunctionArgument { name: Some("self".to_string()), ty: gen_self_type(receiver)?, array_length: None, })), } } } cbindgen-0.27.0/src/bindgen/ir/generic_path.rs000064400000000000000000000255641046102023000173040ustar 00000000000000use std::io::Write; use std::ops::Deref; use syn::ext::IdentExt; use crate::bindgen::cdecl; use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::{DeclarationType, DeclarationTypeResolver}; use crate::bindgen::ir::{ConstExpr, Path, Type}; use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::utilities::IterHelpers; use crate::bindgen::writer::SourceWriter; #[derive(Debug, Clone)] pub enum GenericParamType { Type, Const(Type), } #[derive(Debug, Clone)] pub struct GenericParam { name: Path, ty: GenericParamType, default: Option, } impl GenericParam { pub fn new_type_param(name: &str) -> Self { GenericParam { name: Path::new(name), ty: GenericParamType::Type, default: None, } } pub fn load(param: &syn::GenericParam) -> Result, String> { match *param { syn::GenericParam::Type(syn::TypeParam { ref ident, ref default, .. }) => { let default = match default.as_ref().map(Type::load).transpose()? { None => None, Some(None) => Err(format!("unsupported generic type default: {:?}", default))?, Some(Some(ty)) => Some(GenericArgument::Type(ty)), }; Ok(Some(GenericParam { name: Path::new(ident.unraw().to_string()), ty: GenericParamType::Type, default, })) } syn::GenericParam::Lifetime(_) => Ok(None), syn::GenericParam::Const(syn::ConstParam { ref ident, ref ty, ref default, .. }) => match Type::load(ty)? { None => { // A type that evaporates, like PhantomData. Err(format!("unsupported const generic type: {:?}", ty)) } Some(ty) => Ok(Some(GenericParam { name: Path::new(ident.unraw().to_string()), ty: GenericParamType::Const(ty), default: default .as_ref() .map(ConstExpr::load) .transpose()? .map(GenericArgument::Const), })), }, } } pub fn name(&self) -> &Path { &self.name } } #[derive(Default, Debug, Clone)] pub struct GenericParams(pub Vec); static EMPTY_GENERIC_PARAMS: GenericParams = GenericParams(Vec::new()); impl GenericParams { /// An empty generic params, for convenience. pub fn empty() -> &'static Self { &EMPTY_GENERIC_PARAMS } pub fn load(generics: &syn::Generics) -> Result { let mut params = vec![]; for param in &generics.params { if let Some(p) = GenericParam::load(param)? { params.push(p); } } Ok(GenericParams(params)) } /// Associate each parameter with an argument. pub fn call<'out>( &'out self, item_name: &str, arguments: &'out [GenericArgument], ) -> Vec<(&'out Path, &'out GenericArgument)> { assert!( self.len() >= arguments.len(), "{} has {} params but is being instantiated with {} values", item_name, self.len(), arguments.len(), ); self.iter() .enumerate() .map(|(i, param)| { // Fall back to the GenericParam default if no GenericArgument is available. let arg = arguments .get(i) .or(param.default.as_ref()) .unwrap_or_else(|| { panic!( "{} with {} params is being instantiated with only {} values, \ and param {} lacks a default value", item_name, self.len(), arguments.len(), i ) }); (param.name(), arg) }) .collect() } pub(crate) fn write_internal( &self, language_backend: &mut LB, config: &Config, out: &mut SourceWriter, with_default: bool, ) { if !self.0.is_empty() && config.language == Language::Cxx { out.write("template<"); for (i, item) in self.0.iter().enumerate() { if i != 0 { out.write(", "); } match item.ty { GenericParamType::Type => { write!(out, "typename {}", item.name); if let Some(GenericArgument::Type(ref ty)) = item.default { write!(out, " = "); cdecl::write_type(language_backend, out, ty, config); } else if with_default { write!(out, " = void"); } } GenericParamType::Const(ref ty) => { cdecl::write_field(language_backend, out, ty, item.name.name(), config); if let Some(GenericArgument::Const(ref expr)) = item.default { write!(out, " = {}", expr.as_str()); } else if with_default { write!(out, " = 0"); } } } } out.write(">"); out.new_line(); } } pub fn write_with_default( &self, language_backend: &mut LB, config: &Config, out: &mut SourceWriter, ) { self.write_internal(language_backend, config, out, true); } } impl Deref for GenericParams { type Target = [GenericParam]; fn deref(&self) -> &[GenericParam] { &self.0 } } /// A (non-lifetime) argument passed to a generic, either a type or a constant expression. /// /// Note: Both arguments in a type like `Array` are represented as /// `GenericArgument::Type`s, even if `N` is actually the name of a const. This /// is a consequence of `syn::GenericArgument` doing the same thing. #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum GenericArgument { Type(Type), Const(ConstExpr), } impl GenericArgument { pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> GenericArgument { match *self { GenericArgument::Type(ref ty) => { if let Type::Path(ref path) = *ty { if path.is_single_identifier() { // See note on `GenericArgument` above: `ty` may // actually be the name of a const. Check for that now. for &(name, value) in mappings { if *name == path.path { return value.clone(); } } } } GenericArgument::Type(ty.specialize(mappings)) } GenericArgument::Const(ref expr) => GenericArgument::Const(expr.clone()), } } pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) { match *self { GenericArgument::Type(ref mut ty) => ty.rename_for_config(config, generic_params), GenericArgument::Const(ref mut expr) => expr.rename_for_config(config), } } } #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct GenericPath { path: Path, export_name: String, generics: Vec, ctype: Option, } impl GenericPath { pub fn new(path: Path, generics: Vec) -> Self { let export_name = path.name().to_owned(); Self { path, export_name, generics, ctype: None, } } pub fn self_path() -> Self { Self::new(Path::new("Self"), vec![]) } pub fn replace_self_with(&mut self, self_ty: &Path) { if self.path.replace_self_with(self_ty) { self_ty.name().clone_into(&mut self.export_name); } // Caller deals with generics. } pub fn path(&self) -> &Path { &self.path } pub fn generics(&self) -> &[GenericArgument] { &self.generics } pub fn generics_mut(&mut self) -> &mut [GenericArgument] { &mut self.generics } pub fn ctype(&self) -> Option<&DeclarationType> { self.ctype.as_ref() } pub fn name(&self) -> &str { self.path.name() } pub fn export_name(&self) -> &str { &self.export_name } pub fn is_single_identifier(&self) -> bool { self.generics.is_empty() } pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) { for generic in &mut self.generics { generic.rename_for_config(config, generic_params); } if !generic_params.iter().any(|param| param.name == self.path) { config.export.rename(&mut self.export_name); } } pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) { self.ctype = resolver.type_for(&self.path); } pub fn load(path: &syn::Path) -> Result { assert!( !path.segments.is_empty(), "{:?} doesn't have any segments", path ); let last_segment = path.segments.last().unwrap(); let name = last_segment.ident.unraw().to_string(); let path = Path::new(name); let phantom_data_path = Path::new("PhantomData"); if path == phantom_data_path { return Ok(Self::new(path, Vec::new())); } let generics = match last_segment.arguments { syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { ref args, .. }) => args.iter().try_skip_map(|x| match *x { syn::GenericArgument::Type(ref x) => Ok(Type::load(x)?.map(GenericArgument::Type)), syn::GenericArgument::Lifetime(_) => Ok(None), syn::GenericArgument::Const(ref x) => { Ok(Some(GenericArgument::Const(ConstExpr::load(x)?))) } _ => Err(format!("can't handle generic argument {:?}", x)), })?, syn::PathArguments::Parenthesized(_) => { return Err("Path contains parentheses.".to_owned()); } _ => Vec::new(), }; Ok(Self::new(path, generics)) } } cbindgen-0.27.0/src/bindgen/ir/global.rs000064400000000000000000000056301046102023000161040ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::bindgen::config::Config; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ AnnotationSet, Cfg, Documentation, GenericParams, Item, ItemContainer, Path, Type, }; use crate::bindgen::library::Library; #[derive(Debug, Clone)] pub struct Static { pub path: Path, pub export_name: String, pub ty: Type, pub mutable: bool, pub cfg: Option, pub annotations: AnnotationSet, pub documentation: Documentation, } impl Static { pub fn load( path: Path, item: &syn::ItemStatic, mod_cfg: Option<&Cfg>, ) -> Result { let ty = Type::load(&item.ty)?; if ty.is_none() { return Err("Cannot have a zero sized static definition.".to_owned()); } Ok(Static::new( path, ty.unwrap(), matches!(item.mutability, syn::StaticMutability::Mut(_)), Cfg::append(mod_cfg, Cfg::load(&item.attrs)), AnnotationSet::load(&item.attrs)?, Documentation::load(&item.attrs), )) } pub fn new( path: Path, ty: Type, mutable: bool, cfg: Option, annotations: AnnotationSet, documentation: Documentation, ) -> Self { let export_name = path.name().to_owned(); Self { path, export_name, ty, mutable, cfg, annotations, documentation, } } pub fn simplify_standard_types(&mut self, config: &Config) { self.ty.simplify_standard_types(config); } } impl Item for Static { fn path(&self) -> &Path { &self.path } fn export_name(&self) -> &str { &self.export_name } fn cfg(&self) -> Option<&Cfg> { self.cfg.as_ref() } fn annotations(&self) -> &AnnotationSet { &self.annotations } fn annotations_mut(&mut self) -> &mut AnnotationSet { &mut self.annotations } fn documentation(&self) -> &Documentation { &self.documentation } fn container(&self) -> ItemContainer { ItemContainer::Static(self.clone()) } fn rename_for_config(&mut self, config: &Config) { self.ty.rename_for_config(config, &Default::default()); } fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) { self.ty.resolve_declaration_types(resolver); } fn generic_params(&self) -> &GenericParams { GenericParams::empty() } fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { self.ty.add_dependencies(library, out); } } cbindgen-0.27.0/src/bindgen/ir/item.rs000064400000000000000000000152511046102023000156020ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use indexmap::IndexMap; use std::mem; use crate::bindgen::config::Config; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ AnnotationSet, Cfg, Constant, Documentation, Enum, GenericArgument, GenericParams, OpaqueItem, Path, Static, Struct, Typedef, Union, }; use crate::bindgen::library::Library; use crate::bindgen::monomorph::Monomorphs; /// An item is any type of rust item besides a function pub trait Item { fn path(&self) -> &Path; fn name(&self) -> &str { self.path().name() } fn export_name(&self) -> &str { self.name() } fn cfg(&self) -> Option<&Cfg>; fn annotations(&self) -> &AnnotationSet; fn annotations_mut(&mut self) -> &mut AnnotationSet; fn documentation(&self) -> &Documentation; fn container(&self) -> ItemContainer; fn collect_declaration_types(&self, _resolver: &mut DeclarationTypeResolver) { unimplemented!() } fn resolve_declaration_types(&mut self, _resolver: &DeclarationTypeResolver) { unimplemented!() } fn generic_params(&self) -> &GenericParams; fn is_generic(&self) -> bool { !self.generic_params().is_empty() } fn rename_for_config(&mut self, _config: &Config) {} fn add_dependencies(&self, _library: &Library, _out: &mut Dependencies) {} fn instantiate_monomorph( &self, _generics: &[GenericArgument], _library: &Library, _out: &mut Monomorphs, ) { unreachable!("Cannot instantiate {} as a generic.", self.name()) } } #[derive(Debug, Clone)] pub enum ItemContainer { Constant(Constant), Static(Static), OpaqueItem(OpaqueItem), Struct(Struct), Union(Union), Enum(Enum), Typedef(Typedef), } impl ItemContainer { pub fn deref(&self) -> &dyn Item { match *self { ItemContainer::Constant(ref x) => x, ItemContainer::Static(ref x) => x, ItemContainer::OpaqueItem(ref x) => x, ItemContainer::Struct(ref x) => x, ItemContainer::Union(ref x) => x, ItemContainer::Enum(ref x) => x, ItemContainer::Typedef(ref x) => x, } } } #[derive(Debug, Clone)] pub enum ItemValue { Cfg(Vec), Single(T), } #[derive(Debug, Clone)] pub struct ItemMap { data: IndexMap>, } impl Default for ItemMap { fn default() -> ItemMap { ItemMap { data: Default::default(), } } } impl ItemMap { pub fn rebuild(&mut self) { let old = mem::take(self); old.for_all_items(|x| { self.try_insert(x.clone()); }); } pub fn try_insert(&mut self, item: T) -> bool { match (item.cfg().is_some(), self.data.get_mut(item.path())) { (true, Some(&mut ItemValue::Cfg(ref mut items))) => { items.push(item); return true; } (false, Some(&mut ItemValue::Cfg(_))) => { return false; } (true, Some(&mut ItemValue::Single(_))) => { return false; } (false, Some(&mut ItemValue::Single(_))) => { return false; } _ => {} } let path = item.path().clone(); if item.cfg().is_some() { self.data.insert(path, ItemValue::Cfg(vec![item])); } else { self.data.insert(path, ItemValue::Single(item)); } true } pub fn extend_with(&mut self, other: &ItemMap) { other.for_all_items(|x| { self.try_insert(x.clone()); }); } pub fn to_vec(&self) -> Vec { let mut result = Vec::with_capacity(self.data.len()); for container in self.data.values() { match *container { ItemValue::Cfg(ref items) => result.extend_from_slice(items), ItemValue::Single(ref item) => { result.push(item.clone()); } } } result } pub fn get_items(&self, path: &Path) -> Option> { Some(match *self.data.get(path)? { ItemValue::Cfg(ref items) => items.iter().map(|x| x.container()).collect(), ItemValue::Single(ref item) => vec![item.container()], }) } pub fn filter(&mut self, callback: F) where F: Fn(&T) -> bool, { self.data.retain(|_, container| match *container { ItemValue::Cfg(ref mut items) => { items.retain(|item| !callback(item)); !items.is_empty() } ItemValue::Single(ref item) => !callback(item), }); } pub fn for_all_items(&self, mut callback: F) where F: FnMut(&T), { for container in self.data.values() { match *container { ItemValue::Cfg(ref items) => { for item in items { callback(item); } } ItemValue::Single(ref item) => callback(item), } } } pub fn for_all_items_mut(&mut self, mut callback: F) where F: FnMut(&mut T), { for container in self.data.values_mut() { match *container { ItemValue::Cfg(ref mut items) => { for item in items { callback(item); } } ItemValue::Single(ref mut item) => callback(item), } } } pub fn for_items(&self, path: &Path, mut callback: F) where F: FnMut(&T), { match self.data.get(path) { Some(ItemValue::Cfg(items)) => { for item in items { callback(item); } } Some(ItemValue::Single(item)) => { callback(item); } None => {} } } pub fn for_items_mut(&mut self, path: &Path, mut callback: F) where F: FnMut(&mut T), { match self.data.get_mut(path) { Some(&mut ItemValue::Cfg(ref mut items)) => { for item in items { callback(item); } } Some(&mut ItemValue::Single(ref mut item)) => { callback(item); } None => {} } } } cbindgen-0.27.0/src/bindgen/ir/mod.rs000064400000000000000000000017451046102023000154260ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ pub mod annotation; pub mod cfg; pub mod constant; pub mod documentation; pub mod enumeration; pub mod field; pub mod function; pub mod generic_path; pub mod global; pub mod item; pub mod opaque; pub mod path; pub mod repr; pub mod structure; pub mod ty; pub mod typedef; pub mod union; pub use self::annotation::{AnnotationSet, AnnotationValue, DeprecatedNoteKind}; pub use self::cfg::*; pub use self::constant::*; pub use self::documentation::Documentation; pub use self::enumeration::*; pub use self::field::*; pub use self::function::*; pub use self::generic_path::*; pub use self::global::*; pub use self::item::*; pub use self::opaque::*; pub use self::path::*; pub use self::repr::*; pub use self::structure::*; pub use self::ty::*; pub use self::typedef::*; pub use self::union::*; cbindgen-0.27.0/src/bindgen/ir/opaque.rs000064400000000000000000000073721046102023000161430ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::bindgen::config::Config; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ AnnotationSet, Cfg, Documentation, GenericArgument, GenericParams, Item, ItemContainer, Path, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; use crate::bindgen::monomorph::Monomorphs; #[derive(Debug, Clone)] pub struct OpaqueItem { pub path: Path, pub export_name: String, pub generic_params: GenericParams, pub cfg: Option, pub annotations: AnnotationSet, pub documentation: Documentation, } impl OpaqueItem { pub fn load( path: Path, generics: &syn::Generics, attrs: &[syn::Attribute], mod_cfg: Option<&Cfg>, ) -> Result { Ok(Self::new( path, GenericParams::load(generics)?, Cfg::append(mod_cfg, Cfg::load(attrs)), AnnotationSet::load(attrs).unwrap_or_else(|_| AnnotationSet::new()), Documentation::load(attrs), )) } pub fn new( path: Path, generic_params: GenericParams, cfg: Option, annotations: AnnotationSet, documentation: Documentation, ) -> OpaqueItem { let export_name = path.name().to_owned(); Self { path, export_name, generic_params, cfg, annotations, documentation, } } } impl Item for OpaqueItem { fn path(&self) -> &Path { &self.path } fn export_name(&self) -> &str { &self.export_name } fn cfg(&self) -> Option<&Cfg> { self.cfg.as_ref() } fn annotations(&self) -> &AnnotationSet { &self.annotations } fn annotations_mut(&mut self) -> &mut AnnotationSet { &mut self.annotations } fn documentation(&self) -> &Documentation { &self.documentation } fn container(&self) -> ItemContainer { ItemContainer::OpaqueItem(self.clone()) } fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) { resolver.add_struct(&self.path); } fn generic_params(&self) -> &GenericParams { &self.generic_params } fn rename_for_config(&mut self, config: &Config) { config.export.rename(&mut self.export_name); } fn add_dependencies(&self, _: &Library, _: &mut Dependencies) {} fn instantiate_monomorph( &self, generic_values: &[GenericArgument], library: &Library, out: &mut Monomorphs, ) { assert!(self.is_generic(), "{} is not generic", self.path); // We can be instantiated with less generic params because of default // template parameters, or because of empty types that we remove during // parsing (`()`). assert!( self.generic_params.len() >= generic_values.len(), "{} has {} params but is being instantiated with {} values", self.path, self.generic_params.len(), generic_values.len(), ); let mangled_path = mangle::mangle_path( &self.path, generic_values, &library.get_config().export.mangle, ); let monomorph = OpaqueItem::new( mangled_path, GenericParams::default(), self.cfg.clone(), self.annotations.clone(), self.documentation.clone(), ); out.insert_opaque(self, monomorph, generic_values.to_owned()); } } cbindgen-0.27.0/src/bindgen/ir/path.rs000064400000000000000000000020611046102023000155730ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::cmp::Ordering; use std::fmt; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Path { name: String, } impl Path { pub fn new(name: T) -> Self where String: From, { Self { name: name.into() } } pub fn name(&self) -> &str { &self.name } pub fn replace_self_with(&mut self, path: &Self) -> bool { if self.name() != "Self" { return false; } *self = path.clone(); true } } impl PartialOrd for Path { fn partial_cmp(&self, other: &Path) -> Option { Some(self.cmp(other)) } } impl Ord for Path { fn cmp(&self, other: &Path) -> Ordering { self.name.cmp(&other.name) } } impl fmt::Display for Path { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.name) } } cbindgen-0.27.0/src/bindgen/ir/repr.rs000064400000000000000000000153011046102023000156100ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::bindgen::ir::ty::{IntKind, PrimitiveType}; #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] pub enum ReprStyle { #[default] Rust, C, Transparent, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct ReprType { kind: IntKind, signed: bool, } impl ReprType { pub(crate) fn to_primitive(self) -> PrimitiveType { PrimitiveType::Integer { kind: self.kind, signed: self.signed, zeroable: true, } } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ReprAlign { Packed, Align(u64), } #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] pub struct Repr { pub style: ReprStyle, pub ty: Option, pub align: Option, } impl Repr { pub fn load(attrs: &[syn::Attribute]) -> Result { let mut ids = Vec::new(); // We want only the `repr` attributes let iter = attrs.iter().filter(|attr| attr.path().is_ident("repr")); for repr_attr in iter { let reprs = repr_attr .parse_args_with( syn::punctuated::Punctuated::::parse_terminated, ) .map_err(|err| format!("Invalid `#[repr]` attribute: {err}"))?; for meta in reprs { match meta { // #[repr(C)] and #[repr(transparent)] and #[repr(INT)] syn::Meta::Path(path) => { if let Some(ident) = path.get_ident() { let ident_s = ident.to_string(); ids.push((ident_s, None)) } else { return Err("Invalid `repr` attribute".to_string()); } } // #[repr(align(N))] syn::Meta::List(meta) if meta.path.is_ident("align") => { let lit: syn::LitInt = meta .parse_args() .map_err(|err| format!("Invalid align argument: {err}"))?; ids.push(("align".to_string(), Some(lit.to_string()))) } // #[repr(packed(N))] syn::Meta::List(meta) if meta.path.is_ident("packed") => { // no arguments if meta.tokens.is_empty() { ids.push(("packed".to_string(), None)) } else { let lit: syn::LitInt = meta .parse_args() .map_err(|err| format!("Invalid packed argument: {err}"))?; ids.push(("packed".to_string(), Some(lit.to_string()))) } } _ => return Err("Invalid `repr` attribute".to_string()), } } } let mut repr = Repr::default(); for id in ids { let (int_kind, signed) = match (id.0.as_ref(), id.1) { ("u8", None) => (IntKind::B8, false), ("u16", None) => (IntKind::B16, false), ("u32", None) => (IntKind::B32, false), ("u64", None) => (IntKind::B64, false), ("usize", None) => (IntKind::Size, false), ("i8", None) => (IntKind::B8, true), ("i16", None) => (IntKind::B16, true), ("i32", None) => (IntKind::B32, true), ("i64", None) => (IntKind::B64, true), ("isize", None) => (IntKind::Size, true), ("C", None) => { repr.style = ReprStyle::C; continue; } ("transparent", None) => { repr.style = ReprStyle::Transparent; continue; } ("packed", args) => { // #[repr(packed(n))] not supported because of some open questions about how // to calculate the native alignment of types. See mozilla/cbindgen#433. if args.is_some() { return Err( "Not-yet-implemented #[repr(packed(...))] encountered.".to_string() ); } let align = ReprAlign::Packed; // Only permit a single alignment-setting repr. if let Some(old_align) = repr.align { return Err(format!( "Conflicting #[repr(align(...))] type hints {:?} and {:?}.", old_align, align )); } repr.align = Some(align); continue; } ("align", Some(arg)) => { // Must be a positive integer. let align = match arg.parse::() { Ok(align) => align, Err(_) => return Err(format!("Non-unsigned #[repr(align({}))].", arg)), }; // Must be a power of 2. if !align.is_power_of_two() || align == 0 { return Err(format!("Invalid alignment to #[repr(align({}))].", align)); } // Only permit a single alignment-setting repr. if let Some(old_align) = repr.align { return Err(format!( "Conflicting #[repr(align(...))] type hints {:?} and {:?}.", old_align, ReprAlign::Align(align) )); } repr.align = Some(ReprAlign::Align(align)); continue; } (path, arg) => match arg { None => return Err(format!("Unsupported #[repr({})].", path)), Some(arg) => { return Err(format!("Unsupported #[repr({}({}))].", path, arg)); } }, }; let ty = ReprType { kind: int_kind, signed, }; if let Some(old_ty) = repr.ty { return Err(format!( "Conflicting #[repr(...)] type hints {:?} and {:?}.", old_ty, ty )); } repr.ty = Some(ty); } Ok(repr) } } cbindgen-0.27.0/src/bindgen/ir/structure.rs000064400000000000000000000273141046102023000167070ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::io::Write; use syn::ext::IdentExt; use crate::bindgen::config::{Config, Language, LayoutConfig}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ AnnotationSet, Cfg, Constant, Documentation, Field, GenericArgument, GenericParams, Item, ItemContainer, Path, Repr, ReprAlign, ReprStyle, Type, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; use crate::bindgen::monomorph::Monomorphs; use crate::bindgen::rename::{IdentifierType, RenameRule}; use crate::bindgen::reserved; use crate::bindgen::utilities::IterHelpers; use crate::bindgen::writer::SourceWriter; #[derive(Debug, Clone)] pub struct Struct { pub path: Path, pub export_name: String, pub generic_params: GenericParams, pub fields: Vec, /// Whether there's a tag field on the body of this struct. When this is /// true, is_enum_variant_body is also guaranteed to be true. pub has_tag_field: bool, /// Whether this is an enum variant body. pub is_enum_variant_body: bool, pub alignment: Option, pub is_transparent: bool, pub cfg: Option, pub annotations: AnnotationSet, pub documentation: Documentation, pub associated_constants: Vec, } impl Struct { /// Whether this struct can derive operator== / operator!=. pub fn can_derive_eq(&self) -> bool { !self.fields.is_empty() && self.fields.iter().all(|x| x.ty.can_cmp_eq()) } pub fn add_associated_constant(&mut self, c: Constant) { self.associated_constants.push(c); } pub fn load( layout_config: &LayoutConfig, item: &syn::ItemStruct, mod_cfg: Option<&Cfg>, ) -> Result { let repr = Repr::load(&item.attrs)?; let is_transparent = match repr.style { ReprStyle::C => false, ReprStyle::Transparent => true, _ => { return Err("Struct is not marked #[repr(C)] or #[repr(transparent)].".to_owned()); } }; let path = Path::new(item.ident.unraw().to_string()); // Ensure we can safely represent the struct given the configuration. if let Some(align) = repr.align { layout_config.ensure_safe_to_represent(&align)?; } let fields = match item.fields { syn::Fields::Unit => Vec::new(), syn::Fields::Named(ref fields) => fields .named .iter() .try_skip_map(|field| Field::load(field, &path))?, syn::Fields::Unnamed(ref fields) => { let mut out = Vec::new(); let mut current = 0; for field in fields.unnamed.iter() { if let Some(mut ty) = Type::load(&field.ty)? { ty.replace_self_with(&path); out.push(Field { name: format!("{}", current), ty, cfg: Cfg::load(&field.attrs), annotations: AnnotationSet::load(&field.attrs)?, documentation: Documentation::load(&field.attrs), }); current += 1; } } out } }; let has_tag_field = false; let is_enum_variant_body = false; Ok(Struct::new( path, GenericParams::load(&item.generics)?, fields, has_tag_field, is_enum_variant_body, repr.align, is_transparent, Cfg::append(mod_cfg, Cfg::load(&item.attrs)), AnnotationSet::load(&item.attrs)?, Documentation::load(&item.attrs), )) } #[allow(clippy::too_many_arguments)] pub fn new( path: Path, generic_params: GenericParams, fields: Vec, has_tag_field: bool, is_enum_variant_body: bool, alignment: Option, is_transparent: bool, cfg: Option, annotations: AnnotationSet, documentation: Documentation, ) -> Self { let export_name = path.name().to_owned(); Self { path, export_name, generic_params, fields, has_tag_field, is_enum_variant_body, alignment, is_transparent, cfg, annotations, documentation, associated_constants: vec![], } } pub fn simplify_standard_types(&mut self, config: &Config) { for field in &mut self.fields { field.ty.simplify_standard_types(config); } } pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) { // Generic structs can instantiate monomorphs only once they've been // instantiated. See `instantiate_monomorph` for more details. if self.is_generic() { return; } for field in &self.fields { field.ty.add_monomorphs(library, out); } } pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) { for field in &mut self.fields { field.ty.mangle_paths(monomorphs); } } pub fn specialize( &self, generic_values: &[GenericArgument], mappings: &[(&Path, &GenericArgument)], config: &Config, ) -> Self { let mangled_path = mangle::mangle_path(&self.path, generic_values, &config.export.mangle); Struct::new( mangled_path, GenericParams::default(), self.fields .iter() .map(|field| Field { name: field.name.clone(), ty: field.ty.specialize(mappings), cfg: field.cfg.clone(), annotations: field.annotations.clone(), documentation: field.documentation.clone(), }) .collect(), self.has_tag_field, self.is_enum_variant_body, self.alignment, self.is_transparent, self.cfg.clone(), self.annotations.clone(), self.documentation.clone(), ) } pub(crate) fn emit_bitflags_binop( &self, constexpr_prefix: &str, operator: char, other: &str, out: &mut SourceWriter, ) { let bits = &self.fields[0].name; out.new_line(); write!( out, "{}{} operator{}(const {}& {}) const", constexpr_prefix, self.export_name(), operator, self.export_name(), other ); out.open_brace(); write!( out, "return {} {{ static_cast(this->{bits} {operator} {other}.{bits}) }};", self.export_name() ); out.close_brace(false); out.new_line(); write!( out, "{}& operator{}=(const {}& {})", self.export_name(), operator, self.export_name(), other ); out.open_brace(); write!(out, "*this = (*this {} {});", operator, other); out.new_line(); write!(out, "return *this;"); out.close_brace(false); } } impl Item for Struct { fn path(&self) -> &Path { &self.path } fn export_name(&self) -> &str { &self.export_name } fn cfg(&self) -> Option<&Cfg> { self.cfg.as_ref() } fn annotations(&self) -> &AnnotationSet { &self.annotations } fn annotations_mut(&mut self) -> &mut AnnotationSet { &mut self.annotations } fn documentation(&self) -> &Documentation { &self.documentation } fn container(&self) -> ItemContainer { ItemContainer::Struct(self.clone()) } fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) { if self.is_transparent { resolver.add_none(&self.path); } else { resolver.add_struct(&self.path); } } fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) { for field in &mut self.fields { field.ty.resolve_declaration_types(resolver); } } fn generic_params(&self) -> &GenericParams { &self.generic_params } fn rename_for_config(&mut self, config: &Config) { // Rename the name of the struct if !(self.has_tag_field && config.language == Language::Cxx) { config.export.rename(&mut self.export_name); } // Rename the types used in fields { let fields = self.fields.iter_mut().skip(self.has_tag_field as usize); for field in fields { field.ty.rename_for_config(config, &self.generic_params); } } // Apply renaming rules to fields in the following order // 1. `cbindgen::field-names` annotation // 2. `cbindgen::rename-all` annotation // 3. config struct rename rule // If the struct is a tuple struct and we have not renamed the // fields, then prefix each of them with an underscore. // If any field is a reserved keyword, then postfix it with an // underscore. // Scope for mutable borrow of fields { let names = self.fields.iter_mut().map(|field| &mut field.name); let field_rules = self .annotations .parse_atom::("rename-all") .unwrap_or(config.structure.rename_fields); if let Some(o) = self.annotations.list("field-names") { for (dest, src) in names.zip(o) { *dest = src; } } else if let Some(r) = field_rules.not_none() { for name in names { *name = r.apply(name, IdentifierType::StructMember).into_owned(); } } else { // If we don't have any rules for a tuple struct, prefix them with // an underscore so it still compiles. for name in names { if name.starts_with(|c: char| c.is_ascii_digit()) { name.insert(0, '_'); } } } } for field in &mut self.fields { reserved::escape(&mut field.name); } for c in self.associated_constants.iter_mut() { c.rename_for_config(config); } } fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { let mut fields = self.fields.iter(); // If there is a tag field, skip it if self.has_tag_field { fields.next(); } for field in fields { field .ty .add_dependencies_ignoring_generics(&self.generic_params, library, out); } for c in &self.associated_constants { c.add_dependencies(library, out); } } fn instantiate_monomorph( &self, generic_values: &[GenericArgument], library: &Library, out: &mut Monomorphs, ) { let mappings = self.generic_params.call(self.path.name(), generic_values); let monomorph = self.specialize(generic_values, &mappings, library.get_config()); out.insert_struct(library, self, monomorph, generic_values.to_owned()); } } cbindgen-0.27.0/src/bindgen/ir/ty.rs000064400000000000000000000752241046102023000153060ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::borrow::Cow; use syn::ext::IdentExt; use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{GenericArgument, GenericParams, GenericPath, Path}; use crate::bindgen::library::Library; use crate::bindgen::monomorph::Monomorphs; use crate::bindgen::utilities::IterHelpers; #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum PrimitiveType { Void, Bool, Char, SChar, UChar, Char32, Float, Double, VaList, PtrDiffT, Integer { zeroable: bool, signed: bool, kind: IntKind, }, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum IntKind { Short, Int, Long, LongLong, SizeT, Size, B8, B16, B32, B64, } impl PrimitiveType { pub fn maybe(path: &str) -> Option { Some(match path { "c_void" => PrimitiveType::Void, "c_char" => PrimitiveType::Char, "c_schar" => PrimitiveType::SChar, "c_uchar" => PrimitiveType::UChar, "c_float" => PrimitiveType::Float, "c_double" => PrimitiveType::Double, "ptrdiff_t" => PrimitiveType::PtrDiffT, "VaList" => PrimitiveType::VaList, "bool" => PrimitiveType::Bool, "char" => PrimitiveType::Char32, "f32" => PrimitiveType::Float, "f64" => PrimitiveType::Double, _ => return Self::maybe_integer(path), }) } // Converts well-known integral types to their respective `PrimitiveType` fn maybe_integer(path: &str) -> Option { let (kind, signed) = match path { "c_short" => (IntKind::Short, true), "c_int" => (IntKind::Int, true), "c_long" => (IntKind::Long, true), "c_longlong" => (IntKind::LongLong, true), "ssize_t" => (IntKind::SizeT, true), "c_ushort" => (IntKind::Short, false), "c_uint" => (IntKind::Int, false), "c_ulong" => (IntKind::Long, false), "c_ulonglong" => (IntKind::LongLong, false), "size_t" => (IntKind::SizeT, false), "RawFd" => (IntKind::Int, true), "isize" | "intptr_t" => (IntKind::Size, true), "usize" | "uintptr_t" => (IntKind::Size, false), "u8" | "uint8_t" => (IntKind::B8, false), "u16" | "uint16_t" => (IntKind::B16, false), "u32" | "uint32_t" => (IntKind::B32, false), "u64" | "uint64_t" => (IntKind::B64, false), "i8" | "int8_t" => (IntKind::B8, true), "i16" | "int16_t" => (IntKind::B16, true), "i32" | "int32_t" => (IntKind::B32, true), "i64" | "int64_t" => (IntKind::B64, true), _ => return Self::maybe_nonzero_integer(path), }; Some(PrimitiveType::Integer { zeroable: true, signed, kind, }) } // Converts well-known typedefs for [`NonZero`] to their respective `PrimitiveType`. // // NOTE: This performs the type erasure directly, as if `NonZero` had been specified, because // it's actually more code to register an erased typedef instead. fn maybe_nonzero_integer(path: &str) -> Option { let (kind, signed) = match path { "NonZeroU8" => (IntKind::B8, false), "NonZeroU16" => (IntKind::B16, false), "NonZeroU32" => (IntKind::B32, false), "NonZeroU64" => (IntKind::B64, false), "NonZeroUSize" => (IntKind::Size, false), "NonZeroI8" => (IntKind::B8, true), "NonZeroI16" => (IntKind::B16, true), "NonZeroI32" => (IntKind::B32, true), "NonZeroI64" => (IntKind::B64, true), "NonZeroISize" => (IntKind::Size, true), _ => return None, }; Some(PrimitiveType::Integer { zeroable: false, signed, kind, }) } pub fn to_repr_rust(&self) -> &'static str { match *self { PrimitiveType::Bool => "bool", PrimitiveType::Void => "c_void", PrimitiveType::Char => "c_char", PrimitiveType::SChar => "c_schar", PrimitiveType::UChar => "c_uchar", PrimitiveType::Char32 => "char", PrimitiveType::Integer { kind, signed, zeroable: _, } => match (kind, signed) { (IntKind::Short, true) => "c_short", (IntKind::Short, false) => "c_ushort", (IntKind::Int, true) => "c_int", (IntKind::Int, false) => "c_uint", (IntKind::Long, true) => "c_long", (IntKind::Long, false) => "c_ulong", (IntKind::LongLong, true) => "c_longlong", (IntKind::LongLong, false) => "c_ulonglong", (IntKind::SizeT, true) => "ssize_t", (IntKind::SizeT, false) => "size_t", (IntKind::Size, true) => "isize", (IntKind::Size, false) => "usize", (IntKind::B8, true) => "i8", (IntKind::B8, false) => "u8", (IntKind::B16, true) => "i16", (IntKind::B16, false) => "u16", (IntKind::B32, true) => "i32", (IntKind::B32, false) => "u32", (IntKind::B64, true) => "i64", (IntKind::B64, false) => "u64", }, PrimitiveType::Float => "f32", PrimitiveType::Double => "f64", PrimitiveType::PtrDiffT => "ptrdiff_t", PrimitiveType::VaList => "va_list", } } pub fn to_repr_c(&self, config: &Config) -> &'static str { match *self { PrimitiveType::Void => "void", PrimitiveType::Bool => "bool", PrimitiveType::Char => "char", PrimitiveType::SChar => "signed char", PrimitiveType::UChar => "unsigned char", // NOTE: It'd be nice to use a char32_t, but: // // * uchar.h is not present on mac (see #423). // // * char32_t isn't required to be compatible with Rust's char, as // the C++ spec only requires it to be the same size as // uint_least32_t, which is _not_ guaranteed to be 4-bytes. // PrimitiveType::Char32 => "uint32_t", PrimitiveType::Integer { kind, signed, zeroable: _, } => match (kind, signed) { (IntKind::Short, true) => "short", (IntKind::Short, false) => "unsigned short", (IntKind::Int, true) => "int", (IntKind::Int, false) => "unsigned int", (IntKind::Long, true) => "long", (IntKind::Long, false) => "unsigned long", (IntKind::LongLong, true) => "long long", (IntKind::LongLong, false) => "unsigned long long", (IntKind::SizeT, true) => "ssize_t", (IntKind::SizeT, false) => "size_t", (IntKind::Size, true) if config.usize_is_size_t => "ptrdiff_t", (IntKind::Size, false) if config.usize_is_size_t => "size_t", (IntKind::Size, true) => "intptr_t", (IntKind::Size, false) => "uintptr_t", (IntKind::B8, true) => "int8_t", (IntKind::B8, false) => "uint8_t", (IntKind::B16, true) => "int16_t", (IntKind::B16, false) => "uint16_t", (IntKind::B32, true) => "int32_t", (IntKind::B32, false) => "uint32_t", (IntKind::B64, true) => "int64_t", (IntKind::B64, false) => "uint64_t", }, PrimitiveType::Float => "float", PrimitiveType::Double => "double", PrimitiveType::PtrDiffT => "ptrdiff_t", PrimitiveType::VaList => "...", } } fn can_cmp_order(&self) -> bool { !matches!(*self, PrimitiveType::Bool) } fn can_cmp_eq(&self) -> bool { true } } /// Constant expressions. /// /// Used for the `U` part of `[T; U]` and const generics. We support a very /// limited vocabulary here: only identifiers and literals. #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum ConstExpr { Name(String), Value(String), } impl ConstExpr { pub fn as_str(&self) -> &str { match *self { ConstExpr::Name(ref string) | ConstExpr::Value(ref string) => string, } } pub fn rename_for_config(&mut self, config: &Config) { if let ConstExpr::Name(ref mut name) = self { config.export.rename(name); } } pub fn load(expr: &syn::Expr) -> Result { match *expr { syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => { let val = match *lit { syn::Lit::Bool(syn::LitBool { value, .. }) => value.to_string(), syn::Lit::Int(ref len) => len.base10_digits().to_string(), syn::Lit::Byte(ref byte) => u8::to_string(&byte.value()), syn::Lit::Char(ref ch) => u32::to_string(&ch.value().into()), _ => return Err(format!("can't handle const expression {:?}", lit)), }; Ok(ConstExpr::Value(val)) } syn::Expr::Path(ref path) => { let generic_path = GenericPath::load(&path.path)?; Ok(ConstExpr::Name(generic_path.export_name().to_owned())) } _ => Err(format!("can't handle const expression {:?}", expr)), } } pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> ConstExpr { match *self { ConstExpr::Name(ref name) => { let path = Path::new(name); for &(param, value) in mappings { if path == *param { match *value { GenericArgument::Type(Type::Path(ref path)) if path.is_single_identifier() => { // This happens when the generic argument is a path. return ConstExpr::Name(path.name().to_string()); } GenericArgument::Const(ref expr) => { return expr.clone(); } _ => { // unsupported argument type - really should be an error } } } } } ConstExpr::Value(_) => {} } self.clone() } } #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum Type { Ptr { ty: Box, is_const: bool, is_nullable: bool, // FIXME: This is a bit of a hack, this is only to get us to codegen // `T&` / `const T&`, but we should probably pass that down as an option // to code generation or something. is_ref: bool, }, Path(GenericPath), Primitive(PrimitiveType), Array(Box, ConstExpr), FuncPtr { ret: Box, args: Vec<(Option, Type)>, is_nullable: bool, never_return: bool, }, } impl Type { pub fn const_ref_to(ty: &Self) -> Self { Type::Ptr { ty: Box::new(ty.clone()), is_const: true, is_nullable: false, is_ref: true, } } pub fn load_from_output(output: &syn::ReturnType) -> Result<(Type, bool), String> { let mut never_return = false; let ty = match output { syn::ReturnType::Default => Type::Primitive(PrimitiveType::Void), syn::ReturnType::Type(_, ref ty) => { if let syn::Type::Never(_) = ty.as_ref() { never_return = true; Type::Primitive(PrimitiveType::Void) } else { Type::load(ty)?.unwrap_or(Type::Primitive(PrimitiveType::Void)) } } }; Ok((ty, never_return)) } pub fn load(ty: &syn::Type) -> Result, String> { let converted = match *ty { syn::Type::Reference(ref reference) => { let converted = Type::load(&reference.elem)?; let converted = match converted { Some(converted) => converted, None => Type::Primitive(PrimitiveType::Void), }; // TODO(emilio): we could make these use is_ref: true. let is_const = reference.mutability.is_none(); Type::Ptr { ty: Box::new(converted), is_const, is_nullable: false, is_ref: false, } } syn::Type::Ptr(ref pointer) => { let converted = Type::load(&pointer.elem)?; let converted = match converted { Some(converted) => converted, None => Type::Primitive(PrimitiveType::Void), }; let is_const = pointer.mutability.is_none(); Type::Ptr { ty: Box::new(converted), is_const, is_nullable: true, is_ref: false, } } syn::Type::Path(ref path) => { let generic_path = GenericPath::load(&path.path)?; if generic_path.name() == "PhantomData" || generic_path.name() == "PhantomPinned" { return Ok(None); } if let Some(prim) = PrimitiveType::maybe(generic_path.name()) { if !generic_path.generics().is_empty() { return Err("Primitive has generics.".to_owned()); } Type::Primitive(prim) } else { Type::Path(generic_path) } } syn::Type::Array(syn::TypeArray { ref elem, ref len, .. }) => { let converted = Type::load(elem)?; let converted = match converted { Some(converted) => converted, None => return Err("Cannot have an array of zero sized types.".to_owned()), }; let len = ConstExpr::load(len)?; Type::Array(Box::new(converted), len) } syn::Type::BareFn(ref function) => { let mut wildcard_counter = 0; let mut args = function.inputs.iter().try_skip_map(|x| { Type::load(&x.ty).map(|opt_ty| { opt_ty.map(|ty| { ( x.name.as_ref().map(|(ref ident, _)| { if ident == "_" { wildcard_counter += 1; if wildcard_counter == 1 { "_".to_owned() } else { format!("_{}", wildcard_counter - 1) } } else { ident.unraw().to_string() } }), ty, ) }) }) })?; if function.variadic.is_some() { args.push((None, Type::Primitive(super::PrimitiveType::VaList))) } let (ret, never_return) = Type::load_from_output(&function.output)?; Type::FuncPtr { ret: Box::new(ret), args, is_nullable: false, never_return, } } syn::Type::Tuple(ref tuple) => { if tuple.elems.is_empty() { return Ok(None); } return Err("Tuples are not supported types.".to_owned()); } syn::Type::Verbatim(ref tokens) if tokens.to_string() == "..." => { Type::Primitive(PrimitiveType::VaList) } _ => return Err(format!("Unsupported type: {:?}", ty)), }; Ok(Some(converted)) } pub fn is_ptr(&self) -> bool { matches!(*self, Type::Ptr { .. } | Type::FuncPtr { .. }) } pub fn is_primitive_or_ptr_primitive(&self) -> bool { match *self { Type::Primitive(..) => true, Type::Ptr { ref ty, .. } => matches!(ty.as_ref(), Type::Primitive(..)), _ => false, } } pub fn make_zeroable(&self, new_zeroable: bool) -> Option { match *self { Type::Primitive(PrimitiveType::Integer { zeroable: old_zeroable, kind, signed, }) if old_zeroable != new_zeroable => Some(Type::Primitive(PrimitiveType::Integer { kind, signed, zeroable: new_zeroable, })), _ => None, } } pub fn make_nullable(&self) -> Option { match *self { Type::Ptr { ref ty, is_const, is_ref, is_nullable: false, } => Some(Type::Ptr { ty: ty.clone(), is_const, is_ref, is_nullable: true, }), Type::FuncPtr { ref ret, ref args, is_nullable: false, never_return, } => Some(Type::FuncPtr { ret: ret.clone(), args: args.clone(), is_nullable: true, never_return, }), _ => None, } } fn simplified_type(&self, config: &Config) -> Option { let path = match *self { Type::Path(ref p) => p, _ => return None, }; if path.generics().is_empty() { return None; } if path.generics().len() != 1 { return None; } let unsimplified_generic = match path.generics()[0] { GenericArgument::Type(ref ty) => ty, GenericArgument::Const(_) => return None, }; let generic = match unsimplified_generic.simplified_type(config) { Some(generic) => Cow::Owned(generic), None => Cow::Borrowed(unsimplified_generic), }; match path.name() { "Option" => generic .make_nullable() .or_else(|| generic.make_zeroable(true)), "NonNull" => Some(Type::Ptr { ty: Box::new(generic.into_owned()), is_const: false, is_nullable: false, is_ref: false, }), "NonZero" => generic.make_zeroable(false), "Box" if config.language != Language::Cxx => Some(Type::Ptr { ty: Box::new(generic.into_owned()), is_const: false, is_nullable: false, is_ref: false, }), "Cell" => Some(generic.into_owned()), "ManuallyDrop" | "MaybeUninit" | "Pin" if config.language != Language::Cxx => { Some(generic.into_owned()) } _ => None, } } pub fn simplify_standard_types(&mut self, config: &Config) { self.visit_types(|ty| ty.simplify_standard_types(config)); if let Some(ty) = self.simplified_type(config) { *self = ty; } } pub fn replace_self_with(&mut self, self_ty: &Path) { if let Type::Path(ref mut generic_path) = *self { generic_path.replace_self_with(self_ty); } self.visit_types(|ty| ty.replace_self_with(self_ty)) } fn visit_types(&mut self, mut visitor: impl FnMut(&mut Type)) { match *self { Type::Array(ref mut ty, ..) | Type::Ptr { ref mut ty, .. } => visitor(ty), Type::Path(ref mut path) => { for generic in path.generics_mut() { match *generic { GenericArgument::Type(ref mut ty) => visitor(ty), GenericArgument::Const(_) => {} } } } Type::Primitive(..) => {} Type::FuncPtr { ref mut ret, ref mut args, .. } => { visitor(ret); for arg in args { visitor(&mut arg.1) } } } } pub fn get_root_path(&self) -> Option { let mut current = self; loop { match *current { Type::Ptr { ref ty, .. } => current = ty, Type::Path(ref generic) => { return Some(generic.path().clone()); } Type::Primitive(..) => { return None; } Type::Array(..) => { return None; } Type::FuncPtr { .. } => { return None; } }; } } pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> Type { match *self { Type::Ptr { ref ty, is_const, is_nullable, is_ref, } => Type::Ptr { ty: Box::new(ty.specialize(mappings)), is_const, is_nullable, is_ref, }, Type::Path(ref generic_path) => { for &(param, value) in mappings { if generic_path.path() == param { if let GenericArgument::Type(ref ty) = *value { return ty.clone(); } } } let specialized = GenericPath::new( generic_path.path().clone(), generic_path .generics() .iter() .map(|x| x.specialize(mappings)) .collect(), ); Type::Path(specialized) } Type::Primitive(ref primitive) => Type::Primitive(primitive.clone()), Type::Array(ref ty, ref constant) => Type::Array( Box::new(ty.specialize(mappings)), constant.specialize(mappings), ), Type::FuncPtr { ref ret, ref args, is_nullable, never_return, } => Type::FuncPtr { ret: Box::new(ret.specialize(mappings)), args: args .iter() .cloned() .map(|(name, ty)| (name, ty.specialize(mappings))) .collect(), is_nullable, never_return, }, } } pub fn add_dependencies_ignoring_generics( &self, generic_params: &GenericParams, library: &Library, out: &mut Dependencies, ) { match *self { Type::Ptr { ref ty, .. } => { ty.add_dependencies_ignoring_generics(generic_params, library, out); } Type::Path(ref generic) => { for generic_value in generic.generics() { if let GenericArgument::Type(ref ty) = *generic_value { ty.add_dependencies_ignoring_generics(generic_params, library, out); } } let path = generic.path(); if !generic_params.iter().any(|param| param.name() == path) { if let Some(items) = library.get_items(path) { if !out.items.contains(path) { out.items.insert(path.clone()); for item in &items { item.deref().add_dependencies(library, out); } for item in items { out.order.push(item); } } } else { warn!( "Can't find {}. This usually means that this type was incompatible or \ not found.", path ); } } } Type::Primitive(_) => {} Type::Array(ref ty, _) => { ty.add_dependencies_ignoring_generics(generic_params, library, out); } Type::FuncPtr { ref ret, ref args, .. } => { ret.add_dependencies_ignoring_generics(generic_params, library, out); for (_, ref arg) in args { arg.add_dependencies_ignoring_generics(generic_params, library, out); } } } } pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { self.add_dependencies_ignoring_generics(&GenericParams::default(), library, out) } pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) { match *self { Type::Ptr { ref ty, .. } => { ty.add_monomorphs(library, out); } Type::Path(ref generic) => { if generic.generics().is_empty() || out.contains(generic) { return; } let path = generic.path(); if let Some(items) = library.get_items(path) { for item in items { item.deref() .instantiate_monomorph(generic.generics(), library, out); } } } Type::Primitive(_) => {} Type::Array(ref ty, _) => { ty.add_monomorphs(library, out); } Type::FuncPtr { ref ret, ref args, .. } => { ret.add_monomorphs(library, out); for (_, ref arg) in args { arg.add_monomorphs(library, out); } } } } pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) { match *self { Type::Ptr { ref mut ty, .. } => { ty.rename_for_config(config, generic_params); } Type::Path(ref mut ty) => { ty.rename_for_config(config, generic_params); } Type::Primitive(_) => {} Type::Array(ref mut ty, ref mut len) => { ty.rename_for_config(config, generic_params); len.rename_for_config(config); } Type::FuncPtr { ref mut ret, ref mut args, .. } => { ret.rename_for_config(config, generic_params); for (_, arg) in args { arg.rename_for_config(config, generic_params); } } } } pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) { match *self { Type::Ptr { ref mut ty, .. } => { ty.resolve_declaration_types(resolver); } Type::Path(ref mut generic_path) => { generic_path.resolve_declaration_types(resolver); } Type::Primitive(_) => {} Type::Array(ref mut ty, _) => { ty.resolve_declaration_types(resolver); } Type::FuncPtr { ref mut ret, ref mut args, .. } => { ret.resolve_declaration_types(resolver); for (_, ref mut arg) in args { arg.resolve_declaration_types(resolver); } } } } pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) { match *self { Type::Ptr { ref mut ty, .. } => { ty.mangle_paths(monomorphs); } Type::Path(ref mut generic_path) => { if generic_path.generics().is_empty() { return; } if let Some(mangled_path) = monomorphs.mangle_path(generic_path) { *generic_path = GenericPath::new(mangled_path.clone(), vec![]); } else { warn!( "Cannot find a mangling for generic path {:?}. This usually means that a \ type referenced by this generic was incompatible or not found.", generic_path ); } } Type::Primitive(_) => {} Type::Array(ref mut ty, _) => { ty.mangle_paths(monomorphs); } Type::FuncPtr { ref mut ret, ref mut args, .. } => { ret.mangle_paths(monomorphs); for (_, ref mut arg) in args { arg.mangle_paths(monomorphs); } } } } pub fn can_cmp_order(&self) -> bool { match *self { // FIXME: Shouldn't this look at ty.can_cmp_order() as well? Type::Ptr { is_ref, .. } => !is_ref, Type::Path(..) => true, Type::Primitive(ref p) => p.can_cmp_order(), Type::Array(..) => false, Type::FuncPtr { .. } => false, } } pub fn can_cmp_eq(&self) -> bool { match *self { Type::Ptr { ref ty, is_ref, .. } => !is_ref || ty.can_cmp_eq(), Type::Path(..) => true, Type::Primitive(ref p) => p.can_cmp_eq(), Type::Array(..) => false, Type::FuncPtr { .. } => true, } } } cbindgen-0.27.0/src/bindgen/ir/typedef.rs000064400000000000000000000123051046102023000163010ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::collections::HashMap; use syn::ext::IdentExt; use crate::bindgen::config::Config; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ AnnotationSet, Cfg, Documentation, GenericArgument, GenericParams, Item, ItemContainer, Path, Type, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; use crate::bindgen::monomorph::Monomorphs; /// A type alias that is represented as a C typedef #[derive(Debug, Clone)] pub struct Typedef { pub path: Path, pub export_name: String, pub generic_params: GenericParams, pub aliased: Type, pub cfg: Option, pub annotations: AnnotationSet, pub documentation: Documentation, } impl Typedef { pub fn load(item: &syn::ItemType, mod_cfg: Option<&Cfg>) -> Result { if let Some(x) = Type::load(&item.ty)? { let path = Path::new(item.ident.unraw().to_string()); Ok(Typedef::new( path, GenericParams::load(&item.generics)?, x, Cfg::append(mod_cfg, Cfg::load(&item.attrs)), AnnotationSet::load(&item.attrs)?, Documentation::load(&item.attrs), )) } else { Err("Cannot have a typedef of a zero sized type.".to_owned()) } } pub fn new( path: Path, generic_params: GenericParams, aliased: Type, cfg: Option, annotations: AnnotationSet, documentation: Documentation, ) -> Self { let export_name = path.name().to_owned(); Self { path, export_name, generic_params, aliased, cfg, annotations, documentation, } } pub fn simplify_standard_types(&mut self, config: &Config) { self.aliased.simplify_standard_types(config); } pub fn transfer_annotations(&mut self, out: &mut HashMap) { if self.annotations.is_empty() { return; } if let Some(alias_path) = self.aliased.get_root_path() { if out.contains_key(&alias_path) { warn!( "Multiple typedef's with annotations for {}. Ignoring annotations from {}.", alias_path, self.path ); return; } out.insert(alias_path, self.annotations.clone()); self.annotations = AnnotationSet::new(); } } pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) { // Generic structs can instantiate monomorphs only once they've been // instantiated. See `instantiate_monomorph` for more details. if !self.is_generic() { self.aliased.add_monomorphs(library, out); } } pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) { self.aliased.mangle_paths(monomorphs); } } impl Item for Typedef { fn path(&self) -> &Path { &self.path } fn export_name(&self) -> &str { &self.export_name } fn cfg(&self) -> Option<&Cfg> { self.cfg.as_ref() } fn annotations(&self) -> &AnnotationSet { &self.annotations } fn annotations_mut(&mut self) -> &mut AnnotationSet { &mut self.annotations } fn documentation(&self) -> &Documentation { &self.documentation } fn container(&self) -> ItemContainer { ItemContainer::Typedef(self.clone()) } fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) { resolver.add_none(&self.path); } fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) { self.aliased.resolve_declaration_types(resolver); } fn generic_params(&self) -> &GenericParams { &self.generic_params } fn rename_for_config(&mut self, config: &Config) { config.export.rename(&mut self.export_name); self.aliased.rename_for_config(config, &self.generic_params); } fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { self.aliased .add_dependencies_ignoring_generics(&self.generic_params, library, out); } fn instantiate_monomorph( &self, generic_values: &[GenericArgument], library: &Library, out: &mut Monomorphs, ) { let mappings = self.generic_params.call(self.path.name(), generic_values); let mangled_path = mangle::mangle_path( &self.path, generic_values, &library.get_config().export.mangle, ); let monomorph = Typedef::new( mangled_path, GenericParams::default(), self.aliased.specialize(&mappings), self.cfg.clone(), self.annotations.clone(), self.documentation.clone(), ); out.insert_typedef(library, self, monomorph, generic_values.to_owned()); } } cbindgen-0.27.0/src/bindgen/ir/union.rs000064400000000000000000000174261046102023000160020ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use syn::ext::IdentExt; use crate::bindgen::config::{Config, LayoutConfig}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ AnnotationSet, Cfg, Documentation, Field, GenericArgument, GenericParams, Item, ItemContainer, Path, Repr, ReprAlign, ReprStyle, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; use crate::bindgen::monomorph::Monomorphs; use crate::bindgen::rename::{IdentifierType, RenameRule}; use crate::bindgen::utilities::IterHelpers; #[derive(Debug, Clone)] pub struct Union { pub path: Path, pub export_name: String, pub generic_params: GenericParams, pub fields: Vec, pub tuple_union: bool, pub alignment: Option, pub cfg: Option, pub annotations: AnnotationSet, pub documentation: Documentation, } impl Union { pub fn load( layout_config: &LayoutConfig, item: &syn::ItemUnion, mod_cfg: Option<&Cfg>, ) -> Result { let repr = Repr::load(&item.attrs)?; if repr.style != ReprStyle::C { return Err("Union is not marked #[repr(C)].".to_owned()); } // Ensure we can safely represent the union given the configuration. if let Some(align) = repr.align { layout_config.ensure_safe_to_represent(&align)?; } let path = Path::new(item.ident.unraw().to_string()); let (fields, tuple_union) = { let out = item .fields .named .iter() .try_skip_map(|field| Field::load(field, &path))?; (out, false) }; Ok(Union::new( path, GenericParams::load(&item.generics)?, fields, repr.align, tuple_union, Cfg::append(mod_cfg, Cfg::load(&item.attrs)), AnnotationSet::load(&item.attrs)?, Documentation::load(&item.attrs), )) } #[allow(clippy::too_many_arguments)] pub fn new( path: Path, generic_params: GenericParams, fields: Vec, alignment: Option, tuple_union: bool, cfg: Option, annotations: AnnotationSet, documentation: Documentation, ) -> Self { let export_name = path.name().to_owned(); Self { path, export_name, generic_params, fields, tuple_union, alignment, cfg, annotations, documentation, } } pub fn simplify_standard_types(&mut self, config: &Config) { for field in &mut self.fields { field.ty.simplify_standard_types(config); } } pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) { // Generic unions can instantiate monomorphs only once they've been // instantiated. See `instantiate_monomorph` for more details. if self.is_generic() { return; } for field in &self.fields { field.ty.add_monomorphs(library, out); } } pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) { for field in &mut self.fields { field.ty.mangle_paths(monomorphs); } } } impl Item for Union { fn path(&self) -> &Path { &self.path } fn export_name(&self) -> &str { &self.export_name } fn cfg(&self) -> Option<&Cfg> { self.cfg.as_ref() } fn annotations(&self) -> &AnnotationSet { &self.annotations } fn annotations_mut(&mut self) -> &mut AnnotationSet { &mut self.annotations } fn documentation(&self) -> &Documentation { &self.documentation } fn container(&self) -> ItemContainer { ItemContainer::Union(self.clone()) } fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) { resolver.add_union(&self.path); } fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) { for field in &mut self.fields { field.ty.resolve_declaration_types(resolver); } } fn generic_params(&self) -> &GenericParams { &self.generic_params } fn rename_for_config(&mut self, config: &Config) { config.export.rename(&mut self.export_name); for field in &mut self.fields { field.ty.rename_for_config(config, &self.generic_params); } let rules = self .annotations .parse_atom::("rename-all") .unwrap_or(config.structure.rename_fields); if let Some(o) = self.annotations.list("field-names") { let mut overriden_fields = Vec::new(); for (i, field) in self.fields.iter().enumerate() { if i >= o.len() { overriden_fields.push(field.clone()); } else { overriden_fields.push(Field { name: o[i].clone(), ty: field.ty.clone(), cfg: field.cfg.clone(), annotations: field.annotations.clone(), documentation: field.documentation.clone(), }); } } self.fields = overriden_fields; } else if let Some(r) = rules.not_none() { self.fields = self .fields .iter() .map(|field| Field { name: r .apply(&field.name, IdentifierType::StructMember) .into_owned(), ty: field.ty.clone(), cfg: field.cfg.clone(), annotations: field.annotations.clone(), documentation: field.documentation.clone(), }) .collect(); } else if self.tuple_union { // If we don't have any rules for a tuple union, prefix them with // an underscore so it still compiles for field in &mut self.fields { field.name.insert(0, '_'); } } } fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { for field in &self.fields { field .ty .add_dependencies_ignoring_generics(&self.generic_params, library, out); } } fn instantiate_monomorph( &self, generic_values: &[GenericArgument], library: &Library, out: &mut Monomorphs, ) { let mappings = self.generic_params.call(self.path.name(), generic_values); let mangled_path = mangle::mangle_path( &self.path, generic_values, &library.get_config().export.mangle, ); let monomorph = Union::new( mangled_path, GenericParams::default(), self.fields .iter() .map(|field| Field { name: field.name.clone(), ty: field.ty.specialize(&mappings), cfg: field.cfg.clone(), annotations: field.annotations.clone(), documentation: field.documentation.clone(), }) .collect(), self.alignment, self.tuple_union, self.cfg.clone(), self.annotations.clone(), self.documentation.clone(), ); out.insert_union(library, self, monomorph, generic_values.to_owned()); } } cbindgen-0.27.0/src/bindgen/language_backend/clike.rs000064400000000000000000001035521046102023000205550ustar 00000000000000use crate::bindgen::ir::{ to_known_assoc_constant, ConditionWrite, DeprecatedNoteKind, Documentation, Enum, EnumVariant, Field, GenericParams, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, ToCondition, Type, Typedef, Union, }; use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::rename::IdentifierType; use crate::bindgen::writer::{ListType, SourceWriter}; use crate::bindgen::{cdecl, Bindings, Config, Language}; use crate::bindgen::{DocumentationLength, DocumentationStyle}; use std::io::Write; pub struct CLikeLanguageBackend<'a> { config: &'a Config, } impl<'a> CLikeLanguageBackend<'a> { pub fn new(config: &'a Config) -> Self { Self { config } } fn write_enum_variant(&mut self, out: &mut SourceWriter, u: &EnumVariant) { let condition = u.cfg.to_condition(self.config); condition.write_before(self.config, out); self.write_documentation(out, &u.documentation); write!(out, "{}", u.export_name); if let Some(note) = u .body .annotations() .deprecated_note(self.config, DeprecatedNoteKind::EnumVariant) { write!(out, " {}", note); } if let Some(discriminant) = &u.discriminant { out.write(" = "); self.write_literal(out, discriminant); } out.write(","); condition.write_after(self.config, out); } fn write_field(&mut self, out: &mut SourceWriter, f: &Field) { let condition = f.cfg.to_condition(self.config); condition.write_before(self.config, out); self.write_documentation(out, &f.documentation); cdecl::write_field(self, out, &f.ty, &f.name, self.config); if let Some(bitfield) = f.annotations.atom("bitfield") { write!(out, ": {}", bitfield.unwrap_or_default()); } condition.write_after(self.config, out); // FIXME(#634): `write_vertical_source_list` should support // configuring list elements natively. For now we print a newline // here to avoid printing `#endif;` with semicolon. if condition.is_some() { out.new_line(); } } fn write_generic_param(&mut self, out: &mut SourceWriter, g: &GenericParams) { g.write_internal(self, self.config, out, false); } fn open_close_namespaces(&mut self, out: &mut SourceWriter, open: bool) { let mut namespaces = if self.config.language != Language::Cxx && !self.config.cpp_compatible_c() { vec![] } else { let mut ret = vec![]; if let Some(ref namespace) = self.config.namespace { ret.push(&**namespace); } if let Some(ref namespaces) = self.config.namespaces { for namespace in namespaces { ret.push(&**namespace); } } ret }; if namespaces.is_empty() { return; } if !open { namespaces.reverse(); } if self.config.cpp_compatible_c() { out.new_line_if_not_start(); out.write("#ifdef __cplusplus"); } for namespace in namespaces { out.new_line(); if open { write!(out, "namespace {} {{", namespace) } else { write!(out, "}} // namespace {}", namespace) } } out.new_line(); if self.config.cpp_compatible_c() { out.write("#endif // __cplusplus"); out.new_line(); } } fn generate_typedef(&self) -> bool { self.config.language == Language::C && self.config.style.generate_typedef() } fn write_derived_cpp_ops(&mut self, out: &mut SourceWriter, s: &Struct) { let mut wrote_start_newline = false; if self.config.structure.derive_constructor(&s.annotations) && !s.fields.is_empty() { if !wrote_start_newline { wrote_start_newline = true; out.new_line(); } out.new_line(); let renamed_fields: Vec<_> = s .fields .iter() .map(|field| { self.config .function .rename_args .apply(&field.name, IdentifierType::FunctionArg) .into_owned() }) .collect(); write!(out, "{}(", s.export_name()); let vec: Vec<_> = s .fields .iter() .zip(&renamed_fields) .map(|(field, renamed)| { Field::from_name_and_type( // const-ref args to constructor format!("const& {}", renamed), field.ty.clone(), ) }) .collect(); out.write_vertical_source_list(self, &vec[..], ListType::Join(","), Self::write_field); write!(out, ")"); out.new_line(); write!(out, " : "); let vec: Vec<_> = s .fields .iter() .zip(&renamed_fields) .map(|(field, renamed)| format!("{}({})", field.name, renamed)) .collect(); out.write_vertical_source_list(self, &vec[..], ListType::Join(","), |_, out, s| { write!(out, "{}", s) }); out.new_line(); write!(out, "{{}}"); out.new_line(); } let other = self .config .function .rename_args .apply("other", IdentifierType::FunctionArg); if s.annotations .bool("internal-derive-bitflags") .unwrap_or(false) { assert_eq!(s.fields.len(), 1); let bits = &s.fields[0].name; if !wrote_start_newline { wrote_start_newline = true; out.new_line(); } let constexpr_prefix = if self.config.constant.allow_constexpr { "constexpr " } else { "" }; out.new_line(); write!(out, "{}explicit operator bool() const", constexpr_prefix); out.open_brace(); write!(out, "return !!{bits};"); out.close_brace(false); out.new_line(); write!( out, "{}{} operator~() const", constexpr_prefix, s.export_name() ); out.open_brace(); write!( out, "return {} {{ static_cast(~{bits}) }};", s.export_name() ); out.close_brace(false); s.emit_bitflags_binop(constexpr_prefix, '|', &other, out); s.emit_bitflags_binop(constexpr_prefix, '&', &other, out); s.emit_bitflags_binop(constexpr_prefix, '^', &other, out); } // Generate a serializer function that allows dumping this struct // to an std::ostream. It's defined as a friend function inside the // struct definition, and doesn't need the `inline` keyword even // though it's implemented right in the generated header file. if self.config.structure.derive_ostream(&s.annotations) { if !wrote_start_newline { wrote_start_newline = true; out.new_line(); } out.new_line(); let stream = self .config .function .rename_args .apply("stream", IdentifierType::FunctionArg); let instance = self .config .function .rename_args .apply("instance", IdentifierType::FunctionArg); write!( out, "friend std::ostream& operator<<(std::ostream& {}, const {}& {})", stream, s.export_name(), instance, ); out.open_brace(); write!(out, "return {} << \"{{ \"", stream); let vec: Vec<_> = s .fields .iter() .map(|x| format!(" << \"{}=\" << {}.{}", x.name, instance, x.name)) .collect(); out.write_vertical_source_list( self, &vec[..], ListType::Join(" << \", \""), |_, out, s| write!(out, "{}", s), ); out.write(" << \" }\";"); out.close_brace(false); } let skip_fields = s.has_tag_field as usize; macro_rules! emit_op { ($op_name:expr, $op:expr, $conjuc:expr) => {{ if !wrote_start_newline { #[allow(unused_assignments)] { wrote_start_newline = true; } out.new_line(); } out.new_line(); if let Some(Some(attrs)) = s.annotations.atom(concat!($op_name, "-attributes")) { write!(out, "{} ", attrs); } write!( out, "bool operator{}(const {}& {}) const", $op, s.export_name(), other ); out.open_brace(); out.write("return "); let vec: Vec<_> = s .fields .iter() .skip(skip_fields) .map(|field| format!("{} {} {}.{}", field.name, $op, other, field.name)) .collect(); out.write_vertical_source_list( self, &vec[..], ListType::Join(&format!(" {}", $conjuc)), |_, out, s| write!(out, "{}", s), ); out.write(";"); out.close_brace(false); }}; } if self.config.structure.derive_eq(&s.annotations) && s.can_derive_eq() { emit_op!("eq", "==", "&&"); } if self.config.structure.derive_neq(&s.annotations) && s.can_derive_eq() { emit_op!("neq", "!=", "||"); } if self.config.structure.derive_lt(&s.annotations) && s.fields.len() == 1 && s.fields[0].ty.can_cmp_order() { emit_op!("lt", "<", "&&"); } if self.config.structure.derive_lte(&s.annotations) && s.fields.len() == 1 && s.fields[0].ty.can_cmp_order() { emit_op!("lte", "<=", "&&"); } if self.config.structure.derive_gt(&s.annotations) && s.fields.len() == 1 && s.fields[0].ty.can_cmp_order() { emit_op!("gt", ">", "&&"); } if self.config.structure.derive_gte(&s.annotations) && s.fields.len() == 1 && s.fields[0].ty.can_cmp_order() { emit_op!("gte", ">=", "&&"); } } } impl LanguageBackend for CLikeLanguageBackend<'_> { fn write_headers(&self, out: &mut SourceWriter, package_version: &str) { if self.config.package_version { write!(out, "/* Package version: {} */", package_version); out.new_line(); } if let Some(ref f) = self.config.header { out.new_line_if_not_start(); write!(out, "{}", f); out.new_line(); } if let Some(f) = self.config.include_guard() { out.new_line_if_not_start(); write!(out, "#ifndef {}", f); out.new_line(); write!(out, "#define {}", f); out.new_line(); } if self.config.pragma_once { out.new_line_if_not_start(); write!(out, "#pragma once"); out.new_line(); } if self.config.include_version { out.new_line_if_not_start(); write!( out, "/* Generated with cbindgen:{} */", crate::bindgen::config::VERSION ); out.new_line(); } if let Some(ref f) = self.config.autogen_warning { out.new_line_if_not_start(); write!(out, "{}", f); out.new_line(); } if self.config.no_includes && self.config.sys_includes().is_empty() && self.config.includes().is_empty() && self.config.after_includes.is_none() { return; } out.new_line_if_not_start(); if !self.config.no_includes { match self.config.language { Language::C => { out.write("#include "); out.new_line(); out.write("#include "); out.new_line(); if self.config.usize_is_size_t { out.write("#include "); out.new_line(); } out.write("#include "); out.new_line(); out.write("#include "); out.new_line(); } Language::Cxx => { out.write("#include "); out.new_line(); if self.config.usize_is_size_t { out.write("#include "); out.new_line(); } out.write("#include "); out.new_line(); out.write("#include "); out.new_line(); out.write("#include "); out.new_line(); out.write("#include "); out.new_line(); if self.config.enumeration.cast_assert_name.is_none() && (self.config.enumeration.derive_mut_casts || self.config.enumeration.derive_const_casts) { out.write("#include "); out.new_line(); } } _ => {} } } for include in self.config.sys_includes() { write!(out, "#include <{}>", include); out.new_line(); } for include in self.config.includes() { write!(out, "#include \"{}\"", include); out.new_line(); } if let Some(ref line) = self.config.after_includes { write!(out, "{}", line); out.new_line(); } } fn open_namespaces(&mut self, out: &mut SourceWriter) { self.open_close_namespaces(out, true); } fn close_namespaces(&mut self, out: &mut SourceWriter) { self.open_close_namespaces(out, false) } fn write_footers(&mut self, out: &mut SourceWriter) { if let Some(f) = self.config.include_guard() { out.new_line_if_not_start(); if self.config.language == Language::C { write!(out, "#endif /* {} */", f); } else { write!(out, "#endif // {}", f); } out.new_line(); } } fn write_enum(&mut self, out: &mut SourceWriter, e: &Enum) { let size = e.repr.ty.map(|ty| ty.to_primitive().to_repr_c(self.config)); let has_data = e.tag.is_some(); let inline_tag_field = Enum::inline_tag_field(&e.repr); let tag_name = e.tag_name(); let condition = e.cfg.to_condition(self.config); condition.write_before(self.config, out); self.write_documentation(out, &e.documentation); self.write_generic_param(out, &e.generic_params); // If the enum has data, we need to emit a struct or union for the data // and enum for the tag. C++ supports nested type definitions, so we open // the struct or union here and define the tag enum inside it (*). if has_data && self.config.language == Language::Cxx { e.open_struct_or_union(self.config, out, inline_tag_field); } // Emit the tag enum and everything related to it. e.write_tag_enum(self.config, self, out, size, Self::write_enum_variant); // If the enum has data, we need to emit structs for the variants and gather them together. if has_data { e.write_variant_defs(self.config, self, out); out.new_line(); out.new_line(); // Open the struct or union for the data (**), gathering all the variants with data // together, unless it's C++, then we have already opened that struct/union at (*) and // are currently inside it. if self.config.language != Language::Cxx { e.open_struct_or_union(self.config, out, inline_tag_field); } // Emit tag field that is separate from all variants. e.write_tag_field(self.config, out, size, inline_tag_field, tag_name); out.new_line(); // Open union of all variants with data, only in the non-inline tag scenario. if !inline_tag_field { out.write("union"); out.open_brace(); } // Emit fields for all variants with data. e.write_variant_fields(self.config, self, out, inline_tag_field, Self::write_field); // Close union of all variants with data, only in the non-inline tag scenario. if !inline_tag_field { out.close_brace(true); } // Emit convenience methods for the struct or enum for the data. e.write_derived_functions_data(self.config, self, out, tag_name, Self::write_field); // Emit the post_body section, if relevant. if let Some(body) = self.config.export.post_body(&e.path) { out.new_line(); out.write_raw_block(body); } // Close the struct or union opened either at (*) or at (**). if self.generate_typedef() { out.close_brace(false); write!(out, " {};", e.export_name); } else { out.close_brace(true); } } condition.write_after(self.config, out); } fn write_struct(&mut self, out: &mut SourceWriter, s: &Struct) { if s.is_transparent { let typedef = Typedef { path: s.path.clone(), export_name: s.export_name.to_owned(), generic_params: s.generic_params.clone(), aliased: s.fields[0].ty.clone(), cfg: s.cfg.clone(), annotations: s.annotations.clone(), documentation: s.documentation.clone(), }; self.write_type_def(out, &typedef); for constant in &s.associated_constants { out.new_line(); constant.write(self.config, self, out, Some(s)); } return; } let condition = s.cfg.to_condition(self.config); condition.write_before(self.config, out); self.write_documentation(out, &s.documentation); if !s.is_enum_variant_body { self.write_generic_param(out, &s.generic_params); } // The following results in // C++ or C with Tag as style: // struct Name { // C with Type only style: // typedef struct { // C with Both as style: // typedef struct Name { if self.generate_typedef() { out.write("typedef "); } out.write("struct"); if let Some(align) = s.alignment { match align { ReprAlign::Packed => { if let Some(ref anno) = self.config.layout.packed { write!(out, " {}", anno); } } ReprAlign::Align(n) => { if let Some(ref anno) = self.config.layout.aligned_n { write!(out, " {}({})", anno, n); } } } } if s.annotations.must_use(self.config) { if let Some(ref anno) = self.config.structure.must_use { write!(out, " {}", anno); } } if let Some(note) = s .annotations .deprecated_note(self.config, DeprecatedNoteKind::Struct) { write!(out, " {}", note); } if self.config.language != Language::C || self.config.style.generate_tag() { write!(out, " {}", s.export_name()); } out.open_brace(); // Emit the pre_body section, if relevant if let Some(body) = self.config.export.pre_body(&s.path) { out.write_raw_block(body); out.new_line(); } out.write_vertical_source_list(self, &s.fields, ListType::Cap(";"), Self::write_field); if self.config.language == Language::Cxx { self.write_derived_cpp_ops(out, s); } // Emit the post_body section, if relevant if let Some(body) = self.config.export.post_body(&s.path) { out.new_line(); out.write_raw_block(body); } if self.config.language == Language::Cxx && self.config.structure.associated_constants_in_body && self.config.constant.allow_static_const { for constant in &s.associated_constants { out.new_line(); constant.write_declaration(self.config, self, out, s); } } if self.generate_typedef() { out.close_brace(false); write!(out, " {};", s.export_name()); } else { out.close_brace(true); } for constant in &s.associated_constants { out.new_line(); constant.write(self.config, self, out, Some(s)); } condition.write_after(self.config, out); } fn write_union(&mut self, out: &mut SourceWriter, u: &Union) { let condition = u.cfg.to_condition(self.config); condition.write_before(self.config, out); self.write_documentation(out, &u.documentation); self.write_generic_param(out, &u.generic_params); // The following results in // C++ or C with Tag as style: // union Name { // C with Type only style: // typedef union { // C with Both as style: // typedef union Name { if self.generate_typedef() { out.write("typedef "); } out.write("union"); if let Some(align) = u.alignment { match align { ReprAlign::Packed => { if let Some(ref anno) = self.config.layout.packed { write!(out, " {}", anno); } } ReprAlign::Align(n) => { if let Some(ref anno) = self.config.layout.aligned_n { write!(out, " {}({})", anno, n); } } } } if self.config.language != Language::C || self.config.style.generate_tag() { write!(out, " {}", u.export_name); } out.open_brace(); // Emit the pre_body section, if relevant if let Some(body) = self.config.export.pre_body(&u.path) { out.write_raw_block(body); out.new_line(); } out.write_vertical_source_list(self, &u.fields, ListType::Cap(";"), Self::write_field); // Emit the post_body section, if relevant if let Some(body) = self.config.export.post_body(&u.path) { out.new_line(); out.write_raw_block(body); } if self.generate_typedef() { out.close_brace(false); write!(out, " {};", u.export_name); } else { out.close_brace(true); } condition.write_after(self.config, out); } fn write_opaque_item(&mut self, out: &mut SourceWriter, o: &OpaqueItem) { let condition = o.cfg.to_condition(self.config); condition.write_before(self.config, out); self.write_documentation(out, &o.documentation); o.generic_params.write_with_default(self, self.config, out); if self.generate_typedef() { write!( out, "typedef struct {} {};", o.export_name(), o.export_name() ); } else { write!(out, "struct {};", o.export_name()); } condition.write_after(self.config, out); } fn write_type_def(&mut self, out: &mut SourceWriter, t: &Typedef) { let condition = t.cfg.to_condition(self.config); condition.write_before(self.config, out); self.write_documentation(out, &t.documentation); self.write_generic_param(out, &t.generic_params); if self.config.language == Language::Cxx { write!(out, "using {} = ", t.export_name()); self.write_type(out, &t.aliased); } else { write!(out, "{} ", self.config.language.typedef()); self.write_field( out, &Field::from_name_and_type(t.export_name().to_owned(), t.aliased.clone()), ); } out.write(";"); condition.write_after(self.config, out); } fn write_static(&mut self, out: &mut SourceWriter, s: &Static) { let condition = s.cfg.to_condition(self.config); condition.write_before(self.config, out); self.write_documentation(out, &s.documentation); out.write("extern "); if let Type::Ptr { is_const: true, .. } = s.ty { } else if !s.mutable { out.write("const "); } cdecl::write_field(self, out, &s.ty, &s.export_name, self.config); out.write(";"); condition.write_after(self.config, out); } fn write_type(&mut self, out: &mut SourceWriter, t: &Type) { cdecl::write_type(self, out, t, self.config); } fn write_documentation(&mut self, out: &mut SourceWriter, d: &Documentation) { if d.doc_comment.is_empty() || !self.config.documentation { return; } let end = match self.config.documentation_length { DocumentationLength::Short => 1, DocumentationLength::Full => d.doc_comment.len(), }; let style = match self.config.documentation_style { DocumentationStyle::Auto if self.config.language == Language::C => { DocumentationStyle::Doxy } DocumentationStyle::Auto if self.config.language == Language::Cxx => { DocumentationStyle::Cxx } DocumentationStyle::Auto => DocumentationStyle::C, // Fallback if `Language` gets extended. other => other, }; // Following these documents for style conventions: // https://en.wikibooks.org/wiki/C++_Programming/Code/Style_Conventions/Comments // https://www.cs.cmu.edu/~410/doc/doxygen.html match style { DocumentationStyle::C => { out.write("/*"); out.new_line(); } DocumentationStyle::Doxy => { out.write("/**"); out.new_line(); } _ => (), } for line in &d.doc_comment[..end] { match style { DocumentationStyle::C => out.write(""), DocumentationStyle::Doxy => out.write(" *"), DocumentationStyle::C99 => out.write("//"), DocumentationStyle::Cxx => out.write("///"), DocumentationStyle::Auto => unreachable!(), // Auto case should always be covered } write!(out, "{}", line); out.new_line(); } match style { DocumentationStyle::C => { out.write(" */"); out.new_line(); } DocumentationStyle::Doxy => { out.write(" */"); out.new_line(); } _ => (), } } fn write_literal(&mut self, out: &mut SourceWriter, l: &Literal) { match l { Literal::Expr(v) => write!(out, "{}", v), Literal::Path { ref associated_to, ref name, } => { if let Some((ref path, ref export_name)) = associated_to { if let Some(known) = to_known_assoc_constant(path, name) { return write!(out, "{}", known); } let path_separator = if self.config.language == Language::C { "_" } else if self.config.structure.associated_constants_in_body { "::" } else { "_" }; write!(out, "{}{}", export_name, path_separator) } write!(out, "{}", name) } Literal::FieldAccess { ref base, ref field, } => { write!(out, "("); self.write_literal(out, base); write!(out, ").{}", field); } Literal::PostfixUnaryOp { op, ref value } => { write!(out, "{}", op); self.write_literal(out, value); } Literal::BinOp { ref left, op, ref right, } => { write!(out, "("); self.write_literal(out, left); write!(out, " {} ", op); self.write_literal(out, right); write!(out, ")"); } Literal::Cast { ref ty, ref value } => { out.write("("); self.write_type(out, ty); out.write(")"); self.write_literal(out, value); } Literal::Struct { export_name, fields, path, } => { if self.config.language == Language::C { write!(out, "({})", export_name); } else { write!(out, "{}", export_name); } write!(out, "{{ "); let mut is_first_field = true; // In C++, same order as defined is required. let ordered_fields = out.bindings().struct_field_names(path); for ordered_key in ordered_fields.iter() { if let Some(lit) = fields.get(ordered_key) { if !is_first_field { write!(out, ", "); } is_first_field = false; if self.config.language == Language::Cxx { // TODO: Some C++ versions (c++20?) now support designated // initializers, consider generating them. write!(out, "/* .{} = */ ", ordered_key); } else { write!(out, ".{} = ", ordered_key); } self.write_literal(out, lit); } } write!(out, " }}"); } } } fn write_globals(&mut self, out: &mut SourceWriter, b: &Bindings) { // Override default method to open various blocs containing both globals and functions // these blocks are closed in [`write_functions`] that is also overridden if !b.functions.is_empty() || !b.globals.is_empty() { if b.config.cpp_compatible_c() { out.new_line_if_not_start(); out.write("#ifdef __cplusplus"); } if b.config.language == Language::Cxx { if let Some(ref using_namespaces) = b.config.using_namespaces { for namespace in using_namespaces { out.new_line(); write!(out, "using namespace {};", namespace); } out.new_line(); } } if b.config.language == Language::Cxx || b.config.cpp_compatible_c() { out.new_line(); out.write("extern \"C\" {"); out.new_line(); } if b.config.cpp_compatible_c() { out.write("#endif // __cplusplus"); out.new_line(); } self.write_globals_default(out, b); } } fn write_functions(&mut self, out: &mut SourceWriter, b: &Bindings) { // Override default method to close various blocks containing both globals and functions // these blocks are opened in [`write_globals`] that is also overridden if !b.functions.is_empty() || !b.globals.is_empty() { self.write_functions_default(out, b); if b.config.cpp_compatible_c() { out.new_line(); out.write("#ifdef __cplusplus"); } if b.config.language == Language::Cxx || b.config.cpp_compatible_c() { out.new_line(); out.write("} // extern \"C\""); out.new_line(); } if b.config.cpp_compatible_c() { out.write("#endif // __cplusplus"); out.new_line(); } } } } cbindgen-0.27.0/src/bindgen/language_backend/cython.rs000064400000000000000000000351341046102023000207720ustar 00000000000000use crate::bindgen::ir::{ to_known_assoc_constant, ConditionWrite, DeprecatedNoteKind, Documentation, Enum, EnumVariant, Field, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, ToCondition, Type, Typedef, Union, }; use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::writer::{ListType, SourceWriter}; use crate::bindgen::DocumentationLength; use crate::bindgen::{cdecl, Bindings, Config}; use std::io::Write; pub struct CythonLanguageBackend<'a> { config: &'a Config, } impl<'a> CythonLanguageBackend<'a> { pub fn new(config: &'a Config) -> Self { Self { config } } fn write_enum_variant(&mut self, out: &mut SourceWriter, u: &EnumVariant) { self.write_documentation(out, &u.documentation); write!(out, "{}", u.export_name); if let Some(discriminant) = &u.discriminant { // For extern Cython declarations the enumerator value is ignored, // but still useful as documentation, so we write it as a comment. out.write(" # = "); self.write_literal(out, discriminant); } out.write(","); } fn write_field(&mut self, out: &mut SourceWriter, f: &Field) { // Cython doesn't support conditional fields. // let condition = f.cfg.to_condition(self.config); self.write_documentation(out, &f.documentation); cdecl::write_field(self, out, &f.ty, &f.name, self.config); // Cython extern declarations don't manage layouts, layouts are defined entierly by the // corresponding C code. So we can omit bitfield sizes which are not supported by Cython. } } impl LanguageBackend for CythonLanguageBackend<'_> { fn write_headers(&self, out: &mut SourceWriter, package_version: &str) { if self.config.package_version { write!(out, "''' Package version: {} '''", package_version); out.new_line(); } if let Some(ref f) = self.config.header { out.new_line_if_not_start(); write!(out, "{}", f); out.new_line(); } if self.config.include_version { out.new_line_if_not_start(); write!( out, "/* Generated with cbindgen:{} */", crate::bindgen::config::VERSION ); out.new_line(); } if let Some(ref f) = &self.config.autogen_warning { out.new_line_if_not_start(); write!(out, "{}", f); out.new_line(); } if self.config.no_includes && self.config.sys_includes().is_empty() && self.config.includes().is_empty() && (self.config.cython.cimports.is_empty()) && self.config.after_includes.is_none() { return; } out.new_line_if_not_start(); if !&self.config.no_includes { out.write("from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t"); out.new_line(); out.write("from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t"); out.new_line(); out.write("cdef extern from *"); out.open_brace(); out.write("ctypedef bint bool"); out.new_line(); out.write("ctypedef struct va_list"); out.new_line(); out.close_brace(false); } for (module, names) in &self.config.cython.cimports { write!(out, "from {} cimport {}", module, names.join(", ")); out.new_line(); } if let Some(ref line) = &self.config.after_includes { write!(out, "{}", line); out.new_line(); } } fn open_namespaces(&mut self, out: &mut SourceWriter) { out.new_line(); let header = &self.config.cython.header.as_deref().unwrap_or("*"); write!(out, "cdef extern from {}", header); out.open_brace(); } fn close_namespaces(&mut self, out: &mut SourceWriter) { out.close_brace(false); } fn write_footers(&mut self, _out: &mut SourceWriter) {} fn write_enum(&mut self, out: &mut SourceWriter, e: &Enum) { let size = e.repr.ty.map(|ty| ty.to_primitive().to_repr_c(self.config)); let has_data = e.tag.is_some(); let inline_tag_field = Enum::inline_tag_field(&e.repr); let tag_name = e.tag_name(); let condition = e.cfg.to_condition(self.config); condition.write_before(self.config, out); self.write_documentation(out, &e.documentation); // Emit the tag enum and everything related to it. e.write_tag_enum(self.config, self, out, size, Self::write_enum_variant); // If the enum has data, we need to emit structs for the variants and gather them together. if has_data { e.write_variant_defs(self.config, self, out); out.new_line(); out.new_line(); e.open_struct_or_union(self.config, out, inline_tag_field); // Emit tag field that is separate from all variants. e.write_tag_field(self.config, out, size, inline_tag_field, tag_name); out.new_line(); // Emit fields for all variants with data. e.write_variant_fields(self.config, self, out, inline_tag_field, Self::write_field); // Emit the post_body section, if relevant. if let Some(body) = &self.config.export.post_body(&e.path) { out.new_line(); out.write_raw_block(body); } out.close_brace(true); } condition.write_after(self.config, out); } fn write_struct(&mut self, out: &mut SourceWriter, s: &Struct) { if s.is_transparent { let typedef = Typedef { path: s.path.clone(), export_name: s.export_name.to_owned(), generic_params: s.generic_params.clone(), aliased: s.fields[0].ty.clone(), cfg: s.cfg.clone(), annotations: s.annotations.clone(), documentation: s.documentation.clone(), }; self.write_type_def(out, &typedef); for constant in &s.associated_constants { out.new_line(); constant.write(self.config, self, out, Some(s)); } return; } let condition = s.cfg.to_condition(self.config); condition.write_before(self.config, out); self.write_documentation(out, &s.documentation); out.write(self.config.style.cython_def()); // Cython extern declarations don't manage layouts, layouts are defined entierly by the // corresponding C code. So this `packed` is only for documentation, and missing // `aligned(n)` is also not a problem. if let Some(align) = s.alignment { match align { ReprAlign::Packed => out.write("packed "), ReprAlign::Align(_) => {} // Not supported } } out.write("struct"); if s.annotations.must_use(self.config) { if let Some(ref anno) = &self.config.structure.must_use { write!(out, " {}", anno); } } if let Some(note) = s .annotations .deprecated_note(self.config, DeprecatedNoteKind::Struct) { write!(out, " {}", note); } write!(out, " {}", s.export_name()); out.open_brace(); // Emit the pre_body section, if relevant if let Some(body) = &self.config.export.pre_body(&s.path) { out.write_raw_block(body); out.new_line(); } out.write_vertical_source_list(self, &s.fields, ListType::Cap(";"), Self::write_field); if s.fields.is_empty() { out.write("pass"); } // Emit the post_body section, if relevant if let Some(body) = &self.config.export.post_body(&s.path) { out.new_line(); out.write_raw_block(body); } out.close_brace(true); for constant in &s.associated_constants { out.new_line(); constant.write(self.config, self, out, Some(s)); } condition.write_after(self.config, out); } fn write_union(&mut self, out: &mut SourceWriter, u: &Union) { let condition = u.cfg.to_condition(self.config); condition.write_before(self.config, out); self.write_documentation(out, &u.documentation); out.write(self.config.style.cython_def()); out.write("union"); write!(out, " {}", u.export_name); out.open_brace(); // Emit the pre_body section, if relevant if let Some(body) = &self.config.export.pre_body(&u.path) { out.write_raw_block(body); out.new_line(); } out.write_vertical_source_list(self, &u.fields, ListType::Cap(";"), Self::write_field); if u.fields.is_empty() { out.write("pass"); } // Emit the post_body section, if relevant if let Some(body) = &self.config.export.post_body(&u.path) { out.new_line(); out.write_raw_block(body); } out.close_brace(true); condition.write_after(self.config, out); } fn write_opaque_item(&mut self, out: &mut SourceWriter, o: &OpaqueItem) { let condition = o.cfg.to_condition(self.config); condition.write_before(self.config, out); self.write_documentation(out, &o.documentation); o.generic_params.write_with_default(self, self.config, out); write!( out, "{}struct {}", &self.config.style.cython_def(), o.export_name() ); out.open_brace(); out.write("pass"); out.close_brace(false); condition.write_after(self.config, out); } fn write_type_def(&mut self, out: &mut SourceWriter, t: &Typedef) { let condition = t.cfg.to_condition(self.config); condition.write_before(self.config, out); self.write_documentation(out, &t.documentation); write!(out, "{} ", &self.config.language.typedef()); self.write_field( out, &Field::from_name_and_type(t.export_name().to_owned(), t.aliased.clone()), ); out.write(";"); condition.write_after(self.config, out); } fn write_static(&mut self, out: &mut SourceWriter, s: &Static) { let condition = s.cfg.to_condition(self.config); condition.write_before(self.config, out); self.write_documentation(out, &s.documentation); out.write("extern "); if let Type::Ptr { is_const: true, .. } = s.ty { } else if !s.mutable { out.write("const "); } cdecl::write_field(self, out, &s.ty, &s.export_name, self.config); out.write(";"); condition.write_after(self.config, out); } fn write_type(&mut self, out: &mut SourceWriter, t: &Type) { cdecl::write_type(self, out, t, self.config); } fn write_documentation(&mut self, out: &mut SourceWriter, d: &Documentation) { if d.doc_comment.is_empty() || !&self.config.documentation { return; } let end = match &self.config.documentation_length { DocumentationLength::Short => 1, DocumentationLength::Full => d.doc_comment.len(), }; // Cython uses Python-style comments, so `documentation_style` is not relevant. for line in &d.doc_comment[..end] { write!(out, "#{}", line); out.new_line(); } } fn write_literal(&mut self, out: &mut SourceWriter, l: &Literal) { match l { Literal::Expr(v) => match &**v { "true" => write!(out, "True"), "false" => write!(out, "False"), v => write!(out, "{}", v), }, Literal::Path { ref associated_to, ref name, } => { if let Some((ref path, ref export_name)) = associated_to { if let Some(known) = to_known_assoc_constant(path, name) { return write!(out, "{}", known); } write!(out, "{}_", export_name) } write!(out, "{}", name) } Literal::FieldAccess { ref base, ref field, } => { write!(out, "("); self.write_literal(out, base); write!(out, ").{}", field); } Literal::PostfixUnaryOp { op, ref value } => { write!(out, "{}", op); self.write_literal(out, value); } Literal::BinOp { ref left, op, ref right, } => { write!(out, "("); self.write_literal(out, left); write!(out, " {} ", op); self.write_literal(out, right); write!(out, ")"); } Literal::Cast { ref ty, ref value } => { out.write("<"); self.write_type(out, ty); out.write(">"); self.write_literal(out, value); } Literal::Struct { export_name, fields, path, } => { write!(out, "<{}>", export_name); write!(out, "{{ "); let mut is_first_field = true; // In C++, same order as defined is required. let ordered_fields = out.bindings().struct_field_names(path); for ordered_key in ordered_fields.iter() { if let Some(lit) = fields.get(ordered_key) { if !is_first_field { write!(out, ", "); } else { is_first_field = false; } self.write_literal(out, lit); } } write!(out, " }}"); } } } fn write_functions(&mut self, out: &mut SourceWriter, b: &Bindings) { self.write_functions_default(out, b); if b.globals.is_empty() && b.constants.is_empty() && b.items.is_empty() && b.functions.is_empty() { out.write("pass"); } } } cbindgen-0.27.0/src/bindgen/language_backend/mod.rs000064400000000000000000000167651046102023000202560ustar 00000000000000use crate::bindgen::ir::{ cfg::ConditionWrite, DeprecatedNoteKind, Documentation, Enum, Function, ItemContainer, Literal, OpaqueItem, Static, Struct, ToCondition, Type, Typedef, Union, }; use crate::bindgen::writer::SourceWriter; use crate::bindgen::{cdecl, Bindings, Layout}; use crate::Config; use std::io::Write; mod clike; mod cython; pub use clike::CLikeLanguageBackend; pub use cython::CythonLanguageBackend; pub trait LanguageBackend: Sized { fn open_namespaces(&mut self, out: &mut SourceWriter); fn close_namespaces(&mut self, out: &mut SourceWriter); fn write_headers(&self, out: &mut SourceWriter, package_version: &str); fn write_footers(&mut self, out: &mut SourceWriter); fn write_enum(&mut self, out: &mut SourceWriter, e: &Enum); fn write_struct(&mut self, out: &mut SourceWriter, s: &Struct); fn write_union(&mut self, out: &mut SourceWriter, u: &Union); fn write_opaque_item(&mut self, out: &mut SourceWriter, o: &OpaqueItem); fn write_type_def(&mut self, out: &mut SourceWriter, t: &Typedef); fn write_static(&mut self, out: &mut SourceWriter, s: &Static); fn write_function( &mut self, config: &Config, out: &mut SourceWriter, f: &Function, ) { match config.function.args { Layout::Horizontal => { self.write_function_with_layout(config, out, f, Layout::Horizontal) } Layout::Vertical => self.write_function_with_layout(config, out, f, Layout::Vertical), Layout::Auto => { let max_line_length = config.line_length; if !out.try_write( |out| self.write_function_with_layout(config, out, f, Layout::Horizontal), max_line_length, ) { self.write_function_with_layout(config, out, f, Layout::Vertical); } } } } fn write_function_with_layout( &mut self, config: &Config, out: &mut SourceWriter, func: &Function, layout: Layout, ) { let prefix = config.function.prefix(&func.annotations); let postfix = config.function.postfix(&func.annotations); let condition = func.cfg.to_condition(config); condition.write_before(config, out); self.write_documentation(out, &func.documentation); fn write_space(layout: Layout, out: &mut SourceWriter) { if layout == Layout::Vertical { out.new_line(); } else { out.write(" ") } } if func.extern_decl { out.write("extern "); } else { if let Some(ref prefix) = prefix { write!(out, "{}", prefix); write_space(layout, out); } if func.annotations.must_use(config) { if let Some(ref anno) = config.function.must_use { write!(out, "{}", anno); write_space(layout, out); } } if let Some(note) = func .annotations .deprecated_note(config, DeprecatedNoteKind::Function) { write!(out, "{}", note); write_space(layout, out); } } cdecl::write_func(self, out, func, layout, config); if !func.extern_decl { if let Some(ref postfix) = postfix { write_space(layout, out); write!(out, "{}", postfix); } } if let Some(ref swift_name_macro) = config.function.swift_name_macro { if let Some(swift_name) = func.swift_name(config) { // XXX Should this account for `layout`? write!(out, " {}({})", swift_name_macro, swift_name); } } out.write(";"); condition.write_after(config, out); } fn write_type(&mut self, out: &mut SourceWriter, t: &Type); fn write_documentation(&mut self, out: &mut SourceWriter, d: &Documentation); fn write_literal(&mut self, out: &mut SourceWriter, l: &Literal); fn write_bindings(&mut self, out: &mut SourceWriter, b: &Bindings) { self.write_headers(out, &b.package_version); self.open_namespaces(out); self.write_primitive_constants(out, b); self.write_items(out, b); self.write_non_primitive_constants(out, b); self.write_globals(out, b); self.write_functions(out, b); self.close_namespaces(out); self.write_footers(out); self.write_trailer(out, b); } fn write_primitive_constants(&mut self, out: &mut SourceWriter, b: &Bindings) { for constant in &b.constants { if constant.uses_only_primitive_types() { out.new_line_if_not_start(); constant.write(&b.config, self, out, None); out.new_line(); } } } fn write_items(&mut self, out: &mut SourceWriter, b: &Bindings) { for item in &b.items { if item .deref() .annotations() .bool("no-export") .unwrap_or(false) { continue; } out.new_line_if_not_start(); match *item { ItemContainer::Constant(..) => unreachable!(), ItemContainer::Static(..) => unreachable!(), ItemContainer::Enum(ref x) => self.write_enum(out, x), ItemContainer::Struct(ref x) => self.write_struct(out, x), ItemContainer::Union(ref x) => self.write_union(out, x), ItemContainer::OpaqueItem(ref x) => self.write_opaque_item(out, x), ItemContainer::Typedef(ref x) => self.write_type_def(out, x), } out.new_line(); } } fn write_non_primitive_constants(&mut self, out: &mut SourceWriter, b: &Bindings) { for constant in &b.constants { if !constant.uses_only_primitive_types() { out.new_line_if_not_start(); constant.write(&b.config, self, out, None); out.new_line(); } } } fn write_globals(&mut self, out: &mut SourceWriter, b: &Bindings) { self.write_globals_default(out, b) } fn write_globals_default(&mut self, out: &mut SourceWriter, b: &Bindings) { for global in &b.globals { out.new_line_if_not_start(); self.write_static(out, global); out.new_line(); } } fn write_functions(&mut self, out: &mut SourceWriter, b: &Bindings) { self.write_functions_default(out, b) } fn write_functions_default(&mut self, out: &mut SourceWriter, b: &Bindings) { for function in &b.functions { out.new_line_if_not_start(); self.write_function(&b.config, out, function); out.new_line(); } } fn write_trailer(&mut self, out: &mut SourceWriter, b: &Bindings) { if let Some(ref f) = b.config.trailer { out.new_line_if_not_start(); write!(out, "{}", f); if !f.ends_with('\n') { out.new_line(); } } } } cbindgen-0.27.0/src/bindgen/library.rs000064400000000000000000000350461046102023000157020ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::collections::HashMap; use std::path::PathBuf; use crate::bindgen::bindings::Bindings; use crate::bindgen::config::{Config, Language, SortKey}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::error::Error; use crate::bindgen::ir::{Constant, Enum, Function, Item, ItemContainer, ItemMap}; use crate::bindgen::ir::{OpaqueItem, Path, Static, Struct, Typedef, Union}; use crate::bindgen::monomorph::Monomorphs; use crate::bindgen::ItemType; #[derive(Debug, Clone)] pub struct Library { config: Config, constants: ItemMap, globals: ItemMap, enums: ItemMap, structs: ItemMap, unions: ItemMap, opaque_items: ItemMap, typedefs: ItemMap, functions: Vec, source_files: Vec, package_version: String, } impl Library { #[allow(clippy::too_many_arguments)] pub fn new( config: Config, constants: ItemMap, globals: ItemMap, enums: ItemMap, structs: ItemMap, unions: ItemMap, opaque_items: ItemMap, typedefs: ItemMap, functions: Vec, source_files: Vec, package_version: String, ) -> Library { Library { config, constants, globals, enums, structs, unions, opaque_items, typedefs, functions, source_files, package_version, } } pub fn generate(mut self) -> Result { self.transfer_annotations(); self.simplify_standard_types(); match self.config.function.sort_by.unwrap_or(self.config.sort_by) { SortKey::Name => self.functions.sort_by(|x, y| x.path.cmp(&y.path)), SortKey::None => { /* keep input order */ } } if self.config.language != Language::Cxx { self.instantiate_monomorphs(); } self.remove_excluded(); if self.config.language == Language::C { self.resolve_declaration_types(); } self.rename_items(); let mut dependencies = Dependencies::new(); for function in &self.functions { function.add_dependencies(&self, &mut dependencies); } self.globals.for_all_items(|global| { global.add_dependencies(&self, &mut dependencies); }); self.constants.for_all_items(|constant| { constant.add_dependencies(&self, &mut dependencies); }); for name in &self.config.export.include { let path = Path::new(name.clone()); if let Some(items) = self.get_items(&path) { if dependencies.items.insert(path) { for item in &items { item.deref().add_dependencies(&self, &mut dependencies); } for item in items { dependencies.order.push(item); } } } } dependencies.sort(); let items = dependencies.order; let constants = if self.config.export.should_generate(ItemType::Constants) { let mut constants = self.constants.to_vec(); match self.config.constant.sort_by.unwrap_or(self.config.sort_by) { SortKey::Name => constants.sort_by(|x, y| x.path.cmp(&y.path)), SortKey::None => { /* keep input order */ } } constants } else { vec![] }; let globals = if self.config.export.should_generate(ItemType::Globals) { let mut globals = self.globals.to_vec(); match self.config.constant.sort_by.unwrap_or(self.config.sort_by) { SortKey::Name => globals.sort_by(|x, y| x.path.cmp(&y.path)), SortKey::None => { /* keep input order */ } } globals } else { vec![] }; let functions = if self.config.export.should_generate(ItemType::Functions) { self.functions } else { vec![] }; Ok(Bindings::new( self.config, self.structs, self.typedefs, constants, globals, items, functions, self.source_files, false, self.package_version, )) } pub fn get_items(&self, p: &Path) -> Option> { macro_rules! find { ($field:ident, $kind:ident) => { if self.config.export.should_generate(ItemType::$kind) { if let Some(x) = self.$field.get_items(p) { return Some(x); } } }; } find!(enums, Enums); find!(structs, Structs); find!(unions, Unions); find!(opaque_items, OpaqueItems); find!(typedefs, Typedefs); None } pub fn get_config(&self) -> &Config { &self.config } fn remove_excluded(&mut self) { let config = &self.config; // FIXME: interpret `config.export.exclude` as `Path`s. self.functions .retain(|x| !config.export.exclude.iter().any(|y| y == x.path().name())); self.enums .filter(|x| config.export.exclude.iter().any(|y| y == x.path().name())); self.structs .filter(|x| config.export.exclude.iter().any(|y| y == x.path().name())); self.unions .filter(|x| config.export.exclude.iter().any(|y| y == x.path().name())); self.opaque_items .filter(|x| config.export.exclude.iter().any(|y| y == x.path().name())); self.typedefs .filter(|x| config.export.exclude.iter().any(|y| y == x.path().name())); self.globals .filter(|x| config.export.exclude.iter().any(|y| y == x.path().name())); self.constants .filter(|x| config.export.exclude.iter().any(|y| y == x.path().name())); } fn transfer_annotations(&mut self) { let mut annotations = HashMap::new(); self.typedefs.for_all_items_mut(|x| { x.transfer_annotations(&mut annotations); }); for (alias_path, annotations) in annotations { // TODO let mut transferred = false; self.enums.for_items_mut(&alias_path, |x| { if x.annotations().is_empty() { *x.annotations_mut() = annotations.clone(); transferred = true; } else { warn!( "Can't transfer annotations from typedef to alias ({}) \ that already has annotations.", alias_path ); } }); if transferred { continue; } self.structs.for_items_mut(&alias_path, |x| { if x.annotations().is_empty() { *x.annotations_mut() = annotations.clone(); transferred = true; } else { warn!( "Can't transfer annotations from typedef to alias ({}) \ that already has annotations.", alias_path ); } }); if transferred { continue; } self.unions.for_items_mut(&alias_path, |x| { if x.annotations().is_empty() { *x.annotations_mut() = annotations.clone(); transferred = true; } else { warn!( "Can't transfer annotations from typedef to alias ({}) \ that already has annotations.", alias_path ); } }); if transferred { continue; } self.opaque_items.for_items_mut(&alias_path, |x| { if x.annotations().is_empty() { *x.annotations_mut() = annotations.clone(); transferred = true; } else { warn!( "Can't transfer annotations from typedef to alias ({}) \ that already has annotations.", alias_path ); } }); if transferred { continue; } self.typedefs.for_items_mut(&alias_path, |x| { if x.annotations().is_empty() { *x.annotations_mut() = annotations.clone(); transferred = true; } else { warn!( "Can't transfer annotations from typedef to alias ({}) \ that already has annotations.", alias_path ); } }); if transferred { continue; } } } fn rename_items(&mut self) { let config = &self.config; self.globals .for_all_items_mut(|x| x.rename_for_config(config)); self.globals.rebuild(); self.constants .for_all_items_mut(|x| x.rename_for_config(config)); self.constants.rebuild(); self.structs .for_all_items_mut(|x| x.rename_for_config(config)); self.structs.rebuild(); self.unions .for_all_items_mut(|x| x.rename_for_config(config)); self.unions.rebuild(); self.enums .for_all_items_mut(|x| x.rename_for_config(config)); self.enums.rebuild(); self.opaque_items .for_all_items_mut(|x| x.rename_for_config(config)); self.opaque_items.rebuild(); self.typedefs .for_all_items_mut(|x| x.rename_for_config(config)); self.typedefs.rebuild(); for item in &mut self.functions { item.rename_for_config(&self.config); } } fn resolve_declaration_types(&mut self) { if !self.config.style.generate_tag() { return; } let mut resolver = DeclarationTypeResolver::default(); self.structs.for_all_items(|x| { x.collect_declaration_types(&mut resolver); }); self.enums.for_all_items(|x| { x.collect_declaration_types(&mut resolver); }); self.unions.for_all_items(|x| { x.collect_declaration_types(&mut resolver); }); self.typedefs.for_all_items(|x| { x.collect_declaration_types(&mut resolver); }); // NOTE: Intentionally last, so that in case there's an opaque type // which is conflicting with a non-opaque one, the later wins. self.opaque_items.for_all_items(|x| { x.collect_declaration_types(&mut resolver); }); self.enums .for_all_items_mut(|x| x.resolve_declaration_types(&resolver)); self.structs .for_all_items_mut(|x| x.resolve_declaration_types(&resolver)); self.unions .for_all_items_mut(|x| x.resolve_declaration_types(&resolver)); self.typedefs .for_all_items_mut(|x| x.resolve_declaration_types(&resolver)); self.globals .for_all_items_mut(|x| x.resolve_declaration_types(&resolver)); for item in &mut self.functions { item.resolve_declaration_types(&resolver); } } fn simplify_standard_types(&mut self) { let config = &self.config; self.structs.for_all_items_mut(|x| { x.simplify_standard_types(config); }); self.enums.for_all_items_mut(|x| { x.simplify_standard_types(config); }); self.unions.for_all_items_mut(|x| { x.simplify_standard_types(config); }); self.globals.for_all_items_mut(|x| { x.simplify_standard_types(config); }); self.typedefs.for_all_items_mut(|x| { x.simplify_standard_types(config); }); for x in &mut self.functions { x.simplify_standard_types(config); } } fn instantiate_monomorphs(&mut self) { // Collect a list of monomorphs let mut monomorphs = Monomorphs::default(); self.structs.for_all_items(|x| { x.add_monomorphs(self, &mut monomorphs); }); self.unions.for_all_items(|x| { x.add_monomorphs(self, &mut monomorphs); }); self.enums.for_all_items(|x| { x.add_monomorphs(self, &mut monomorphs); }); self.typedefs.for_all_items(|x| { x.add_monomorphs(self, &mut monomorphs); }); for x in &self.functions { x.add_monomorphs(self, &mut monomorphs); } // Insert the monomorphs into self for monomorph in monomorphs.drain_structs() { self.structs.try_insert(monomorph); } for monomorph in monomorphs.drain_unions() { self.unions.try_insert(monomorph); } for monomorph in monomorphs.drain_opaques() { self.opaque_items.try_insert(monomorph); } for monomorph in monomorphs.drain_typedefs() { self.typedefs.try_insert(monomorph); } for monomorph in monomorphs.drain_enums() { self.enums.try_insert(monomorph); } // Remove structs and opaque items that are generic self.opaque_items.filter(|x| x.is_generic()); self.structs.filter(|x| x.is_generic()); self.unions.filter(|x| x.is_generic()); self.enums.filter(|x| x.is_generic()); self.typedefs.filter(|x| x.is_generic()); // Mangle the paths that remain self.unions .for_all_items_mut(|x| x.mangle_paths(&monomorphs)); self.structs .for_all_items_mut(|x| x.mangle_paths(&monomorphs)); self.enums .for_all_items_mut(|x| x.mangle_paths(&monomorphs)); self.typedefs .for_all_items_mut(|x| x.mangle_paths(&monomorphs)); for x in &mut self.functions { x.mangle_paths(&monomorphs); } } } cbindgen-0.27.0/src/bindgen/mangle.rs000064400000000000000000000232371046102023000155000ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::bindgen::config::MangleConfig; use crate::bindgen::ir::{ConstExpr, GenericArgument, GenericPath, Path, Type}; use crate::bindgen::rename::IdentifierType; pub fn mangle_path(path: &Path, generic_values: &[GenericArgument], config: &MangleConfig) -> Path { Path::new(mangle_name(path.name(), generic_values, config)) } pub fn mangle_name( name: &str, generic_values: &[GenericArgument], config: &MangleConfig, ) -> String { Mangler::new(name, generic_values, /* last = */ true, config).mangle() } enum Separator { OpeningAngleBracket = 1, Comma, ClosingAngleBracket, BeginMutPtr, BeginConstPtr, BeginFn, BetweenFnArg, EndFn, } struct Mangler<'a> { input: &'a str, generic_values: &'a [GenericArgument], output: String, last: bool, config: &'a MangleConfig, } impl<'a> Mangler<'a> { fn new( input: &'a str, generic_values: &'a [GenericArgument], last: bool, config: &'a MangleConfig, ) -> Self { Self { input, generic_values, output: String::new(), last, config, } } fn mangle(mut self) -> String { self.mangle_internal(); self.output } fn push(&mut self, id: Separator) { let count = id as usize; let separator = if self.config.remove_underscores { "" } else { "_" }; self.output.extend(std::iter::repeat(separator).take(count)); } fn append_mangled_argument(&mut self, arg: &GenericArgument, last: bool) { match *arg { GenericArgument::Type(ref ty) => self.append_mangled_type(ty, last), GenericArgument::Const(ConstExpr::Name(ref name)) => { // This must behave the same as a GenericArgument::Type, // because const arguments are commonly represented as Types; // see the comment on `enum GenericArgument`. let fake_ty = Type::Path(GenericPath::new(Path::new(name), vec![])); self.append_mangled_type(&fake_ty, last); } GenericArgument::Const(ConstExpr::Value(ref val)) => self.output.push_str(val), } } fn append_mangled_type(&mut self, ty: &Type, last: bool) { match *ty { Type::Path(ref generic) => { let sub_path = Mangler::new(generic.export_name(), generic.generics(), last, self.config) .mangle(); self.output.push_str( &self .config .rename_types .apply(&sub_path, IdentifierType::Type), ); } Type::Primitive(ref primitive) => { self.output.push_str( &self .config .rename_types .apply(primitive.to_repr_rust(), IdentifierType::Type), ); } Type::Ptr { ref ty, is_const, .. } => { self.push(if is_const { Separator::BeginConstPtr } else { Separator::BeginMutPtr }); self.append_mangled_type(ty, last); } Type::FuncPtr { ref ret, ref args, .. } => { self.push(Separator::BeginFn); self.append_mangled_type(ret, args.is_empty()); for (i, arg) in args.iter().enumerate() { self.push(Separator::BetweenFnArg); let last = last && i == args.len() - 1; self.append_mangled_type(&arg.1, last); } if !self.last { self.push(Separator::EndFn); } } Type::Array(..) => { unimplemented!( "Unable to mangle generic parameter {:?} for '{}'", ty, self.input ); } } } fn mangle_internal(&mut self) { debug_assert!(self.output.is_empty()); self.input.clone_into(&mut self.output); if self.generic_values.is_empty() { return; } self.push(Separator::OpeningAngleBracket); for (i, arg) in self.generic_values.iter().enumerate() { if i != 0 { self.push(Separator::Comma); } let last = self.last && i == self.generic_values.len() - 1; self.append_mangled_argument(arg, last); } // Skip writing the trailing '>' mangling when possible if !self.last { self.push(Separator::ClosingAngleBracket) } } } #[test] fn generics() { use crate::bindgen::ir::{GenericPath, PrimitiveType}; use crate::bindgen::rename::RenameRule::{self, PascalCase}; fn float() -> GenericArgument { GenericArgument::Type(Type::Primitive(PrimitiveType::Float)) } fn c_char() -> GenericArgument { GenericArgument::Type(Type::Primitive(PrimitiveType::Char)) } fn path(path: &str) -> GenericArgument { generic_path(path, &[]) } fn generic_path(path: &str, arguments: &[GenericArgument]) -> GenericArgument { let path = Path::new(path); let generic_path = GenericPath::new(path, arguments.to_owned()); GenericArgument::Type(Type::Path(generic_path)) } // Foo => Foo_f32 assert_eq!( mangle_path(&Path::new("Foo"), &[float()], &MangleConfig::default()), Path::new("Foo_f32") ); // Foo> => Foo_Bar_f32 assert_eq!( mangle_path( &Path::new("Foo"), &[generic_path("Bar", &[float()])], &MangleConfig::default(), ), Path::new("Foo_Bar_f32") ); // Foo => Foo_Bar assert_eq!( mangle_path(&Path::new("Foo"), &[path("Bar")], &MangleConfig::default()), Path::new("Foo_Bar") ); // Foo => FooBar assert_eq!( mangle_path( &Path::new("Foo"), &[path("Bar")], &MangleConfig { remove_underscores: true, rename_types: RenameRule::None, } ), Path::new("FooBar") ); // Foo> => FooBarF32 assert_eq!( mangle_path( &Path::new("Foo"), &[generic_path("Bar", &[float()])], &MangleConfig { remove_underscores: true, rename_types: PascalCase, }, ), Path::new("FooBarF32") ); // Foo> => FooBarCChar assert_eq!( mangle_path( &Path::new("Foo"), &[generic_path("Bar", &[c_char()])], &MangleConfig { remove_underscores: true, rename_types: PascalCase, }, ), Path::new("FooBarCChar") ); // Foo> => Foo_Bar_T assert_eq!( mangle_path( &Path::new("Foo"), &[generic_path("Bar", &[path("T")])], &MangleConfig::default(), ), Path::new("Foo_Bar_T") ); // Foo, E> => Foo_Bar_T_____E assert_eq!( mangle_path( &Path::new("Foo"), &[generic_path("Bar", &[path("T")]), path("E")], &MangleConfig::default(), ), Path::new("Foo_Bar_T_____E") ); // Foo, Bar> => Foo_Bar_T_____Bar_E assert_eq!( mangle_path( &Path::new("Foo"), &[ generic_path("Bar", &[path("T")]), generic_path("Bar", &[path("E")]), ], &MangleConfig::default(), ), Path::new("Foo_Bar_T_____Bar_E") ); // Foo, E> => FooBarTE assert_eq!( mangle_path( &Path::new("Foo"), &[generic_path("Bar", &[path("T")]), path("E")], &MangleConfig { remove_underscores: true, rename_types: PascalCase, }, ), Path::new("FooBarTE") ); // Foo, Bar> => FooBarTBarE assert_eq!( mangle_path( &Path::new("Foo"), &[ generic_path("Bar", &[path("T")]), generic_path("Bar", &[path("E")]), ], &MangleConfig { remove_underscores: true, rename_types: PascalCase, }, ), Path::new("FooBarTBarE") ); assert_eq!( mangle_path( &Path::new("Top"), &[GenericArgument::Const(ConstExpr::Value("40".to_string()))], &MangleConfig::default(), ), Path::new("Top_40") ); assert_eq!( mangle_path( &Path::new("Top"), &[GenericArgument::Const(ConstExpr::Name("N".to_string()))], &MangleConfig::default(), ), Path::new("Top_N") ); assert_eq!( mangle_path( &Path::new("Top"), &[generic_path("N", &[])], &MangleConfig::default(), ), Path::new("Top_N") ); assert_eq!( mangle_path( &Path::new("Foo"), &[ float(), GenericArgument::Const(ConstExpr::Value("40".to_string())) ], &MangleConfig::default(), ), Path::new("Foo_f32__40") ); } cbindgen-0.27.0/src/bindgen/mod.rs000064400000000000000000000036061046102023000150120ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /// A helper macro for deriving deserialize for an enum to be used in toml-rs. /// This macro works be relying on an existing FromStr implementation for the /// desired type. macro_rules! deserialize_enum_str { ($name:ident) => { impl<'de> ::serde::Deserialize<'de> for $name { fn deserialize(deserializer: D) -> Result where D: ::serde::Deserializer<'de>, { struct Visitor; impl<'de> ::serde::de::Visitor<'de> for Visitor { type Value = $name; fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { f.write_str("$name") } fn visit_str(self, v: &str) -> Result<$name, E> where E: ::serde::de::Error, { match v.parse::<$name>() { Ok(v) => Ok(v), Err(m) => Err(E::custom(m)), } } } deserializer.deserialize_str(Visitor) } } }; } mod bindings; mod bitflags; mod builder; mod cargo; mod cdecl; mod config; mod declarationtyperesolver; mod dependencies; mod error; mod ir; mod language_backend; mod library; mod mangle; mod monomorph; mod parser; mod rename; mod reserved; mod utilities; mod writer; #[allow(unused)] pub(crate) use self::cargo::*; pub use self::bindings::Bindings; pub use self::builder::Builder; pub use self::config::Profile; // disambiguate with cargo::Profile pub use self::config::*; pub use self::error::Error; cbindgen-0.27.0/src/bindgen/monomorph.rs000064400000000000000000000076631046102023000162600ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::collections::HashMap; use std::mem; use crate::bindgen::ir::{ Enum, GenericArgument, GenericPath, Item, OpaqueItem, Path, Struct, Typedef, Union, }; use crate::bindgen::library::Library; #[derive(Default, Clone, Debug)] pub struct Monomorphs { replacements: HashMap, opaques: Vec, structs: Vec, unions: Vec, typedefs: Vec, enums: Vec, } impl Monomorphs { pub fn contains(&self, path: &GenericPath) -> bool { self.replacements.contains_key(path) } pub fn insert_struct( &mut self, library: &Library, generic: &Struct, monomorph: Struct, arguments: Vec, ) { let replacement_path = GenericPath::new(generic.path.clone(), arguments); debug_assert!(generic.is_generic()); debug_assert!(!self.contains(&replacement_path)); self.replacements .insert(replacement_path, monomorph.path.clone()); monomorph.add_monomorphs(library, self); self.structs.push(monomorph); } pub fn insert_enum( &mut self, library: &Library, generic: &Enum, monomorph: Enum, arguments: Vec, ) { let replacement_path = GenericPath::new(generic.path.clone(), arguments); debug_assert!(generic.is_generic()); debug_assert!(!self.contains(&replacement_path)); self.replacements .insert(replacement_path, monomorph.path.clone()); monomorph.add_monomorphs(library, self); self.enums.push(monomorph); } pub fn insert_union( &mut self, library: &Library, generic: &Union, monomorph: Union, arguments: Vec, ) { let replacement_path = GenericPath::new(generic.path.clone(), arguments); debug_assert!(generic.is_generic()); debug_assert!(!self.contains(&replacement_path)); self.replacements .insert(replacement_path, monomorph.path.clone()); monomorph.add_monomorphs(library, self); self.unions.push(monomorph); } pub fn insert_opaque( &mut self, generic: &OpaqueItem, monomorph: OpaqueItem, arguments: Vec, ) { let replacement_path = GenericPath::new(generic.path.clone(), arguments); debug_assert!(generic.is_generic()); debug_assert!(!self.contains(&replacement_path)); self.replacements .insert(replacement_path, monomorph.path.clone()); self.opaques.push(monomorph); } pub fn insert_typedef( &mut self, library: &Library, generic: &Typedef, monomorph: Typedef, arguments: Vec, ) { let replacement_path = GenericPath::new(generic.path.clone(), arguments); debug_assert!(generic.is_generic()); debug_assert!(!self.contains(&replacement_path)); self.replacements .insert(replacement_path, monomorph.path.clone()); monomorph.add_monomorphs(library, self); self.typedefs.push(monomorph); } pub fn mangle_path(&self, path: &GenericPath) -> Option<&Path> { self.replacements.get(path) } pub fn drain_opaques(&mut self) -> Vec { mem::take(&mut self.opaques) } pub fn drain_structs(&mut self) -> Vec { mem::take(&mut self.structs) } pub fn drain_unions(&mut self) -> Vec { mem::take(&mut self.unions) } pub fn drain_typedefs(&mut self) -> Vec { mem::take(&mut self.typedefs) } pub fn drain_enums(&mut self) -> Vec { mem::take(&mut self.enums) } } cbindgen-0.27.0/src/bindgen/parser.rs000064400000000000000000001070341046102023000155270ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::fs::File; use std::io::Read; use std::path::{Path as FilePath, PathBuf as FilePathBuf}; use syn::ext::IdentExt; use crate::bindgen::bitflags; use crate::bindgen::cargo::{Cargo, PackageRef}; use crate::bindgen::config::{Config, ParseConfig}; use crate::bindgen::error::Error; use crate::bindgen::ir::{ AnnotationSet, AnnotationValue, Cfg, Constant, Documentation, Enum, Function, GenericParam, GenericParams, ItemMap, OpaqueItem, Path, Static, Struct, Type, Typedef, Union, }; use crate::bindgen::utilities::{SynAbiHelpers, SynAttributeHelpers, SynItemHelpers}; const STD_CRATES: &[&str] = &[ "std", "std_unicode", "alloc", "collections", "core", "proc_macro", ]; type ParseResult = Result; /// Parses a single rust source file, not following `mod` or `extern crate`. pub fn parse_src(src_file: &FilePath, config: &Config) -> ParseResult { let mod_name = src_file.file_stem().unwrap().to_str().unwrap(); let mut config = config.clone(); config.parse = ParseConfig { parse_deps: true, ..ParseConfig::default() }; let mut context = Parser { binding_crate_name: mod_name.to_owned(), config: &config, lib: None, parsed_crates: HashSet::new(), cache_src: HashMap::new(), cache_expanded_crate: HashMap::new(), cfg_stack: Vec::new(), out: Parse::new(), }; let pkg_ref = PackageRef { name: mod_name.to_owned(), version: None, }; context.parse_mod(&pkg_ref, src_file, 0)?; context.out.source_files = context.cache_src.keys().map(|k| k.to_owned()).collect(); Ok(context.out) } /// Recursively parses a rust library starting at the root crate's directory. /// /// Inside a crate, `mod` and `extern crate` declarations are followed /// and parsed. To find an external crate, the parser uses the `cargo metadata` /// command to find the location of dependencies. pub(crate) fn parse_lib(lib: Cargo, config: &Config) -> ParseResult { let mut context = Parser { binding_crate_name: lib.binding_crate_name().to_owned(), config, lib: Some(lib), parsed_crates: HashSet::new(), cache_src: HashMap::new(), cache_expanded_crate: HashMap::new(), cfg_stack: Vec::new(), out: Parse::new(), }; let binding_crate = context.lib.as_ref().unwrap().binding_crate_ref(); context.parse_crate(&binding_crate)?; context.out.source_files = context.cache_src.keys().map(|k| k.to_owned()).collect(); context.out.package_version = context .lib .as_ref() .unwrap() .binding_crate_ref() .version .unwrap(); Ok(context.out) } #[derive(Debug, Clone)] struct Parser<'a> { binding_crate_name: String, lib: Option, config: &'a Config, parsed_crates: HashSet, cache_src: HashMap>, cache_expanded_crate: HashMap>, cfg_stack: Vec, out: Parse, } impl<'a> Parser<'a> { fn should_parse_dependency(&self, pkg_name: &str) -> bool { if self.parsed_crates.contains(pkg_name) { return false; } if !self.config.parse.parse_deps { return false; } // Skip any whitelist or blacklist for expand if self .config .parse .expand .crates .iter() .any(|name| name == pkg_name) { return true; } // If we have a whitelist, check it if let Some(ref include) = self.config.parse.include { if !include.iter().any(|name| name == pkg_name) { debug!("Excluding crate {}", pkg_name); return false; } } // Check the blacklist !STD_CRATES.contains(&pkg_name) && !self .config .parse .exclude .iter() .any(|name| name == pkg_name) } fn parse_crate(&mut self, pkg: &PackageRef) -> Result<(), Error> { assert!(self.lib.is_some()); debug!("Parsing crate {}", pkg.name); self.parsed_crates.insert(pkg.name.clone()); // Check if we should use cargo expand for this crate if self.config.parse.expand.crates.contains(&pkg.name) { self.parse_expand_crate(pkg)?; } else { // Parse the crate before the dependencies otherwise the same-named idents we // want to generate bindings for would be replaced by the ones provided // by the first dependency containing it. let crate_src = self.lib.as_ref().unwrap().find_crate_src(pkg); match crate_src { Some(crate_src) => self.parse_mod(pkg, crate_src.as_path(), 0)?, None => { // This should be an error, but is common enough to just elicit a warning warn!( "Parsing crate `{}`: can't find lib.rs with `cargo metadata`. \ The crate may be available only on a particular platform, \ so consider setting `fetch_all_dependencies` in your cbindgen configuration.", pkg.name ); } } } for (dep_pkg, cfg) in self.lib.as_ref().unwrap().dependencies(pkg) { if !self.should_parse_dependency(&dep_pkg.name) { continue; } if let Some(ref cfg) = cfg { self.cfg_stack.push(cfg.clone()); } self.parse_crate(&dep_pkg)?; if cfg.is_some() { self.cfg_stack.pop(); } } Ok(()) } fn parse_expand_crate(&mut self, pkg: &PackageRef) -> Result<(), Error> { assert!(self.lib.is_some()); let mod_items = { if !self.cache_expanded_crate.contains_key(&pkg.name) { let s = self .lib .as_ref() .unwrap() .expand_crate( pkg, self.config.parse.expand.all_features, self.config.parse.expand.default_features, &self.config.parse.expand.features, self.config.parse.expand.profile, ) .map_err(|x| Error::CargoExpand(pkg.name.clone(), x))?; let i = syn::parse_file(&s).map_err(|x| Error::ParseSyntaxError { crate_name: pkg.name.clone(), src_path: "".to_owned(), error: x, })?; self.cache_expanded_crate.insert(pkg.name.clone(), i.items); } self.cache_expanded_crate.get(&pkg.name).unwrap().clone() }; self.process_mod( pkg, None, None, &mod_items, 0, /* is_mod_rs = */ true, /* is_inline = */ false, ) } fn parse_mod( &mut self, pkg: &PackageRef, mod_path: &FilePath, depth: usize, ) -> Result<(), Error> { let mod_items = match self.cache_src.entry(mod_path.to_path_buf()) { Entry::Vacant(vacant_entry) => { let mut s = String::new(); let mut f = File::open(mod_path).map_err(|_| Error::ParseCannotOpenFile { crate_name: pkg.name.clone(), src_path: mod_path.to_str().unwrap().to_owned(), })?; f.read_to_string(&mut s) .map_err(|_| Error::ParseCannotOpenFile { crate_name: pkg.name.clone(), src_path: mod_path.to_str().unwrap().to_owned(), })?; let i = syn::parse_file(&s).map_err(|x| Error::ParseSyntaxError { crate_name: pkg.name.clone(), src_path: mod_path.to_string_lossy().into(), error: x, })?; vacant_entry.insert(i.items).clone() } Entry::Occupied(occupied_entry) => occupied_entry.get().clone(), }; // Compute module directory according to Rust 2018 rules let submod_dir_2018; let mod_dir = mod_path.parent().unwrap(); let is_mod_rs = depth == 0 || mod_path.ends_with("mod.rs"); let submod_dir = if is_mod_rs { mod_dir } else { submod_dir_2018 = mod_path .parent() .unwrap() .join(mod_path.file_stem().unwrap()); &submod_dir_2018 }; self.process_mod( pkg, Some(mod_dir), Some(submod_dir), &mod_items, depth, /* is_inline = */ false, is_mod_rs, ) } /// `mod_dir` is the path to the current directory of the module. It may be /// `None` for pre-expanded modules. /// /// `submod_dir` is the path to search submodules in by default, which might /// be different for rust 2018 for example. #[allow(clippy::too_many_arguments)] fn process_mod( &mut self, pkg: &PackageRef, mod_dir: Option<&FilePath>, submod_dir: Option<&FilePath>, items: &[syn::Item], depth: usize, is_inline: bool, is_in_mod_rs: bool, ) -> Result<(), Error> { debug_assert_eq!(mod_dir.is_some(), submod_dir.is_some()); // We process the items first then the nested modules. let nested_modules = self.out.load_syn_crate_mod( self.config, &self.binding_crate_name, &pkg.name, Cfg::join(&self.cfg_stack).as_ref(), items, ); for item in nested_modules { let next_mod_name = item.ident.unraw().to_string(); let cfg = Cfg::load(&item.attrs); if let Some(ref cfg) = cfg { self.cfg_stack.push(cfg.clone()); } if let Some((_, ref inline_items)) = item.content { // TODO(emilio): This should use #[path] attribute if present, // rather than next_mod_name. let next_submod_dir = submod_dir.map(|dir| dir.join(&next_mod_name)); let next_mod_dir = mod_dir.map(|dir| dir.join(&next_mod_name)); self.process_mod( pkg, next_mod_dir.as_deref(), next_submod_dir.as_deref(), inline_items, depth, /* is_inline = */ true, is_in_mod_rs, )?; } else if let Some(mod_dir) = mod_dir { let submod_dir = submod_dir.unwrap(); let next_mod_path1 = submod_dir.join(next_mod_name.clone() + ".rs"); let next_mod_path2 = submod_dir.join(next_mod_name.clone()).join("mod.rs"); if next_mod_path1.exists() { self.parse_mod(pkg, next_mod_path1.as_path(), depth + 1)?; } else if next_mod_path2.exists() { self.parse_mod(pkg, next_mod_path2.as_path(), depth + 1)?; } else { // Last chance to find a module path let mut path_attr_found = false; for attr in &item.attrs { if let syn::Meta::NameValue(syn::MetaNameValue { path, value: syn::Expr::Lit(syn::ExprLit { lit, .. }), .. }) = &attr.meta { match lit { syn::Lit::Str(ref path_lit) if path.is_ident("path") => { path_attr_found = true; // https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute // // For path attributes on modules not inside inline module blocks, the file path // is relative to the directory the source file is located. // // For path attributes inside inline module blocks, the relative location of the // file path depends on the kind of source file the path attribute is located // in. "mod-rs" source files are root modules (such as lib.rs or main.rs) and // modules with files named mod.rs. "non-mod-rs" source files are all other // module files. // // Paths for path attributes inside inline module blocks in a mod-rs file are // relative to the directory of the mod-rs file including the inline module // components as directories. For non-mod-rs files, it is the same except the // path starts with a directory with the name of the non-mod-rs module. // let base = if is_inline && !is_in_mod_rs { submod_dir } else { mod_dir }; self.parse_mod(pkg, &base.join(path_lit.value()), depth + 1)?; break; } _ => (), } } } // This should be an error, but it's common enough to // just elicit a warning if !path_attr_found { warn!( "Parsing crate `{}`: can't find mod {}`.", pkg.name, next_mod_name ); } } } else { warn!( "Parsing expanded crate `{}`: can't find mod {}`.", pkg.name, next_mod_name ); } if cfg.is_some() { self.cfg_stack.pop(); } } Ok(()) } } #[derive(Debug, Clone)] pub struct Parse { pub constants: ItemMap, pub globals: ItemMap, pub enums: ItemMap, pub structs: ItemMap, pub unions: ItemMap, pub opaque_items: ItemMap, pub typedefs: ItemMap, pub functions: Vec, pub source_files: Vec, pub package_version: String, } impl Parse { pub fn new() -> Parse { Parse { constants: ItemMap::default(), globals: ItemMap::default(), enums: ItemMap::default(), structs: ItemMap::default(), unions: ItemMap::default(), opaque_items: ItemMap::default(), typedefs: ItemMap::default(), functions: Vec::new(), source_files: Vec::new(), package_version: String::new(), } } pub fn add_std_types(&mut self) { let mut add_opaque = |path: &str, generic_params: Vec<&str>| { let path = Path::new(path); let generic_params: Vec<_> = generic_params .into_iter() .map(GenericParam::new_type_param) .collect(); self.opaque_items.try_insert(OpaqueItem::new( path, GenericParams(generic_params), None, AnnotationSet::new(), Documentation::none(), )) }; add_opaque("String", vec![]); add_opaque("Box", vec!["T"]); add_opaque("RefCell", vec!["T"]); add_opaque("Rc", vec!["T"]); add_opaque("Arc", vec!["T"]); add_opaque("Result", vec!["T", "E"]); add_opaque("Option", vec!["T"]); add_opaque("NonNull", vec!["T"]); add_opaque("Vec", vec!["T"]); add_opaque("HashMap", vec!["K", "V", "Hasher"]); add_opaque("BTreeMap", vec!["K", "V"]); add_opaque("HashSet", vec!["T"]); add_opaque("BTreeSet", vec!["T"]); add_opaque("LinkedList", vec!["T"]); add_opaque("VecDeque", vec!["T"]); add_opaque("ManuallyDrop", vec!["T"]); add_opaque("MaybeUninit", vec!["T"]); } pub fn extend_with(&mut self, other: &Parse) { self.constants.extend_with(&other.constants); self.globals.extend_with(&other.globals); self.enums.extend_with(&other.enums); self.structs.extend_with(&other.structs); self.unions.extend_with(&other.unions); self.opaque_items.extend_with(&other.opaque_items); self.typedefs.extend_with(&other.typedefs); self.functions.extend_from_slice(&other.functions); self.source_files.extend_from_slice(&other.source_files); self.package_version.clone_from(&other.package_version); } fn load_syn_crate_mod<'a>( &mut self, config: &Config, binding_crate_name: &str, crate_name: &str, mod_cfg: Option<&Cfg>, items: &'a [syn::Item], ) -> Vec<&'a syn::ItemMod> { let mut impls_with_assoc_consts = Vec::new(); let mut nested_modules = Vec::new(); for item in items { if item.should_skip_parsing() { continue; } match item { syn::Item::ForeignMod(ref item) => { self.load_syn_foreign_mod( config, binding_crate_name, crate_name, mod_cfg, item, ); } syn::Item::Fn(ref item) => { self.load_syn_fn(config, binding_crate_name, crate_name, mod_cfg, item); } syn::Item::Const(ref item) => { self.load_syn_const(config, binding_crate_name, crate_name, mod_cfg, item); } syn::Item::Static(ref item) => { self.load_syn_static(config, binding_crate_name, crate_name, mod_cfg, item); } syn::Item::Struct(ref item) => { self.load_syn_struct(config, crate_name, mod_cfg, item); } syn::Item::Union(ref item) => { self.load_syn_union(config, crate_name, mod_cfg, item); } syn::Item::Enum(ref item) => { self.load_syn_enum(config, crate_name, mod_cfg, item); } syn::Item::Type(ref item) => { self.load_syn_ty(crate_name, mod_cfg, item); } syn::Item::Impl(ref item_impl) => { let has_assoc_const = item_impl .items .iter() .any(|item| matches!(item, syn::ImplItem::Const(_))); if has_assoc_const { impls_with_assoc_consts.push(item_impl); } if let syn::Type::Path(ref path) = *item_impl.self_ty { if let Some(type_name) = path.path.get_ident() { for method in item_impl.items.iter().filter_map(|item| match item { syn::ImplItem::Fn(method) if !method.should_skip_parsing() => { Some(method) } _ => None, }) { self.load_syn_method( config, binding_crate_name, crate_name, mod_cfg, &Path::new(type_name.unraw().to_string()), method, ) } } } } syn::Item::Macro(ref item) => { self.load_builtin_macro(config, crate_name, mod_cfg, item); } syn::Item::Mod(ref item) => { nested_modules.push(item); } _ => {} } } for item_impl in impls_with_assoc_consts { self.load_syn_assoc_consts_from_impl(crate_name, mod_cfg, item_impl) } nested_modules } fn load_syn_assoc_consts_from_impl( &mut self, crate_name: &str, mod_cfg: Option<&Cfg>, item_impl: &syn::ItemImpl, ) { let associated_constants = item_impl.items.iter().filter_map(|item| match item { syn::ImplItem::Const(ref associated_constant) if !associated_constant.should_skip_parsing() => { Some(associated_constant) } _ => None, }); self.load_syn_assoc_consts( crate_name, mod_cfg, &item_impl.self_ty, associated_constants, ); } /// Enters a `extern "C" { }` declaration and loads function declarations. fn load_syn_foreign_mod( &mut self, config: &Config, binding_crate_name: &str, crate_name: &str, mod_cfg: Option<&Cfg>, item: &syn::ItemForeignMod, ) { if !item.abi.is_c() && !item.abi.is_omitted() { info!("Skip {} - (extern block must be extern C).", crate_name); return; } let mod_cfg = Cfg::append(mod_cfg, Cfg::load(&item.attrs)); for foreign_item in &item.items { if let syn::ForeignItem::Fn(ref function) = *foreign_item { if !config .parse .should_generate_top_level_item(crate_name, binding_crate_name) { info!( "Skip {}::{} - (fn's outside of the binding crate are not used).", crate_name, &function.sig.ident ); return; } let path = Path::new(function.sig.ident.unraw().to_string()); match Function::load( path, None, &function.sig, true, &function.attrs, mod_cfg.as_ref(), ) { Ok(func) => { info!("Take {}::{}.", crate_name, &function.sig.ident); self.functions.push(func); } Err(msg) => { error!( "Cannot use fn {}::{} ({}).", crate_name, &function.sig.ident, msg ); } } } } } /// Loads a `fn` declaration inside an `impl` block, if the type is a simple identifier fn load_syn_method( &mut self, config: &Config, binding_crate_name: &str, crate_name: &str, mod_cfg: Option<&Cfg>, self_type: &Path, item: &syn::ImplItemFn, ) { self.load_fn_declaration( config, binding_crate_name, crate_name, mod_cfg, item, Some(self_type), &item.sig, &item.attrs, ) } /// Loads a `fn` declaration fn load_syn_fn( &mut self, config: &Config, binding_crate_name: &str, crate_name: &str, mod_cfg: Option<&Cfg>, item: &syn::ItemFn, ) { self.load_fn_declaration( config, binding_crate_name, crate_name, mod_cfg, item, None, &item.sig, &item.attrs, ); } #[allow(clippy::too_many_arguments)] fn load_fn_declaration( &mut self, config: &Config, binding_crate_name: &str, crate_name: &str, mod_cfg: Option<&Cfg>, named_symbol: &dyn SynItemHelpers, self_type: Option<&Path>, sig: &syn::Signature, attrs: &[syn::Attribute], ) { if !config .parse .should_generate_top_level_item(crate_name, binding_crate_name) { info!( "Skip {}::{} - (fn's outside of the binding crate are not used).", crate_name, &sig.ident ); return; } let loggable_item_name = || { let mut items = Vec::with_capacity(3); items.push(crate_name.to_owned()); if let Some(ref self_type) = self_type { items.push(self_type.to_string()); } items.push(sig.ident.unraw().to_string()); items.join("::") }; let is_extern_c = sig.abi.is_omitted() || sig.abi.is_c(); let exported_name = named_symbol.exported_name(); match (is_extern_c, exported_name) { (true, Some(exported_name)) => { let path = Path::new(exported_name); match Function::load(path, self_type, sig, false, attrs, mod_cfg) { Ok(func) => { info!("Take {}.", loggable_item_name()); self.functions.push(func); } Err(msg) => { error!("Cannot use fn {} ({}).", loggable_item_name(), msg); } } } (true, None) => { warn!( "Skipping {} - (not `no_mangle`, and has no `export_name` attribute)", loggable_item_name() ); } (false, Some(_exported_name)) => { warn!("Skipping {} - (not `extern \"C\"`)", loggable_item_name()); } (false, None) => {} } } /// Loads associated `const` declarations fn load_syn_assoc_consts<'a, I>( &mut self, crate_name: &str, mod_cfg: Option<&Cfg>, impl_ty: &syn::Type, items: I, ) where I: IntoIterator, { let ty = match Type::load(impl_ty) { Ok(ty) => ty, Err(e) => { warn!("Skipping associated constants for {:?}: {:?}", impl_ty, e); return; } }; let ty = match ty { Some(ty) => ty, None => return, }; let impl_path = match ty.get_root_path() { Some(p) => p, None => { warn!( "Couldn't find path for {:?}, skipping associated constants", ty ); return; } }; for item in items.into_iter() { if let syn::Visibility::Public(_) = item.vis { } else { warn!("Skip {}::{} - (not `pub`).", crate_name, &item.ident); return; } let path = Path::new(item.ident.unraw().to_string()); match Constant::load( path, mod_cfg, &item.ty, &item.expr, &item.attrs, Some(impl_path.clone()), ) { Ok(constant) => { info!("Take {}::{}::{}.", crate_name, impl_path, &item.ident); let mut any = false; self.structs.for_items_mut(&impl_path, |item| { any = true; item.add_associated_constant(constant.clone()); }); // Handle associated constants to other item types that are // not structs like enums or such as regular constants. if !any && !self.constants.try_insert(constant) { error!( "Conflicting name for constant {}::{}::{}.", crate_name, impl_path, &item.ident, ); } } Err(msg) => { warn!("Skip {}::{} - ({})", crate_name, &item.ident, msg); } } } } /// Loads a `const` declaration fn load_syn_const( &mut self, config: &Config, binding_crate_name: &str, crate_name: &str, mod_cfg: Option<&Cfg>, item: &syn::ItemConst, ) { if !config .parse .should_generate_top_level_item(crate_name, binding_crate_name) { info!( "Skip {}::{} - (const's outside of the binding crate are not used).", crate_name, &item.ident ); return; } if let syn::Visibility::Public(_) = item.vis { } else { warn!("Skip {}::{} - (not `pub`).", crate_name, &item.ident); return; } let path = Path::new(item.ident.unraw().to_string()); match Constant::load(path, mod_cfg, &item.ty, &item.expr, &item.attrs, None) { Ok(constant) => { info!("Take {}::{}.", crate_name, &item.ident); let full_name = constant.path.clone(); if !self.constants.try_insert(constant) { error!("Conflicting name for constant {}", full_name); } } Err(msg) => { warn!("Skip {}::{} - ({})", crate_name, &item.ident, msg); } } } /// Loads a `static` declaration fn load_syn_static( &mut self, config: &Config, binding_crate_name: &str, crate_name: &str, mod_cfg: Option<&Cfg>, item: &syn::ItemStatic, ) { if !config .parse .should_generate_top_level_item(crate_name, binding_crate_name) { info!( "Skip {}::{} - (static's outside of the binding crate are not used).", crate_name, &item.ident ); return; } if let Some(exported_name) = item.exported_name() { let path = Path::new(exported_name); match Static::load(path, item, mod_cfg) { Ok(constant) => { info!("Take {}::{}.", crate_name, &item.ident); self.globals.try_insert(constant); } Err(msg) => { warn!("Skip {}::{} - ({})", crate_name, &item.ident, msg); } } } else { warn!("Skip {}::{} - (not `no_mangle`).", crate_name, &item.ident); } } /// Loads a `struct` declaration fn load_syn_struct( &mut self, config: &Config, crate_name: &str, mod_cfg: Option<&Cfg>, item: &syn::ItemStruct, ) { match Struct::load(&config.layout, item, mod_cfg) { Ok(st) => { info!("Take {}::{}.", crate_name, &item.ident); self.structs.try_insert(st); } Err(msg) => { info!("Take {}::{} - opaque ({}).", crate_name, &item.ident, msg); let path = Path::new(item.ident.unraw().to_string()); self.opaque_items.try_insert( OpaqueItem::load(path, &item.generics, &item.attrs, mod_cfg).unwrap(), ); } } } /// Loads a `union` declaration fn load_syn_union( &mut self, config: &Config, crate_name: &str, mod_cfg: Option<&Cfg>, item: &syn::ItemUnion, ) { match Union::load(&config.layout, item, mod_cfg) { Ok(st) => { info!("Take {}::{}.", crate_name, &item.ident); self.unions.try_insert(st); } Err(msg) => { info!("Take {}::{} - opaque ({}).", crate_name, &item.ident, msg); let path = Path::new(item.ident.unraw().to_string()); self.opaque_items.try_insert( OpaqueItem::load(path, &item.generics, &item.attrs, mod_cfg).unwrap(), ); } } } /// Loads a `enum` declaration fn load_syn_enum( &mut self, config: &Config, crate_name: &str, mod_cfg: Option<&Cfg>, item: &syn::ItemEnum, ) { match Enum::load(item, mod_cfg, config) { Ok(en) => { info!("Take {}::{}.", crate_name, &item.ident); self.enums.try_insert(en); } Err(msg) => { info!("Take {}::{} - opaque ({}).", crate_name, &item.ident, msg); let path = Path::new(item.ident.unraw().to_string()); self.opaque_items.try_insert( OpaqueItem::load(path, &item.generics, &item.attrs, mod_cfg).unwrap(), ); } } } /// Loads a `type` declaration fn load_syn_ty(&mut self, crate_name: &str, mod_cfg: Option<&Cfg>, item: &syn::ItemType) { match Typedef::load(item, mod_cfg) { Ok(st) => { info!("Take {}::{}.", crate_name, &item.ident); self.typedefs.try_insert(st); } Err(msg) => { info!("Take {}::{} - opaque ({}).", crate_name, &item.ident, msg); let path = Path::new(item.ident.unraw().to_string()); self.opaque_items.try_insert( OpaqueItem::load(path, &item.generics, &item.attrs, mod_cfg).unwrap(), ); } } } fn load_builtin_macro( &mut self, config: &Config, crate_name: &str, mod_cfg: Option<&Cfg>, item: &syn::ItemMacro, ) { let name = match item.mac.path.segments.last() { Some(n) => n.ident.unraw().to_string(), None => return, }; if name != "bitflags" || !config.macro_expansion.bitflags { return; } let bitflags = match bitflags::parse(item.mac.tokens.clone()) { Ok(bf) => bf, Err(e) => { warn!("Failed to parse bitflags invocation: {:?}", e); return; } }; let (struct_, impl_) = bitflags.expand(); if let Some(struct_) = struct_ { self.load_syn_struct(config, crate_name, mod_cfg, &struct_); } if let syn::Type::Path(ref path) = *impl_.self_ty { if let Some(type_name) = path.path.get_ident() { self.structs .for_items_mut(&Path::new(type_name.unraw().to_string()), |item| { item.annotations .add_default("internal-derive-bitflags", AnnotationValue::Bool(true)); }); } } self.load_syn_assoc_consts_from_impl(crate_name, mod_cfg, &impl_) } } cbindgen-0.27.0/src/bindgen/rename.rs000064400000000000000000000113621046102023000155000ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::borrow::Cow; use std::str::FromStr; /// The type of identifier to be renamed. #[derive(Debug, Clone, Copy)] pub enum IdentifierType<'a> { StructMember, EnumVariant { prefix: &'a str }, FunctionArg, Type, Enum, } impl<'a> IdentifierType<'a> { fn to_str(self) -> &'static str { match self { IdentifierType::StructMember => "m", IdentifierType::EnumVariant { .. } => "", IdentifierType::FunctionArg => "a", IdentifierType::Type => "", IdentifierType::Enum => "", } } } /// A rule to apply to an identifier when generating bindings. #[derive(Debug, Clone, Copy, Default)] pub enum RenameRule { /// Do not apply any renaming. The default. #[default] None, /// Converts the identifier to PascalCase and adds a context dependent prefix GeckoCase, /// Converts the identifier to lower case. LowerCase, /// Converts the identifier to upper case. UpperCase, /// Converts the identifier to PascalCase. PascalCase, /// Converts the identifier to camelCase. CamelCase, /// Converts the identifier to snake_case. SnakeCase, /// Converts the identifier to SCREAMING_SNAKE_CASE. ScreamingSnakeCase, /// Converts the identifier to SCREAMING_SNAKE_CASE and prefixes enum variants /// with the enum name. QualifiedScreamingSnakeCase, } impl RenameRule { pub(crate) fn not_none(self) -> Option { match self { RenameRule::None => None, other => Some(other), } } /// Applies the rename rule to a string pub fn apply<'a>(self, text: &'a str, context: IdentifierType) -> Cow<'a, str> { use heck::*; if text.is_empty() { return Cow::Borrowed(text); } Cow::Owned(match self { RenameRule::None => return Cow::Borrowed(text), RenameRule::GeckoCase => context.to_str().to_owned() + &text.to_upper_camel_case(), RenameRule::LowerCase => text.to_lowercase(), RenameRule::UpperCase => text.to_uppercase(), RenameRule::PascalCase => text.to_pascal_case(), RenameRule::CamelCase => text.to_lower_camel_case(), RenameRule::SnakeCase => text.to_snake_case(), RenameRule::ScreamingSnakeCase => text.to_shouty_snake_case(), RenameRule::QualifiedScreamingSnakeCase => { let mut result = String::new(); if let IdentifierType::EnumVariant { prefix } = context { result.push_str( &RenameRule::ScreamingSnakeCase.apply(prefix, IdentifierType::Enum), ); result.push('_'); } result.push_str(&RenameRule::ScreamingSnakeCase.apply(text, context)); result } }) } } impl FromStr for RenameRule { type Err = String; fn from_str(s: &str) -> Result { match s { "none" => Ok(RenameRule::None), "None" => Ok(RenameRule::None), "mGeckoCase" => Ok(RenameRule::GeckoCase), "GeckoCase" => Ok(RenameRule::GeckoCase), "gecko_case" => Ok(RenameRule::GeckoCase), "lowercase" => Ok(RenameRule::LowerCase), "LowerCase" => Ok(RenameRule::LowerCase), "lower_case" => Ok(RenameRule::LowerCase), "UPPERCASE" => Ok(RenameRule::UpperCase), "UpperCase" => Ok(RenameRule::UpperCase), "upper_case" => Ok(RenameRule::UpperCase), "PascalCase" => Ok(RenameRule::PascalCase), "pascal_case" => Ok(RenameRule::PascalCase), "camelCase" => Ok(RenameRule::CamelCase), "CamelCase" => Ok(RenameRule::CamelCase), "camel_case" => Ok(RenameRule::CamelCase), "snake_case" => Ok(RenameRule::SnakeCase), "SnakeCase" => Ok(RenameRule::SnakeCase), "SCREAMING_SNAKE_CASE" => Ok(RenameRule::ScreamingSnakeCase), "ScreamingSnakeCase" => Ok(RenameRule::ScreamingSnakeCase), "screaming_snake_case" => Ok(RenameRule::ScreamingSnakeCase), "QUALIFIED_SCREAMING_SNAKE_CASE" => Ok(RenameRule::QualifiedScreamingSnakeCase), "QualifiedScreamingSnakeCase" => Ok(RenameRule::QualifiedScreamingSnakeCase), "qualified_screaming_snake_case" => Ok(RenameRule::QualifiedScreamingSnakeCase), _ => Err(format!("Unrecognized RenameRule: '{}'.", s)), } } } deserialize_enum_str!(RenameRule); cbindgen-0.27.0/src/bindgen/reserved.rs000064400000000000000000000031701046102023000160460ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /// Taken from `https://en.cppreference.com/w/cpp/keyword` /// Some experimental keywords were filtered out and the resulting list was /// sorted using a rust program. const RESERVED_KEYWORDS: &[&str] = &[ "alignas", "alignof", "auto", "bool", "break", "case", "catch", "char", "char16_t", "char32_t", "char8_t", "class", "const", "const_cast", "consteval", "constexpr", "continue", "decltype", "default", "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable", "namespace", "new", "noexcept", "nullptr", "operator", "private", "protected", "public", "register", "reinterpret_cast", "return", "short", "signed", "sizeof", "static", "static_assert", "static_cast", "struct", "switch", "template", "this", "thread_local", "throw", "true", "try", "typedef", "typename", "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t", "while", ]; pub fn escape(rust_identifier: &mut String) { if RESERVED_KEYWORDS .binary_search(&rust_identifier.as_ref()) .is_ok() { rust_identifier.push('_'); } } cbindgen-0.27.0/src/bindgen/utilities.rs000064400000000000000000000261301046102023000162430ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![allow(clippy::redundant_closure_call)] use syn::ext::IdentExt; pub trait IterHelpers: Iterator { fn try_skip_map(&mut self, f: F) -> Result, E> where F: FnMut(&Self::Item) -> Result, E>; } impl IterHelpers for I where I: Iterator, { fn try_skip_map(&mut self, mut f: F) -> Result, E> where F: FnMut(&Self::Item) -> Result, E>, { let mut out = Vec::new(); for item in self { if let Some(x) = f(&item)? { out.push(x); } } Ok(out) } } pub trait SynItemHelpers: SynAttributeHelpers { fn exported_name(&self) -> Option; } impl SynItemHelpers for syn::ItemFn { fn exported_name(&self) -> Option { self.attrs .attr_name_value_lookup("export_name") .or_else(|| { if self.is_no_mangle() { Some(self.sig.ident.unraw().to_string()) } else { None } }) } } impl SynItemHelpers for syn::ImplItemFn { fn exported_name(&self) -> Option { self.attrs .attr_name_value_lookup("export_name") .or_else(|| { if self.is_no_mangle() { Some(self.sig.ident.unraw().to_string()) } else { None } }) } } impl SynItemHelpers for syn::ItemStatic { fn exported_name(&self) -> Option { self.attrs .attr_name_value_lookup("export_name") .or_else(|| { if self.is_no_mangle() { Some(self.ident.unraw().to_string()) } else { None } }) } } /// Returns whether this attribute causes us to skip at item. This basically /// checks for `#[cfg(test)]`, `#[test]`, `/// cbindgen::ignore` and /// variations thereof. fn is_skip_item_attr(attr: &syn::Meta) -> bool { match *attr { syn::Meta::Path(ref path) => { // TODO(emilio): It'd be great if rustc allowed us to use a syntax // like `#[cbindgen::ignore]` or such. path.is_ident("test") } syn::Meta::List(ref list) => { if !list.path.is_ident("cfg") { return false; } // Remove commas of the question by parsing let parser = syn::punctuated::Punctuated::::parse_terminated; let Ok(tokens) = list.parse_args_with(parser) else { // cfg attr is a list separated by comma, if that fails, that is probably a malformed cfg attribute return false; }; for token in tokens { let Ok(path) = syn::parse2::(token) else { // we are looking for `test`, that should always happen only as path return false; }; if path.is_ident("test") { return true; } } false // list.nested.iter().any(|nested| match *nested { // syn::NestedMeta::Meta(ref meta) => is_skip_item_attr(meta), // syn::NestedMeta::Lit(..) => false, // }) } syn::Meta::NameValue(ref name_value) => { if name_value.path.is_ident("doc") { if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(ref content), .. }) = name_value.value { // FIXME(emilio): Maybe should use the general annotation // mechanism, but it seems overkill for this. if content.value().trim() == "cbindgen:ignore" { return true; } } } false } } } pub trait SynAttributeHelpers { /// Returns the list of attributes for an item. fn attrs(&self) -> &[syn::Attribute]; /// Searches for attributes like `#[test]`. /// Example: /// - `item.has_attr_word("test")` => `#[test]` fn has_attr_word(&self, name: &str) -> bool { self.attrs().iter().any(|attr| { if let syn::Meta::Path(ref path) = &attr.meta { path.is_ident(name) } else { false } }) } fn find_deprecated_note(&self) -> Option { let attrs = self.attrs(); // #[deprecated = ""] if let Some(note) = attrs.attr_name_value_lookup("deprecated") { return Some(note); } // #[deprecated] if attrs.has_attr_word("deprecated") { return Some(String::new()); } // #[deprecated(note = "")] let attr = attrs.iter().find(|attr| { if let syn::Meta::List(list) = &attr.meta { list.path.is_ident("deprecated") } else { false } })?; let args: syn::punctuated::Punctuated = match attr.parse_args_with(syn::punctuated::Punctuated::parse_terminated) { Ok(args) => args, Err(_) => { warn!("couldn't parse deprecated attribute"); return None; } }; let arg = args.iter().find(|arg| arg.path.is_ident("note"))?; if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(ref lit), .. }) = arg.value { Some(lit.value()) } else { warn!("deprecated attribute must be a string"); None } } fn is_no_mangle(&self) -> bool { self.has_attr_word("no_mangle") } /// Sees whether we should skip parsing a given item. fn should_skip_parsing(&self) -> bool { for attr in self.attrs() { if is_skip_item_attr(&attr.meta) { return true; } } false } fn attr_name_value_lookup(&self, name: &str) -> Option { self.attrs() .iter() .filter_map(|attr| { if let syn::Meta::NameValue(syn::MetaNameValue { path, value: syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit), .. }), .. }) = &attr.meta { if path.is_ident(name) { return Some(lit.value()); } } None }) .next() } fn get_comment_lines(&self) -> Vec { let mut comment = Vec::new(); for attr in self.attrs() { if attr.style == syn::AttrStyle::Outer { if let syn::Meta::NameValue(syn::MetaNameValue { path, value: syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(content), .. }), .. }) = &attr.meta { if path.is_ident("doc") { comment.extend(split_doc_attr(&content.value())); } } } } comment } } macro_rules! syn_item_match_helper { ($s:ident => has_attrs: |$i:ident| $a:block, otherwise: || $b:block) => { match *$s { syn::Item::Const(ref $i) => $a, syn::Item::Enum(ref $i) => $a, syn::Item::ExternCrate(ref $i) => $a, syn::Item::Fn(ref $i) => $a, syn::Item::ForeignMod(ref $i) => $a, syn::Item::Impl(ref $i) => $a, syn::Item::Macro(ref $i) => $a, syn::Item::Mod(ref $i) => $a, syn::Item::Static(ref $i) => $a, syn::Item::Struct(ref $i) => $a, syn::Item::Trait(ref $i) => $a, syn::Item::Type(ref $i) => $a, syn::Item::Union(ref $i) => $a, syn::Item::Use(ref $i) => $a, syn::Item::TraitAlias(ref $i) => $a, syn::Item::Verbatim(_) => $b, _ => panic!("Unhandled syn::Item: {:?}", $s), } }; } impl SynAttributeHelpers for syn::Item { fn attrs(&self) -> &[syn::Attribute] { syn_item_match_helper!(self => has_attrs: |item| { &item.attrs }, otherwise: || { &[] } ) } } macro_rules! impl_syn_item_helper { ($t:ty) => { impl SynAttributeHelpers for $t { fn attrs(&self) -> &[syn::Attribute] { &self.attrs } } }; } impl_syn_item_helper!(syn::ItemExternCrate); impl_syn_item_helper!(syn::ItemUse); impl_syn_item_helper!(syn::ItemStatic); impl_syn_item_helper!(syn::ItemConst); impl_syn_item_helper!(syn::ItemFn); impl_syn_item_helper!(syn::ImplItemConst); impl_syn_item_helper!(syn::ImplItemFn); impl_syn_item_helper!(syn::ItemMod); impl_syn_item_helper!(syn::ItemForeignMod); impl_syn_item_helper!(syn::ItemType); impl_syn_item_helper!(syn::ItemStruct); impl_syn_item_helper!(syn::ItemEnum); impl_syn_item_helper!(syn::ItemUnion); impl_syn_item_helper!(syn::ItemTrait); impl_syn_item_helper!(syn::ItemImpl); impl_syn_item_helper!(syn::ItemMacro); impl_syn_item_helper!(syn::ItemTraitAlias); /// Helper function for accessing Abi information pub trait SynAbiHelpers { fn is_c(&self) -> bool; fn is_omitted(&self) -> bool; } impl SynAbiHelpers for Option { fn is_c(&self) -> bool { if let Some(ref abi) = *self { if let Some(ref lit_string) = abi.name { return matches!(lit_string.value().as_str(), "C" | "C-unwind"); } } false } fn is_omitted(&self) -> bool { if let Some(ref abi) = *self { abi.name.is_none() } else { false } } } impl SynAbiHelpers for syn::Abi { fn is_c(&self) -> bool { if let Some(ref lit_string) = self.name { matches!(lit_string.value().as_str(), "C" | "C-unwind") } else { false } } fn is_omitted(&self) -> bool { self.name.is_none() } } impl SynAttributeHelpers for [syn::Attribute] { fn attrs(&self) -> &[syn::Attribute] { self } } fn split_doc_attr(input: &str) -> Vec { input // Convert two newline (indicate "new paragraph") into two line break. .replace("\n\n", " \n \n") // Convert newline after two spaces (indicate "line break") into line break. .split(" \n") // Convert single newline (indicate hard-wrapped) into space. .map(|s| s.replace('\n', " ")) .map(|s| s.trim_end().to_string()) .collect() } cbindgen-0.27.0/src/bindgen/writer.rs000064400000000000000000000164771046102023000155610ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::cmp; use std::io; use std::io::Write; use crate::bindgen::config::{Braces, Language}; use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::Bindings; /// A type of way to format a list. pub enum ListType<'a> { /// Join each adjacent item with a str. Join(&'a str), /// End each item with a str. Cap(&'a str), } /// A utility wrapper to write unbuffered data and correctly adjust positions. struct InnerWriter<'a, 'b: 'a, F: 'a + Write>(&'a mut SourceWriter<'b, F>); impl<'a, 'b, F: Write> Write for InnerWriter<'a, 'b, F> { fn write(&mut self, buf: &[u8]) -> io::Result { let writer = &mut self.0; if !writer.line_started { for _ in 0..writer.spaces() { write!(writer.out, " ").unwrap(); } writer.line_started = true; writer.line_length += writer.spaces(); } let written = writer.out.write(buf)?; writer.line_length += written; writer.max_line_length = cmp::max(writer.max_line_length, writer.line_length); Ok(written) } fn flush(&mut self) -> io::Result<()> { self.0.out.flush() } } /// A utility writer for generating code easier. pub struct SourceWriter<'a, F: Write> { out: F, bindings: &'a Bindings, spaces: Vec, line_started: bool, line_length: usize, line_number: usize, max_line_length: usize, } pub type MeasureWriter<'a> = SourceWriter<'a, &'a mut Vec>; impl<'a, F: Write> SourceWriter<'a, F> { pub fn new(out: F, bindings: &'a Bindings) -> Self { SourceWriter { out, bindings, spaces: vec![0], line_started: false, line_length: 0, line_number: 1, max_line_length: 0, } } pub fn bindings(&self) -> &Bindings { self.bindings } /// Takes a function that writes source and returns the maximum line length /// written. pub fn try_write(&mut self, func: T, max_line_length: usize) -> bool where T: FnOnce(&mut MeasureWriter), { if self.line_length > max_line_length { return false; } let mut buffer = Vec::new(); let line_length = { let mut measurer = SourceWriter { out: &mut buffer, bindings: self.bindings, spaces: self.spaces.clone(), line_started: self.line_started, line_length: self.line_length, line_number: self.line_number, max_line_length: self.line_length, }; func(&mut measurer); measurer.max_line_length }; if line_length > max_line_length { return false; } // We don't want the extra alignment, it's already accounted for by the // measurer. self.line_started = true; InnerWriter(self).write_all(&buffer).unwrap(); true } fn spaces(&self) -> usize { *self.spaces.last().unwrap() } pub fn push_set_spaces(&mut self, spaces: usize) { self.spaces.push(spaces); } pub fn pop_set_spaces(&mut self) { self.pop_tab() } pub fn line_length_for_align(&self) -> usize { if self.line_started { self.line_length } else { self.line_length + self.spaces() } } pub fn push_tab(&mut self) { let spaces = self.spaces() - (self.spaces() % self.bindings.config.tab_width) + self.bindings.config.tab_width; self.spaces.push(spaces); } pub fn pop_tab(&mut self) { assert!(!self.spaces.is_empty()); self.spaces.pop(); } pub fn new_line(&mut self) { self.out .write_all(self.bindings.config.line_endings.as_str().as_bytes()) .unwrap(); self.line_started = false; self.line_length = 0; self.line_number += 1; } pub fn new_line_if_not_start(&mut self) { if self.line_number != 1 { self.new_line(); } } pub fn open_brace(&mut self) { match self.bindings.config.language { Language::Cxx | Language::C => match self.bindings.config.braces { Braces::SameLine => { self.write(" {"); self.push_tab(); self.new_line(); } Braces::NextLine => { self.new_line(); self.write("{"); self.push_tab(); self.new_line(); } }, Language::Cython => { self.write(":"); self.new_line(); self.push_tab(); } } } pub fn close_brace(&mut self, semicolon: bool) { self.pop_tab(); match self.bindings.config.language { Language::Cxx | Language::C => { self.new_line(); if semicolon { self.write("};"); } else { self.write("}"); } } Language::Cython => {} } } pub fn write(&mut self, text: &'static str) { write!(self, "{}", text); } pub fn write_raw_block(&mut self, block: &str) { self.line_started = true; write!(self, "{}", block); } pub fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) { InnerWriter(self).write_fmt(fmt).unwrap(); } pub fn write_horizontal_source_list< LB: LanguageBackend, S, WF: Fn(&mut LB, &mut SourceWriter, &S), >( &mut self, language_backend: &mut LB, items: &[S], list_type: ListType<'_>, writer: WF, ) { for (i, item) in items.iter().enumerate() { writer(language_backend, self, item); match list_type { ListType::Join(text) => { if i != items.len() - 1 { write!(self, "{}", text); } } ListType::Cap(text) => { write!(self, "{}", text); } } } } pub fn write_vertical_source_list< LB: LanguageBackend, S, WF: Fn(&mut LB, &mut SourceWriter, &S), >( &mut self, language_backend: &mut LB, items: &[S], list_type: ListType<'_>, writer: WF, ) { let align_length = self.line_length_for_align(); self.push_set_spaces(align_length); for (i, item) in items.iter().enumerate() { writer(language_backend, self, item); match list_type { ListType::Join(text) => { if i != items.len() - 1 { write!(self, "{}", text); } } ListType::Cap(text) => { write!(self, "{}", text); } } if i != items.len() - 1 { self.new_line(); } } self.pop_tab(); } } cbindgen-0.27.0/src/lib.rs000064400000000000000000000021031046102023000133620ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #[macro_use] extern crate log; extern crate proc_macro2; #[macro_use] extern crate serde; extern crate serde_json; #[macro_use] extern crate quote; #[macro_use] extern crate syn; extern crate toml; mod bindgen; pub use crate::bindgen::*; use std::path::Path; /// A utility function for build scripts to generate bindings for a crate, using /// a `cbindgen.toml` if it exists. pub fn generate>(crate_dir: P) -> Result { let config = Config::from_root_or_default(crate_dir.as_ref()); generate_with_config(crate_dir, config) } /// A utility function for build scripts to generate bindings for a crate with a /// custom config. pub fn generate_with_config>( crate_dir: P, config: Config, ) -> Result { Builder::new() .with_config(config) .with_crate(crate_dir) .generate() } cbindgen-0.27.0/src/logging.rs000064400000000000000000000047311046102023000142530ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::io; use std::io::Write; use log::*; pub struct TraceLogger; pub struct WarnLogger; pub struct InfoLogger; pub struct ErrorLogger; impl TraceLogger { pub fn init() -> Result<(), SetLoggerError> { log::set_logger(&TraceLogger)?; log::set_max_level(LevelFilter::Trace); Ok(()) } } impl log::Log for TraceLogger { fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= Level::Trace } fn log(&self, record: &Record) { if self.enabled(record.metadata()) { eprintln!("{}: {}", record.level(), record.args()); } } fn flush(&self) { io::stderr().flush().unwrap(); } } impl WarnLogger { pub fn init() -> Result<(), SetLoggerError> { log::set_logger(&WarnLogger)?; log::set_max_level(LevelFilter::Warn); Ok(()) } } impl log::Log for WarnLogger { fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= Level::Warn } fn log(&self, record: &Record) { if self.enabled(record.metadata()) { eprintln!("{}: {}", record.level(), record.args()); } } fn flush(&self) { io::stderr().flush().unwrap(); } } impl ErrorLogger { pub fn init() -> Result<(), SetLoggerError> { log::set_logger(&ErrorLogger)?; log::set_max_level(LevelFilter::Error); Ok(()) } } impl log::Log for ErrorLogger { fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= Level::Error } fn log(&self, record: &Record) { if self.enabled(record.metadata()) { eprintln!("{}: {}", record.level(), record.args()); } } fn flush(&self) { io::stderr().flush().unwrap(); } } impl InfoLogger { pub fn init() -> Result<(), SetLoggerError> { log::set_logger(&InfoLogger)?; log::set_max_level(LevelFilter::Info); Ok(()) } } impl log::Log for InfoLogger { fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= Level::Info } fn log(&self, record: &Record) { if self.enabled(record.metadata()) { eprintln!("{}: {}", record.level(), record.args()); } } fn flush(&self) { io::stderr().flush().unwrap(); } } cbindgen-0.27.0/src/main.rs000064400000000000000000000272061046102023000135530ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::env; use std::io; use std::path::{Path, PathBuf}; use std::str::FromStr; extern crate clap; #[macro_use] extern crate log; extern crate proc_macro2; #[macro_use] extern crate serde; extern crate serde_json; #[macro_use] extern crate quote; #[macro_use] extern crate syn; extern crate toml; use clap::{value_parser, Arg, ArgAction, ArgMatches, Command}; mod bindgen; mod logging; use bindgen::{Bindings, Builder, Cargo, Config, Error}; fn apply_config_overrides(config: &mut Config, matches: &ArgMatches) { // We allow specifying a language to override the config default. This is // used by compile-tests. match matches.try_get_one::("lang") { Ok(Some(lang)) => { config.language = bindgen::Language::from_str(lang).unwrap(); } Err(reason) => { error!("{}", reason); return; } _ => (), } if matches.get_flag("cpp-compat") { config.cpp_compat = true; } if matches.get_flag("only-target-dependencies") { config.only_target_dependencies = true; } if matches.get_flag("package-version") { config.package_version = true; } match matches.try_get_one::("style") { Ok(Some(style)) => { config.style = bindgen::Style::from_str(style).unwrap(); } Err(_) => { error!("Unknown style specified."); return; } _ => (), } match matches.try_get_one::("profile") { Ok(Some(profile)) => { config.parse.expand.profile = bindgen::Profile::from_str(profile).unwrap(); } Err(e) => { error!("{}", e); return; } _ => (), } if matches.get_flag("d") { config.parse.parse_deps = true; } } fn load_bindings(input: &Path, matches: &ArgMatches) -> Result { // If a file is specified then we load it as a single source if !input.is_dir() { // Load any config specified or search in the input directory let mut config = match matches.get_one::("config") { Some(c) => Config::from_file(c).unwrap(), None => Config::from_root_or_default( input .parent() .expect("All files should have a parent directory"), ), }; apply_config_overrides(&mut config, matches); return Builder::new() .with_config(config) .with_src(input) .generate(); } // We have to load a whole crate, so we use cargo to gather metadata let lib = Cargo::load( input, matches.get_one::("lockfile").map(|s| s.as_path()), matches.get_one::("crate").map(|s| s.as_str()), true, matches.get_flag("clean"), matches.get_flag("only-target-dependencies"), matches.get_one::("metadata").map(|p| p.as_path()), )?; // Load any config specified or search in the binding crate directory let mut config = match matches.get_one::("config") { Some(c) => Config::from_file(c).unwrap(), None => { let binding_crate_dir = lib.find_crate_dir(&lib.binding_crate_ref()); if let Some(binding_crate_dir) = binding_crate_dir { Config::from_root_or_default(binding_crate_dir) } else { // This shouldn't happen Config::from_root_or_default(input) } } }; apply_config_overrides(&mut config, matches); Builder::new() .with_config(config) .with_cargo(lib) .generate() } fn main() { let matches = Command::new("cbindgen") .version(bindgen::VERSION) .about("Generate C bindings for a Rust library") .arg( Arg::new("v") .short('v') .action(ArgAction::Count) .help("Enable verbose logging"), ) .arg( Arg::new("verify") .long("verify") .action(ArgAction::SetTrue) .help("Generate bindings and compare it to the existing bindings file and error if they are different"), ) .arg( Arg::new("config") .short('c') .long("config") .value_name("PATH") .value_parser(value_parser!(PathBuf)) .help("Specify path to a `cbindgen.toml` config to use"), ) .arg( Arg::new("lang") .short('l') .long("lang") .value_name("LANGUAGE") .help("Specify the language to output bindings in") .value_parser(["c++", "C++", "c", "C", "cython", "Cython"]), ) .arg( Arg::new("package-version") .long("package-version") .action(ArgAction::SetTrue) .help("Include the package version in the header comment") ) .arg( Arg::new("cpp-compat") .long("cpp-compat") .action(ArgAction::SetTrue) .help("Whether to add C++ compatibility to generated C bindings") ) .arg( Arg::new("only-target-dependencies") .long("only-target-dependencies") .action(ArgAction::SetTrue) .help("Only fetch dependencies needed by the target platform. \ The target platform defaults to the host platform; set TARGET to override.") ) .arg( Arg::new("style") .short('s') .long("style") .value_name("STYLE") .help("Specify the declaration style to use for bindings") .value_parser(["Both", "both", "Tag", "tag", "Type", "type"]), ) .arg( Arg::new("d") .short('d') .long("parse-dependencies") .action(ArgAction::SetTrue) .help("Whether to parse dependencies when generating bindings"), ) .arg( Arg::new("clean") .long("clean") .action(ArgAction::SetTrue) .help( "Whether to use a new temporary directory for expanding macros. \ Affects performance, but might be required in certain build processes.") .required(false) ) .arg( Arg::new("INPUT") .help( "A crate directory or source file to generate bindings for. \ In general this is the folder where the Cargo.toml file of \ source Rust library resides.") .required(false) .value_parser(value_parser!(PathBuf)) .index(1), ) .arg( Arg::new("crate") .long("crate") .value_name("CRATE_NAME") .help( "If generating bindings for a crate, \ the specific crate to generate bindings for", ) .required(false), ) .arg( Arg::new("out") .short('o') .long("output") .value_name("PATH") .help("The file to output the bindings to") .value_parser(value_parser!(PathBuf)) .required(false), ) .arg( Arg::new("lockfile") .long("lockfile") .value_name("PATH") .help( "Specify the path to the Cargo.lock file explicitly. If this \ is not specified, the Cargo.lock file is searched for in the \ same folder as the Cargo.toml file. This option is useful for \ projects that use workspaces.") .value_parser(value_parser!(PathBuf)) .required(false), ) .arg( Arg::new("metadata") .long("metadata") .value_name("PATH") .help( "Specify the path to the output of a `cargo metadata` \ command that allows to get dependency information. \ This is useful because cargo metadata may be the longest \ part of cbindgen runtime, and you may want to share it \ across cbindgen invocations. By default cbindgen will run \ `cargo metadata --all-features --format-version 1 \ --manifest-path " ) .value_parser(value_parser!(PathBuf)) .required(false), ) .arg( Arg::new("profile") .long("profile") .value_name("PROFILE") .help( "Specify the profile to use when expanding macros. \ Has no effect otherwise." ) .value_parser(["Debug", "debug", "Release", "release"]), ) .arg( Arg::new("quiet") .short('q') .long("quiet") .action(ArgAction::SetTrue) .help("Report errors only (overrides verbosity options).") .required(false), ) .arg( Arg::new("depfile") .value_name("PATH") .long("depfile") .num_args(1) .required(false) .value_parser(value_parser!(PathBuf)) .help("Generate a depfile at the given Path listing the source files \ cbindgen traversed when generating the bindings. Useful when \ integrating cbindgen into 3rd party build-systems. \ This option is ignored if `--out` is missing." ) ) .get_matches(); if matches.get_flag("verify") && !matches.contains_id("out") { error!( "Cannot verify bindings against `stdout`, please specify a file to compare against." ); std::process::exit(2); } // Initialize logging if matches.get_flag("quiet") { logging::ErrorLogger::init().unwrap(); } else { match matches.get_count("v") { 0 => logging::WarnLogger::init().unwrap(), 1 => logging::InfoLogger::init().unwrap(), _ => logging::TraceLogger::init().unwrap(), } } // Find the input directory let input: PathBuf = matches .get_one("INPUT") .cloned() .unwrap_or_else(|| env::current_dir().unwrap()); let bindings = match load_bindings(&input, &matches) { Ok(bindings) => bindings, Err(msg) => { error!("{}", msg); error!("Couldn't generate bindings for {}.", input.display()); std::process::exit(1); } }; // Write the bindings file match matches.get_one::("out") { Some(file) => { let changed = bindings.write_to_file(file); if matches.get_flag("verify") && changed { error!("Bindings changed: {}", file.display()); std::process::exit(2); } if let Some(depfile) = matches.get_one("depfile") { bindings.generate_depfile(file, depfile) } } _ => { bindings.write(io::stdout()); } } } cbindgen-0.27.0/template.toml000064400000000000000000000056371046102023000142060ustar 00000000000000# This is a template cbindgen.toml file with all of the default values. # Some values are commented out because their absence is the real default. # # See https://github.com/mozilla/cbindgen/blob/master/docs.md#cbindgentoml # for detailed documentation of every option here. language = "C++" ############## Options for Wrapping the Contents of the Header ################# # header = "/* Text to put at the beginning of the generated file. Probably a license. */" # trailer = "/* Text to put at the end of the generated file */" # include_guard = "my_bindings_h" # pragma_once = true # autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" include_version = false # namespace = "my_namespace" namespaces = [] using_namespaces = [] sys_includes = [] includes = [] no_includes = false # cpp_compat = true after_includes = "" ############################ Code Style Options ################################ braces = "SameLine" line_length = 100 tab_width = 2 documentation = true documentation_style = "auto" documentation_length = "full" line_endings = "LF" # also "CR", "CRLF", "Native" ############################# Codegen Options ################################## style = "both" sort_by = "Name" # default for `fn.sort_by` and `const.sort_by` usize_is_size_t = true [defines] # "target_os = freebsd" = "DEFINE_FREEBSD" # "feature = serde" = "DEFINE_SERDE" [export] include = [] exclude = [] # prefix = "CAPI_" item_types = [] renaming_overrides_prefixing = false [export.rename] [export.body] [export.mangle] [fn] rename_args = "None" # must_use = "MUST_USE_FUNC" # deprecated = "DEPRECATED_FUNC" # deprecated_with_note = "DEPRECATED_FUNC_WITH_NOTE" # no_return = "NO_RETURN" # prefix = "START_FUNC" # postfix = "END_FUNC" args = "auto" sort_by = "Name" [struct] rename_fields = "None" # must_use = "MUST_USE_STRUCT" # deprecated = "DEPRECATED_STRUCT" # deprecated_with_note = "DEPRECATED_STRUCT_WITH_NOTE" derive_constructor = false derive_eq = false derive_neq = false derive_lt = false derive_lte = false derive_gt = false derive_gte = false [enum] rename_variants = "None" # must_use = "MUST_USE_ENUM" # deprecated = "DEPRECATED_ENUM" # deprecated_with_note = "DEPRECATED_ENUM_WITH_NOTE" add_sentinel = false prefix_with_name = false derive_helper_methods = false derive_const_casts = false derive_mut_casts = false # cast_assert_name = "ASSERT" derive_tagged_enum_destructor = false derive_tagged_enum_copy_constructor = false enum_class = true private_default_tagged_enum_constructor = false [const] allow_static_const = true allow_constexpr = false sort_by = "Name" [macro_expansion] bitflags = false ############## Options for How Your Rust library Should Be Parsed ############## [parse] parse_deps = false # include = [] exclude = [] clean = false extra_bindings = [] [parse.expand] crates = [] all_features = false default_features = true features = [] cbindgen-0.27.0/tests/depfile/Readme.md000064400000000000000000000012761046102023000157620ustar 00000000000000This a folder containing tests for `--depfile` parameter. Each test is in a subfolder and defines a minimum CMake project, which uses cbindgen to generate Rust bindings and the `--depfile` parameter to determine when to regenerate. The outer test can the build the project, assert that rebuilding does not regenerate the bindings, and then assert that touching the files involved does trigger rebuilding. The test project must contain an `expectations` folder, containing a file `dependencies`. This `dependencies` should list all files that should be listed as dependencies in the generated depfile. The paths should be relative to the project folder (i.e. to the folder containing `expectations`). cbindgen-0.27.0/tests/depfile/cbindgen_test.cmake000064400000000000000000000017041046102023000200510ustar 00000000000000# Common code used across the different tests if(NOT DEFINED CBINDGEN_PATH) message(FATAL_ERROR "Path to cbindgen not specified") endif() # Promote to cache set(CBINDGEN_PATH "${CBINDGEN_PATH}" CACHE INTERNAL "") function(add_cbindgen_command custom_target_name header_destination) # Place the depfile always at the same location, so the outer test framework can locate the file easily set(depfile_destination "${CMAKE_BINARY_DIR}/depfile.d") add_custom_command( OUTPUT "${header_destination}" "${depfile_destination}" COMMAND "${CBINDGEN_PATH}" --output "${header_destination}" --depfile "${depfile_destination}" ${ARGN} DEPFILE "${depfile_destination}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMMENT "Running cbindgen" COMMAND_EXPAND_LISTS ) add_custom_target("${custom_target_name}" ALL DEPENDS "${header_destination}") endfunction()cbindgen-0.27.0/tests/depfile.rs000064400000000000000000000077061046102023000146150ustar 00000000000000use std::fs::read_to_string; use std::path::PathBuf; use std::process::Command; static CBINDGEN_PATH: &str = env!("CARGO_BIN_EXE_cbindgen"); fn test_project(project_path: &str) { let mut cmake_cmd = Command::new("cmake"); cmake_cmd.arg("--version"); cmake_cmd .output() .expect("CMake --version failed - Is CMake installed?"); let mut cmake_configure = Command::new("cmake"); let build_dir = PathBuf::from(project_path).join("build"); if build_dir.exists() { std::fs::remove_dir_all(&build_dir).expect("Failed to remove old build directory"); } let project_dir = PathBuf::from(project_path); let cbindgen_define = format!("-DCBINDGEN_PATH={}", CBINDGEN_PATH); cmake_configure .arg("-S") .arg(project_path) .arg("-B") .arg(&build_dir) .arg(cbindgen_define); let output = cmake_configure.output().expect("Failed to execute process"); let stdout_str = String::from_utf8(output.stdout).unwrap(); let stderr_str = String::from_utf8(output.stderr).unwrap(); assert!( output.status.success(), "Configuring test project failed: stdout: `{}`, stderr: `{}`", stdout_str, stderr_str ); let depfile_path = build_dir.join("depfile.d"); assert!( !depfile_path.exists(), "depfile should not exist before building" ); // Do the clean first build let mut cmake_build = Command::new("cmake"); cmake_build.arg("--build").arg(&build_dir); let output = cmake_build.output().expect("Failed to execute process"); assert!( output.status.success(), "Building test project failed: {:?}", output ); let out_str = String::from_utf8(output.stdout).unwrap(); assert!( out_str.contains("Running cbindgen"), "cbindgen rule did not run. Output: {}", out_str ); assert!( depfile_path.exists(), "depfile does not exist after building" ); let expected_dependencies_filepath = PathBuf::from(project_path) .join("expectations") .join("dependencies"); assert!( expected_dependencies_filepath.exists(), "Test did not define expected dependencies. Please read the Readme.md" ); let expected_deps = read_to_string(expected_dependencies_filepath).expect("Failed to read dependencies"); let depinfo = read_to_string(depfile_path).expect("Failed to read dependencies"); // Assumes a single rule in the file - all deps are listed to the rhs of the `:`. let actual_deps = depinfo.split(':').collect::>()[1]; // Strip the line breaks. let actual_deps = actual_deps.replace("\\\n", " "); // I don't want to deal with supporting escaped whitespace when splitting at whitespace, // so the tests don't support being run in a directory containing whitespace. assert!( !actual_deps.contains("\\ "), "The tests directory may not contain any whitespace" ); let dep_list: Vec<&str> = actual_deps.split_ascii_whitespace().collect(); let expected_dep_list: Vec = expected_deps .lines() .map(|dep| project_dir.join(dep).to_str().unwrap().to_string()) .collect(); assert_eq!(dep_list, expected_dep_list); let output = cmake_build.output().expect("Failed to execute process"); assert!( output.status.success(), "Building test project failed: {:?}", output ); let out_str = String::from_utf8(output.stdout).unwrap(); assert!( !out_str.contains("Running cbindgen"), "cbindgen rule ran on second build" ); std::fs::remove_dir_all(build_dir).expect("Failed to remove old build directory"); } macro_rules! test_file { ($test_function_name:ident, $name:expr, $file:tt) => { #[test] fn $test_function_name() { test_project($file); } }; } // This file is generated by build.rs include!(concat!(env!("OUT_DIR"), "/depfile_tests.rs")); cbindgen-0.27.0/tests/rust/abi_string.rs000064400000000000000000000001331046102023000163060ustar 00000000000000#[no_mangle] pub extern "C" fn c() {} #[no_mangle] pub extern "C-unwind" fn c_unwind() {} cbindgen-0.27.0/tests/rust/alias.rs000064400000000000000000000005611046102023000152630ustar 00000000000000#[repr(C)] struct Dep { a: i32, b: f32, } #[repr(C)] struct Foo { a: X, b: X, c: Dep, } #[repr(u32)] enum Status { Ok, Err, } type IntFoo = Foo; type DoubleFoo = Foo; type Unit = i32; type SpecialStatus = Status; #[no_mangle] pub extern "C" fn root( x: IntFoo, y: DoubleFoo, z: Unit, w: SpecialStatus ) { } cbindgen-0.27.0/tests/rust/annotation.rs000064400000000000000000000011611046102023000163410ustar 00000000000000/// cbindgen:derive-lt=true /// cbindgen:derive-lte=true /// cbindgen:derive-constructor=true /// cbindgen:rename-all=GeckoCase #[repr(C)] struct A(i32); /// cbindgen:field-names=[x, y] #[repr(C)] struct B(i32, f32); /// cbindgen:trailing-values=[Z, W] #[repr(u32)] enum C { X = 2, Y, } /// cbindgen:derive-helper-methods=true #[repr(u8)] enum F { Foo(i16), Bar { x: u8, y: i16 }, Baz } /// cbindgen:derive-helper-methods #[repr(C, u8)] enum H { Hello(i16), There { x: u8, y: i16 }, Everyone } #[no_mangle] pub extern "C" fn root( x: A, y: B, z: C, f: F, h: H, ) { } cbindgen-0.27.0/tests/rust/array.rs000064400000000000000000000001311046102023000153010ustar 00000000000000#[repr(C)] enum Foo { A([f32; 20]) } #[no_mangle] pub extern "C" fn root(a: Foo) {} cbindgen-0.27.0/tests/rust/array.toml000064400000000000000000000000441046102023000156330ustar 00000000000000[enum] derive_helper_methods = true cbindgen-0.27.0/tests/rust/asserted_cast.rs000064400000000000000000000014121046102023000170120ustar 00000000000000/// cbindgen:prefix-with-name #[repr(C, u8)] pub enum H { /// cbindgen:variant-mut-cast-attributes=MY_ATTRS Foo(i16), /// cbindgen:variant-const-cast-attributes=MY_ATTRS Bar { x: u8, y: i16 }, /// cbindgen:variant-is-attributes=MY_ATTRS Baz } /// cbindgen:prefix-with-name #[repr(C, u8, u16)] pub enum I { /// cbindgen:variant-constructor-attributes=MY_ATTRS Foo(i16), /// cbindgen:eq-attributes=MY_ATTRS Bar { x: u8, y: i16 }, Baz } /// cbindgen:prefix-with-name #[repr(C, u8)] pub enum J { Foo(i16), Bar { x: u8, y: i16 }, Baz } /// cbindgen:prefix-with-name #[repr(u8)] pub enum K { Foo(i16), Bar { x: u8, y: i16 }, Baz } #[no_mangle] pub extern "C" fn foo( h: H, i: I, j: J, k: K, ) {} cbindgen-0.27.0/tests/rust/asserted_cast.toml000064400000000000000000000003301046102023000173370ustar 00000000000000header = """ #define MY_ASSERT(...) do { } while (0) #define MY_ATTRS __attribute((noinline)) """ [enum] derive_helper_methods = true derive_const_casts = true derive_mut_casts = true cast_assert_name = "MY_ASSERT" cbindgen-0.27.0/tests/rust/assoc_const_conflict.rs000064400000000000000000000001361046102023000203670ustar 00000000000000#[repr(C)] struct Foo {} pub const Foo_FOO: u32 = 42; impl Foo { const FOO: i32 = 0; } cbindgen-0.27.0/tests/rust/assoc_constant.rs000064400000000000000000000004331046102023000172110ustar 00000000000000#[repr(C)] struct Foo {} impl Foo { pub const GA: i32 = 10; pub const BU: &'static str = "hello world"; pub const ZO: f32 = 3.14; pub(crate) const DONT_EXPORT_CRATE: i32 = 20; const DONT_EXPORT_PRIV: i32 = 30; } #[no_mangle] pub extern "C" fn root(x: Foo) { } cbindgen-0.27.0/tests/rust/associated_constant_panic.rs000064400000000000000000000001121046102023000213640ustar 00000000000000pub trait F { const B: u8; } impl F for u16 { const B: u8 = 3; } cbindgen-0.27.0/tests/rust/associated_in_body.rs000064400000000000000000000031561046102023000200170ustar 00000000000000bitflags! { /// Constants shared by multiple CSS Box Alignment properties /// /// These constants match Gecko's `NS_STYLE_ALIGN_*` constants. #[derive(MallocSizeOf)] #[repr(C)] pub struct AlignFlags: u8 { /// 'auto' const AUTO = 0; /// 'normal' const NORMAL = 1; /// 'start' const START = 1 << 1; /// 'end' const END = 1 << 2; const ALIAS = Self::END.bits; /// 'flex-start' const FLEX_START = 1 << 3; const MIXED = 1 << 4 | AlignFlags::FLEX_START.bits | AlignFlags::END.bits; const MIXED_SELF = 1 << 5 | AlignFlags::FLEX_START.bits | AlignFlags::END.bits; } } /// An arbitrary identifier for a native (OS compositor) surface #[repr(C)] #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] pub struct NativeSurfaceId(pub u64); impl NativeSurfaceId { /// A special id for the native surface that is used for debug / profiler overlays. pub const DEBUG_OVERLAY: NativeSurfaceId = NativeSurfaceId(u64::MAX); } #[repr(C)] #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct NativeTileId { pub surface_id: NativeSurfaceId, pub x: i32, pub y: i32, } impl NativeTileId { /// A special id for the native surface that is used for debug / profiler overlays. pub const DEBUG_OVERLAY: NativeTileId = NativeTileId { surface_id: NativeSurfaceId::DEBUG_OVERLAY, x: 0, y: 0, }; } #[no_mangle] pub extern "C" fn root(flags: AlignFlags, tile: NativeTileId) {} cbindgen-0.27.0/tests/rust/associated_in_body.toml000064400000000000000000000002661046102023000203450ustar 00000000000000[struct] associated_constants_in_body = true [macro_expansion] bitflags = true [export] prefix = "Style" # Just ensuring they play well together :) [const] allow_constexpr = true cbindgen-0.27.0/tests/rust/bitflags.rs000064400000000000000000000027671046102023000157770ustar 00000000000000bitflags! { /// Constants shared by multiple CSS Box Alignment properties /// /// These constants match Gecko's `NS_STYLE_ALIGN_*` constants. #[derive(MallocSizeOf)] #[repr(C)] pub struct AlignFlags: u8 { /// 'auto' const AUTO = 0; /// 'normal' const NORMAL = 1; /// 'start' const START = 1 << 1; /// 'end' const END = 1 << 2; const ALIAS = Self::END.bits(); /// 'flex-start' const FLEX_START = 1 << 3; const MIXED = 1 << 4 | AlignFlags::FLEX_START.bits() | AlignFlags::END.bits(); const MIXED_SELF = 1 << 5 | AlignFlags::FLEX_START.bits() | AlignFlags::END.bits(); } } bitflags! { #[repr(C)] pub struct DebugFlags: u32 { /// Flag with the topmost bit set of the u32 const BIGGEST_ALLOWED = 1 << 31; } } bitflags! { #[repr(C)] pub struct LargeFlags: u64 { /// Flag with a very large shift that usually would be narrowed. const LARGE_SHIFT = 1u64 << 44; const INVERTED = !Self::LARGE_SHIFT.bits(); } } // bitflags 2 allows to define types out-of-line for custom derives // #[derive(SomeTrait)] #[repr(C)] pub struct OutOfLine(u32); bitflags! { impl OutOfLine: u32 { const A = 1; const B = 2; const AB = Self::A.bits() | Self::B.bits(); } } #[no_mangle] pub extern "C" fn root( flags: AlignFlags, bigger_flags: DebugFlags, largest_flags: LargeFlags, out_of_line: OutOfLine, ) { } cbindgen-0.27.0/tests/rust/bitflags.toml000064400000000000000000000001021046102023000163030ustar 00000000000000[macro_expansion] bitflags = true [const] allow_constexpr = true cbindgen-0.27.0/tests/rust/body.rs000064400000000000000000000014761046102023000151350ustar 00000000000000 #[repr(C)] pub struct MyFancyStruct { i: i32, } #[repr(C)] pub enum MyFancyEnum { Foo, Bar(i32), Baz(i32), } #[repr(C)] pub enum MyCLikeEnum { Foo1, Bar1, Baz1, } #[repr(C)] pub union MyUnion { pub f: f32, pub u: u32, } #[repr(C)] pub struct MyFancyStruct_Prepended { i: i32, } #[repr(C)] pub enum MyFancyEnum_Prepended { Foo_Prepended, Bar_Prepended(i32), Baz_Prepended(i32), } #[repr(C)] pub enum MyCLikeEnum_Prepended { Foo1_Prepended, Bar1_Prepended, Baz1_Prepended, } #[repr(C)] pub union MyUnion_Prepended { pub f: f32, pub u: u32, } #[no_mangle] pub extern "C" fn root(s: MyFancyStruct, e: MyFancyEnum, c: MyCLikeEnum, u: MyUnion, sp: MyFancyStruct_Prepended, ep: MyFancyEnum_Prepended, cp: MyCLikeEnum_Prepended, up: MyUnion_Prepended) {} cbindgen-0.27.0/tests/rust/body.toml000064400000000000000000000011451046102023000154550ustar 00000000000000[export.body] "MyFancyStruct" = """ #ifdef __cplusplus inline void foo(); #endif """ "MyFancyEnum" = """ #ifdef __cplusplus inline void wohoo(); #endif """ "MyCLikeEnum" = """ BogusVariantForSerializationForExample, """ "MyUnion" = """ int32_t extra_member; """ [export.pre_body] "MyFancyStruct_Prepended" = """ #ifdef __cplusplus inline void prepended_wohoo(); #endif """ "MyFancyEnum_Prepended" = """ #ifdef __cplusplus inline void wohoo(); #endif """ "MyCLikeEnum_Prepended" = """ BogusVariantForSerializationForExample, """ "MyUnion_Prepended" = """ int32_t extra_member; """ cbindgen-0.27.0/tests/rust/box.rs000064400000000000000000000005131046102023000147570ustar 00000000000000#[repr(C)] pub struct MyStruct { number: Box, } pub struct NotReprC { inner: T, } pub type Foo = NotReprC>; #[no_mangle] pub extern "C" fn root(a: &Foo, with_box: &MyStruct) {} #[no_mangle] pub extern "C" fn drop_box(x: Box) {} #[no_mangle] pub extern "C" fn drop_box_opt(x: Option>) {} cbindgen-0.27.0/tests/rust/box.toml000064400000000000000000000002311046102023000153030ustar 00000000000000header = """ #if 0 ''' ' #endif #ifdef __cplusplus template using Box = T*; #endif #if 0 ' ''' #endif """ [export] exclude = [ "Box", ] cbindgen-0.27.0/tests/rust/cdecl.rs000064400000000000000000000013011046102023000152350ustar 00000000000000type A = fn (); type B = fn () -> (); type C = fn (i32, i32) -> bool; type D = fn (i32) -> fn (f32) -> bool; type E = fn () -> *const [i32; 16]; type F = *const i32; type G = *const *const i32; type H = *const *mut i32; type I = *const [i32; 16]; type J = *const fn (f32) -> f64; type K = [i32; 16]; type L = [*const i32; 16]; type M = [fn (i32, i32) -> bool; 16]; type N = [fn (i32, i32) -> (); 16]; #[no_mangle] pub extern "C" fn O() -> fn () { } type P = fn (named1st: i32, bool, named3rd: bool, _: i32); #[no_mangle] pub extern "C" fn root( a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, p: P ) { } cbindgen-0.27.0/tests/rust/cell.rs000064400000000000000000000003441046102023000151100ustar 00000000000000#[repr(C)] pub struct MyStruct { number: std::cell::Cell, } pub struct NotReprC { inner: T } pub type Foo = NotReprC>; #[no_mangle] pub extern "C" fn root(a: &Foo, with_cell: &MyStruct) {} cbindgen-0.27.0/tests/rust/cfg.rs000064400000000000000000000024221046102023000147270ustar 00000000000000#[cfg(all(unix, x11))] #[repr(u32)] enum FooType { A, B, C, } #[cfg(all(unix, x11))] #[repr(C)] struct FooHandle { ty: FooType, x: i32, y: f32, } #[cfg(any(windows, target_pointer_width="32"))] #[repr(u32)] enum BarType { A, B, C, } #[repr(u8)] pub enum C { C1, C2, #[cfg(windows)] C3, #[cfg(unix)] C5 { int: i32 }, } #[cfg(any(windows, target_pointer_width="32"))] #[repr(C)] struct BarHandle { ty: BarType, x: i32, y: f32, } // FIXME(#634): Support deriving methods for structs with conditional fields. /// cbindgen:derive-eq=false /// cbindgen:derive-neq=false #[repr(C)] struct ConditionalField { #[cfg(x11)] field: i32, } #[cfg(all(unix, x11))] #[no_mangle] pub extern "C" fn root(a: FooHandle, c: C) { } #[cfg(any(windows, target_pointer_width="32"))] #[no_mangle] pub extern "C" fn root(a: BarHandle, c: C) { } #[no_mangle] pub extern "C" fn cond(a: ConditionalField) { } // src/lib.rs #[repr(C)] struct Normal { x: i32, y: f32, } #[cfg(windows)] extern "C" { fn foo() -> i32; fn bar(a: Normal); } #[cfg(windows)] #[no_mangle] pub static mut global_array_with_different_sizes: [i32; 2] = [123, 456]; #[cfg(unix)] #[no_mangle] pub static mut global_array_with_different_sizes: [i32; 1] = [7890]; cbindgen-0.27.0/tests/rust/cfg.toml000064400000000000000000000007171046102023000152630ustar 00000000000000header = """ #if 0 DEF PLATFORM_UNIX = 0 DEF PLATFORM_WIN = 0 DEF X11 = 0 DEF M_32 = 0 #endif """ [defines] "unix" = "PLATFORM_UNIX" "windows" = "PLATFORM_WIN" "x11" = "X11" "target_pointer_width = 32" = "M_32" [enum] derive_tagged_enum_destructor = true derive_tagged_enum_copy_constructor = true derive_tagged_enum_copy_assignment = true derive_helper_methods = true private_default_tagged_enum_constructor = true [struct] derive_eq = true derive_neq = true cbindgen-0.27.0/tests/rust/cfg_2.rs000064400000000000000000000005471046102023000151560ustar 00000000000000#[cfg(any(windows, unix))] #[repr(C)] struct Foo { x: i32, } #[cfg(windows)] #[repr(C)] struct Bar { y: Foo, } #[cfg(unix)] #[repr(C)] struct Bar { z: Foo, } #[repr(C)] struct Root { w: Bar, } #[cfg(windows)] pub const DEFAULT_X: i32 = 0x08; #[cfg(unix)] pub const DEFAULT_X: i32 = 0x2a; #[no_mangle] pub extern "C" fn root(a: Root) { } cbindgen-0.27.0/tests/rust/cfg_2.toml000064400000000000000000000002221046102023000154730ustar 00000000000000header = """ #if 0 DEF DEFINED = 1 DEF NOT_DEFINED = 0 #endif """ [defines] "unix" = "DEFINED" "macos" = "NOT_DEFINED" "windows" = "NOT_DEFINED" cbindgen-0.27.0/tests/rust/cfg_field.rs000064400000000000000000000001701046102023000160700ustar 00000000000000struct Foo { #[cfg(windows)] x: i32, } pub fn foo() { Foo { #[cfg(windows)] x: 0, }; } cbindgen-0.27.0/tests/rust/char.rs000064400000000000000000000001271046102023000151050ustar 00000000000000#[repr(C)] struct Foo { a: char, } #[no_mangle] pub extern "C" fn root(a: Foo) {} cbindgen-0.27.0/tests/rust/const_conflict.rs000064400000000000000000000001361046102023000171770ustar 00000000000000#[repr(C)] struct Foo {} impl Foo { const FOO: i32 = 0; } pub const Foo_FOO: u32 = 42; cbindgen-0.27.0/tests/rust/const_generics.rs000064400000000000000000000004411046102023000171740ustar 00000000000000#[repr(transparent)] pub struct CArrayString { pub chars: [i8; CAP], } pub const TITLE_SIZE: usize = 80; #[repr(C)] pub struct Book { pub title: CArrayString, pub author: CArrayString<40>, } #[no_mangle] pub extern "C" fn root(a: *mut Book) {} cbindgen-0.27.0/tests/rust/const_generics_arrayvec.rs000064400000000000000000000005631046102023000210750ustar 00000000000000#[repr(C)] pub struct ArrayVec { // the `len` first elements of the array are initialized xs: [T; CAP], len: u32, } #[no_mangle] pub unsafe extern "C" fn push(v: *mut ArrayVec<*mut u8, 100>, elem: *mut u8) -> i32 { if (*v).len < 100 { (*v).xs[(*v).len] = elem; (*v).len += 1; 1 } else { 0 } } cbindgen-0.27.0/tests/rust/const_generics_bool.rs000064400000000000000000000024511046102023000202120ustar 00000000000000use std::mem::MaybeUninit; use libc::c_char; #[repr(C)] pub struct HashTable { num_buckets: usize, capacity: usize, occupied: *mut u8, keys: *mut MaybeUninit, vals: *mut MaybeUninit, } type Str = *const c_char; pub type HashMap = HashTable; pub type HashSet = HashTable; impl HashTable { pub fn new() -> Self { HashTable { num_buckets: 0, capacity: 0, occupied: std::ptr::null_mut(), keys: std::ptr::null_mut(), vals: std::ptr::null_mut(), } } } // with alias type MySet = HashTable; #[no_mangle] pub extern "C" fn new_set() -> *mut MySet { Box::into_raw(Box::new(HashSet::new())) } type SetCallback = unsafe extern "C" fn(key: Str); #[no_mangle] pub unsafe extern "C" fn set_for_each(set: *const MySet, callback: SetCallback) { todo!(); } // without alias #[no_mangle] pub extern "C" fn new_map() -> *mut HashTable { Box::into_raw(Box::new(HashMap::new())) } type MapCallback = unsafe extern "C" fn(key: Str, val: u64); #[no_mangle] pub unsafe extern "C" fn map_for_each(map: *const HashTable, callback: MapCallback) { todo!(); } cbindgen-0.27.0/tests/rust/const_generics_byte.rs000064400000000000000000000014001046102023000202130ustar 00000000000000// Name mangling can cope with char-like byte literals. #[repr(C)] pub struct Parser { pub buf: *mut u8, pub len: usize, } #[no_mangle] pub unsafe extern "C" fn init_parens_parser(p: *mut Parser, buf: *mut u8, len: usize) { unsafe { *p = Parser { buf, len }; } } // The same type as above, because `b'(' == 40 && b')' == 41`. And it happens // to mangle to the same C identifier. It doesn't always work out that way! #[no_mangle] pub unsafe extern "C" fn destroy_parens_parser(p: *mut Parser<40, 41>) { // nothing to do } #[no_mangle] pub unsafe extern "C" fn init_braces_parser(p: *mut Parser, buf: *mut u8, len: usize) { unsafe { *p = Parser { buf, len }; } } cbindgen-0.27.0/tests/rust/const_generics_char.rs000064400000000000000000000005671046102023000202020ustar 00000000000000use std::marker::PhantomData; #[repr(C)] struct TakeUntil<'a, const V: char> { marker: PhantomData<&'a str>, start: *const u8, len: usize, point: usize, } #[no_mangle] pub unsafe extern "C" fn until_nul(start: *const u8, len: usize) -> TakeUntil<'a, '\0'> { TakeUntil { marker: PhantomData, start, len, point: 0, } } cbindgen-0.27.0/tests/rust/const_generics_constant.rs000064400000000000000000000006741046102023000211150ustar 00000000000000#[repr(C)] pub struct FixedPoint { value: u16, } pub const FONT_WEIGHT_FRACTION_BITS: u16 = 6; pub type FontWeightFixedPoint = FixedPoint; #[repr(C)] pub struct FontWeight(FontWeightFixedPoint); impl FontWeight { pub const NORMAL: FontWeight = FontWeight(FontWeightFixedPoint { value: 400 << FONT_WEIGHT_FRACTION_BITS }); } #[no_mangle] pub extern "C" fn root(w: FontWeight) {} cbindgen-0.27.0/tests/rust/const_generics_thru.rs000064400000000000000000000007211046102023000202370ustar 00000000000000// Propagating const arguments through generics that use generics. #[repr(C)] pub struct Inner { pub bytes: [u8; N], } #[repr(C)] pub struct Outer { pub inner: Inner, // don't declare two different structs named `Inner_N` } #[no_mangle] pub extern "C" fn one() -> Outer<1> { Outer { inner: Inner { bytes: [0] } } } #[no_mangle] pub extern "C" fn two() -> Outer<2> { Outer { inner: Inner { bytes: [0, 0] } } } cbindgen-0.27.0/tests/rust/const_transparent.rs000064400000000000000000000021371046102023000177420ustar 00000000000000#[repr(transparent)] struct TransparentStruct { field: u8 } impl TransparentStruct { pub const ASSOC_STRUCT_FOO: i64 = 1; pub const ASSOC_STRUCT_BAR: TransparentStruct = TransparentStruct { field: 2 }; // TODO: Only C++ supports template constants so far. pub const ASSOC_STRUCT_BAZ: Wrapper = Wrapper { field: TransparentStruct { field: 3 } }; } #[repr(transparent)] struct TransparentTupleStruct(u8); #[repr(transparent)] struct Wrapper { field: T } pub const STRUCT_FOO: TransparentStruct = TransparentStruct { field: 4 }; pub const STRUCT_BAR: TransparentTupleStruct = TransparentTupleStruct(5); // TODO: Only C++ supports template constants so far. pub const STRUCT_BAZ: Wrapper = Wrapper { field: TransparentStruct { field: 6 } }; #[repr(transparent)] struct TransparentStructWithErasedField { field: Wrapper, } // TODO: Only C++ supports template constants so far. pub const COMPLEX: TransparentStructWithErasedField = TransparentStructWithErasedField { field: Wrapper { field: TransparentStruct { field: 7 } } }; cbindgen-0.27.0/tests/rust/constant.rs000064400000000000000000000015471046102023000160300ustar 00000000000000pub const FOO: i32 = 10; pub const BAR: &'static str = "hello world"; pub const DELIMITER: char = ':'; pub const LEFTCURLY: char = '{'; pub const QUOTE: char = '\''; pub const TAB: char = '\t'; pub const NEWLINE: char = '\n'; pub const HEART: char = '❤'; pub const EQUID: char = '𐂃'; pub const ZOM: f32 = 3.14; pub(crate) const DONT_EXPORT_CRATE: i32 = 20; const DONT_EXPORT_PRIV: i32 = 30; /// A single-line doc comment. pub const POS_ONE: i8 = 1; /// A /// multi-line /// doc /// comment. pub const NEG_ONE: i8 = -1; // Some doc for shifting // pub const SHIFT: i64 = 3; pub const XBOOL: i64 = 1; pub const XFALSE: i64 = (0 << SHIFT) | XBOOL; pub const XTRUE: i64 = 1 << (SHIFT | XBOOL); pub const CAST: u8 = 'A' as u8; pub const DOUBLE_CAST: u32 = 1 as f32 as u32; #[repr(C)] struct Foo { x: [i32; FOO], } #[no_mangle] pub extern "C" fn root(x: Foo) {} cbindgen-0.27.0/tests/rust/constant_big.rs000064400000000000000000000005141046102023000166420ustar 00000000000000pub const UNSIGNED_NEEDS_ULL_SUFFIX: u64 = 0x8000_0000_0000_0000; pub const UNSIGNED_DOESNT_NEED_ULL_SUFFIX: u64 = 0x7000_0000_0000_0000; // i64::min_value() pub const SIGNED_NEEDS_ULL_SUFFIX: i64 = -9223372036854775808; // i64::min_value() + 1 pub const SIGNED_DOESNT_NEED_ULL_SUFFIX: i64 = -9223372036854775807; cbindgen-0.27.0/tests/rust/constant_constexpr.rs000064400000000000000000000004321046102023000201250ustar 00000000000000pub const CONSTANT_I64: i64 = 216; pub const CONSTANT_FLOAT32: f32 = 312.292; pub const DELIMITER: char = ':'; pub const LEFTCURLY: char = '{'; #[repr(C)] struct Foo { x: i32, } pub const SomeFoo: Foo = Foo { x: 99, }; impl Foo { pub const CONSTANT_I64_BODY: i64 = 216; } cbindgen-0.27.0/tests/rust/constant_constexpr.toml000064400000000000000000000001161046102023000204530ustar 00000000000000[const] allow_constexpr = false [struct] associated_constants_in_body = true cbindgen-0.27.0/tests/rust/constant_sort_name.rs000064400000000000000000000001611046102023000200660ustar 00000000000000pub const B: u8 = 0; pub const A: u8 = 0; #[no_mangle] pub static D: u8 = 0; #[no_mangle] pub static C: u8 = 0; cbindgen-0.27.0/tests/rust/constant_sort_name.toml000064400000000000000000000000311046102023000204110ustar 00000000000000[const] sort_by = "Name" cbindgen-0.27.0/tests/rust/constant_sort_none.rs000064400000000000000000000001611046102023000201050ustar 00000000000000pub const B: u8 = 0; pub const A: u8 = 0; #[no_mangle] pub static D: u8 = 0; #[no_mangle] pub static C: u8 = 0; cbindgen-0.27.0/tests/rust/constant_user_defined_type.rs000064400000000000000000000003241046102023000215750ustar 00000000000000#[repr(C)] pub struct S { field: u8, } /// cbindgen:enum-class=false #[repr(C)] pub enum E { V, } use E::*; pub type A = u8; pub const C1: S = S { field: 0 }; pub const C2: E = V; pub const C3: A = 0; cbindgen-0.27.0/tests/rust/custom_header.rs000064400000000000000000000000521046102023000170070ustar 00000000000000#[no_mangle] pub extern "C" fn root() { } cbindgen-0.27.0/tests/rust/custom_header.toml000064400000000000000000000003271046102023000173430ustar 00000000000000no_includes = true header = """ #if 0 # This file is generated by cbindgen. DO NOT EDIT #endif """ trailer = """ #if 0 # This is a simple test to ensure that trailers do not cause extra newlines in files #endif """ cbindgen-0.27.0/tests/rust/cython_options.rs000064400000000000000000000000001046102023000172350ustar 00000000000000cbindgen-0.27.0/tests/rust/cython_options.toml000064400000000000000000000001611046102023000175740ustar 00000000000000[cython] header = '"my_header.h"' [cython.cimports] "libc.stdint" = ["int8_t", "int16_t"] "libc.stddef" = ["*"] cbindgen-0.27.0/tests/rust/decl_name_conflicting.rs000064400000000000000000000004451046102023000204610ustar 00000000000000mod uhoh { enum BindingType { Buffer, NotBuffer } } #[repr(u32)] pub enum BindingType { Buffer = 0, NotBuffer = 1 } #[repr(C)] pub struct BindGroupLayoutEntry { pub ty: BindingType, // This is the repr(u32) enum } #[no_mangle] pub extern "C" fn root(entry: BindGroupLayoutEntry) {} cbindgen-0.27.0/tests/rust/deprecated.rs000064400000000000000000000030401046102023000162650ustar 00000000000000#[no_mangle] #[deprecated] pub extern "C" fn deprecated_without_note() {} #[no_mangle] #[deprecated = "This is a note"] pub extern "C" fn deprecated_without_bracket() {} #[no_mangle] #[deprecated(note = "This is a note")] pub extern "C" fn deprecated_with_note() {} #[no_mangle] #[deprecated(note = "This is a note", since = "1.0.0")] pub extern "C" fn deprecated_with_note_and_since() {} #[no_mangle] #[deprecated(note = "This quote \" requires to be quoted, and this [\n] requires to be escaped")] pub extern "C" fn deprecated_with_note_which_requires_to_be_escaped() {} #[repr(i32)] #[deprecated] pub enum DeprecatedEnum { A = 0, } #[repr(i32)] #[deprecated(note = "This is a note")] pub enum DeprecatedEnumWithNote { B = 0, } #[repr(i32)] pub enum EnumWithDeprecatedVariants { C = 0, #[deprecated] D = 1, #[deprecated(note = "This is a note")] E = 2, #[deprecated(note = "This is a note", since = "1.0.0")] F = 3, } #[repr(u8)] enum EnumWithDeprecatedStructVariants { Foo(i16), #[deprecated] Bar { x: u8, y: i16 }, #[deprecated(note = "This is a note")] Baz { x: u8, y: u8 }, } #[repr(C)] #[deprecated] pub struct DeprecatedStruct { pub a: i32, } #[repr(C)] #[deprecated(note = "This is a note")] pub struct DeprecatedStructWithNote { pub a: i32, } #[no_mangle] pub extern "C" fn dummy( a: DeprecatedEnum, b: DeprecatedEnumWithNote, c: EnumWithDeprecatedVariants, d: DeprecatedStruct, e: DeprecatedStructWithNote, f: EnumWithDeprecatedStructVariants, ) -> void { } cbindgen-0.27.0/tests/rust/deprecated.toml000064400000000000000000000017121046102023000166200ustar 00000000000000header = """ #define DEPRECATED_FUNC __attribute__((deprecated)) #define DEPRECATED_STRUCT __attribute__((deprecated)) #define DEPRECATED_ENUM __attribute__((deprecated)) #define DEPRECATED_ENUM_VARIANT __attribute__((deprecated)) #define DEPRECATED_FUNC_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__))) #define DEPRECATED_STRUCT_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__))) #define DEPRECATED_ENUM_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__))) #define DEPRECATED_ENUM_VARIANT_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__))) """ [fn] deprecated = "DEPRECATED_FUNC" deprecated_with_note = "DEPRECATED_FUNC_WITH_NOTE({})" [struct] deprecated = "DEPRECATED_STRUCT" deprecated_with_note = "DEPRECATED_STRUCT_WITH_NOTE({})" [enum] deprecated = "DEPRECATED_ENUM" deprecated_with_note = "DEPRECATED_ENUM_WITH_NOTE({})" deprecated_variant = "DEPRECATED_ENUM_VARIANT" deprecated_variant_with_note = "DEPRECATED_ENUM_VARIANT_WITH_NOTE({})" cbindgen-0.27.0/tests/rust/derive_ostream.rs000064400000000000000000000014651046102023000172060ustar 00000000000000/// cbindgen:derive-ostream #[repr(C)] pub struct A(i32); /// cbindgen:field-names=[x, y] /// cbindgen:derive-ostream #[repr(C)] pub struct B(i32, f32); /// cbindgen:derive-ostream #[repr(u32)] pub enum C { X = 2, Y, } /// cbindgen:derive-ostream #[repr(C)] pub struct D { List: u8, Of: usize, Things: B, } /// cbindgen:derive-ostream #[repr(u8)] pub enum F { Foo(i16), Bar { x: u8, y: i16 }, Baz } /// cbindgen:derive-ostream #[repr(C, u8)] pub enum H { Hello(i16), There { x: u8, y: i16 }, Everyone } /// cbindgen:derive-ostream=false #[repr(C, u8)] pub enum I { /// cbindgen:derive-ostream=true ThereAgain { x: u8, y: i16 }, SomethingElse } #[no_mangle] pub extern "C" fn root( a: A, b: B, c: C, d: D, f: F, h: H, i: I, ) { } cbindgen-0.27.0/tests/rust/destructor_and_copy_ctor.rs000064400000000000000000000035221046102023000212730ustar 00000000000000use std::ptr::NonNull; /// This will have a destructor manually implemented via variant_body, and /// similarly a Drop impl in Rust. #[repr(C)] pub struct OwnedSlice { len: usize, ptr: NonNull, } #[repr(u8)] pub enum FillRule { A, B } #[repr(C)] pub struct Polygon { pub fill: FillRule, pub coordinates: OwnedSlice, } #[repr(C, u8)] pub enum Foo { Bar, Polygon1(Polygon), Slice1(OwnedSlice), Slice2(OwnedSlice), Slice3 { fill: FillRule, coords: OwnedSlice, }, Slice4 { fill: FillRule, coords: OwnedSlice, }, } #[repr(u8)] pub enum Baz { Bar2, Polygon21(Polygon), Slice21(OwnedSlice), Slice22(OwnedSlice), Slice23 { fill: FillRule, coords: OwnedSlice, }, Slice24 { fill: FillRule, coords: OwnedSlice, }, } #[repr(u8)] pub enum Taz { Bar3, Taz1(i32), Taz3(OwnedSlice), } /// cbindgen:derive-tagged-enum-destructor=false /// cbindgen:derive-tagged-enum-copy-constructor=false #[repr(u8)] pub enum Tazz { Bar4, Taz2(i32), } /// cbindgen:derive-tagged-enum-copy-assignment=false #[repr(u8)] pub enum Tazzz { Bar5, Taz5(i32), } #[repr(u8)] pub enum Tazzzz { Taz6(i32), Taz7(u32), } /// cbindgen:derive-eq=true /// cbindgen:derive-neq=true /// cbindgen:neq-attributes=NODISCARD /// cbindgen:eq-attributes=NODISCARD /// cbindgen:destructor-attributes=NOINLINE /// cbindgen:copy-constructor-attributes=NOINLINE /// cbindgen:copy-assignment-attributes=NOINLINE #[repr(u8)] pub enum Qux { /// cbindgen:derive-eq=true Qux1(i32), /// cbindgen:derive-eq=true Qux2(u32), } #[no_mangle] pub extern "C" fn root(a: &Foo, b: &Baz, c: &Taz, d: Tazz, e: &Tazzz, f: &Tazzzz, g: &Qux) {} cbindgen-0.27.0/tests/rust/destructor_and_copy_ctor.toml000064400000000000000000000005431046102023000216220ustar 00000000000000header = """ #define NOINLINE __attribute__((noinline)) #define NODISCARD [[nodiscard]] """ [enum] derive_tagged_enum_destructor = true derive_tagged_enum_copy_constructor = true derive_tagged_enum_copy_assignment = true derive_helper_methods = true private_default_tagged_enum_constructor = true [export.body] "OwnedSlice" = """ ~OwnedSlice() {} """ cbindgen-0.27.0/tests/rust/display_list.rs000064400000000000000000000005341046102023000166720ustar 00000000000000#[repr(u8)] pub enum DisplayItem { Fill(Rect, Color), Image { id: u32, bounds: Rect }, ClearScreen, } #[repr(C)] pub struct Rect { x: f32, y: f32, w: f32, h: f32 } #[repr(C)] pub struct Color { r: u8, g: u8, b: u8, a: u8 } #[no_mangle] pub extern "C" fn push_item(item: DisplayItem) -> bool { ::std::mem::drop(item); true } cbindgen-0.27.0/tests/rust/doclength_short.rs000064400000000000000000000006541046102023000173630ustar 00000000000000/// The root of all evil. /// /// But at least it contains some more documentation as someone would expect /// from a simple test case like this. Though, this shouldn't appear in the /// output. #[no_mangle] pub extern "C" fn root() { } /// A little above the root, and a lot more visible, with a run-on sentence /// to test going over the first line. /// /// Still not here, though. #[no_mangle] pub extern "C" fn trunk() { } cbindgen-0.27.0/tests/rust/doclength_short.toml000064400000000000000000000000371046102023000177050ustar 00000000000000documentation_length = "short" cbindgen-0.27.0/tests/rust/docstyle_auto.rs000064400000000000000000000001041046102023000170410ustar 00000000000000/// The root of all evil. #[no_mangle] pub extern "C" fn root() { } cbindgen-0.27.0/tests/rust/docstyle_auto.toml000064400000000000000000000000351046102023000173730ustar 00000000000000documentation_style = "auto" cbindgen-0.27.0/tests/rust/docstyle_c99.rs000064400000000000000000000001041046102023000164750ustar 00000000000000/// The root of all evil. #[no_mangle] pub extern "C" fn root() { } cbindgen-0.27.0/tests/rust/docstyle_c99.toml000064400000000000000000000000341046102023000170260ustar 00000000000000documentation_style = "c99" cbindgen-0.27.0/tests/rust/docstyle_doxy.rs000064400000000000000000000001041046102023000170540ustar 00000000000000/// The root of all evil. #[no_mangle] pub extern "C" fn root() { } cbindgen-0.27.0/tests/rust/docstyle_doxy.toml000064400000000000000000000000351046102023000174060ustar 00000000000000documentation_style = "doxy" cbindgen-0.27.0/tests/rust/documentation.rs000064400000000000000000000013411046102023000170400ustar 00000000000000/// The root of all evil. /// /// But at least it contains some more documentation as someone would expect /// from a simple test case like this. /// /// # Hint /// /// Always ensure that everything is properly documented, even if you feel lazy. /// **Sometimes** it is also helpful to include some markdown formatting. /// /// //////////////////////////////////////////////////////////////////////////// /// /// Attention: /// /// Rust is going to trim all leading `/` symbols. If you want to use them as a /// marker you need to add at least a single whitespace inbetween the tripple /// slash doc-comment marker and the rest. /// #[no_mangle] pub extern "C" fn root() {} /// Some docs. #[no_mangle] pub static FOO: u32 = 4; cbindgen-0.27.0/tests/rust/documentation_attr.rs000064400000000000000000000007301046102023000200730ustar 00000000000000#[doc="With doc attr, each attr contribute to one line of document"] #[doc="like this one with a new line character at its end"] #[doc="and this one as well. So they are in the same paragraph"] #[doc=""] #[doc="Line ends with one new line\nshould not break"] #[doc=""] #[doc="Line ends with two spaces and a new line \nshould break to next line"] #[doc=""] #[doc="Line ends with two new lines\n\nShould break to next paragraph"] #[no_mangle] pub extern "C" fn root() { } cbindgen-0.27.0/tests/rust/enum.rs000064400000000000000000000032271046102023000151400ustar 00000000000000enum Opaque { Foo(i32), Bar, } #[repr(u64)] enum A { a1 = 0, a2 = 2, a3, a4 = 5, } #[repr(u32)] enum B { b1 = 0, b2 = 2, b3, b4 = 5, } #[repr(u16)] enum C { c1 = 0, c2 = 2, c3, c4 = 5, } #[repr(u8)] enum D { d1 = 0, d2 = 2, d3, d4 = 5, } #[repr(usize)] enum E { e1 = 0, e2 = 2, e3, e4 = 5, } #[repr(isize)] enum F { f1 = 0, f2 = 2, f3, f4 = 5, } #[repr(u8)] enum G { Foo(i16), Bar { x: u8, y: i16 }, Baz, } /// cbindgen:prefix-with-name #[repr(C)] enum H { Foo(i16), Bar { x: u8, y: i16 }, Baz, } /// cbindgen:prefix-with-name #[repr(C, u8)] enum I { Foo(i16), Bar { x: u8, y: i16 }, Baz, } #[repr(C, u8, u16)] enum J { Foo(i16), Bar { x: u8, y: i16 }, Baz, } #[repr(C, u8, unknown_hint)] enum K { Foo(i16), Bar { x: u8, y: i16 }, Baz, } #[repr(C)] enum L { l1, l2, l3, l4, } #[repr(i8)] enum M { m1 = -1, m2 = 0, m3 = 1, } /// cbindgen:enum-class=false #[repr(C)] enum N { n1, n2, n3, n4, } /// cbindgen:enum-class=false #[repr(i8)] enum O { o1, o2, o3, o4, } #[repr(C, u8)] enum P { P0(u8), P1(u8, u8, u8), } #[repr(C)] enum Q { Ok(Box), Err(u32), } /// cbindgen:rename-variant-name-fields=None #[repr(C)] enum R { IRFoo(i16), IRBar { x: u8, y: i16 }, IRBaz, } #[no_mangle] pub extern "C" fn root( opaque: *mut Opaque, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R, ) { } cbindgen-0.27.0/tests/rust/enum.toml000064400000000000000000000011441046102023000154630ustar 00000000000000header = """ #if 0 ''' ' #endif #ifdef __cplusplus template using Box = T*; #endif #if 0 ' ''' #endif """ trailer = """ #if 0 ''' ' #endif #include #include "testing-helpers.h" static_assert(offsetof(CBINDGEN_STRUCT(P), tag) == 0, "unexpected offset for tag"); static_assert(offsetof(CBINDGEN_STRUCT(P), p0) == 1, "unexpected offset for p0"); static_assert(offsetof(CBINDGEN_STRUCT(P), p0) == 1, "unexpected offset for p1"); static_assert(sizeof(CBINDGEN_STRUCT(P)) == 4, "unexpected size for P"); #if 0 ' ''' #endif """ [export] exclude = [ "Box", ] [export.rename] "I" = "ExI" cbindgen-0.27.0/tests/rust/enum_discriminant.rs000064400000000000000000000003111046102023000176730ustar 00000000000000pub const FOUR: i8 = 4; #[repr(i8)] enum E { A = 1, B = -1, C = 1 + 2, D = FOUR, F = (5), G = b'6' as i8, H = false as i8, } #[no_mangle] pub extern "C" fn root(_: &E) {} cbindgen-0.27.0/tests/rust/enum_discriminant.toml000064400000000000000000000000471046102023000202300ustar 00000000000000[export.rename] "FOUR" = "FOURTY_FOUR" cbindgen-0.27.0/tests/rust/enum_self.rs000064400000000000000000000003471046102023000161510ustar 00000000000000#[repr(C)] pub struct Foo { something: *const i32, phantom: std::marker::PhantomData, } #[repr(u8)] pub enum Bar { Min(Foo), Max(Foo), Other, } #[no_mangle] pub extern "C" fn root(b: Bar) {} cbindgen-0.27.0/tests/rust/euclid.rs000064400000000000000000000041161046102023000154370ustar 00000000000000struct UnknownUnit; struct LayoutUnit; #[repr(C)] struct TypedLength(T, PhantomData); #[repr(C)] struct TypedSideOffsets2D { top: T, right: T, bottom: T, left: T, _phantom: PhantomData, } #[repr(C)] struct TypedSize2D { width: T, height: T, _phantom: PhantomData, } #[repr(C)] struct TypedPoint2D { x: T, y: T, _phantom: PhantomData, } #[repr(C)] struct TypedRect { origin: TypedPoint2D, size: TypedSize2D, _phantom: PhantomData, } #[repr(C)] struct TypedTransform2D { m11: T, m12: T, m21: T, m22: T, m31: T, m32: T, _phantom: PhantomData, } type Length = TypedLength; type SideOffsets2D = TypedSideOffsets2D; type Size2D = TypedSize2D; type Point2D = TypedPoint2D; type Rect = TypedRect; type LayoutLength = TypedLength; type LayoutSideOffsets2D = TypedSideOffsets2D; type LayoutSize2D = TypedSize2D; type LayoutPoint2D = TypedPoint2D; type LayoutRect = TypedRect; #[no_mangle] pub extern "C" fn root( length_a: TypedLength, length_b: TypedLength, length_c: Length, length_d: LayoutLength, side_offsets_a: TypedSideOffsets2D, side_offsets_b: TypedSideOffsets2D, side_offsets_c: SideOffsets2D, side_offsets_d: LayoutSideOffsets2D, size_a: TypedSize2D, size_b: TypedSize2D, size_c: Size2D, size_d: LayoutSize2D, point_a: TypedPoint2D, point_b: TypedPoint2D, point_c: Point2D, point_d: LayoutPoint2D, rect_a: TypedRect, rect_b: TypedRect, rect_c: Rect, rect_d: LayoutRect, transform_a: TypedTransform2D, transform_b: TypedTransform2D ) { } cbindgen-0.27.0/tests/rust/exclude_generic_monomorph.rs000064400000000000000000000002241046102023000214110ustar 00000000000000#[repr(transparent)] pub struct Foo(NonZeroU64); #[repr(C)] pub struct Bar { foo: Option, } #[no_mangle] pub extern "C" fn root(f: Bar) {} cbindgen-0.27.0/tests/rust/exclude_generic_monomorph.toml000064400000000000000000000003571046102023000217470ustar 00000000000000language = "C" header = """ #include #if 0 ''' ' #endif typedef uint64_t Option_Foo; #if 0 ' ''' #endif #if 0 from libc.stdint cimport uint64_t ctypedef uint64_t Option_Foo #endif """ [export] exclude = [ "Option_Foo", ] cbindgen-0.27.0/tests/rust/export_name.rs000064400000000000000000000001651046102023000165130ustar 00000000000000#[export_name = "do_the_thing_with_export_name"] pub extern "C" fn do_the_thing() { println!("doing the thing!"); }cbindgen-0.27.0/tests/rust/extern.rs000064400000000000000000000002231046102023000154720ustar 00000000000000#[repr(C)] struct Normal { x: i32, y: f32, } extern "C" { fn foo() -> i32; fn bar(a: Normal); } extern { fn baz() -> i32; } cbindgen-0.27.0/tests/rust/extern_2.rs000064400000000000000000000001651046102023000157200ustar 00000000000000#[no_mangle] pub extern "C" fn first() { } #[no_mangle] pub extern fn second() { } #[no_mangle] pub fn third() { } cbindgen-0.27.0/tests/rust/fns.rs000064400000000000000000000005021046102023000147530ustar 00000000000000#[repr(C)] pub struct Fns { noArgs: fn(), anonymousArg: fn(i32), returnsNumber: fn() -> i32, namedArgs: fn(first: i32, snd: i16) -> i8, namedArgsWildcards: fn(_: i32, named: i16, _: i64) -> i8, } #[no_mangle] pub extern "C" fn root(_fns: Fns) {} #[no_mangle] pub extern "C" fn no_return() -> ! { loop {} } cbindgen-0.27.0/tests/rust/forward_declaration.rs000064400000000000000000000004611046102023000202020ustar 00000000000000#[repr(C)] struct TypeInfo { data: TypeData, } #[repr(C)] enum TypeData { Primitive, Struct(StructInfo), } #[repr(C)] struct StructInfo { fields: *const *const TypeInfo, // requires forward declaration num_fields: usize, } #[no_mangle] pub extern "C" fn root( x: TypeInfo, ) {} cbindgen-0.27.0/tests/rust/forward_declaration.toml000064400000000000000000000003751046102023000205350ustar 00000000000000header = """ #if 0 ''' ' #endif #if defined(CBINDGEN_STYLE_TYPE) /* ANONYMOUS STRUCTS DO NOT SUPPORT FORWARD DECLARATIONS! #endif #if 0 ' ''' #endif """ trailer = """ #if 0 ''' ' #endif #if defined(CBINDGEN_STYLE_TYPE) */ #endif #if 0 ' ''' #endif """ cbindgen-0.27.0/tests/rust/function_args.rs000064400000000000000000000007231046102023000170330ustar 00000000000000#[no_mangle] pub unsafe extern fn array_print(a: &[u64]) { eprintln!("{:?}", a); } #[no_mangle] pub unsafe extern fn array_test(a: [u64; 3]) { array_print(&a); } #[no_mangle] pub unsafe extern fn unnamed(_: *const u64) { } #[no_mangle] pub unsafe extern fn pointer_test(a: *const u64) { let a = std::slice::from_raw_parts(a, 3); array_print(a); } #[no_mangle] pub unsafe extern fn print_from_rust() { let a = [0, 1, 2]; array_print(&a); } cbindgen-0.27.0/tests/rust/function_noreturn.rs000064400000000000000000000003761046102023000177570ustar 00000000000000#[no_mangle] pub extern "C" fn loop_forever() -> ! { loop {} } #[no_mangle] pub extern "C" fn normal_return(arg: Example, other: extern "C" fn(u8) -> !) -> u8 { 0 } #[repr(C)] pub struct Example { pub f: extern "C" fn(usize, usize) -> !, } cbindgen-0.27.0/tests/rust/function_noreturn.toml000064400000000000000000000003701046102023000203000ustar 00000000000000after_includes = """ #ifndef NO_RETURN_ATTR #ifdef __GNUC__ #define NO_RETURN_ATTR __attribute__ ((noreturn)) #else // __GNUC__ #define NO_RETURN_ATTR #endif // __GNUC__ #endif // NO_RETURN_ATTR """ [fn] no_return = "NO_RETURN_ATTR" cbindgen-0.27.0/tests/rust/function_ptr.rs000064400000000000000000000004301046102023000166770ustar 00000000000000pub type MyCallback = Option; pub type MyOtherCallback = Option; #[no_mangle] pub extern "C" fn my_function(a: MyCallback, b: MyOtherCallback) {} cbindgen-0.27.0/tests/rust/function_sort_name.rs000064400000000000000000000002371046102023000200660ustar 00000000000000#[no_mangle] pub extern "C" fn C() { } #[no_mangle] pub extern "C" fn B() { } #[no_mangle] pub extern "C" fn D() { } #[no_mangle] pub extern "C" fn A() { } cbindgen-0.27.0/tests/rust/function_sort_name.toml000064400000000000000000000000261046102023000204110ustar 00000000000000[fn] sort_by = "Name" cbindgen-0.27.0/tests/rust/function_sort_none.rs000064400000000000000000000002371046102023000201050ustar 00000000000000#[no_mangle] pub extern "C" fn C() { } #[no_mangle] pub extern "C" fn B() { } #[no_mangle] pub extern "C" fn D() { } #[no_mangle] pub extern "C" fn A() { } cbindgen-0.27.0/tests/rust/generic_defaults.rs000064400000000000000000000004411046102023000174720ustar 00000000000000#[repr(transparent)] pub struct Foo { field: T, _phantom: std::marker::PhantomData

, } #[repr(C)] pub struct Bar { f: Foo, p: P, } pub type Baz = Foo; #[no_mangle] pub extern "C" fn foo_root(f: Foo, b: Bar, z: Baz) {} cbindgen-0.27.0/tests/rust/generic_pointer.rs000064400000000000000000000002011046102023000173350ustar 00000000000000#[repr(C)] pub struct Foo { a: T, } pub type Boo = Foo<*mut u8>; #[no_mangle] pub extern "C" fn root( x: Boo, ) { } cbindgen-0.27.0/tests/rust/global_attr.rs000064400000000000000000000000341046102023000164570ustar 00000000000000#![allow(unused_variables)] cbindgen-0.27.0/tests/rust/global_variable.rs000064400000000000000000000002171046102023000172750ustar 00000000000000#[no_mangle] pub static mut MUT_GLOBAL_ARRAY: [c_char; 128] = [0; 128]; #[no_mangle] pub static CONST_GLOBAL_ARRAY: [c_char; 128] = [0; 128]; cbindgen-0.27.0/tests/rust/ignore.rs000064400000000000000000000017071046102023000154600ustar 00000000000000/// cbindgen:ignore #[no_mangle] pub extern "C" fn root() {} /// cbindgen:ignore /// /// Something else. #[no_mangle] pub extern "C" fn another_root() {} #[no_mangle] pub extern "C" fn no_ignore_root() {} /// cbindgen:ignore #[repr(C)] pub struct IgnoreStruct {} pub struct IgnoreStructWithImpl; /// cbindgen:ignore impl IgnoreStructWithImpl { #[no_mangle] pub extern "C" fn ignore_associated_method() {} pub const IGNORE_INNER_CONST: u32 = 0; } /// cbindgen:ignore pub const IGNORE_CONST: u32 = 0; pub const NO_IGNORE_CONST: u32 = 0; pub struct NoIgnoreStructWithImpl; impl NoIgnoreStructWithImpl { /// cbindgen:ignore #[no_mangle] pub extern "C" fn ignore_associated_method() {} #[no_mangle] pub extern "C" fn no_ignore_associated_method() {} /// cbindgen:ignore pub const IGNORE_INNER_CONST: u32 = 0; pub const NO_IGNORE_INNER_CONST: u32 = 0; } /// cbindgen:ignore enum IgnoreEnum {} enum NoIgnoreEnum {} cbindgen-0.27.0/tests/rust/include.rs000064400000000000000000000000001046102023000156010ustar 00000000000000cbindgen-0.27.0/tests/rust/include.toml000064400000000000000000000000321046102023000161350ustar 00000000000000sys_includes = ["math.h"] cbindgen-0.27.0/tests/rust/include_guard.rs000064400000000000000000000000521046102023000167720ustar 00000000000000#[no_mangle] pub extern "C" fn root() { } cbindgen-0.27.0/tests/rust/include_guard.toml000064400000000000000000000000651046102023000173250ustar 00000000000000include_guard = "INCLUDE_GUARD_H" no_includes = true cbindgen-0.27.0/tests/rust/include_item.rs000064400000000000000000000001261046102023000166300ustar 00000000000000#[repr(C)] struct A { x: i32, y: f32, } #[repr(C)] struct B { data: A, } cbindgen-0.27.0/tests/rust/include_item.toml000064400000000000000000000000311046102023000171520ustar 00000000000000[export] include = ["B"] cbindgen-0.27.0/tests/rust/include_specific.rs000064400000000000000000000000001046102023000174460ustar 00000000000000cbindgen-0.27.0/tests/rust/include_specific.toml000064400000000000000000000000551046102023000200070ustar 00000000000000sys_includes = ["math.h"] no_includes = true cbindgen-0.27.0/tests/rust/infinite_recursion_typedef_monomorph.rs000064400000000000000000000001551046102023000237050ustar 00000000000000pub type TryVec = fallible_collections::TryVec; pub type TryString = fallible_collections::TryVec; cbindgen-0.27.0/tests/rust/inner_mod.rs000064400000000000000000000001701046102023000161400ustar 00000000000000mod foo { #[repr(C)] struct Foo { x: f32, } } #[no_mangle] pub extern "C" fn root(a: foo::Foo) { } cbindgen-0.27.0/tests/rust/item_types.rs000064400000000000000000000002301046102023000163450ustar 00000000000000 pub const MY_CONST: u8 = 4; #[no_mangle] pub extern "C" fn ExternFunction() { } #[repr(u8)] pub enum OnlyThisShouldBeGenerated { Foo, Bar, } cbindgen-0.27.0/tests/rust/item_types.toml000064400000000000000000000001101046102023000166710ustar 00000000000000[export] item_types = ["enums"] include = ["OnlyThisShouldBeGenerated"] cbindgen-0.27.0/tests/rust/item_types_renamed.rs000064400000000000000000000002301046102023000200400ustar 00000000000000 pub const MY_CONST: u8 = 4; #[no_mangle] pub extern "C" fn ExternFunction() { } #[repr(u8)] pub enum OnlyThisShouldBeGenerated { Foo, Bar, } cbindgen-0.27.0/tests/rust/item_types_renamed.toml000064400000000000000000000001311046102023000203670ustar 00000000000000[export] item_types = ["enums"] include = ["OnlyThisShouldBeGenerated"] prefix = "Style" cbindgen-0.27.0/tests/rust/layout.rs000064400000000000000000000037001046102023000155050ustar 00000000000000#[repr(align(1), C)] pub struct Align1Struct { pub arg1: usize, pub arg2: *mut u8, } #[repr(align(2), C)] pub struct Align2Struct { pub arg1: usize, pub arg2: *mut u8, } #[repr(align(4), C)] pub struct Align4Struct { pub arg1: usize, pub arg2: *mut u8, } #[repr(align(8), C)] pub struct Align8Struct { pub arg1: usize, pub arg2: *mut u8, } #[repr(align(32), C)] pub struct Align32Struct { pub arg1: usize, pub arg2: *mut u8, } #[repr(packed, C)] pub struct PackedStruct { pub arg1: usize, pub arg2: *mut u8, } #[repr(align(1), C)] pub union Align1Union { pub variant1: usize, pub variant2: *mut u8, } #[repr(align(4), C)] pub union Align4Union { pub variant1: usize, pub variant2: *mut u8, } #[repr(align(16), C)] pub union Align16Union { pub variant1: usize, pub variant2: *mut u8, } #[repr(packed, C)] pub union PackedUnion { pub variant1: usize, pub variant2: *mut u8, } // #[repr(packed(n), C)] structs are currently unsupported. #[repr(packed(4), C)] pub struct UnsupportedPacked4Struct { pub arg1: usize, pub arg2: *mut u8, } // #[repr(packed(n), C)] unions are currently unsupported. #[repr(packed(4), C)] pub union UnsupportedPacked4Union { pub variant1: usize, pub variant2: *mut u8, } // #[repr(align(n), C)] enums are currently unsupported. #[repr(align(4), C)] pub enum UnsupportedAlign4Enum { Variant1, Variant2, } // Non-repr(C) structs aren't translated. #[repr(align(4))] pub struct RustAlign4Struct { pub arg1: usize, pub arg2: *mut u8, } // Non-repr(C) structs aren't translated. #[repr(packed)] pub struct RustPackedStruct { pub arg1: usize, pub arg2: *mut u8, } // Non-repr(C) unions aren't translated. #[repr(align(4))] pub struct RustAlign4Union { pub arg1: usize, pub arg2: *mut u8, } // Non-repr(C) unions aren't translated. #[repr(packed)] pub struct RustPackedUnion { pub arg1: usize, pub arg2: *mut u8, } cbindgen-0.27.0/tests/rust/layout.toml000064400000000000000000000010501046102023000160300ustar 00000000000000header = """ #define CBINDGEN_PACKED __attribute__ ((packed)) #define CBINDGEN_ALIGNED(n) __attribute__ ((aligned(n))) """ [layout] packed = "CBINDGEN_PACKED" aligned_n = "CBINDGEN_ALIGNED" [export] include = [ "Align1Struct", "Align2Struct", "Align4Struct", "Align8Struct", "Align32Struct", "PackedStruct", "Align1Union", "Align4Union", "Align16Union", "PackedUnion", "UnsupportedPacked4Struct", "UnsupportedPacked4Union", "UnsupportedAlign4Enum", "RustAlign4Struct", "RustPackedStruct", "RustAlign4Union", "RustPackedUnion", ] cbindgen-0.27.0/tests/rust/layout_aligned_opaque.rs000064400000000000000000000025231046102023000205440ustar 00000000000000#[repr(packed, C)] pub struct PackedStruct { pub arg1: usize, pub arg2: *mut u8, } #[repr(packed, C)] pub union PackedUnion { pub variant1: usize, pub variant2: *mut u8, } // Opaque because aligned_n is not defined. #[repr(align(1), C)] pub union OpaqueAlign1Union { pub variant1: usize, pub variant2: *mut u8, } // Opaque because aligned_n is not defined. #[repr(align(4), C)] pub union OpaqueAlign4Union { pub variant1: usize, pub variant2: *mut u8, } // Opaque because aligned_n is not defined. #[repr(align(16), C)] pub union OpaqueAlign16Union { pub variant1: usize, pub variant2: *mut u8, } // Opaque because aligned_n is not defined. #[repr(align(1), C)] pub struct OpaqueAlign1Struct { pub arg1: usize, pub arg2: *mut u8, } // Opaque because aligned_n is not defined. #[repr(align(2), C)] pub struct OpaqueAlign2Struct { pub arg1: usize, pub arg2: *mut u8, } // Opaque because aligned_n is not defined. #[repr(align(4), C)] pub struct OpaqueAlign4Struct { pub arg1: usize, pub arg2: *mut u8, } // Opaque because aligned_n is not defined. #[repr(align(8), C)] pub struct OpaqueAlign8Struct { pub arg1: usize, pub arg2: *mut u8, } // Opaque because aligned_n is not defined. #[repr(align(32), C)] pub struct OpaqueAlign32Struct { pub arg1: usize, pub arg2: *mut u8, } cbindgen-0.27.0/tests/rust/layout_aligned_opaque.toml000064400000000000000000000006701046102023000210740ustar 00000000000000header = """ #define CBINDGEN_PACKED __attribute__ ((packed)) #define CBINDGEN_ALIGNED(n) __attribute__ ((aligned(n))) """ [layout] # We do not define aligned_n. packed = "CBINDGEN_PACKED" [export] include = [ "PackedStruct", "PackedUnion", "OpaqueAlign1Union", "OpaqueAlign4Union", "OpaqueAlign16Union", "OpaqueAlign1Struct", "OpaqueAlign2Struct", "OpaqueAlign4Struct", "OpaqueAlign8Struct", "OpaqueAlign32Struct", ] cbindgen-0.27.0/tests/rust/layout_packed_opaque.rs000064400000000000000000000020411046102023000203630ustar 00000000000000#[repr(align(1), C)] pub union Align1Union { pub variant1: usize, pub variant2: *mut u8, } #[repr(align(4), C)] pub union Align4Union { pub variant1: usize, pub variant2: *mut u8, } #[repr(align(16), C)] pub union Align16Union { pub variant1: usize, pub variant2: *mut u8, } #[repr(align(1), C)] pub struct Align1Struct { pub arg1: usize, pub arg2: *mut u8, } #[repr(align(2), C)] pub struct Align2Struct { pub arg1: usize, pub arg2: *mut u8, } #[repr(align(4), C)] pub struct Align4Struct { pub arg1: usize, pub arg2: *mut u8, } #[repr(align(8), C)] pub struct Align8Struct { pub arg1: usize, pub arg2: *mut u8, } #[repr(align(32), C)] pub struct Align32Struct { pub arg1: usize, pub arg2: *mut u8, } // Opaque because packed is not defined. #[repr(packed, C)] pub struct OpaquePackedStruct { pub arg1: usize, pub arg2: *mut u8, } // Opaque because packed is not defined. #[repr(packed, C)] pub union OpaquePackedUnion { pub variant1: usize, pub variant2: *mut u8, } cbindgen-0.27.0/tests/rust/layout_packed_opaque.toml000064400000000000000000000006251046102023000207200ustar 00000000000000header = """ #define CBINDGEN_PACKED __attribute__ ((packed)) #define CBINDGEN_ALIGNED(n) __attribute__ ((aligned(n))) """ [layout] # We do not define packed. aligned_n = "CBINDGEN_ALIGNED" [export] include = [ "Align1Union", "Align4Union", "Align16Union", "Align1Struct", "Align2Struct", "Align4Struct", "Align8Struct", "Align32Struct", "OpaquePackedStruct", "OpaquePackedUnion", ] cbindgen-0.27.0/tests/rust/lifetime_arg.rs000064400000000000000000000002421046102023000166150ustar 00000000000000#[repr(C)] struct A<'a> { data: &'a i32 } #[repr(C)] enum E<'a> { V, U(&'a u8), } #[no_mangle] pub extern "C" fn root<'a>(_a: A<'a>, _e: E<'a>) { } cbindgen-0.27.0/tests/rust/linestyle_cr.rs000064400000000000000000000001461046102023000166650ustar 00000000000000#[repr(C)] struct Dummy { x: i32, y: f32, } #[no_mangle] pub extern "C" fn root(d: Dummy) {} cbindgen-0.27.0/tests/rust/linestyle_cr.toml000064400000000000000000000000241046102023000172070ustar 00000000000000line_endings = "CR" cbindgen-0.27.0/tests/rust/linestyle_crlf.rs000064400000000000000000000001461046102023000172070ustar 00000000000000#[repr(C)] struct Dummy { x: i32, y: f32, } #[no_mangle] pub extern "C" fn root(d: Dummy) {} cbindgen-0.27.0/tests/rust/linestyle_crlf.toml000064400000000000000000000000261046102023000175330ustar 00000000000000line_endings = "CRLF" cbindgen-0.27.0/tests/rust/linestyle_lf.rs000064400000000000000000000001461046102023000166620ustar 00000000000000#[repr(C)] struct Dummy { x: i32, y: f32, } #[no_mangle] pub extern "C" fn root(d: Dummy) {} cbindgen-0.27.0/tests/rust/linestyle_lf.toml000064400000000000000000000000241046102023000172040ustar 00000000000000line_endings = "LF" cbindgen-0.27.0/tests/rust/mangle.rs000064400000000000000000000003351046102023000154340ustar 00000000000000#[repr(C)] pub struct Foo { a: T, } pub type Boo = Foo; /// cbindgen:prefix-with-name=true #[repr(C)] pub enum Bar { Some, Thing, } #[no_mangle] pub extern "C" fn root( x: Boo, y: Bar, ) { } cbindgen-0.27.0/tests/rust/mangle.toml000064400000000000000000000001061046102023000157570ustar 00000000000000[export.mangle] remove_underscores = true rename_types = "PascalCase" cbindgen-0.27.0/tests/rust/manuallydrop.rs000064400000000000000000000006201046102023000166750ustar 00000000000000#[repr(C)] pub struct Point { x: i32, y: i32, } #[repr(C)] pub struct MyStruct { point: std::mem::ManuallyDrop, } pub struct NotReprC { inner: T, } pub type Foo = NotReprC>; #[no_mangle] pub extern "C" fn root(a: &Foo, with_manual_drop: &MyStruct) {} #[no_mangle] pub extern "C" fn take(with_manual_drop: std::mem::ManuallyDrop) {} cbindgen-0.27.0/tests/rust/manuallydrop.toml000064400000000000000000000002521046102023000172250ustar 00000000000000header = """ #if 0 ''' ' #endif #ifdef __cplusplus template using ManuallyDrop = T; #endif #if 0 ' ''' #endif """ [export] exclude = [ "ManuallyDrop", ] cbindgen-0.27.0/tests/rust/maybeuninit.rs000064400000000000000000000004301046102023000165110ustar 00000000000000#[repr(C)] pub struct MyStruct<'a> { number: std::mem::MaybeUninit<&'a i32>, } pub struct NotReprC { inner: T, } pub type Foo<'a> = NotReprC>; #[no_mangle] pub extern "C" fn root<'a, 'b>(a: &'a Foo, with_maybe_uninit: &'b MyStruct) {} cbindgen-0.27.0/tests/rust/maybeuninit.toml000064400000000000000000000002501046102023000170400ustar 00000000000000header = """ #if 0 ''' ' #endif #ifdef __cplusplus template using MaybeUninit = T; #endif #if 0 ' ''' #endif """ [export] exclude = [ "MaybeUninit", ] cbindgen-0.27.0/tests/rust/monomorph_1.rs000064400000000000000000000006271046102023000164330ustar 00000000000000#[repr(C)] struct Foo { data: *const T } struct Bar { data: *const T } #[repr(C)] struct Tuple { a: *const T, b: *const E, } type Indirection = Tuple; #[no_mangle] pub extern "C" fn root( a: Foo, b: Foo, c: Bar, d: Foo>, e: Bar>, f: Bar>, g: Tuple, f32>, h: Indirection ) { } cbindgen-0.27.0/tests/rust/monomorph_2.rs000064400000000000000000000003041046102023000164240ustar 00000000000000#[repr(C)] struct List { members: *mut T, count: usize } struct A; struct B; #[no_mangle] pub extern "C" fn foo(a: List) { } #[no_mangle] pub extern "C" fn bar(b: List) { } cbindgen-0.27.0/tests/rust/monomorph_3.rs000064400000000000000000000006241046102023000164320ustar 00000000000000#[repr(C)] union Foo { data: *const T } union Bar { data: *const T } #[repr(C)] union Tuple { a: *const T, b: *const E, } type Indirection = Tuple; #[no_mangle] pub extern "C" fn root( a: Foo, b: Foo, c: Bar, d: Foo>, e: Bar>, f: Bar>, g: Tuple, f32>, h: Indirection ) { } cbindgen-0.27.0/tests/rust/must_use.rs000064400000000000000000000004051046102023000160330ustar 00000000000000 #[repr(C)] #[must_use] pub struct OwnedPtr { ptr: *mut T, } #[repr(C, u8)] #[must_use] pub enum MaybeOwnedPtr { Owned(*mut T), None, } #[no_mangle] #[must_use] pub extern "C" fn maybe_consume(input: OwnedPtr) -> MaybeOwnedPtr { } cbindgen-0.27.0/tests/rust/must_use.toml000064400000000000000000000004171046102023000163650ustar 00000000000000header = """ #define MUST_USE_FUNC __attribute__((warn_unused_result)) #define MUST_USE_STRUCT __attribute__((warn_unused)) #define MUST_USE_ENUM /* nothing */ """ [fn] must_use = "MUST_USE_FUNC" [struct] must_use = "MUST_USE_STRUCT" [enum] must_use = "MUST_USE_ENUM" cbindgen-0.27.0/tests/rust/namespace_constant.rs000064400000000000000000000003001046102023000200260ustar 00000000000000pub const FOO: i32 = 10; pub const BAR: &'static str = "hello world"; pub const ZOM: f32 = 3.14; #[repr(C)] struct Foo { x: [i32; FOO], } #[no_mangle] pub extern "C" fn root(x: Foo) { } cbindgen-0.27.0/tests/rust/namespace_constant.toml000064400000000000000000000000301046102023000203550ustar 00000000000000namespace = "constants" cbindgen-0.27.0/tests/rust/namespaces_constant.rs000064400000000000000000000003001046102023000202110ustar 00000000000000pub const FOO: i32 = 10; pub const BAR: &'static str = "hello world"; pub const ZOM: f32 = 3.14; #[repr(C)] struct Foo { x: [i32; FOO], } #[no_mangle] pub extern "C" fn root(x: Foo) { } cbindgen-0.27.0/tests/rust/namespaces_constant.toml000064400000000000000000000000431046102023000205440ustar 00000000000000namespaces = ["constants", "test"] cbindgen-0.27.0/tests/rust/nested_import.rs000064400000000000000000000000501046102023000170370ustar 00000000000000use std::{result, marker::PhantomData}; cbindgen-0.27.0/tests/rust/no_includes.rs000064400000000000000000000000521046102023000164670ustar 00000000000000#[no_mangle] pub extern "C" fn root() { } cbindgen-0.27.0/tests/rust/no_includes.toml000064400000000000000000000000231046102023000170140ustar 00000000000000no_includes = true cbindgen-0.27.0/tests/rust/non_pub_extern.rs000064400000000000000000000002651046102023000172200ustar 00000000000000#[no_mangle] static FIRST: u32 = 10; #[export_name = "RENAMED"] static SECOND: u32 = 42; #[no_mangle] extern "C" fn first() { } #[export_name = "renamed"] extern fn second() { } cbindgen-0.27.0/tests/rust/nonnull.rs000064400000000000000000000006571046102023000156650ustar 00000000000000use std::ptr::NonNull; struct Opaque; #[repr(C)] pub struct Foo { a: NonNull, b: NonNull, c: NonNull, d: NonNull>, e: NonNull>, f: NonNull>, g: Option>, h: Option>, i: Option>>, } #[no_mangle] pub extern "C" fn root(arg: NonNull, foo: *mut Foo, d: NonNull>) { } cbindgen-0.27.0/tests/rust/nonnull_attribute.rs000064400000000000000000000022271046102023000177430ustar 00000000000000use std::ptr::NonNull; struct Opaque; #[repr(C)] pub struct Pointers { a: NonNull, b: NonNull, c: NonNull, d: NonNull>, e: NonNull>, f: NonNull>, g: Option>, h: Option>, i: Option>>, j: *const T, k: *mut T, } #[repr(C)] pub struct References<'a> { a: &'a Opaque, b: &'a mut Opaque, c: Option<&'a Opaque>, d: Option<&'a mut Opaque>, } #[no_mangle] pub extern "C" fn value_arg(arg: References<'static>) {} #[no_mangle] pub extern "C" fn mutltiple_args( arg: NonNull, foo: *mut Pointers, d: NonNull>, ) { } #[no_mangle] pub extern "C" fn ref_arg(arg: &Pointers) {} #[no_mangle] pub extern "C" fn mut_ref_arg(arg: &mut Pointers) {} #[no_mangle] pub extern "C" fn optional_ref_arg(arg: Option<&Pointers>) {} #[no_mangle] pub extern "C" fn optional_mut_ref_arg(arg: Option<&mut Pointers>) {} #[no_mangle] pub extern "C" fn nullable_const_ptr(arg: *const Pointers) {} #[no_mangle] pub extern "C" fn nullable_mut_ptr(arg: *mut Pointers) {} cbindgen-0.27.0/tests/rust/nonnull_attribute.toml000064400000000000000000000002301046102023000202620ustar 00000000000000header = """ #ifdef __clang__ #define CBINDGEN_NONNULL _Nonnull #else #define CBINDGEN_NONNULL #endif """ [ptr] non_null_attribute = "CBINDGEN_NONNULL"cbindgen-0.27.0/tests/rust/nonzero.rs000064400000000000000000000023661046102023000156710ustar 00000000000000use std::num::*; #[repr(C)] pub struct NonZeroAliases { pub a: NonZeroU8, pub b: NonZeroU16, pub c: NonZeroU32, pub d: NonZeroU64, pub e: NonZeroI8, pub f: NonZeroI16, pub g: NonZeroI32, pub h: NonZeroI64, pub i: Option, pub j: *const Option>, } #[no_mangle] pub extern "C" fn root_nonzero_aliases( test: NonZeroAliases, a: NonZeroU8, b: NonZeroU16, c: NonZeroU32, d: NonZeroU64, e: NonZeroI8, f: NonZeroI16, g: NonZeroI32, h: NonZeroI64, i: Option, j: *const Option>, ) {} #[repr(C)] pub struct NonZeroGenerics { pub a: NonZero, pub b: NonZero, pub c: NonZero, pub d: NonZero, pub e: NonZero, pub f: NonZero, pub g: NonZero, pub h: NonZero, pub i: Option>, pub j: *const Option>>, } #[no_mangle] pub extern "C" fn root_nonzero_generics( test: NonZeroGenerics, a: NonZero, b: NonZero, c: NonZero, d: NonZero, e: NonZero, f: NonZero, g: NonZero, h: NonZero, i: Option>, j: *const Option>>, ) {} cbindgen-0.27.0/tests/rust/nonzero.toml000064400000000000000000000001461046102023000162120ustar 00000000000000header = """ #if 0 ''' ' #endif #ifdef __cplusplus struct NonZeroI64; #endif #if 0 ' ''' #endif """ cbindgen-0.27.0/tests/rust/opaque.rs000064400000000000000000000004551046102023000154660ustar 00000000000000/// Fast hash map used internally. type FastHashMap = std::collections::HashMap>; pub type Foo = FastHashMap; pub type Bar = Result; #[no_mangle] pub extern "C" fn root(a: &Foo, b: &Bar) {} cbindgen-0.27.0/tests/rust/opaque.toml000064400000000000000000000003141046102023000160070ustar 00000000000000header = """ #if 0 ''' ' #endif #ifdef __cplusplus // These could be added as opaque types I guess. template struct BuildHasherDefault; struct DefaultHasher; #endif #if 0 ' ''' #endif """ cbindgen-0.27.0/tests/rust/pin.rs000064400000000000000000000002411046102023000147530ustar 00000000000000#[repr(C)] struct PinTest { pinned_box: Pin>, pinned_ref: Pin<&mut i32> } #[no_mangle] pub extern "C" fn root(s: Pin<&mut i32>, p: PinTest) {} cbindgen-0.27.0/tests/rust/pin.toml000064400000000000000000000003121046102023000153010ustar 00000000000000header = """ #if 0 ''' ' #endif #ifdef __cplusplus template using Pin = T; template using Box = T*; #endif #if 0 ' ''' #endif """ [export] exclude = [ "Pin", "Box" ] cbindgen-0.27.0/tests/rust/pragma_once.skip_warning_as_error.rs000064400000000000000000000000521046102023000230260ustar 00000000000000#[no_mangle] pub extern "C" fn root() { } cbindgen-0.27.0/tests/rust/pragma_once.skip_warning_as_error.toml000064400000000000000000000000231046102023000233530ustar 00000000000000pragma_once = true cbindgen-0.27.0/tests/rust/prefix.rs000064400000000000000000000005571046102023000154740ustar 00000000000000pub const LEN: i32 = 22; pub type NamedLenArray = [i32; LEN]; pub type ValuedLenArray = [i32; 22]; #[repr(u8)] pub enum AbsoluteFontWeight { Weight(f32), Normal, Bold, } #[no_mangle] pub extern "C" fn root(x: NamedLenArray, y: ValuedLenArray, z: AbsoluteFontWeight) {} #[no_mangle] pub const X: i64 = 22 << 22; #[no_mangle] pub const Y: i64 = X + X; cbindgen-0.27.0/tests/rust/prefix.toml000064400000000000000000000000341046102023000160110ustar 00000000000000[export] prefix = "PREFIX_" cbindgen-0.27.0/tests/rust/prefixed_struct_literal.rs000064400000000000000000000003161046102023000211160ustar 00000000000000#[repr(C)] struct Foo { a: i32, b: u32, } impl Foo { pub const FOO: Foo = Foo{ a: 42, b: 47, }; } pub const BAR: Foo = Foo{ a: 42, b: 1337, }; #[no_mangle] pub extern "C" fn root(x: Foo) { } cbindgen-0.27.0/tests/rust/prefixed_struct_literal.toml000064400000000000000000000000331046102023000214410ustar 00000000000000[export] prefix = "PREFIX" cbindgen-0.27.0/tests/rust/prefixed_struct_literal_deep.rs000064400000000000000000000003471046102023000221170ustar 00000000000000#[repr(C)] struct Foo { a: i32, b: u32, bar: Bar, } #[repr(C)] struct Bar { a: i32, } pub const VAL: Foo = Foo { a: 42, b: 1337, bar: Bar { a: 323 }, }; #[no_mangle] pub extern "C" fn root(x: Foo) {} cbindgen-0.27.0/tests/rust/prefixed_struct_literal_deep.toml000064400000000000000000000000331046102023000224360ustar 00000000000000[export] prefix = "PREFIX" cbindgen-0.27.0/tests/rust/ptrs_as_arrays.rs000064400000000000000000000013061046102023000172240ustar 00000000000000/// cbindgen:ptrs-as-arrays=[[arg;3]] #[no_mangle] pub unsafe extern "C" fn ptr_as_array(n: u32, arg: *const u32, v: *const u64) {} /// cbindgen:ptrs-as-arrays=[[arg;3], [v; 4]] #[no_mangle] pub unsafe extern "C" fn ptr_as_array1(n: u32, arg: *const u32, v: *mut u64) {} /// cbindgen:ptrs-as-arrays=[[n;2], [arg; ], [v;], [k; 3]] #[no_mangle] pub unsafe extern "C" fn ptr_as_array2(n: u32, arg: *mut u32, v: *const u64) {} /// cbindgen:ptrs-as-arrays=[[a;2;3]] #[no_mangle] pub unsafe extern "C" fn ptr_as_array_wrong_syntax(arg: *mut u32, v: *const u32, _: *const u32) {} /// cbindgen:ptrs-as-arrays=[[_;2], [_;3]] #[no_mangle] pub unsafe extern "C" fn ptr_as_array_unnamed(_: *mut u32, _: *const u32) {} cbindgen-0.27.0/tests/rust/raw_ident.rs000064400000000000000000000004251046102023000161450ustar 00000000000000#[repr(u8)] pub enum r#Enum { r#a, r#b, } #[repr(C)] pub struct r#Struct { r#field: r#Enum, } #[no_mangle] pub extern "C" fn r#fn(r#arg: r#Struct) { println!("Hello world"); } pub mod r#mod { #[no_mangle] pub static r#STATIC: r#Enum = r#Enum::r#b; } cbindgen-0.27.0/tests/rust/raw_lines.rs000064400000000000000000000000521046102023000161500ustar 00000000000000#[no_mangle] pub extern "C" fn root() { } cbindgen-0.27.0/tests/rust/raw_lines.toml000064400000000000000000000001331046102023000164770ustar 00000000000000include_guard = "INCLUDE_GUARD_H" no_includes = false after_includes = "#define VERSION 1" cbindgen-0.27.0/tests/rust/rename.rs000064400000000000000000000007011046102023000154350ustar 00000000000000struct A { x: i32, y: f32, } #[repr(C)] struct B { x: i32, y: f32, } union C { x: i32, y: f32, } #[repr(C)] union D { x: i32, y: f32, } #[repr(u8)] enum E { x = 0, y = 1, } type F = A; #[no_mangle] pub static G: i32 = 10; pub const H: i32 = 10; pub const I: isize = 10 as *mut F as isize; #[no_mangle] pub extern "C" fn root( a: *const A, b: B, c: C, d: D, e: E, f: F, ) { } cbindgen-0.27.0/tests/rust/rename.toml000064400000000000000000000000711046102023000157640ustar 00000000000000[export] prefix = "C_" [export.rename] "B" = "AwesomeB" cbindgen-0.27.0/tests/rust/rename_case.rs000064400000000000000000000010031046102023000164240ustar 00000000000000/// cbindgen:rename-all=CamelCase #[no_mangle] pub extern "C" fn test_camel_case(foo_bar: i32) {} /// cbindgen:rename-all=PascalCase #[no_mangle] pub extern "C" fn test_pascal_case(foo_bar: i32) {} /// cbindgen:rename-all=SnakeCase #[no_mangle] pub extern "C" fn test_snake_case(foo_bar: i32) {} /// cbindgen:rename-all=ScreamingSnakeCase #[no_mangle] pub extern "C" fn test_screaming_snake_case(foo_bar: i32) {} /// cbindgen:rename-all=GeckoCase #[no_mangle] pub extern "C" fn test_gecko_case(foo_bar: i32) {} cbindgen-0.27.0/tests/rust/renaming_overrides_prefixing.rs000064400000000000000000000002211046102023000221200ustar 00000000000000struct A { x: i32, y: f32, } #[repr(C)] struct B { x: i32, y: f32, } #[no_mangle] pub extern "C" fn root(a: *const A, b: B) {} cbindgen-0.27.0/tests/rust/renaming_overrides_prefixing.toml000064400000000000000000000001671046102023000224600ustar 00000000000000[export] prefix = "Style" renaming_overrides_prefixing = true [export.rename] "B" = "B" # B should remain unprefixed. cbindgen-0.27.0/tests/rust/reserved.rs000064400000000000000000000007031046102023000160070ustar 00000000000000#[repr(C)] struct A { namespace: i32, float: f32, } /// cbindgen:field-names=[namespace, float] #[repr(C)] struct B(i32, f32); #[repr(C, u8)] enum C { D { namespace: i32, float: f32 }, } #[repr(C, u8)] enum E { Double(f64), Float(f32), } #[repr(C, u8)] enum F { double(f64), float(f32), } #[no_mangle] pub extern "C" fn root( a: A, b: B, c: C, e: E, f: F, namespace: i32, float: f32, ) { } cbindgen-0.27.0/tests/rust/sentinel.rs000064400000000000000000000003531046102023000160120ustar 00000000000000#[repr(u8)] pub enum A { A1, A2, A3, } #[repr(u8)] pub enum B { B1, B2, B3, } #[repr(u8)] pub enum C { C1 { a: u32 }, C2 { b: u32 }, C3, } #[no_mangle] pub extern "C" fn root(a: A, b: B, c: C) {} cbindgen-0.27.0/tests/rust/sentinel.toml000064400000000000000000000000631046102023000163370ustar 00000000000000[enum] add_sentinel = true prefix_with_name = true cbindgen-0.27.0/tests/rust/simplify_option_ptr.rs000064400000000000000000000007501046102023000203030ustar 00000000000000 struct Opaque(); #[repr(C)] struct Foo { x: Option<&Opaque>, y: Option<&mut Opaque>, z: Option ()>, zz: *mut Option ()>, } #[repr(C)] union Bar { x: Option<&Opaque>, y: Option<&mut Opaque>, z: Option ()>, zz: *mut Option ()>, } #[no_mangle] pub extern "C" fn root( a: Option<&Opaque>, b: Option<&mut Opaque>, c: Foo, d: Bar, e: *mut Option<*mut Opaque>, f: extern "C" fn(Option<&Opaque>), ) { } cbindgen-0.27.0/tests/rust/size_types.rs000064400000000000000000000002721046102023000163670ustar 00000000000000type Usize = usize; type Isize = isize; #[repr(usize)] enum UE { UV, } #[repr(isize)] enum IE { IV, } #[no_mangle] pub extern "C" fn root(_: Usize, _: Isize, _: UE, _: IE) {} cbindgen-0.27.0/tests/rust/size_types.toml000064400000000000000000000000271046102023000167140ustar 00000000000000usize_is_size_t = true cbindgen-0.27.0/tests/rust/static.rs000064400000000000000000000003351046102023000154600ustar 00000000000000#[no_mangle] pub static NUMBER: i32 = 10; #[repr(C)] struct Foo { } struct Bar { } #[no_mangle] pub static mut FOO: Foo = Foo { }; #[no_mangle] pub static BAR: Bar = Bar { }; #[no_mangle] pub extern "C" fn root() { } cbindgen-0.27.0/tests/rust/std_lib.rs000064400000000000000000000001561046102023000156120ustar 00000000000000#[no_mangle] pub extern "C" fn root( a: &Vec, b: &Option, c: &Result ) { } cbindgen-0.27.0/tests/rust/struct.rs000064400000000000000000000010301046102023000155060ustar 00000000000000use std::marker::PhantomData; struct Opaque { x: i32, y: f32, } #[repr(C)] struct Normal { x: i32, y: f32, } #[repr(C)] struct NormalWithZST { x: i32, y: f32, z: (), w: PhantomData, v: PhantomPinned, } /// cbindgen:rename-all=GeckoCase #[repr(C)] struct TupleRenamed(i32, f32); /// cbindgen:field-names=[x, y] #[repr(C)] struct TupleNamed(i32, f32); #[no_mangle] pub extern "C" fn root( a: *mut Opaque, b: Normal, c: NormalWithZST, d: TupleRenamed, e: TupleNamed ) { } cbindgen-0.27.0/tests/rust/struct_literal.rs000064400000000000000000000007061046102023000172330ustar 00000000000000#[repr(C)] struct Foo { a: i32, b: u32, } struct Bar { a: i32, b: u32, } impl Foo { pub const FOO: Foo = Foo { a: 42, b: 47, }; pub const FOO2: Self = Foo { a: 42, b: 47, }; pub const FOO3: Self = Self { a: 42, b: 47, }; pub const BAZ: Bar = Bar { a: 42, b: 47, }; } pub const BAR: Foo = Foo { a: 42, b: 1337, }; pub const BAZZ: Bar = Bar { a: 42, b: 1337, }; #[no_mangle] pub extern "C" fn root(x: Foo, bar: Bar) { } cbindgen-0.27.0/tests/rust/struct_literal_order.rs000064400000000000000000000010511046102023000204200ustar 00000000000000#[repr(C)] struct ABC { pub a: f32, pub b: u32, pub c: u32, } #[repr(C)] struct BAC { pub b: u32, pub a: f32, pub c: i32, } impl ABC { pub const abc: ABC = ABC { a: 1.0, b: 2, c: 3 }; pub const bac: ABC = ABC { b: 2, a: 1.0, c: 3 }; pub const cba: ABC = ABC { c: 3, b: 2, a: 1.0 }; } impl BAC { pub const abc: BAC = BAC { a: 2.0, b: 1, c: 3 }; pub const bac: BAC = BAC { b: 1, a: 2.0, c: 3 }; pub const cba: BAC = BAC { c: 3, b: 1, a: 2.0 }; } #[no_mangle] pub extern "C" fn root(a1: ABC, a2: BAC) {} cbindgen-0.27.0/tests/rust/struct_self.rs000064400000000000000000000003501046102023000165230ustar 00000000000000#[repr(C)] pub struct Foo { something: *const i32, phantom: std::marker::PhantomData, } #[repr(C)] pub struct Bar { something: i32, subexpressions: Foo, } #[no_mangle] pub extern "C" fn root(b: Bar) {} cbindgen-0.27.0/tests/rust/style_crash.rs000064400000000000000000000002641046102023000165120ustar 00000000000000pub trait SpecifiedValueInfo { const SUPPORTED_TYPES: u8 = 0; } impl SpecifiedValueInfo for [T] { const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES; } cbindgen-0.27.0/tests/rust/swift_name.rs000064400000000000000000000072441046102023000163330ustar 00000000000000#[export_name="rust_print_hello_world"] pub extern fn say_hello() { println!("Hello, World!"); } #[repr(C)] pub struct SelfTypeTestStruct { times: u8, } impl SelfTypeTestStruct { #[export_name="SelfTypeTestStruct_should_exist_ref"] #[no_mangle] pub extern fn should_exist_ref(&self) { println!("should_exist_ref"); } #[export_name="SelfTypeTestStruct_should_exist_ref_mut"] #[no_mangle] pub extern fn should_exist_ref_mut(&mut self) { println!("should_exist_ref_mut"); } #[export_name="SelfTypeTestStruct_should_not_exist_box"] #[no_mangle] pub extern fn should_not_exist_box(self: Box) { println!("should_not_exist_box"); } #[export_name="SelfTypeTestStruct_should_not_exist_return_box"] #[no_mangle] pub extern fn should_not_exist_box() -> Box { println!("should_not_exist_box"); } #[export_name="SelfTypeTestStruct_should_exist_annotated_self"] #[no_mangle] pub extern fn should_exist_annotated_self(self: Self) { println!("should_exist_annotated_self"); } #[export_name="SelfTypeTestStruct_should_exist_annotated_mut_self"] #[no_mangle] #[allow(unused_mut)] pub extern fn should_exist_annotated_mut_self(mut self: Self) { println!("should_exist_annotated_mut_self"); } #[export_name="SelfTypeTestStruct_should_exist_annotated_by_name"] #[no_mangle] pub extern fn should_exist_annotated_by_name(self: SelfTypeTestStruct) { println!("should_exist_annotated_by_name"); } #[export_name="SelfTypeTestStruct_should_exist_annotated_mut_by_name"] #[no_mangle] #[allow(unused_mut)] pub extern fn should_exist_annotated_mut_by_name(mut self: SelfTypeTestStruct) { println!("should_exist_annotated_mut_by_name"); } #[export_name="SelfTypeTestStruct_should_exist_unannotated"] #[no_mangle] pub extern fn should_exist_unannotated(self) { println!("should_exist_unannotated"); } #[export_name="SelfTypeTestStruct_should_exist_mut_unannotated"] #[no_mangle] #[allow(unused_mut)] pub extern fn should_exist_mut_unannotated(mut self) { println!("should_exist_mut_unannotated"); } } #[no_mangle] #[allow(unused_variables)] pub extern fn free_function_should_exist_ref(test_struct: &SelfTypeTestStruct) { println!("free_function_should_exist_ref"); } #[no_mangle] #[allow(unused_variables)] pub extern fn free_function_should_exist_ref_mut(test_struct: &mut SelfTypeTestStruct) { println!("free_function_should_exist_ref_mut"); } #[no_mangle] pub extern fn unnamed_argument(_: &mut SelfTypeTestStruct) { println!("unnamed_argument"); } #[no_mangle] #[allow(unused_variables)] pub extern fn free_function_should_not_exist_box(boxed: Box) { println!("free_function_should_not_exist_box"); } #[no_mangle] #[allow(unused_variables)] pub extern fn free_function_should_exist_annotated_by_name(test_struct: SelfTypeTestStruct) { println!("free_function_should_exist_annotated_by_name"); } #[no_mangle] #[allow(unused_mut)] #[allow(unused_variables)] pub extern fn free_function_should_exist_annotated_mut_by_name(mut test_struct: SelfTypeTestStruct) { println!("free_function_should_exist_annotated_mut_by_name"); } struct Opaque { times: u8 } #[repr(C)] pub struct PointerToOpaque { ptr: *mut Opaque } impl PointerToOpaque { #[export_name="PointerToOpaque_create"] pub extern fn create(times: u8) -> PointerToOpaque { PointerToOpaque { ptr: Box::into_raw(Box::new(Opaque { times })) } } #[export_name="PointerToOpaque_sayHello"] pub extern fn say_hello(self: PointerToOpaque) { if let Some(nonnull) = std::ptr::NonNull::new(self.ptr) { for _ in 0 .. unsafe { nonnull.as_ref().times } { println!("Hello!") } } } } cbindgen-0.27.0/tests/rust/swift_name.toml000064400000000000000000000001651046102023000166550ustar 00000000000000header = "#define CF_SWIFT_NAME(_name) __attribute__((swift_name(#_name)))" [fn] swift_name_macro = "CF_SWIFT_NAME" cbindgen-0.27.0/tests/rust/transform_op.rs000064400000000000000000000012051046102023000166770ustar 00000000000000#[repr(C)] pub struct Point { pub x: T, pub y: T, } #[repr(u8)] pub enum Foo { Foo { x: i32, y: Point, z: Point, }, Bar(T), Baz(Point), Bazz, } #[repr(C)] pub enum Bar { Bar1 { x: i32, y: Point, z: Point, u: unsafe extern "C" fn(i32) -> i32, }, Bar2(T), Bar3(Point), Bar4, } #[repr(u8)] pub enum Baz { Baz1(Bar), Baz2(Point), Baz3, } #[repr(C, u8)] pub enum Taz { Taz1(Bar), Taz2(Baz), Taz3, } #[no_mangle] pub extern "C" fn foo( foo: *const Foo, bar: *const Bar, baz: *const Baz, taz: *const Taz, ) {} cbindgen-0.27.0/tests/rust/transform_op.toml000064400000000000000000000001611046102023000172260ustar 00000000000000[export] prefix = "Style" [enum] derive_helper_methods = true derive_const_casts = true derive_mut_casts = true cbindgen-0.27.0/tests/rust/transparent.rs000064400000000000000000000042231046102023000165320ustar 00000000000000struct DummyStruct; // Transparent struct tuple wrapping a struct. #[repr(transparent)] struct TransparentComplexWrappingStructTuple(DummyStruct); // Transparent struct tuple wrapping a primitive. #[repr(transparent)] struct TransparentPrimitiveWrappingStructTuple(u32); // Transparent structure wrapping a struct. #[repr(transparent)] struct TransparentComplexWrappingStructure { only_field: DummyStruct } // Transparent structure wrapping a primitive. #[repr(transparent)] struct TransparentPrimitiveWrappingStructure { only_field: u32 } // Transparent struct wrapper with a marker wrapping a struct. #[repr(transparent)] struct TransparentComplexWrapper { only_non_zero_sized_field: DummyStruct, marker: PhantomData, } // Transparent struct wrapper with a marker wrapping a primitive. #[repr(transparent)] struct TransparentPrimitiveWrapper { only_non_zero_sized_field: u32, marker: PhantomData, } // Associated constant declared before struct declaration. impl TransparentPrimitiveWithAssociatedConstants { pub const ZERO: TransparentPrimitiveWithAssociatedConstants = TransparentPrimitiveWithAssociatedConstants { bits: 0 }; } // Transparent structure wrapping a primitive with associated constants. #[repr(transparent)] struct TransparentPrimitiveWithAssociatedConstants { bits: u32 } // Associated constant declared after struct declaration. impl TransparentPrimitiveWithAssociatedConstants { pub const ONE: TransparentPrimitiveWithAssociatedConstants = TransparentPrimitiveWithAssociatedConstants { bits: 1 }; } enum EnumWithAssociatedConstantInImpl { A } impl EnumWithAssociatedConstantInImpl { pub const TEN: TransparentPrimitiveWrappingStructure = TransparentPrimitiveWrappingStructure { only_field: 10 }; } #[no_mangle] pub extern "C" fn root( a: TransparentComplexWrappingStructTuple, b: TransparentPrimitiveWrappingStructTuple, c: TransparentComplexWrappingStructure, d: TransparentPrimitiveWrappingStructure, e: TransparentComplexWrapper, f: TransparentPrimitiveWrapper, g: TransparentPrimitiveWithAssociatedConstants, h: EnumWithAssociatedConstantInImpl, ) { } cbindgen-0.27.0/tests/rust/typedef.rs000064400000000000000000000002141046102023000156250ustar 00000000000000#[repr(C)] struct Foo { x: T, y: U, } type IntFoo = Foo; #[no_mangle] pub extern "C" fn root(a: IntFoo) { } cbindgen-0.27.0/tests/rust/union.rs000064400000000000000000000005011046102023000153140ustar 00000000000000use std::marker::PhantomData; union Opaque { x: i32, y: f32, } #[repr(C)] union Normal { x: i32, y: f32, } #[repr(C)] union NormalWithZST { x: i32, y: f32, z: (), w: PhantomData, } #[no_mangle] pub extern "C" fn root( a: *mut Opaque, b: Normal, c: NormalWithZST ) { } cbindgen-0.27.0/tests/rust/union_self.rs000064400000000000000000000003471046102023000163350ustar 00000000000000#[repr(C)] pub struct Foo { something: *const i32, phantom: std::marker::PhantomData, } #[repr(C)] pub union Bar { something: i32, subexpressions: Foo, } #[no_mangle] pub extern "C" fn root(b: Bar) {} cbindgen-0.27.0/tests/rust/using_namespaces.rs000064400000000000000000000000521046102023000175110ustar 00000000000000#[no_mangle] pub extern "C" fn root() { } cbindgen-0.27.0/tests/rust/using_namespaces.toml000064400000000000000000000000601046102023000200370ustar 00000000000000namespaces = ["root"] using_namespaces = ["std"]cbindgen-0.27.0/tests/rust/va_list.rs000064400000000000000000000015471046102023000156400ustar 00000000000000use std::ffi::VaList; #[no_mangle] pub unsafe extern "C" fn va_list_test(count: int32_t, mut ap: VaList) -> int32_t { ap.arg() } #[no_mangle] pub unsafe extern "C" fn va_list_test2(count: int32_t, mut ap: ...) -> int32_t { ap.arg() } type VaListFnPtr = Option int32_t>; type VaListFnPtr2 = Option int32_t>; #[repr(C)] struct Interface { fn1: T, } #[no_mangle] pub extern "C" fn va_list_fn_ptrs( fn1: Option int32_t>, fn2: Option int32_t>, fn3: VaListFnPtr, fn4: VaListFnPtr2, fn5: Interface int32_t>>, fn6: Interface int32_t>>, ) { } cbindgen-0.27.0/tests/rust/zst.rs000064400000000000000000000003031046102023000150040ustar 00000000000000#[repr(C)] pub struct TraitObject { pub data: *mut (), pub vtable: *mut (), } #[no_mangle] pub extern "C" fn root(ptr: *const (), t: TraitObject) -> *mut () { std::ptr::null_mut() } cbindgen-0.27.0/tests/testing-helpers.h000064400000000000000000000007321046102023000161150ustar 00000000000000#ifndef testing_helpers_h #define testing_helpers_h // This is a helper file to easily add static_asserts to C / C++ tests. #ifndef __cplusplus #include #endif #if defined(CBINDGEN_STYLE_TAG) && !defined(__cplusplus) #define CBINDGEN_STRUCT(name) struct name #define CBINDGEN_UNION(name) union name #define CBINDGEN_ENUM(name) enum name #else #define CBINDGEN_STRUCT(name) name #define CBINDGEN_UNION(name) name #define CBINDGEN_ENUM(name) name #endif #endif cbindgen-0.27.0/tests/tests.rs000064400000000000000000000274261046102023000143500ustar 00000000000000extern crate cbindgen; use cbindgen::*; use std::collections::HashSet; use std::fs::File; use std::io::Read; use std::path::Path; use std::process::Command; use std::{env, fs, str}; use pretty_assertions::assert_eq; // Set automatically by cargo for integration tests static CBINDGEN_PATH: &str = env!("CARGO_BIN_EXE_cbindgen"); fn style_str(style: Style) -> &'static str { match style { Style::Both => "both", Style::Tag => "tag", Style::Type => "type", } } fn run_cbindgen( path: &Path, output: Option<&Path>, language: Language, cpp_compat: bool, style: Option