cxxbridge-macro-1.0.141/.cargo_vcs_info.json0000644000000001430000000000100142540ustar { "git": { "sha1": "926094db8792e43cc0e3f33b422f6319dbd2cfff" }, "path_in_vcs": "macro" }cxxbridge-macro-1.0.141/Cargo.lock0000644000000260060000000000100122350ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "adler2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "anstyle" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "cc" version = "1.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" dependencies = [ "shlex", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clang-ast" version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d710c6f66c1b9fa1418ea7206566c0d86dc2df697a435e0f4385c3e0c2826cb" dependencies = [ "foldhash", "serde", ] [[package]] name = "clap" version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" dependencies = [ "anstyle", "clap_lex", "strsim", ] [[package]] name = "clap_lex" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "codespan-reporting" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ "termcolor", "unicode-width", ] [[package]] name = "crc32fast" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "cxx" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc49567e08c72902f4cbc7242ee8d874ec9cbe97fbabf77b4e0e1f447513e13a" dependencies = [ "cc", "cxxbridge-cmd", "cxxbridge-flags", "cxxbridge-macro 1.0.140", "foldhash", "link-cplusplus", ] [[package]] name = "cxxbridge-cmd" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4315c4ce8d23c26d87f2f83698725fd5718d8e6ace4a9093da2664d23294d372" dependencies = [ "clap", "codespan-reporting", "proc-macro2", "quote", "syn", ] [[package]] name = "cxxbridge-flags" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55d69deb3a92f610a60ecc524a72c7374b6dc822f8fb7bb4e5d9473f10530c4" [[package]] name = "cxxbridge-macro" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bee7a1d9b5091462002c2b8de2a4ed0f0fde011d503cc272633f66075bd5141" dependencies = [ "proc-macro2", "quote", "rustversion", "syn", ] [[package]] name = "cxxbridge-macro" version = "1.0.141" dependencies = [ "clang-ast", "cxx", "flate2", "memmap", "proc-macro2", "quote", "rustversion", "serde", "serde_derive", "serde_json", "syn", ] [[package]] name = "flate2" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", ] [[package]] name = "foldhash" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[package]] name = "itoa" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "libc" version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "link-cplusplus" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" dependencies = [ "cc", ] [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" dependencies = [ "libc", "winapi", ] [[package]] name = "miniz_oxide" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" dependencies = [ "adler2", ] [[package]] name = "proc-macro2" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "rustversion" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "serde" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[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.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "termcolor" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "unicode-ident" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "unicode-width" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[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" cxxbridge-macro-1.0.141/Cargo.toml0000644000000036460000000000100122650ustar # 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 = "2021" rust-version = "1.73" name = "cxxbridge-macro" version = "1.0.141" authors = ["David Tolnay "] build = false exclude = [ "build.rs", "README.md", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Implementation detail of the `cxx` crate." homepage = "https://cxx.rs" readme = "README.md" keywords = ["ffi"] categories = ["development-tools::ffi"] license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/cxx" [package.metadata.docs.rs] rustdoc-args = ["--generate-link-to-definition"] targets = ["x86_64-unknown-linux-gnu"] [features] experimental-async-fn = [] experimental-enum-variants-from-header = [ "clang-ast", "flate2", "memmap", "serde", "serde_derive", "serde_json", ] [lib] name = "cxxbridge_macro" path = "src/lib.rs" proc-macro = true [dependencies.clang-ast] version = "0.1.18" optional = true [dependencies.flate2] version = "1.0.26" optional = true [dependencies.memmap] version = "0.7" optional = true [dependencies.proc-macro2] version = "1.0.74" [dependencies.quote] version = "1.0.35" [dependencies.rustversion] version = "1" [dependencies.serde] version = "1.0.166" optional = true [dependencies.serde_derive] version = "1.0.166" optional = true [dependencies.serde_json] version = "1.0.100" optional = true [dependencies.syn] version = "2.0.46" features = ["full"] [dev-dependencies.cxx] version = "1.0" cxxbridge-macro-1.0.141/Cargo.toml.orig000064400000000000000000000024001046102023000157310ustar 00000000000000[package] name = "cxxbridge-macro" version = "1.0.141" authors = ["David Tolnay "] categories = ["development-tools::ffi"] description = "Implementation detail of the `cxx` crate." edition = "2021" exclude = ["build.rs", "README.md"] homepage = "https://cxx.rs" keywords = ["ffi"] license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/cxx" rust-version = "1.73" [lib] proc-macro = true [features] # incomplete features that are not covered by a compatibility guarantee: experimental-async-fn = [] experimental-enum-variants-from-header = ["clang-ast", "flate2", "memmap", "serde", "serde_derive", "serde_json"] [dependencies] proc-macro2 = "1.0.74" quote = "1.0.35" rustversion = "1" syn = { version = "2.0.46", features = ["full"] } # optional dependencies: clang-ast = { version = "0.1.18", optional = true } flate2 = { version = "1.0.26", optional = true } memmap = { version = "0.7", optional = true } serde = { version = "1.0.166", optional = true } serde_derive = { version = "1.0.166", optional = true } serde_json = { version = "1.0.100", optional = true } [dev-dependencies] cxx = { version = "1.0", path = ".." } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = ["--generate-link-to-definition"] cxxbridge-macro-1.0.141/LICENSE-APACHE000064400000000000000000000227731046102023000150050ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS cxxbridge-macro-1.0.141/LICENSE-MIT000064400000000000000000000017771046102023000145160ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cxxbridge-macro-1.0.141/README.md000064400000000000000000000003121046102023000143210ustar 00000000000000This directory contains CXX's Rust code generator, which is a procedural macro. Users won't depend on this crate directly. Instead they'll invoke its macro through the reexport in the main `cxx` crate. cxxbridge-macro-1.0.141/src/clang.rs000064400000000000000000000024651046102023000152760ustar 00000000000000use serde_derive::{Deserialize, Serialize}; pub(crate) type Node = clang_ast::Node; #[derive(Deserialize, Serialize)] pub(crate) enum Clang { NamespaceDecl(NamespaceDecl), EnumDecl(EnumDecl), EnumConstantDecl(EnumConstantDecl), ImplicitCastExpr, ConstantExpr(ConstantExpr), Unknown, } #[derive(Deserialize, Serialize)] pub(crate) struct NamespaceDecl { #[serde(skip_serializing_if = "Option::is_none")] pub name: Option>, } #[derive(Deserialize, Serialize)] pub(crate) struct EnumDecl { #[serde(skip_serializing_if = "Option::is_none")] pub name: Option>, #[serde( rename = "fixedUnderlyingType", skip_serializing_if = "Option::is_none" )] pub fixed_underlying_type: Option, } #[derive(Deserialize, Serialize)] pub(crate) struct EnumConstantDecl { pub name: Box, } #[derive(Deserialize, Serialize)] pub(crate) struct ConstantExpr { pub value: Box, } #[derive(Deserialize, Serialize)] pub(crate) struct Type { #[serde(rename = "qualType")] pub qual_type: Box, #[serde(rename = "desugaredQualType", skip_serializing_if = "Option::is_none")] pub desugared_qual_type: Option>, } #[cfg(all(test, target_pointer_width = "64"))] const _: [(); core::mem::size_of::()] = [(); 88]; cxxbridge-macro-1.0.141/src/derive.rs000064400000000000000000000246121046102023000154660ustar 00000000000000use crate::syntax::{derive, Enum, Struct}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, quote_spanned, ToTokens}; pub(crate) use crate::syntax::derive::*; pub(crate) fn expand_struct( strct: &Struct, actual_derives: &mut Option, ) -> TokenStream { let mut expanded = TokenStream::new(); let mut traits = Vec::new(); for derive in &strct.derives { let span = derive.span; match derive.what { Trait::Copy => expanded.extend(struct_copy(strct, span)), Trait::Clone => expanded.extend(struct_clone(strct, span)), Trait::Debug => expanded.extend(struct_debug(strct, span)), Trait::Default => expanded.extend(struct_default(strct, span)), Trait::Eq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq)), Trait::ExternType => unreachable!(), Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)), Trait::Ord => expanded.extend(struct_ord(strct, span)), Trait::PartialEq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq)), Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)), Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)), Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)), } } if traits.is_empty() { *actual_derives = None; } else { *actual_derives = Some(quote!(#[derive(#(#traits),*)])); } expanded } pub(crate) fn expand_enum(enm: &Enum, actual_derives: &mut Option) -> TokenStream { let mut expanded = TokenStream::new(); let mut traits = Vec::new(); let mut has_copy = false; let mut has_clone = false; let mut has_eq = false; let mut has_partial_eq = false; for derive in &enm.derives { let span = derive.span; match derive.what { Trait::Copy => { expanded.extend(enum_copy(enm, span)); has_copy = true; } Trait::Clone => { expanded.extend(enum_clone(enm, span)); has_clone = true; } Trait::Debug => expanded.extend(enum_debug(enm, span)), Trait::Default => unreachable!(), Trait::Eq => { traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq)); has_eq = true; } Trait::ExternType => unreachable!(), Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)), Trait::Ord => expanded.extend(enum_ord(enm, span)), Trait::PartialEq => { traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq)); has_partial_eq = true; } Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)), Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)), Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)), } } let span = enm.name.rust.span(); if !has_copy { expanded.extend(enum_copy(enm, span)); } if !has_clone { expanded.extend(enum_clone(enm, span)); } if !has_eq { // Required to be derived in order for the enum's "variants" to be // usable in patterns. traits.push(quote!(::cxx::core::cmp::Eq)); } if !has_partial_eq { traits.push(quote!(::cxx::core::cmp::PartialEq)); } *actual_derives = Some(quote!(#[derive(#(#traits),*)])); expanded } fn struct_copy(strct: &Struct, span: Span) -> TokenStream { let ident = &strct.name.rust; let generics = &strct.generics; quote_spanned! {span=> #[automatically_derived] impl #generics ::cxx::core::marker::Copy for #ident #generics {} } } fn struct_clone(strct: &Struct, span: Span) -> TokenStream { let ident = &strct.name.rust; let generics = &strct.generics; let body = if derive::contains(&strct.derives, Trait::Copy) { quote!(*self) } else { let fields = strct.fields.iter().map(|field| &field.name.rust); let values = strct.fields.iter().map(|field| { let ident = &field.name.rust; let ty = field.ty.to_token_stream(); let span = ty.into_iter().last().unwrap().span(); quote_spanned!(span=> &self.#ident) }); quote_spanned!(span=> #ident { #(#fields: ::cxx::core::clone::Clone::clone(#values),)* }) }; quote_spanned! {span=> #[automatically_derived] #[allow(clippy::expl_impl_clone_on_copy)] impl #generics ::cxx::core::clone::Clone for #ident #generics { fn clone(&self) -> Self { #body } } } } fn struct_debug(strct: &Struct, span: Span) -> TokenStream { let ident = &strct.name.rust; let generics = &strct.generics; let struct_name = ident.to_string(); let fields = strct.fields.iter().map(|field| &field.name.rust); let field_names = fields.clone().map(Ident::to_string); quote_spanned! {span=> #[automatically_derived] impl #generics ::cxx::core::fmt::Debug for #ident #generics { fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result { formatter.debug_struct(#struct_name) #(.field(#field_names, &self.#fields))* .finish() } } } } fn struct_default(strct: &Struct, span: Span) -> TokenStream { let ident = &strct.name.rust; let generics = &strct.generics; let fields = strct.fields.iter().map(|field| &field.name.rust); quote_spanned! {span=> #[automatically_derived] #[allow(clippy::derivable_impls)] // different spans than the derived impl impl #generics ::cxx::core::default::Default for #ident #generics { fn default() -> Self { #ident { #( #fields: ::cxx::core::default::Default::default(), )* } } } } } fn struct_ord(strct: &Struct, span: Span) -> TokenStream { let ident = &strct.name.rust; let generics = &strct.generics; let fields = strct.fields.iter().map(|field| &field.name.rust); quote_spanned! {span=> #[automatically_derived] impl #generics ::cxx::core::cmp::Ord for #ident #generics { fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering { #( match ::cxx::core::cmp::Ord::cmp(&self.#fields, &other.#fields) { ::cxx::core::cmp::Ordering::Equal => {} ordering => return ordering, } )* ::cxx::core::cmp::Ordering::Equal } } } } fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream { let ident = &strct.name.rust; let generics = &strct.generics; let body = if derive::contains(&strct.derives, Trait::Ord) { quote! { ::cxx::core::option::Option::Some(::cxx::core::cmp::Ord::cmp(self, other)) } } else { let fields = strct.fields.iter().map(|field| &field.name.rust); quote! { #( match ::cxx::core::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) { ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal) => {} ordering => return ordering, } )* ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal) } }; quote_spanned! {span=> #[automatically_derived] impl #generics ::cxx::core::cmp::PartialOrd for #ident #generics { #[allow(clippy::non_canonical_partial_ord_impl)] #[allow(renamed_and_removed_lints, clippy::incorrect_partial_ord_impl_on_ord_type)] // Rust 1.73 and older fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> { #body } } } } fn enum_copy(enm: &Enum, span: Span) -> TokenStream { let ident = &enm.name.rust; quote_spanned! {span=> #[automatically_derived] impl ::cxx::core::marker::Copy for #ident {} } } fn enum_clone(enm: &Enum, span: Span) -> TokenStream { let ident = &enm.name.rust; quote_spanned! {span=> #[automatically_derived] #[allow(clippy::expl_impl_clone_on_copy)] impl ::cxx::core::clone::Clone for #ident { fn clone(&self) -> Self { *self } } } } fn enum_debug(enm: &Enum, span: Span) -> TokenStream { let ident = &enm.name.rust; let variants = enm.variants.iter().map(|variant| { let variant = &variant.name.rust; let name = variant.to_string(); quote_spanned! {span=> #ident::#variant => formatter.write_str(#name), } }); let fallback = format!("{}({{}})", ident); quote_spanned! {span=> #[automatically_derived] impl ::cxx::core::fmt::Debug for #ident { fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result { match *self { #(#variants)* _ => ::cxx::core::write!(formatter, #fallback, self.repr), } } } } } fn enum_ord(enm: &Enum, span: Span) -> TokenStream { let ident = &enm.name.rust; quote_spanned! {span=> #[automatically_derived] impl ::cxx::core::cmp::Ord for #ident { fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering { ::cxx::core::cmp::Ord::cmp(&self.repr, &other.repr) } } } } fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream { let ident = &enm.name.rust; quote_spanned! {span=> #[automatically_derived] impl ::cxx::core::cmp::PartialOrd for #ident { #[allow(clippy::non_canonical_partial_ord_impl)] #[allow(renamed_and_removed_lints, clippy::incorrect_partial_ord_impl_on_ord_type)] // Rust 1.73 and older fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> { ::cxx::core::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr) } } } } cxxbridge-macro-1.0.141/src/expand.rs000064400000000000000000002323041046102023000154660ustar 00000000000000use crate::syntax::atom::Atom::*; use crate::syntax::attrs::{self, OtherAttrs}; use crate::syntax::cfg::CfgExpr; use crate::syntax::file::Module; use crate::syntax::instantiate::{ImplKey, NamedImplKey}; use crate::syntax::qualified::QualifiedName; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ self, check, mangle, Api, Doc, Enum, ExternFn, ExternType, Impl, Lifetimes, Pair, Signature, Struct, Trait, Type, TypeAlias, Types, }; use crate::type_id::Crate; use crate::{derive, generics}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; use std::mem; use syn::{parse_quote, punctuated, Generics, Lifetime, Result, Token}; pub(crate) fn bridge(mut ffi: Module) -> Result { let ref mut errors = Errors::new(); let mut cfg = CfgExpr::Unconditional; let mut doc = Doc::new(); let attrs = attrs::parse( errors, mem::take(&mut ffi.attrs), attrs::Parser { cfg: Some(&mut cfg), doc: Some(&mut doc), ..Default::default() }, ); let content = mem::take(&mut ffi.content); let trusted = ffi.unsafety.is_some(); let namespace = &ffi.namespace; let ref mut apis = syntax::parse_items(errors, content, trusted, namespace); #[cfg(feature = "experimental-enum-variants-from-header")] crate::load::load(errors, apis); let ref types = Types::collect(errors, apis); errors.propagate()?; let generator = check::Generator::Macro; check::typecheck(errors, apis, types, generator); errors.propagate()?; Ok(expand(ffi, doc, attrs, apis, types)) } fn expand(ffi: Module, doc: Doc, attrs: OtherAttrs, apis: &[Api], types: &Types) -> TokenStream { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); let mut forbid = TokenStream::new(); for api in apis { if let Api::RustType(ety) = api { expanded.extend(expand_rust_type_import(ety)); hidden.extend(expand_rust_type_assert_unpin(ety, types)); } } for api in apis { match api { Api::Include(_) | Api::Impl(_) => {} Api::Struct(strct) => { expanded.extend(expand_struct(strct)); hidden.extend(expand_struct_operators(strct)); forbid.extend(expand_struct_forbid_drop(strct)); } Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => { let ident = &ety.name.rust; if !types.structs.contains_key(ident) && !types.enums.contains_key(ident) { expanded.extend(expand_cxx_type(ety)); hidden.extend(expand_cxx_type_assert_pinned(ety, types)); } } Api::CxxFunction(efn) => { expanded.extend(expand_cxx_function_shim(efn, types)); } Api::RustType(ety) => { expanded.extend(expand_rust_type_impl(ety)); hidden.extend(expand_rust_type_layout(ety, types)); } Api::RustFunction(efn) => hidden.extend(expand_rust_function_shim(efn, types)), Api::TypeAlias(alias) => { expanded.extend(expand_type_alias(alias)); hidden.extend(expand_type_alias_verify(alias, types)); } } } for (impl_key, &explicit_impl) in &types.impls { match *impl_key { ImplKey::RustBox(ident) => { hidden.extend(expand_rust_box(ident, types, explicit_impl)); } ImplKey::RustVec(ident) => { hidden.extend(expand_rust_vec(ident, types, explicit_impl)); } ImplKey::UniquePtr(ident) => { expanded.extend(expand_unique_ptr(ident, types, explicit_impl)); } ImplKey::SharedPtr(ident) => { expanded.extend(expand_shared_ptr(ident, types, explicit_impl)); } ImplKey::WeakPtr(ident) => { expanded.extend(expand_weak_ptr(ident, types, explicit_impl)); } ImplKey::CxxVector(ident) => { expanded.extend(expand_cxx_vector(ident, explicit_impl, types)); } } } if !forbid.is_empty() { hidden.extend(expand_forbid(forbid)); } // Work around https://github.com/rust-lang/rust/issues/67851. if !hidden.is_empty() { expanded.extend(quote! { #[doc(hidden)] const _: () = { #hidden }; }); } let vis = &ffi.vis; let mod_token = &ffi.mod_token; let ident = &ffi.ident; let span = ffi.brace_token.span; let expanded = quote_spanned!(span=> {#expanded}); quote! { #doc #attrs #[deny(improper_ctypes, improper_ctypes_definitions)] #[allow(clippy::unknown_clippy_lints)] #[allow( non_camel_case_types, non_snake_case, clippy::extra_unused_type_parameters, clippy::items_after_statements, clippy::no_effect_underscore_binding, clippy::ptr_as_ptr, clippy::ref_as_ptr, clippy::upper_case_acronyms, clippy::use_self, )] #vis #mod_token #ident #expanded } } fn expand_struct(strct: &Struct) -> TokenStream { let ident = &strct.name.rust; let doc = &strct.doc; let attrs = &strct.attrs; let generics = &strct.generics; let type_id = type_id(&strct.name); let fields = strct.fields.iter().map(|field| { let doc = &field.doc; let attrs = &field.attrs; // This span on the pub makes "private type in public interface" errors // appear in the right place. let vis = field.visibility; quote!(#doc #attrs #vis #field) }); let mut derives = None; let derived_traits = derive::expand_struct(strct, &mut derives); let span = ident.span(); let visibility = strct.visibility; let struct_token = strct.struct_token; let struct_def = quote_spanned! {span=> #visibility #struct_token #ident #generics { #(#fields,)* } }; quote! { #doc #derives #attrs #[repr(C)] #struct_def #[automatically_derived] unsafe impl #generics ::cxx::ExternType for #ident #generics { #[allow(unused_attributes)] // incorrect lint #[doc(hidden)] type Id = #type_id; type Kind = ::cxx::kind::Trivial; } #derived_traits } } fn expand_struct_operators(strct: &Struct) -> TokenStream { let ident = &strct.name.rust; let generics = &strct.generics; let mut operators = TokenStream::new(); for derive in &strct.derives { let span = derive.span; match derive.what { Trait::PartialEq => { let link_name = mangle::operator(&strct.name, "eq"); let local_name = format_ident!("__operator_eq_{}", strct.name.rust); let prevent_unwind_label = format!("::{} as PartialEq>::eq", strct.name.rust); operators.extend(quote_spanned! {span=> #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_name)] extern "C" fn #local_name #generics(lhs: &#ident #generics, rhs: &#ident #generics) -> bool { let __fn = concat!("<", module_path!(), #prevent_unwind_label); ::cxx::private::prevent_unwind(__fn, || *lhs == *rhs) } }); if !derive::contains(&strct.derives, Trait::Eq) { let link_name = mangle::operator(&strct.name, "ne"); let local_name = format_ident!("__operator_ne_{}", strct.name.rust); let prevent_unwind_label = format!("::{} as PartialEq>::ne", strct.name.rust); operators.extend(quote_spanned! {span=> #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_name)] extern "C" fn #local_name #generics(lhs: &#ident #generics, rhs: &#ident #generics) -> bool { let __fn = concat!("<", module_path!(), #prevent_unwind_label); ::cxx::private::prevent_unwind(__fn, || *lhs != *rhs) } }); } } Trait::PartialOrd => { let link_name = mangle::operator(&strct.name, "lt"); let local_name = format_ident!("__operator_lt_{}", strct.name.rust); let prevent_unwind_label = format!("::{} as PartialOrd>::lt", strct.name.rust); operators.extend(quote_spanned! {span=> #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_name)] extern "C" fn #local_name #generics(lhs: &#ident #generics, rhs: &#ident #generics) -> bool { let __fn = concat!("<", module_path!(), #prevent_unwind_label); ::cxx::private::prevent_unwind(__fn, || *lhs < *rhs) } }); let link_name = mangle::operator(&strct.name, "le"); let local_name = format_ident!("__operator_le_{}", strct.name.rust); let prevent_unwind_label = format!("::{} as PartialOrd>::le", strct.name.rust); operators.extend(quote_spanned! {span=> #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_name)] extern "C" fn #local_name #generics(lhs: &#ident #generics, rhs: &#ident #generics) -> bool { let __fn = concat!("<", module_path!(), #prevent_unwind_label); ::cxx::private::prevent_unwind(__fn, || *lhs <= *rhs) } }); if !derive::contains(&strct.derives, Trait::Ord) { let link_name = mangle::operator(&strct.name, "gt"); let local_name = format_ident!("__operator_gt_{}", strct.name.rust); let prevent_unwind_label = format!("::{} as PartialOrd>::gt", strct.name.rust); operators.extend(quote_spanned! {span=> #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_name)] extern "C" fn #local_name #generics(lhs: &#ident #generics, rhs: &#ident #generics) -> bool { let __fn = concat!("<", module_path!(), #prevent_unwind_label); ::cxx::private::prevent_unwind(__fn, || *lhs > *rhs) } }); let link_name = mangle::operator(&strct.name, "ge"); let local_name = format_ident!("__operator_ge_{}", strct.name.rust); let prevent_unwind_label = format!("::{} as PartialOrd>::ge", strct.name.rust); operators.extend(quote_spanned! {span=> #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_name)] extern "C" fn #local_name #generics(lhs: &#ident #generics, rhs: &#ident #generics) -> bool { let __fn = concat!("<", module_path!(), #prevent_unwind_label); ::cxx::private::prevent_unwind(__fn, || *lhs >= *rhs) } }); } } Trait::Hash => { let link_name = mangle::operator(&strct.name, "hash"); let local_name = format_ident!("__operator_hash_{}", strct.name.rust); let prevent_unwind_label = format!("::{} as Hash>::hash", strct.name.rust); operators.extend(quote_spanned! {span=> #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_name)] #[allow(clippy::cast_possible_truncation)] extern "C" fn #local_name #generics(this: &#ident #generics) -> usize { let __fn = concat!("<", module_path!(), #prevent_unwind_label); ::cxx::private::prevent_unwind(__fn, || ::cxx::private::hash(this)) } }); } _ => {} } } operators } fn expand_struct_forbid_drop(strct: &Struct) -> TokenStream { let ident = &strct.name.rust; let generics = &strct.generics; let span = ident.span(); let impl_token = Token![impl](strct.visibility.span); quote_spanned! {span=> #[automatically_derived] #impl_token #generics self::Drop for super::#ident #generics {} } } fn expand_enum(enm: &Enum) -> TokenStream { let ident = &enm.name.rust; let doc = &enm.doc; let attrs = &enm.attrs; let repr = &enm.repr; let type_id = type_id(&enm.name); let variants = enm.variants.iter().map(|variant| { let doc = &variant.doc; let attrs = &variant.attrs; let variant_ident = &variant.name.rust; let discriminant = &variant.discriminant; let span = variant_ident.span(); Some(quote_spanned! {span=> #doc #attrs #[allow(dead_code)] pub const #variant_ident: Self = #ident { repr: #discriminant }; }) }); let mut derives = None; let derived_traits = derive::expand_enum(enm, &mut derives); let span = ident.span(); let visibility = enm.visibility; let struct_token = Token![struct](enm.enum_token.span); let enum_repr = quote! { #[allow(missing_docs)] pub repr: #repr, }; let enum_def = quote_spanned! {span=> #visibility #struct_token #ident { #enum_repr } }; quote! { #doc #derives #attrs #[repr(transparent)] #enum_def #[automatically_derived] #[allow(non_upper_case_globals)] impl #ident { #(#variants)* } #[automatically_derived] unsafe impl ::cxx::ExternType for #ident { #[allow(unused_attributes)] // incorrect lint #[doc(hidden)] type Id = #type_id; type Kind = ::cxx::kind::Trivial; } #derived_traits } } fn expand_cxx_type(ety: &ExternType) -> TokenStream { let ident = &ety.name.rust; let doc = &ety.doc; let attrs = &ety.attrs; let generics = &ety.generics; let type_id = type_id(&ety.name); let lifetime_fields = ety.generics.lifetimes.iter().map(|lifetime| { let field = format_ident!("_lifetime_{}", lifetime.ident); quote!(#field: ::cxx::core::marker::PhantomData<&#lifetime ()>) }); let repr_fields = quote! { _private: ::cxx::private::Opaque, #(#lifetime_fields,)* }; let span = ident.span(); let visibility = &ety.visibility; let struct_token = Token![struct](ety.type_token.span); let extern_type_def = quote_spanned! {span=> #visibility #struct_token #ident #generics { #repr_fields } }; quote! { #doc #attrs #[repr(C)] #extern_type_def #[automatically_derived] unsafe impl #generics ::cxx::ExternType for #ident #generics { #[allow(unused_attributes)] // incorrect lint #[doc(hidden)] type Id = #type_id; type Kind = ::cxx::kind::Opaque; } } } fn expand_cxx_type_assert_pinned(ety: &ExternType, types: &Types) -> TokenStream { let ident = &ety.name.rust; let infer = Token![_](ident.span()); let resolve = types.resolve(ident); let lifetimes = resolve.generics.to_underscore_lifetimes(); quote! { let _: fn() = { // Derived from https://github.com/nvzqz/static-assertions-rs. trait __AmbiguousIfImpl { fn infer() {} } #[automatically_derived] impl __AmbiguousIfImpl<()> for T where T: ?::cxx::core::marker::Sized {} #[allow(dead_code)] struct __Invalid; #[automatically_derived] impl __AmbiguousIfImpl<__Invalid> for T where T: ?::cxx::core::marker::Sized + ::cxx::core::marker::Unpin, {} // If there is only one specialized trait impl, type inference with // `_` can be resolved and this can compile. Fails to compile if // user has added a manual Unpin impl for their opaque C++ type as // then `__AmbiguousIfImpl<__Invalid>` also exists. <#ident #lifetimes as __AmbiguousIfImpl<#infer>>::infer }; } } fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream { let generics = &efn.generics; let receiver = efn.receiver.iter().map(|receiver| { let receiver_type = receiver.ty(); quote!(_: #receiver_type) }); let args = efn.args.iter().map(|arg| { let var = &arg.name.rust; let colon = arg.colon_token; let ty = expand_extern_type(&arg.ty, types, true); if arg.ty == RustString { quote!(#var #colon *const #ty) } else if let Type::RustVec(_) = arg.ty { quote!(#var #colon *const #ty) } else if let Type::Fn(_) = arg.ty { quote!(#var #colon ::cxx::private::FatFunction) } else if types.needs_indirect_abi(&arg.ty) { quote!(#var #colon *mut #ty) } else { quote!(#var #colon #ty) } }); let all_args = receiver.chain(args); let ret = if efn.throws { quote!(-> ::cxx::private::Result) } else { expand_extern_return_type(&efn.ret, types, true) }; let mut outparam = None; if indirect_return(efn, types) { let ret = expand_extern_type(efn.ret.as_ref().unwrap(), types, true); outparam = Some(quote!(__return: *mut #ret)); } let link_name = mangle::extern_fn(efn, types); let local_name = format_ident!("__{}", efn.name.rust); quote! { #[link_name = #link_name] fn #local_name #generics(#(#all_args,)* #outparam) #ret; } } fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { let doc = &efn.doc; let attrs = &efn.attrs; let decl = expand_cxx_function_decl(efn, types); let receiver = efn.receiver.iter().map(|receiver| { let var = receiver.var; if receiver.pinned { let colon = receiver.colon_token; let ty = receiver.ty_self(); quote!(#var #colon #ty) } else { let ampersand = receiver.ampersand; let lifetime = &receiver.lifetime; let mutability = receiver.mutability; quote!(#ampersand #lifetime #mutability #var) } }); let args = efn.args.iter().map(|arg| quote!(#arg)); let all_args = receiver.chain(args); let ret = if efn.throws { let ok = match &efn.ret { Some(ret) => quote!(#ret), None => quote!(()), }; quote!(-> ::cxx::core::result::Result<#ok, ::cxx::Exception>) } else { expand_return_type(&efn.ret) }; let indirect_return = indirect_return(efn, types); let receiver_var = efn .receiver .iter() .map(|receiver| receiver.var.to_token_stream()); let arg_vars = efn.args.iter().map(|arg| { let var = &arg.name.rust; let span = var.span(); match &arg.ty { Type::Ident(ident) if ident.rust == RustString => { quote_spanned!(span=> #var.as_mut_ptr() as *const ::cxx::private::RustString) } Type::RustBox(ty) => { if types.is_considered_improper_ctype(&ty.inner) { quote_spanned!(span=> ::cxx::alloc::boxed::Box::into_raw(#var).cast()) } else { quote_spanned!(span=> ::cxx::alloc::boxed::Box::into_raw(#var)) } } Type::UniquePtr(ty) => { if types.is_considered_improper_ctype(&ty.inner) { quote_spanned!(span=> ::cxx::UniquePtr::into_raw(#var).cast()) } else { quote_spanned!(span=> ::cxx::UniquePtr::into_raw(#var)) } } Type::RustVec(_) => quote_spanned!(span=> #var.as_mut_ptr() as *const ::cxx::private::RustVec<_>), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident.rust == RustString => match ty.mutable { false => quote_spanned!(span=> ::cxx::private::RustString::from_ref(#var)), true => quote_spanned!(span=> ::cxx::private::RustString::from_mut(#var)), }, Type::RustVec(vec) if vec.inner == RustString => match ty.mutable { false => quote_spanned!(span=> ::cxx::private::RustVec::from_ref_vec_string(#var)), true => quote_spanned!(span=> ::cxx::private::RustVec::from_mut_vec_string(#var)), }, Type::RustVec(_) => match ty.mutable { false => quote_spanned!(span=> ::cxx::private::RustVec::from_ref(#var)), true => quote_spanned!(span=> ::cxx::private::RustVec::from_mut(#var)), }, inner if types.is_considered_improper_ctype(inner) => { let var = match ty.pinned { false => quote!(#var), true => quote_spanned!(span=> ::cxx::core::pin::Pin::into_inner_unchecked(#var)), }; match ty.mutable { false => { quote_spanned!(span=> #var as *const #inner as *const ::cxx::core::ffi::c_void) } true => quote_spanned!(span=> #var as *mut #inner as *mut ::cxx::core::ffi::c_void), } } _ => quote!(#var), }, Type::Ptr(ty) => { if types.is_considered_improper_ctype(&ty.inner) { quote_spanned!(span=> #var.cast()) } else { quote!(#var) } } Type::Str(_) => quote_spanned!(span=> ::cxx::private::RustStr::from(#var)), Type::SliceRef(ty) => match ty.mutable { false => quote_spanned!(span=> ::cxx::private::RustSlice::from_ref(#var)), true => quote_spanned!(span=> ::cxx::private::RustSlice::from_mut(#var)), }, ty if types.needs_indirect_abi(ty) => quote_spanned!(span=> #var.as_mut_ptr()), _ => quote!(#var), } }); let vars = receiver_var.chain(arg_vars); let trampolines = efn .args .iter() .filter_map(|arg| { if let Type::Fn(f) = &arg.ty { let var = &arg.name; Some(expand_function_pointer_trampoline(efn, var, f, types)) } else { None } }) .collect::(); let mut setup = efn .args .iter() .filter(|arg| types.needs_indirect_abi(&arg.ty)) .map(|arg| { let var = &arg.name.rust; let span = var.span(); // These are arguments for which C++ has taken ownership of the data // behind the mut reference it received. quote_spanned! {span=> let mut #var = ::cxx::core::mem::MaybeUninit::new(#var); } }) .collect::(); let local_name = format_ident!("__{}", efn.name.rust); let span = efn.semi_token.span; let call = if indirect_return { let ret = expand_extern_type(efn.ret.as_ref().unwrap(), types, true); setup.extend(quote_spanned! {span=> let mut __return = ::cxx::core::mem::MaybeUninit::<#ret>::uninit(); }); setup.extend(if efn.throws { quote_spanned! {span=> #local_name(#(#vars,)* __return.as_mut_ptr()).exception()?; } } else { quote_spanned! {span=> #local_name(#(#vars,)* __return.as_mut_ptr()); } }); quote_spanned!(span=> __return.assume_init()) } else if efn.throws { quote_spanned! {span=> #local_name(#(#vars),*).exception() } } else { quote_spanned! {span=> #local_name(#(#vars),*) } }; let mut expr; if efn.throws && efn.sig.ret.is_none() { expr = call; } else { expr = match &efn.ret { None => call, Some(ret) => match ret { Type::Ident(ident) if ident.rust == RustString => { quote_spanned!(span=> #call.into_string()) } Type::RustBox(ty) => { if types.is_considered_improper_ctype(&ty.inner) { quote_spanned!(span=> ::cxx::alloc::boxed::Box::from_raw(#call.cast())) } else { quote_spanned!(span=> ::cxx::alloc::boxed::Box::from_raw(#call)) } } Type::RustVec(vec) => { if vec.inner == RustString { quote_spanned!(span=> #call.into_vec_string()) } else { quote_spanned!(span=> #call.into_vec()) } } Type::UniquePtr(ty) => { if types.is_considered_improper_ctype(&ty.inner) { quote_spanned!(span=> ::cxx::UniquePtr::from_raw(#call.cast())) } else { quote_spanned!(span=> ::cxx::UniquePtr::from_raw(#call)) } } Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident.rust == RustString => match ty.mutable { false => quote_spanned!(span=> #call.as_string()), true => quote_spanned!(span=> #call.as_mut_string()), }, Type::RustVec(vec) if vec.inner == RustString => match ty.mutable { false => quote_spanned!(span=> #call.as_vec_string()), true => quote_spanned!(span=> #call.as_mut_vec_string()), }, Type::RustVec(_) => match ty.mutable { false => quote_spanned!(span=> #call.as_vec()), true => quote_spanned!(span=> #call.as_mut_vec()), }, inner if types.is_considered_improper_ctype(inner) => { let mutability = ty.mutability; let deref_mut = quote_spanned!(span=> &#mutability *#call.cast()); match ty.pinned { false => deref_mut, true => { quote_spanned!(span=> ::cxx::core::pin::Pin::new_unchecked(#deref_mut)) } } } _ => call, }, Type::Ptr(ty) => { if types.is_considered_improper_ctype(&ty.inner) { quote_spanned!(span=> #call.cast()) } else { call } } Type::Str(_) => quote_spanned!(span=> #call.as_str()), Type::SliceRef(slice) => { let inner = &slice.inner; match slice.mutable { false => quote_spanned!(span=> #call.as_slice::<#inner>()), true => quote_spanned!(span=> #call.as_mut_slice::<#inner>()), } } _ => call, }, }; if efn.throws { expr = quote_spanned!(span=> ::cxx::core::result::Result::Ok(#expr)); } } let dispatch = quote_spanned!(span=> unsafe { #setup #expr }); let visibility = efn.visibility; let unsafety = &efn.sig.unsafety; let fn_token = efn.sig.fn_token; let ident = &efn.name.rust; let generics = &efn.generics; let arg_list = quote_spanned!(efn.sig.paren_token.span=> (#(#all_args,)*)); let fn_body = quote_spanned!(span=> { #UnsafeExtern extern "C" { #decl } #trampolines #dispatch }); match &efn.receiver { None => { quote! { #doc #attrs #visibility #unsafety #fn_token #ident #generics #arg_list #ret #fn_body } } Some(receiver) => { let elided_generics; let receiver_ident = &receiver.ty.rust; let resolve = types.resolve(&receiver.ty); let receiver_generics = if receiver.ty.generics.lt_token.is_some() { &receiver.ty.generics } else { elided_generics = Lifetimes { lt_token: resolve.generics.lt_token, lifetimes: resolve .generics .lifetimes .pairs() .map(|pair| { let lifetime = Lifetime::new("'_", pair.value().apostrophe); let punct = pair.punct().map(|&&comma| comma); punctuated::Pair::new(lifetime, punct) }) .collect(), gt_token: resolve.generics.gt_token, }; &elided_generics }; quote_spanned! {ident.span()=> #[automatically_derived] impl #generics #receiver_ident #receiver_generics { #doc #attrs #visibility #unsafety #fn_token #ident #arg_list #ret #fn_body } } } } } fn expand_function_pointer_trampoline( efn: &ExternFn, var: &Pair, sig: &Signature, types: &Types, ) -> TokenStream { let c_trampoline = mangle::c_trampoline(efn, var, types); let r_trampoline = mangle::r_trampoline(efn, var, types); let local_name = parse_quote!(__); let prevent_unwind_label = format!("::{}::{}", efn.name.rust, var.rust); let body_span = efn.semi_token.span; let shim = expand_rust_function_shim_impl( sig, types, &r_trampoline, local_name, prevent_unwind_label, None, Some(&efn.generics), &efn.attrs, body_span, ); let var = &var.rust; quote! { let #var = ::cxx::private::FatFunction { trampoline: { #UnsafeExtern extern "C" { #[link_name = #c_trampoline] fn trampoline(); } #shim trampoline as usize as *const ::cxx::core::ffi::c_void }, ptr: #var as usize as *const ::cxx::core::ffi::c_void, }; } } fn expand_rust_type_import(ety: &ExternType) -> TokenStream { let ident = &ety.name.rust; let span = ident.span(); quote_spanned! {span=> use super::#ident; } } fn expand_rust_type_impl(ety: &ExternType) -> TokenStream { let ident = &ety.name.rust; let generics = &ety.generics; let span = ident.span(); let unsafe_impl = quote_spanned!(ety.type_token.span=> unsafe impl); let mut impls = quote_spanned! {span=> #[automatically_derived] #[doc(hidden)] #unsafe_impl #generics ::cxx::private::RustType for #ident #generics {} }; for derive in &ety.derives { if derive.what == Trait::ExternType { let type_id = type_id(&ety.name); let span = derive.span; impls.extend(quote_spanned! {span=> #[automatically_derived] unsafe impl #generics ::cxx::ExternType for #ident #generics { #[allow(unused_attributes)] // incorrect lint #[doc(hidden)] type Id = #type_id; type Kind = ::cxx::kind::Opaque; } }); } } impls } fn expand_rust_type_assert_unpin(ety: &ExternType, types: &Types) -> TokenStream { let ident = &ety.name.rust; let begin_span = Token![::](ety.type_token.span); let unpin = quote_spanned! {ety.semi_token.span=> #begin_span cxx::core::marker::Unpin }; let resolve = types.resolve(ident); let lifetimes = resolve.generics.to_underscore_lifetimes(); quote_spanned! {ident.span()=> let _ = { fn __AssertUnpin() {} __AssertUnpin::<#ident #lifetimes> }; } } fn expand_rust_type_layout(ety: &ExternType, types: &Types) -> TokenStream { // Rustc will render as follows if not sized: // // type TheirType; // -----^^^^^^^^^- // | | // | doesn't have a size known at compile-time // required by this bound in `__AssertSized` let ident = &ety.name.rust; let begin_span = Token![::](ety.type_token.span); let sized = quote_spanned! {ety.semi_token.span=> #begin_span cxx::core::marker::Sized }; let link_sizeof = mangle::operator(&ety.name, "sizeof"); let link_alignof = mangle::operator(&ety.name, "alignof"); let local_sizeof = format_ident!("__sizeof_{}", ety.name.rust); let local_alignof = format_ident!("__alignof_{}", ety.name.rust); let resolve = types.resolve(ident); let lifetimes = resolve.generics.to_underscore_lifetimes(); quote_spanned! {ident.span()=> { #[doc(hidden)] #[allow(clippy::needless_maybe_sized)] fn __AssertSized() -> ::cxx::core::alloc::Layout { ::cxx::core::alloc::Layout::new::() } #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_sizeof)] extern "C" fn #local_sizeof() -> usize { __AssertSized::<#ident #lifetimes>().size() } #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_alignof)] extern "C" fn #local_alignof() -> usize { __AssertSized::<#ident #lifetimes>().align() } } } } fn expand_forbid(impls: TokenStream) -> TokenStream { quote! { mod forbid { pub trait Drop {} #[automatically_derived] #[allow(drop_bounds)] impl self::Drop for T {} #impls } } } fn expand_rust_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { let link_name = mangle::extern_fn(efn, types); let local_name = match &efn.receiver { None => format_ident!("__{}", efn.name.rust), Some(receiver) => format_ident!("__{}__{}", receiver.ty.rust, efn.name.rust), }; let prevent_unwind_label = match &efn.receiver { None => format!("::{}", efn.name.rust), Some(receiver) => format!("::{}::{}", receiver.ty.rust, efn.name.rust), }; let invoke = Some(&efn.name.rust); let body_span = efn.semi_token.span; expand_rust_function_shim_impl( efn, types, &link_name, local_name, prevent_unwind_label, invoke, None, &efn.attrs, body_span, ) } fn expand_rust_function_shim_impl( sig: &Signature, types: &Types, link_name: &Symbol, local_name: Ident, prevent_unwind_label: String, invoke: Option<&Ident>, outer_generics: Option<&Generics>, attrs: &OtherAttrs, body_span: Span, ) -> TokenStream { let generics = outer_generics.unwrap_or(&sig.generics); let receiver_var = sig .receiver .as_ref() .map(|receiver| quote_spanned!(receiver.var.span=> __self)); let receiver = sig.receiver.as_ref().map(|receiver| { let colon = receiver.colon_token; let receiver_type = receiver.ty(); quote!(#receiver_var #colon #receiver_type) }); let args = sig.args.iter().map(|arg| { let var = &arg.name.rust; let colon = arg.colon_token; let ty = expand_extern_type(&arg.ty, types, false); if types.needs_indirect_abi(&arg.ty) { quote!(#var #colon *mut #ty) } else { quote!(#var #colon #ty) } }); let all_args = receiver.into_iter().chain(args); let mut requires_unsafe = false; let arg_vars = sig.args.iter().map(|arg| { let var = &arg.name.rust; let span = var.span(); match &arg.ty { Type::Ident(i) if i.rust == RustString => { requires_unsafe = true; quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_string())) } Type::RustBox(_) => { requires_unsafe = true; quote_spanned!(span=> ::cxx::alloc::boxed::Box::from_raw(#var)) } Type::RustVec(vec) => { requires_unsafe = true; if vec.inner == RustString { quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_vec_string())) } else { quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_vec())) } } Type::UniquePtr(_) => { requires_unsafe = true; quote_spanned!(span=> ::cxx::UniquePtr::from_raw(#var)) } Type::Ref(ty) => match &ty.inner { Type::Ident(i) if i.rust == RustString => match ty.mutable { false => quote_spanned!(span=> #var.as_string()), true => quote_spanned!(span=> #var.as_mut_string()), }, Type::RustVec(vec) if vec.inner == RustString => match ty.mutable { false => quote_spanned!(span=> #var.as_vec_string()), true => quote_spanned!(span=> #var.as_mut_vec_string()), }, Type::RustVec(_) => match ty.mutable { false => quote_spanned!(span=> #var.as_vec()), true => quote_spanned!(span=> #var.as_mut_vec()), }, _ => quote!(#var), }, Type::Str(_) => { requires_unsafe = true; quote_spanned!(span=> #var.as_str()) } Type::SliceRef(slice) => { requires_unsafe = true; let inner = &slice.inner; match slice.mutable { false => quote_spanned!(span=> #var.as_slice::<#inner>()), true => quote_spanned!(span=> #var.as_mut_slice::<#inner>()), } } ty if types.needs_indirect_abi(ty) => { requires_unsafe = true; quote_spanned!(span=> ::cxx::core::ptr::read(#var)) } _ => quote!(#var), } }); let vars: Vec<_> = receiver_var.into_iter().chain(arg_vars).collect(); let wrap_super = invoke.map(|invoke| expand_rust_function_shim_super(sig, &local_name, invoke)); let mut requires_closure; let mut call = match invoke { Some(_) => { requires_closure = false; quote!(#local_name) } None => { requires_closure = true; requires_unsafe = true; quote!(::cxx::core::mem::transmute::<*const (), #sig>(__extern)) } }; requires_closure |= !vars.is_empty(); call.extend(quote! { (#(#vars),*) }); let span = body_span; let conversion = sig.ret.as_ref().and_then(|ret| match ret { Type::Ident(ident) if ident.rust == RustString => { Some(quote_spanned!(span=> ::cxx::private::RustString::from)) } Type::RustBox(_) => Some(quote_spanned!(span=> ::cxx::alloc::boxed::Box::into_raw)), Type::RustVec(vec) => { if vec.inner == RustString { Some(quote_spanned!(span=> ::cxx::private::RustVec::from_vec_string)) } else { Some(quote_spanned!(span=> ::cxx::private::RustVec::from)) } } Type::UniquePtr(_) => Some(quote_spanned!(span=> ::cxx::UniquePtr::into_raw)), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident.rust == RustString => match ty.mutable { false => Some(quote_spanned!(span=> ::cxx::private::RustString::from_ref)), true => Some(quote_spanned!(span=> ::cxx::private::RustString::from_mut)), }, Type::RustVec(vec) if vec.inner == RustString => match ty.mutable { false => Some(quote_spanned!(span=> ::cxx::private::RustVec::from_ref_vec_string)), true => Some(quote_spanned!(span=> ::cxx::private::RustVec::from_mut_vec_string)), }, Type::RustVec(_) => match ty.mutable { false => Some(quote_spanned!(span=> ::cxx::private::RustVec::from_ref)), true => Some(quote_spanned!(span=> ::cxx::private::RustVec::from_mut)), }, _ => None, }, Type::Str(_) => Some(quote_spanned!(span=> ::cxx::private::RustStr::from)), Type::SliceRef(ty) => match ty.mutable { false => Some(quote_spanned!(span=> ::cxx::private::RustSlice::from_ref)), true => Some(quote_spanned!(span=> ::cxx::private::RustSlice::from_mut)), }, _ => None, }); let mut expr = match conversion { None => call, Some(conversion) if !sig.throws => { requires_closure = true; quote_spanned!(span=> #conversion(#call)) } Some(conversion) => { requires_closure = true; quote_spanned!(span=> ::cxx::core::result::Result::map(#call, #conversion)) } }; let mut outparam = None; let indirect_return = indirect_return(sig, types); if indirect_return { let ret = expand_extern_type(sig.ret.as_ref().unwrap(), types, false); outparam = Some(quote_spanned!(span=> __return: *mut #ret,)); } if sig.throws { let out = match sig.ret { Some(_) => quote_spanned!(span=> __return), None => quote_spanned!(span=> &mut ()), }; requires_closure = true; requires_unsafe = true; expr = quote_spanned!(span=> ::cxx::private::r#try(#out, #expr)); } else if indirect_return { requires_closure = true; requires_unsafe = true; expr = quote_spanned!(span=> ::cxx::core::ptr::write(__return, #expr)); } if requires_unsafe { expr = quote_spanned!(span=> unsafe { #expr }); } let closure = if requires_closure { quote_spanned!(span=> move || #expr) } else { quote!(#local_name) }; expr = quote_spanned!(span=> ::cxx::private::prevent_unwind(__fn, #closure)); let ret = if sig.throws { quote!(-> ::cxx::private::Result) } else { expand_extern_return_type(&sig.ret, types, false) }; let pointer = match invoke { None => Some(quote_spanned!(span=> __extern: *const ())), Some(_) => None, }; quote_spanned! {span=> #attrs #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_name)] unsafe extern "C" fn #local_name #generics(#(#all_args,)* #outparam #pointer) #ret { let __fn = ::cxx::private::concat!(::cxx::private::module_path!(), #prevent_unwind_label); #wrap_super #expr } } } // A wrapper like `fn f(x: Arg) { super::f(x) }` just to ensure we have the // accurate unsafety declaration and no problematic elided lifetimes. fn expand_rust_function_shim_super( sig: &Signature, local_name: &Ident, invoke: &Ident, ) -> TokenStream { let unsafety = sig.unsafety; let generics = &sig.generics; let receiver_var = sig .receiver .as_ref() .map(|receiver| Ident::new("__self", receiver.var.span)); let receiver = sig.receiver.iter().map(|receiver| { let receiver_type = receiver.ty(); quote!(#receiver_var: #receiver_type) }); let args = sig.args.iter().map(|arg| quote!(#arg)); let all_args = receiver.chain(args); let ret = if let Some((result, _langle, rangle)) = sig.throws_tokens { let ok = match &sig.ret { Some(ret) => quote!(#ret), None => quote!(()), }; // Set spans that result in the `Result<...>` written by the user being // highlighted as the cause if their error type has no Display impl. let result_begin = quote_spanned!(result.span=> ::cxx::core::result::Result<#ok, impl); let result_end = if rustversion::cfg!(since(1.82)) { // https://blog.rust-lang.org/2024/10/17/Rust-1.82.0.html#precise-capturing-use-syntax quote_spanned!(rangle.span=> ::cxx::core::fmt::Display + use<>>) } else { quote_spanned!(rangle.span=> ::cxx::core::fmt::Display>) }; quote!(-> #result_begin #result_end) } else { expand_return_type(&sig.ret) }; let arg_vars = sig.args.iter().map(|arg| &arg.name.rust); let vars = receiver_var.iter().chain(arg_vars); let span = invoke.span(); let call = match &sig.receiver { None => quote_spanned!(span=> super::#invoke), Some(receiver) => { let receiver_type = &receiver.ty.rust; quote_spanned!(span=> #receiver_type::#invoke) } }; let mut body = quote_spanned!(span=> #call(#(#vars,)*)); let mut allow_unused_unsafe = None; if unsafety.is_some() { body = quote_spanned!(span=> unsafe { #body }); allow_unused_unsafe = Some(quote_spanned!(span=> #[allow(unused_unsafe)])); } quote_spanned! {span=> #allow_unused_unsafe #unsafety fn #local_name #generics(#(#all_args,)*) #ret { #body } } } fn expand_type_alias(alias: &TypeAlias) -> TokenStream { let doc = &alias.doc; let attrs = &alias.attrs; let visibility = alias.visibility; let type_token = alias.type_token; let ident = &alias.name.rust; let generics = &alias.generics; let eq_token = alias.eq_token; let ty = &alias.ty; let semi_token = alias.semi_token; quote! { #doc #attrs #visibility #type_token #ident #generics #eq_token #ty #semi_token } } fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream { let ident = &alias.name.rust; let type_id = type_id(&alias.name); let begin_span = alias.type_token.span; let end_span = alias.semi_token.span; let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<); let end = quote_spanned!(end_span=> >); let mut verify = quote! { const _: fn() = #begin #ident, #type_id #end; }; if types.required_trivial.contains_key(&alias.name.rust) { let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<); verify.extend(quote! { const _: fn() = #begin #ident, ::cxx::kind::Trivial #end; }); } verify } fn type_id(name: &Pair) -> TokenStream { let namespace_segments = name.namespace.iter(); let mut segments = Vec::with_capacity(namespace_segments.len() + 1); segments.extend(namespace_segments.cloned()); segments.push(Ident::new(&name.cxx.to_string(), Span::call_site())); let qualified = QualifiedName { segments }; crate::type_id::expand(Crate::Cxx, qualified) } fn expand_rust_box(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl>) -> TokenStream { let ident = key.rust; let resolve = types.resolve(ident); let link_prefix = format!("cxxbridge1$box${}$", resolve.name.to_symbol()); let link_alloc = format!("{}alloc", link_prefix); let link_dealloc = format!("{}dealloc", link_prefix); let link_drop = format!("{}drop", link_prefix); let local_prefix = format_ident!("{}__box_", ident); let local_alloc = format_ident!("{}alloc", local_prefix); let local_dealloc = format_ident!("{}dealloc", local_prefix); let local_drop = format_ident!("{}drop", local_prefix); let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve); let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span); let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span.join()); let unsafe_token = format_ident!("unsafe", span = begin_span); let prevent_unwind_drop_label = format!("::{} as Drop>::drop", ident); quote_spanned! {end_span=> #[automatically_derived] #[doc(hidden)] #unsafe_token impl #impl_generics ::cxx::private::ImplBox for #ident #ty_generics {} #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_alloc)] unsafe extern "C" fn #local_alloc #impl_generics() -> *mut ::cxx::core::mem::MaybeUninit<#ident #ty_generics> { // No prevent_unwind: the global allocator is not allowed to panic. // // TODO: replace with Box::new_uninit when stable. // https://doc.rust-lang.org/std/boxed/struct.Box.html#method.new_uninit // https://github.com/rust-lang/rust/issues/63291 ::cxx::alloc::boxed::Box::into_raw(::cxx::alloc::boxed::Box::new(::cxx::core::mem::MaybeUninit::uninit())) } #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_dealloc)] unsafe extern "C" fn #local_dealloc #impl_generics(ptr: *mut ::cxx::core::mem::MaybeUninit<#ident #ty_generics>) { // No prevent_unwind: the global allocator is not allowed to panic. let _ = unsafe { ::cxx::alloc::boxed::Box::from_raw(ptr) }; } #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_drop)] unsafe extern "C" fn #local_drop #impl_generics(this: *mut ::cxx::alloc::boxed::Box<#ident #ty_generics>) { let __fn = concat!("<", module_path!(), #prevent_unwind_drop_label); ::cxx::private::prevent_unwind(__fn, || unsafe { ::cxx::core::ptr::drop_in_place(this) }); } } } fn expand_rust_vec(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl>) -> TokenStream { let elem = key.rust; let resolve = types.resolve(elem); let link_prefix = format!("cxxbridge1$rust_vec${}$", resolve.name.to_symbol()); let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); let link_capacity = format!("{}capacity", link_prefix); let link_data = format!("{}data", link_prefix); let link_reserve_total = format!("{}reserve_total", link_prefix); let link_set_len = format!("{}set_len", link_prefix); let link_truncate = format!("{}truncate", link_prefix); let local_prefix = format_ident!("{}__vec_", elem); let local_new = format_ident!("{}new", local_prefix); let local_drop = format_ident!("{}drop", local_prefix); let local_len = format_ident!("{}len", local_prefix); let local_capacity = format_ident!("{}capacity", local_prefix); let local_data = format_ident!("{}data", local_prefix); let local_reserve_total = format_ident!("{}reserve_total", local_prefix); let local_set_len = format_ident!("{}set_len", local_prefix); let local_truncate = format_ident!("{}truncate", local_prefix); let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve); let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span); let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span.join()); let unsafe_token = format_ident!("unsafe", span = begin_span); let prevent_unwind_drop_label = format!("::{} as Drop>::drop", elem); quote_spanned! {end_span=> #[automatically_derived] #[doc(hidden)] #unsafe_token impl #impl_generics ::cxx::private::ImplVec for #elem #ty_generics {} #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_new)] unsafe extern "C" fn #local_new #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>) { // No prevent_unwind: cannot panic. unsafe { ::cxx::core::ptr::write(this, ::cxx::private::RustVec::new()); } } #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_drop)] unsafe extern "C" fn #local_drop #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>) { let __fn = concat!("<", module_path!(), #prevent_unwind_drop_label); ::cxx::private::prevent_unwind( __fn, || unsafe { ::cxx::core::ptr::drop_in_place(this) }, ); } #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_len)] unsafe extern "C" fn #local_len #impl_generics(this: *const ::cxx::private::RustVec<#elem #ty_generics>) -> usize { // No prevent_unwind: cannot panic. unsafe { (*this).len() } } #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_capacity)] unsafe extern "C" fn #local_capacity #impl_generics(this: *const ::cxx::private::RustVec<#elem #ty_generics>) -> usize { // No prevent_unwind: cannot panic. unsafe { (*this).capacity() } } #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_data)] unsafe extern "C" fn #local_data #impl_generics(this: *const ::cxx::private::RustVec<#elem #ty_generics>) -> *const #elem #ty_generics { // No prevent_unwind: cannot panic. unsafe { (*this).as_ptr() } } #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_reserve_total)] unsafe extern "C" fn #local_reserve_total #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>, new_cap: usize) { // No prevent_unwind: the global allocator is not allowed to panic. unsafe { (*this).reserve_total(new_cap); } } #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_set_len)] unsafe extern "C" fn #local_set_len #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>, len: usize) { // No prevent_unwind: cannot panic. unsafe { (*this).set_len(len); } } #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_truncate)] unsafe extern "C" fn #local_truncate #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>, len: usize) { let __fn = concat!("<", module_path!(), #prevent_unwind_drop_label); ::cxx::private::prevent_unwind( __fn, || unsafe { (*this).truncate(len) }, ); } } } fn expand_unique_ptr( key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl>, ) -> TokenStream { let ident = key.rust; let name = ident.to_string(); let resolve = types.resolve(ident); let prefix = format!("cxxbridge1$unique_ptr${}$", resolve.name.to_symbol()); let link_null = format!("{}null", prefix); let link_uninit = format!("{}uninit", prefix); let link_raw = format!("{}raw", prefix); let link_get = format!("{}get", prefix); let link_release = format!("{}release", prefix); let link_drop = format!("{}drop", prefix); let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve); let can_construct_from_value = types.is_maybe_trivial(ident); let new_method = if can_construct_from_value { Some(quote! { fn __new(value: Self) -> ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void> { #UnsafeExtern extern "C" { #[link_name = #link_uninit] fn __uninit(this: *mut ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *mut ::cxx::core::ffi::c_void; } let mut repr = ::cxx::core::mem::MaybeUninit::uninit(); unsafe { __uninit(&mut repr).cast::<#ident #ty_generics>().write(value); } repr } }) } else { None }; let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span); let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span.join()); let unsafe_token = format_ident!("unsafe", span = begin_span); quote_spanned! {end_span=> #[automatically_derived] #unsafe_token impl #impl_generics ::cxx::private::UniquePtrTarget for #ident #ty_generics { fn __typename(f: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result { f.write_str(#name) } fn __null() -> ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void> { #UnsafeExtern extern "C" { #[link_name = #link_null] fn __null(this: *mut ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>); } let mut repr = ::cxx::core::mem::MaybeUninit::uninit(); unsafe { __null(&mut repr); } repr } #new_method unsafe fn __raw(raw: *mut Self) -> ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void> { #UnsafeExtern extern "C" { #[link_name = #link_raw] fn __raw(this: *mut ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>, raw: *mut ::cxx::core::ffi::c_void); } let mut repr = ::cxx::core::mem::MaybeUninit::uninit(); unsafe { __raw(&mut repr, raw.cast()); } repr } unsafe fn __get(repr: ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *const Self { #UnsafeExtern extern "C" { #[link_name = #link_get] fn __get(this: *const ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *const ::cxx::core::ffi::c_void; } unsafe { __get(&repr).cast() } } unsafe fn __release(mut repr: ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *mut Self { #UnsafeExtern extern "C" { #[link_name = #link_release] fn __release(this: *mut ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *mut ::cxx::core::ffi::c_void; } unsafe { __release(&mut repr).cast() } } unsafe fn __drop(mut repr: ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) { #UnsafeExtern extern "C" { #[link_name = #link_drop] fn __drop(this: *mut ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>); } unsafe { __drop(&mut repr); } } } } } fn expand_shared_ptr( key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl>, ) -> TokenStream { let ident = key.rust; let name = ident.to_string(); let resolve = types.resolve(ident); let prefix = format!("cxxbridge1$shared_ptr${}$", resolve.name.to_symbol()); let link_null = format!("{}null", prefix); let link_uninit = format!("{}uninit", prefix); let link_clone = format!("{}clone", prefix); let link_get = format!("{}get", prefix); let link_drop = format!("{}drop", prefix); let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve); let can_construct_from_value = types.is_maybe_trivial(ident); let new_method = if can_construct_from_value { Some(quote! { unsafe fn __new(value: Self, new: *mut ::cxx::core::ffi::c_void) { #UnsafeExtern extern "C" { #[link_name = #link_uninit] fn __uninit(new: *mut ::cxx::core::ffi::c_void) -> *mut ::cxx::core::ffi::c_void; } unsafe { __uninit(new).cast::<#ident #ty_generics>().write(value); } } }) } else { None }; let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span); let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span.join()); let unsafe_token = format_ident!("unsafe", span = begin_span); quote_spanned! {end_span=> #[automatically_derived] #unsafe_token impl #impl_generics ::cxx::private::SharedPtrTarget for #ident #ty_generics { fn __typename(f: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result { f.write_str(#name) } unsafe fn __null(new: *mut ::cxx::core::ffi::c_void) { #UnsafeExtern extern "C" { #[link_name = #link_null] fn __null(new: *mut ::cxx::core::ffi::c_void); } unsafe { __null(new); } } #new_method unsafe fn __clone(this: *const ::cxx::core::ffi::c_void, new: *mut ::cxx::core::ffi::c_void) { #UnsafeExtern extern "C" { #[link_name = #link_clone] fn __clone(this: *const ::cxx::core::ffi::c_void, new: *mut ::cxx::core::ffi::c_void); } unsafe { __clone(this, new); } } unsafe fn __get(this: *const ::cxx::core::ffi::c_void) -> *const Self { #UnsafeExtern extern "C" { #[link_name = #link_get] fn __get(this: *const ::cxx::core::ffi::c_void) -> *const ::cxx::core::ffi::c_void; } unsafe { __get(this).cast() } } unsafe fn __drop(this: *mut ::cxx::core::ffi::c_void) { #UnsafeExtern extern "C" { #[link_name = #link_drop] fn __drop(this: *mut ::cxx::core::ffi::c_void); } unsafe { __drop(this); } } } } } fn expand_weak_ptr(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl>) -> TokenStream { let ident = key.rust; let name = ident.to_string(); let resolve = types.resolve(ident); let prefix = format!("cxxbridge1$weak_ptr${}$", resolve.name.to_symbol()); let link_null = format!("{}null", prefix); let link_clone = format!("{}clone", prefix); let link_downgrade = format!("{}downgrade", prefix); let link_upgrade = format!("{}upgrade", prefix); let link_drop = format!("{}drop", prefix); let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve); let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span); let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span.join()); let unsafe_token = format_ident!("unsafe", span = begin_span); quote_spanned! {end_span=> #[automatically_derived] #unsafe_token impl #impl_generics ::cxx::private::WeakPtrTarget for #ident #ty_generics { fn __typename(f: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result { f.write_str(#name) } unsafe fn __null(new: *mut ::cxx::core::ffi::c_void) { #UnsafeExtern extern "C" { #[link_name = #link_null] fn __null(new: *mut ::cxx::core::ffi::c_void); } unsafe { __null(new); } } unsafe fn __clone(this: *const ::cxx::core::ffi::c_void, new: *mut ::cxx::core::ffi::c_void) { #UnsafeExtern extern "C" { #[link_name = #link_clone] fn __clone(this: *const ::cxx::core::ffi::c_void, new: *mut ::cxx::core::ffi::c_void); } unsafe { __clone(this, new); } } unsafe fn __downgrade(shared: *const ::cxx::core::ffi::c_void, weak: *mut ::cxx::core::ffi::c_void) { #UnsafeExtern extern "C" { #[link_name = #link_downgrade] fn __downgrade(shared: *const ::cxx::core::ffi::c_void, weak: *mut ::cxx::core::ffi::c_void); } unsafe { __downgrade(shared, weak); } } unsafe fn __upgrade(weak: *const ::cxx::core::ffi::c_void, shared: *mut ::cxx::core::ffi::c_void) { #UnsafeExtern extern "C" { #[link_name = #link_upgrade] fn __upgrade(weak: *const ::cxx::core::ffi::c_void, shared: *mut ::cxx::core::ffi::c_void); } unsafe { __upgrade(weak, shared); } } unsafe fn __drop(this: *mut ::cxx::core::ffi::c_void) { #UnsafeExtern extern "C" { #[link_name = #link_drop] fn __drop(this: *mut ::cxx::core::ffi::c_void); } unsafe { __drop(this); } } } } } fn expand_cxx_vector( key: NamedImplKey, explicit_impl: Option<&Impl>, types: &Types, ) -> TokenStream { let elem = key.rust; let name = elem.to_string(); let resolve = types.resolve(elem); let prefix = format!("cxxbridge1$std$vector${}$", resolve.name.to_symbol()); let link_new = format!("{}new", prefix); let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); let link_push_back = format!("{}push_back", prefix); let link_pop_back = format!("{}pop_back", prefix); let unique_ptr_prefix = format!( "cxxbridge1$unique_ptr$std$vector${}$", resolve.name.to_symbol(), ); let link_unique_ptr_null = format!("{}null", unique_ptr_prefix); let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix); let link_unique_ptr_get = format!("{}get", unique_ptr_prefix); let link_unique_ptr_release = format!("{}release", unique_ptr_prefix); let link_unique_ptr_drop = format!("{}drop", unique_ptr_prefix); let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve); let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span); let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span.join()); let unsafe_token = format_ident!("unsafe", span = begin_span); let can_pass_element_by_value = types.is_maybe_trivial(elem); let by_value_methods = if can_pass_element_by_value { Some(quote_spanned! {end_span=> unsafe fn __push_back( this: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector>, value: &mut ::cxx::core::mem::ManuallyDrop, ) { #UnsafeExtern extern "C" { #[link_name = #link_push_back] fn __push_back #impl_generics( this: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector<#elem #ty_generics>>, value: *mut ::cxx::core::ffi::c_void, ); } unsafe { __push_back( this, value as *mut ::cxx::core::mem::ManuallyDrop as *mut ::cxx::core::ffi::c_void, ); } } unsafe fn __pop_back( this: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector>, out: &mut ::cxx::core::mem::MaybeUninit, ) { #UnsafeExtern extern "C" { #[link_name = #link_pop_back] fn __pop_back #impl_generics( this: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector<#elem #ty_generics>>, out: *mut ::cxx::core::ffi::c_void, ); } unsafe { __pop_back( this, out as *mut ::cxx::core::mem::MaybeUninit as *mut ::cxx::core::ffi::c_void, ); } } }) } else { None }; quote_spanned! {end_span=> #[automatically_derived] #unsafe_token impl #impl_generics ::cxx::private::VectorElement for #elem #ty_generics { fn __typename(f: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result { f.write_str(#name) } fn __vector_new() -> *mut ::cxx::CxxVector { #UnsafeExtern extern "C" { #[link_name = #link_new] fn __vector_new #impl_generics() -> *mut ::cxx::CxxVector<#elem #ty_generics>; } unsafe { __vector_new() } } fn __vector_size(v: &::cxx::CxxVector) -> usize { #UnsafeExtern extern "C" { #[link_name = #link_size] fn __vector_size #impl_generics(_: &::cxx::CxxVector<#elem #ty_generics>) -> usize; } unsafe { __vector_size(v) } } unsafe fn __get_unchecked(v: *mut ::cxx::CxxVector, pos: usize) -> *mut Self { #UnsafeExtern extern "C" { #[link_name = #link_get_unchecked] fn __get_unchecked #impl_generics( v: *mut ::cxx::CxxVector<#elem #ty_generics>, pos: usize, ) -> *mut ::cxx::core::ffi::c_void; } unsafe { __get_unchecked(v, pos) as *mut Self } } #by_value_methods fn __unique_ptr_null() -> ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void> { #UnsafeExtern extern "C" { #[link_name = #link_unique_ptr_null] fn __unique_ptr_null(this: *mut ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>); } let mut repr = ::cxx::core::mem::MaybeUninit::uninit(); unsafe { __unique_ptr_null(&mut repr); } repr } unsafe fn __unique_ptr_raw(raw: *mut ::cxx::CxxVector) -> ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void> { #UnsafeExtern extern "C" { #[link_name = #link_unique_ptr_raw] fn __unique_ptr_raw #impl_generics(this: *mut ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>, raw: *mut ::cxx::CxxVector<#elem #ty_generics>); } let mut repr = ::cxx::core::mem::MaybeUninit::uninit(); unsafe { __unique_ptr_raw(&mut repr, raw); } repr } unsafe fn __unique_ptr_get(repr: ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *const ::cxx::CxxVector { #UnsafeExtern extern "C" { #[link_name = #link_unique_ptr_get] fn __unique_ptr_get #impl_generics(this: *const ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *const ::cxx::CxxVector<#elem #ty_generics>; } unsafe { __unique_ptr_get(&repr) } } unsafe fn __unique_ptr_release(mut repr: ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *mut ::cxx::CxxVector { #UnsafeExtern extern "C" { #[link_name = #link_unique_ptr_release] fn __unique_ptr_release #impl_generics(this: *mut ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *mut ::cxx::CxxVector<#elem #ty_generics>; } unsafe { __unique_ptr_release(&mut repr) } } unsafe fn __unique_ptr_drop(mut repr: ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) { #UnsafeExtern extern "C" { #[link_name = #link_unique_ptr_drop] fn __unique_ptr_drop(this: *mut ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>); } unsafe { __unique_ptr_drop(&mut repr); } } } } } fn expand_return_type(ret: &Option) -> TokenStream { match ret { Some(ret) => quote!(-> #ret), None => TokenStream::new(), } } fn indirect_return(sig: &Signature, types: &Types) -> bool { sig.ret .as_ref() .is_some_and(|ret| sig.throws || types.needs_indirect_abi(ret)) } fn expand_extern_type(ty: &Type, types: &Types, proper: bool) -> TokenStream { match ty { Type::Ident(ident) if ident.rust == RustString => { let span = ident.rust.span(); quote_spanned!(span=> ::cxx::private::RustString) } Type::RustBox(ty) | Type::UniquePtr(ty) => { let span = ty.name.span(); if proper && types.is_considered_improper_ctype(&ty.inner) { quote_spanned!(span=> *mut ::cxx::core::ffi::c_void) } else { let inner = expand_extern_type(&ty.inner, types, proper); quote_spanned!(span=> *mut #inner) } } Type::RustVec(ty) => { let span = ty.name.span(); let langle = ty.langle; let elem = expand_extern_type(&ty.inner, types, proper); let rangle = ty.rangle; quote_spanned!(span=> ::cxx::private::RustVec #langle #elem #rangle) } Type::Ref(ty) => { let ampersand = ty.ampersand; let lifetime = &ty.lifetime; let mutability = ty.mutability; match &ty.inner { Type::Ident(ident) if ident.rust == RustString => { let span = ident.rust.span(); quote_spanned!(span=> #ampersand #lifetime #mutability ::cxx::private::RustString) } Type::RustVec(ty) => { let span = ty.name.span(); let langle = ty.langle; let inner = expand_extern_type(&ty.inner, types, proper); let rangle = ty.rangle; quote_spanned!(span=> #ampersand #lifetime #mutability ::cxx::private::RustVec #langle #inner #rangle) } inner if proper && types.is_considered_improper_ctype(inner) => { let star = Token![*](ampersand.span); match ty.mutable { false => quote!(#star const ::cxx::core::ffi::c_void), true => quote!(#star #mutability ::cxx::core::ffi::c_void), } } _ => quote!(#ty), } } Type::Ptr(ty) => { if proper && types.is_considered_improper_ctype(&ty.inner) { let star = ty.star; let mutability = ty.mutability; let constness = ty.constness; quote!(#star #mutability #constness ::cxx::core::ffi::c_void) } else { quote!(#ty) } } Type::Str(ty) => { let span = ty.ampersand.span; let rust_str = Ident::new("RustStr", syn::spanned::Spanned::span(&ty.inner)); quote_spanned!(span=> ::cxx::private::#rust_str) } Type::SliceRef(ty) => { let span = ty.ampersand.span; let rust_slice = Ident::new("RustSlice", ty.bracket.span.join()); quote_spanned!(span=> ::cxx::private::#rust_slice) } _ => quote!(#ty), } } fn expand_extern_return_type(ret: &Option, types: &Types, proper: bool) -> TokenStream { let ret = match ret { Some(ret) if !types.needs_indirect_abi(ret) => ret, _ => return TokenStream::new(), }; let ty = expand_extern_type(ret, types, proper); quote!(-> #ty) } // #UnsafeExtern extern "C" {...} // https://blog.rust-lang.org/2024/10/17/Rust-1.82.0.html#safe-items-with-unsafe-extern struct UnsafeExtern; impl ToTokens for UnsafeExtern { fn to_tokens(&self, tokens: &mut TokenStream) { if rustversion::cfg!(since(1.82)) { Token![unsafe](Span::call_site()).to_tokens(tokens); } } } // #[#UnsafeAttr(#ExportNameAttr = "...")] // https://blog.rust-lang.org/2024/10/17/Rust-1.82.0.html#unsafe-attributes struct UnsafeAttr; struct ExportNameAttr; impl ToTokens for UnsafeAttr { fn to_tokens(&self, tokens: &mut TokenStream) { if rustversion::cfg!(since(1.82)) { Token![unsafe](Span::call_site()).to_tokens(tokens); } else { Ident::new("cfg_attr", Span::call_site()).to_tokens(tokens); } } } impl ToTokens for ExportNameAttr { fn to_tokens(&self, tokens: &mut TokenStream) { if rustversion::cfg!(since(1.82)) { Ident::new("export_name", Span::call_site()).to_tokens(tokens); } else { tokens.extend(quote!(all(), export_name)); } } } cxxbridge-macro-1.0.141/src/generics.rs000064400000000000000000000047311046102023000160070ustar 00000000000000use crate::syntax::instantiate::NamedImplKey; use crate::syntax::resolve::Resolution; use crate::syntax::{Impl, Lifetimes}; use proc_macro2::TokenStream; use quote::ToTokens; use syn::{Lifetime, Token}; pub(crate) struct ImplGenerics<'a> { explicit_impl: Option<&'a Impl>, resolve: Resolution<'a>, } pub(crate) struct TyGenerics<'a> { key: NamedImplKey<'a>, explicit_impl: Option<&'a Impl>, resolve: Resolution<'a>, } pub(crate) fn split_for_impl<'a>( key: NamedImplKey<'a>, explicit_impl: Option<&'a Impl>, resolve: Resolution<'a>, ) -> (ImplGenerics<'a>, TyGenerics<'a>) { let impl_generics = ImplGenerics { explicit_impl, resolve, }; let ty_generics = TyGenerics { key, explicit_impl, resolve, }; (impl_generics, ty_generics) } impl<'a> ToTokens for ImplGenerics<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { if let Some(imp) = self.explicit_impl { imp.impl_generics.to_tokens(tokens); } else { self.resolve.generics.to_tokens(tokens); } } } impl<'a> ToTokens for TyGenerics<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { if let Some(imp) = self.explicit_impl { imp.ty_generics.to_tokens(tokens); } else if !self.resolve.generics.lifetimes.is_empty() { let span = self.key.rust.span(); self.key .lt_token .unwrap_or_else(|| Token![<](span)) .to_tokens(tokens); self.resolve.generics.lifetimes.to_tokens(tokens); self.key .gt_token .unwrap_or_else(|| Token![>](span)) .to_tokens(tokens); } } } pub(crate) struct UnderscoreLifetimes<'a> { generics: &'a Lifetimes, } impl Lifetimes { pub(crate) fn to_underscore_lifetimes(&self) -> UnderscoreLifetimes { UnderscoreLifetimes { generics: self } } } impl<'a> ToTokens for UnderscoreLifetimes<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let Lifetimes { lt_token, lifetimes, gt_token, } = self.generics; lt_token.to_tokens(tokens); for pair in lifetimes.pairs() { let (lifetime, punct) = pair.into_tuple(); let lifetime = Lifetime::new("'_", lifetime.span()); lifetime.to_tokens(tokens); punct.to_tokens(tokens); } gt_token.to_tokens(tokens); } } cxxbridge-macro-1.0.141/src/lib.rs000064400000000000000000000052011046102023000147470ustar 00000000000000#![allow( clippy::cast_sign_loss, clippy::doc_markdown, clippy::enum_glob_use, clippy::inherent_to_string, clippy::items_after_statements, clippy::match_bool, clippy::match_same_arms, clippy::needless_lifetimes, clippy::needless_pass_by_value, clippy::nonminimal_bool, clippy::redundant_else, clippy::ref_option, clippy::single_match_else, clippy::struct_field_names, clippy::too_many_arguments, clippy::too_many_lines, clippy::toplevel_ref_arg, clippy::uninlined_format_args )] mod derive; mod expand; mod generics; mod syntax; mod tokens; mod type_id; #[cfg(feature = "experimental-enum-variants-from-header")] mod clang; #[cfg(feature = "experimental-enum-variants-from-header")] mod load; use crate::syntax::file::Module; use crate::syntax::namespace::Namespace; use crate::syntax::qualified::QualifiedName; use crate::type_id::Crate; use proc_macro::TokenStream; use syn::parse::{Parse, ParseStream, Parser, Result}; use syn::parse_macro_input; /// `#[cxx::bridge] mod ffi { ... }` /// /// Refer to the crate-level documentation for the explanation of how this macro /// is intended to be used. /// /// The only additional thing to note here is namespace support — if the /// types and functions on the `extern "C++"` side of our bridge are in a /// namespace, specify that namespace as an argument of the cxx::bridge /// attribute macro. /// /// ``` /// #[cxx::bridge(namespace = "mycompany::rust")] /// # mod ffi {} /// ``` /// /// The types and functions from the `extern "Rust"` side of the bridge will be /// placed into that same namespace in the generated C++ code. #[proc_macro_attribute] pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { let _ = syntax::error::ERRORS; let namespace = match Namespace::parse_bridge_attr_namespace.parse(args) { Ok(namespace) => namespace, Err(err) => return err.to_compile_error().into(), }; let mut ffi = parse_macro_input!(input as Module); ffi.namespace = namespace; expand::bridge(ffi) .unwrap_or_else(|err| err.to_compile_error()) .into() } #[doc(hidden)] #[proc_macro] pub fn type_id(input: TokenStream) -> TokenStream { struct TypeId { krate: Crate, path: QualifiedName, } impl Parse for TypeId { fn parse(input: ParseStream) -> Result { let krate = input.parse().map(Crate::DollarCrate)?; let path = QualifiedName::parse_quoted_or_unquoted(input)?; Ok(TypeId { krate, path }) } } let arg = parse_macro_input!(input as TypeId); type_id::expand(arg.krate, arg.path).into() } cxxbridge-macro-1.0.141/src/load.rs000064400000000000000000000255301046102023000151270ustar 00000000000000use crate::clang::{Clang, Node}; use crate::syntax::attrs::OtherAttrs; use crate::syntax::cfg::CfgExpr; use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::{Api, Discriminant, Doc, Enum, EnumRepr, ForeignName, Pair, Variant}; use flate2::write::GzDecoder; use memmap::Mmap; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{format_ident, quote, quote_spanned}; use std::env; use std::fmt::{self, Display}; use std::fs::File; use std::io::Write; use std::path::PathBuf; use std::str::FromStr; use syn::{parse_quote, Path}; const CXX_CLANG_AST: &str = "CXX_CLANG_AST"; pub(crate) fn load(cx: &mut Errors, apis: &mut [Api]) { let ref mut variants_from_header = Vec::new(); for api in apis { if let Api::Enum(enm) = api { if enm.variants_from_header { if enm.variants.is_empty() { variants_from_header.push(enm); } else { let span = span_for_enum_error(enm); cx.error( span, "enum with #![variants_from_header] must be written with no explicit variants", ); } } } } let span = match variants_from_header.first() { None => return, Some(enm) => enm.variants_from_header_attr.clone().unwrap(), }; let ast_dump_path = match env::var_os(CXX_CLANG_AST) { Some(ast_dump_path) => PathBuf::from(ast_dump_path), None => { let msg = format!( "environment variable ${} has not been provided", CXX_CLANG_AST, ); return cx.error(span, msg); } }; let memmap = File::open(&ast_dump_path).and_then(|file| unsafe { Mmap::map(&file) }); let mut gunzipped; let ast_dump_bytes = match match memmap { Ok(ref memmap) => { let is_gzipped = memmap.get(..2) == Some(b"\x1f\x8b"); if is_gzipped { gunzipped = Vec::new(); let decode_result = GzDecoder::new(&mut gunzipped).write_all(memmap); decode_result.map(|()| gunzipped.as_slice()) } else { Ok(memmap as &[u8]) } } Err(error) => Err(error), } { Ok(bytes) => bytes, Err(error) => { let msg = format!("failed to read {}: {}", ast_dump_path.display(), error); return cx.error(span, msg); } }; let ref root: Node = match serde_json::from_slice(ast_dump_bytes) { Ok(root) => root, Err(error) => { let msg = format!("failed to read {}: {}", ast_dump_path.display(), error); return cx.error(span, msg); } }; let ref mut namespace = Vec::new(); traverse(cx, root, namespace, variants_from_header, None); for enm in variants_from_header { if enm.variants.is_empty() { let span = &enm.variants_from_header_attr; let name = CxxName(&enm.name); let msg = format!("failed to find any C++ definition of enum {}", name); cx.error(span, msg); } } } fn traverse<'a>( cx: &mut Errors, node: &'a Node, namespace: &mut Vec<&'a str>, variants_from_header: &mut [&mut Enum], mut idx: Option, ) { match &node.kind { Clang::NamespaceDecl(decl) => { let Some(name) = &decl.name else { // Can ignore enums inside an anonymous namespace. return; }; namespace.push(name); idx = None; } Clang::EnumDecl(decl) => { let Some(name) = &decl.name else { return; }; idx = None; for (i, enm) in variants_from_header.iter_mut().enumerate() { if enm.name.cxx == **name && enm.name.namespace.iter().eq(&*namespace) { if !enm.variants.is_empty() { let span = &enm.variants_from_header_attr; let qual_name = CxxName(&enm.name); let msg = format!("found multiple C++ definitions of enum {}", qual_name); cx.error(span, msg); return; } let Some(fixed_underlying_type) = &decl.fixed_underlying_type else { let span = &enm.variants_from_header_attr; let name = &enm.name.cxx; let qual_name = CxxName(&enm.name); let msg = format!( "implicit implementation-defined repr for enum {} is not supported yet; consider changing its C++ definition to `enum {}: int {{...}}", qual_name, name, ); cx.error(span, msg); return; }; let repr = translate_qual_type( cx, enm, fixed_underlying_type .desugared_qual_type .as_ref() .unwrap_or(&fixed_underlying_type.qual_type), ); enm.repr = EnumRepr::Foreign { rust_type: repr }; idx = Some(i); break; } } if idx.is_none() { return; } } Clang::EnumConstantDecl(decl) => { if let Some(idx) = idx { let enm = &mut *variants_from_header[idx]; let span = enm .variants_from_header_attr .as_ref() .unwrap() .path() .get_ident() .unwrap() .span(); let Ok(cxx_name) = ForeignName::parse(&decl.name, span) else { let span = &enm.variants_from_header_attr; let msg = format!("unsupported C++ variant name: {}", decl.name); return cx.error(span, msg); }; let rust_name: Ident = match syn::parse_str(&decl.name) { Ok(ident) => ident, Err(_) => format_ident!("__Variant{}", enm.variants.len()), }; let discriminant = match discriminant_value(&node.inner) { ParsedDiscriminant::Constant(discriminant) => discriminant, ParsedDiscriminant::Successor => match enm.variants.last() { None => Discriminant::zero(), Some(last) => match last.discriminant.checked_succ() { Some(discriminant) => discriminant, None => { let span = &enm.variants_from_header_attr; let msg = format!( "overflow processing discriminant value for variant: {}", decl.name, ); return cx.error(span, msg); } }, }, ParsedDiscriminant::Fail => { let span = &enm.variants_from_header_attr; let msg = format!( "failed to obtain discriminant value for variant: {}", decl.name, ); cx.error(span, msg); Discriminant::zero() } }; enm.variants.push(Variant { cfg: CfgExpr::Unconditional, doc: Doc::new(), attrs: OtherAttrs::none(), name: Pair { namespace: Namespace::ROOT, cxx: cxx_name, rust: rust_name, }, discriminant, expr: None, }); } } _ => {} } for inner in &node.inner { traverse(cx, inner, namespace, variants_from_header, idx); } if let Clang::NamespaceDecl(_) = &node.kind { let _ = namespace.pop().unwrap(); } } fn translate_qual_type(cx: &mut Errors, enm: &Enum, qual_type: &str) -> Path { let rust_std_name = match qual_type { "char" => "c_char", "int" => "c_int", "long" => "c_long", "long long" => "c_longlong", "signed char" => "c_schar", "short" => "c_short", "unsigned char" => "c_uchar", "unsigned int" => "c_uint", "unsigned long" => "c_ulong", "unsigned long long" => "c_ulonglong", "unsigned short" => "c_ushort", unsupported => { let span = &enm.variants_from_header_attr; let qual_name = CxxName(&enm.name); let msg = format!( "unsupported underlying type for {}: {}", qual_name, unsupported, ); cx.error(span, msg); "c_int" } }; let span = enm .variants_from_header_attr .as_ref() .unwrap() .path() .get_ident() .unwrap() .span(); let ident = Ident::new(rust_std_name, span); let path = quote_spanned!(span=> ::cxx::core::ffi::#ident); parse_quote!(#path) } enum ParsedDiscriminant { Constant(Discriminant), Successor, Fail, } fn discriminant_value(mut clang: &[Node]) -> ParsedDiscriminant { if clang.is_empty() { // No discriminant expression provided; use successor of previous // discriminant. return ParsedDiscriminant::Successor; } loop { if clang.len() != 1 { return ParsedDiscriminant::Fail; } let node = &clang[0]; match &node.kind { Clang::ImplicitCastExpr => clang = &node.inner, Clang::ConstantExpr(expr) => match Discriminant::from_str(&expr.value) { Ok(discriminant) => return ParsedDiscriminant::Constant(discriminant), Err(_) => return ParsedDiscriminant::Fail, }, _ => return ParsedDiscriminant::Fail, } } } fn span_for_enum_error(enm: &Enum) -> TokenStream { let enum_token = enm.enum_token; let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new()); brace_token.set_span(enm.brace_token.span.join()); quote!(#enum_token #brace_token) } struct CxxName<'a>(&'a Pair); impl<'a> Display for CxxName<'a> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { for namespace in &self.0.namespace { write!(formatter, "{}::", namespace)?; } write!(formatter, "{}", self.0.cxx) } } cxxbridge-macro-1.0.141/src/syntax/atom.rs000064400000000000000000000043141046102023000164730ustar 00000000000000use crate::syntax::Type; use proc_macro2::Ident; use std::fmt::{self, Display}; #[derive(Copy, Clone, PartialEq)] pub(crate) enum Atom { Bool, Char, // C char, not Rust char U8, U16, U32, U64, Usize, I8, I16, I32, I64, Isize, F32, F64, CxxString, RustString, } impl Atom { pub(crate) fn from(ident: &Ident) -> Option { Self::from_str(ident.to_string().as_str()) } pub(crate) fn from_str(s: &str) -> Option { use self::Atom::*; match s { "bool" => Some(Bool), "c_char" => Some(Char), "u8" => Some(U8), "u16" => Some(U16), "u32" => Some(U32), "u64" => Some(U64), "usize" => Some(Usize), "i8" => Some(I8), "i16" => Some(I16), "i32" => Some(I32), "i64" => Some(I64), "isize" => Some(Isize), "f32" => Some(F32), "f64" => Some(F64), "CxxString" => Some(CxxString), "String" => Some(RustString), _ => None, } } } impl Display for Atom { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str(self.as_ref()) } } impl AsRef for Atom { fn as_ref(&self) -> &str { use self::Atom::*; match self { Bool => "bool", Char => "c_char", U8 => "u8", U16 => "u16", U32 => "u32", U64 => "u64", Usize => "usize", I8 => "i8", I16 => "i16", I32 => "i32", I64 => "i64", Isize => "isize", F32 => "f32", F64 => "f64", CxxString => "CxxString", RustString => "String", } } } impl PartialEq for Type { fn eq(&self, atom: &Atom) -> bool { match self { Type::Ident(ident) => ident.rust == atom, _ => false, } } } impl PartialEq for &Ident { fn eq(&self, atom: &Atom) -> bool { *self == atom } } impl PartialEq for &Type { fn eq(&self, atom: &Atom) -> bool { *self == atom } } cxxbridge-macro-1.0.141/src/syntax/attrs.rs000064400000000000000000000236121046102023000166720ustar 00000000000000use crate::syntax::cfg::CfgExpr; use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::Atom::{self, *}; use crate::syntax::{cfg, Derive, Doc, ForeignName}; use proc_macro2::{Ident, TokenStream}; use quote::ToTokens; use syn::parse::ParseStream; use syn::{Attribute, Error, Expr, Lit, LitStr, Meta, Path, Result, Token}; // Intended usage: // // let mut doc = Doc::new(); // let mut cxx_name = None; // let mut rust_name = None; // /* ... */ // let attrs = attrs::parse( // cx, // item.attrs, // attrs::Parser { // doc: Some(&mut doc), // cxx_name: Some(&mut cxx_name), // rust_name: Some(&mut rust_name), // /* ... */ // ..Default::default() // }, // ); // #[derive(Default)] pub(crate) struct Parser<'a> { pub cfg: Option<&'a mut CfgExpr>, pub doc: Option<&'a mut Doc>, pub derives: Option<&'a mut Vec>, pub repr: Option<&'a mut Option>, pub namespace: Option<&'a mut Namespace>, pub cxx_name: Option<&'a mut Option>, pub rust_name: Option<&'a mut Option>, pub variants_from_header: Option<&'a mut Option>, pub ignore_unrecognized: bool, // Suppress clippy needless_update lint ("struct update has no effect, all // the fields in the struct have already been specified") when preemptively // writing `..Default::default()`. pub(crate) _more: (), } pub(crate) fn parse(cx: &mut Errors, attrs: Vec, mut parser: Parser) -> OtherAttrs { let mut passthrough_attrs = Vec::new(); for attr in attrs { let attr_path = attr.path(); if attr_path.is_ident("doc") { match parse_doc_attribute(&attr.meta) { Ok(attr) => { if let Some(doc) = &mut parser.doc { match attr { DocAttribute::Doc(lit) => doc.push(lit), DocAttribute::Hidden => doc.hidden = true, } continue; } } Err(err) => { cx.push(err); break; } } } else if attr_path.is_ident("derive") { match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) { Ok(attr) => { if let Some(derives) = &mut parser.derives { derives.extend(attr); continue; } } Err(err) => { cx.push(err); break; } } } else if attr_path.is_ident("repr") { match attr.parse_args_with(parse_repr_attribute) { Ok(attr) => { if let Some(repr) = &mut parser.repr { **repr = Some(attr); continue; } } Err(err) => { cx.push(err); break; } } } else if attr_path.is_ident("namespace") { match Namespace::parse_meta(&attr.meta) { Ok(attr) => { if let Some(namespace) = &mut parser.namespace { **namespace = attr; continue; } } Err(err) => { cx.push(err); break; } } } else if attr_path.is_ident("cxx_name") { match parse_cxx_name_attribute(&attr.meta) { Ok(attr) => { if let Some(cxx_name) = &mut parser.cxx_name { **cxx_name = Some(attr); continue; } } Err(err) => { cx.push(err); break; } } } else if attr_path.is_ident("rust_name") { match parse_rust_name_attribute(&attr.meta) { Ok(attr) => { if let Some(rust_name) = &mut parser.rust_name { **rust_name = Some(attr); continue; } } Err(err) => { cx.push(err); break; } } } else if attr_path.is_ident("cfg") { match cfg::parse_attribute(&attr) { Ok(cfg_expr) => { if let Some(cfg) = &mut parser.cfg { cfg.merge(cfg_expr); passthrough_attrs.push(attr); continue; } } Err(err) => { cx.push(err); break; } } } else if attr_path.is_ident("variants_from_header") && cfg!(feature = "experimental-enum-variants-from-header") { if let Err(err) = attr.meta.require_path_only() { cx.push(err); } if let Some(variants_from_header) = &mut parser.variants_from_header { **variants_from_header = Some(attr); continue; } } else if attr_path.is_ident("allow") || attr_path.is_ident("warn") || attr_path.is_ident("deny") || attr_path.is_ident("forbid") || attr_path.is_ident("deprecated") || attr_path.is_ident("must_use") { // https://doc.rust-lang.org/reference/attributes/diagnostics.html passthrough_attrs.push(attr); continue; } else if attr_path.is_ident("serde") { passthrough_attrs.push(attr); continue; } else if attr_path.segments.len() > 1 { let tool = &attr_path.segments.first().unwrap().ident; if tool == "rustfmt" { // Skip, rustfmt only needs to find it in the pre-expansion source file. continue; } else if tool == "clippy" { passthrough_attrs.push(attr); continue; } } if !parser.ignore_unrecognized { cx.error(attr, "unsupported attribute"); break; } } OtherAttrs(passthrough_attrs) } enum DocAttribute { Doc(LitStr), Hidden, } mod kw { syn::custom_keyword!(hidden); } fn parse_doc_attribute(meta: &Meta) -> Result { match meta { Meta::NameValue(meta) => { if let Expr::Lit(expr) = &meta.value { if let Lit::Str(lit) = &expr.lit { return Ok(DocAttribute::Doc(lit.clone())); } } } Meta::List(meta) => { meta.parse_args::()?; return Ok(DocAttribute::Hidden); } Meta::Path(_) => {} } Err(Error::new_spanned(meta, "unsupported doc attribute")) } fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result> { let paths = input.parse_terminated(Path::parse_mod_style, Token![,])?; let mut derives = Vec::new(); for path in paths { if let Some(ident) = path.get_ident() { if let Some(derive) = Derive::from(ident) { derives.push(derive); continue; } } cx.error(path, "unsupported derive"); } Ok(derives) } fn parse_repr_attribute(input: ParseStream) -> Result { let begin = input.cursor(); let ident: Ident = input.parse()?; if let Some(atom) = Atom::from(&ident) { match atom { U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => { return Ok(atom); } _ => {} } } Err(Error::new_spanned( begin.token_stream(), "unrecognized repr", )) } fn parse_cxx_name_attribute(meta: &Meta) -> Result { if let Meta::NameValue(meta) = meta { match &meta.value { Expr::Lit(expr) => { if let Lit::Str(lit) = &expr.lit { return ForeignName::parse(&lit.value(), lit.span()); } } Expr::Path(expr) => { if let Some(ident) = expr.path.get_ident() { return ForeignName::parse(&ident.to_string(), ident.span()); } } _ => {} } } Err(Error::new_spanned(meta, "unsupported cxx_name attribute")) } fn parse_rust_name_attribute(meta: &Meta) -> Result { if let Meta::NameValue(meta) = meta { match &meta.value { Expr::Lit(expr) => { if let Lit::Str(lit) = &expr.lit { return lit.parse(); } } Expr::Path(expr) => { if let Some(ident) = expr.path.get_ident() { return Ok(ident.clone()); } } _ => {} } } Err(Error::new_spanned(meta, "unsupported rust_name attribute")) } #[derive(Clone)] pub(crate) struct OtherAttrs(Vec); impl OtherAttrs { pub(crate) fn none() -> Self { OtherAttrs(Vec::new()) } pub(crate) fn extend(&mut self, other: Self) { self.0.extend(other.0); } } impl ToTokens for OtherAttrs { fn to_tokens(&self, tokens: &mut TokenStream) { for attr in &self.0 { let Attribute { pound_token, style, bracket_token, meta, } = attr; pound_token.to_tokens(tokens); let _ = style; // ignore; render outer and inner attrs both as outer bracket_token.surround(tokens, |tokens| meta.to_tokens(tokens)); } } } cxxbridge-macro-1.0.141/src/syntax/cfg.rs000064400000000000000000000047371046102023000163030ustar 00000000000000use proc_macro2::Ident; use std::mem; use syn::parse::{Error, ParseStream, Result}; use syn::{parenthesized, token, Attribute, LitStr, Token}; #[derive(Clone)] pub(crate) enum CfgExpr { Unconditional, #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro Eq(Ident, Option), All(Vec), #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro Any(Vec), #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro Not(Box), } impl CfgExpr { pub(crate) fn merge(&mut self, expr: CfgExpr) { if let CfgExpr::Unconditional = self { *self = expr; } else if let CfgExpr::All(list) = self { list.push(expr); } else { let prev = mem::replace(self, CfgExpr::Unconditional); *self = CfgExpr::All(vec![prev, expr]); } } } pub(crate) fn parse_attribute(attr: &Attribute) -> Result { attr.parse_args_with(|input: ParseStream| { let cfg_expr = input.call(parse_single)?; input.parse::>()?; Ok(cfg_expr) }) } fn parse_single(input: ParseStream) -> Result { let ident: Ident = input.parse()?; let lookahead = input.lookahead1(); if input.peek(token::Paren) { let content; parenthesized!(content in input); if ident == "all" { let list = content.call(parse_multiple)?; Ok(CfgExpr::All(list)) } else if ident == "any" { let list = content.call(parse_multiple)?; Ok(CfgExpr::Any(list)) } else if ident == "not" { let expr = content.call(parse_single)?; content.parse::>()?; Ok(CfgExpr::Not(Box::new(expr))) } else { Err(Error::new(ident.span(), "unrecognized cfg expression")) } } else if lookahead.peek(Token![=]) { input.parse::()?; let string: LitStr = input.parse()?; Ok(CfgExpr::Eq(ident, Some(string))) } else if lookahead.peek(Token![,]) || input.is_empty() { Ok(CfgExpr::Eq(ident, None)) } else { Err(lookahead.error()) } } fn parse_multiple(input: ParseStream) -> Result> { let mut vec = Vec::new(); while !input.is_empty() { let expr = input.call(parse_single)?; vec.push(expr); if input.is_empty() { break; } input.parse::()?; } Ok(vec) } cxxbridge-macro-1.0.141/src/syntax/check.rs000064400000000000000000000557421046102023000166230ustar 00000000000000use crate::syntax::atom::Atom::{self, *}; use crate::syntax::report::Errors; use crate::syntax::visit::{self, Visit}; use crate::syntax::{ error, ident, trivial, Api, Array, Enum, ExternFn, ExternType, Impl, Lang, Lifetimes, NamedType, Ptr, Receiver, Ref, Signature, SliceRef, Struct, Trait, Ty1, Type, TypeAlias, Types, }; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; use std::fmt::Display; use syn::{GenericParam, Generics, Lifetime}; pub(crate) struct Check<'a> { apis: &'a [Api], types: &'a Types<'a>, errors: &'a mut Errors, generator: Generator, } pub(crate) enum Generator { // cxx-build crate, cxxbridge cli, cxx-gen. #[allow(dead_code)] Build, // cxxbridge-macro. This is relevant in that the macro output is going to // get fed straight to rustc, so for errors that rustc already contains // logic to catch (probably with a better diagnostic than what the proc // macro API is able to produce), we avoid duplicating them in our own // diagnostics. #[allow(dead_code)] Macro, } pub(crate) fn typecheck(cx: &mut Errors, apis: &[Api], types: &Types, generator: Generator) { do_typecheck(&mut Check { apis, types, errors: cx, generator, }); } fn do_typecheck(cx: &mut Check) { ident::check_all(cx, cx.apis); for ty in cx.types { match ty { Type::Ident(ident) => check_type_ident(cx, ident), Type::RustBox(ptr) => check_type_box(cx, ptr), Type::RustVec(ty) => check_type_rust_vec(cx, ty), Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr), Type::SharedPtr(ptr) => check_type_shared_ptr(cx, ptr), Type::WeakPtr(ptr) => check_type_weak_ptr(cx, ptr), Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr), Type::Ref(ty) => check_type_ref(cx, ty), Type::Ptr(ty) => check_type_ptr(cx, ty), Type::Array(array) => check_type_array(cx, array), Type::Fn(ty) => check_type_fn(cx, ty), Type::SliceRef(ty) => check_type_slice_ref(cx, ty), Type::Str(_) | Type::Void(_) => {} } } for api in cx.apis { match api { Api::Include(_) => {} Api::Struct(strct) => check_api_struct(cx, strct), Api::Enum(enm) => check_api_enum(cx, enm), Api::CxxType(ety) | Api::RustType(ety) => check_api_type(cx, ety), Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn), Api::TypeAlias(alias) => check_api_type_alias(cx, alias), Api::Impl(imp) => check_api_impl(cx, imp), } } } impl Check<'_> { pub(crate) fn error(&mut self, sp: impl ToTokens, msg: impl Display) { self.errors.error(sp, msg); } } fn check_type_ident(cx: &mut Check, name: &NamedType) { let ident = &name.rust; if Atom::from(ident).is_none() && !cx.types.structs.contains_key(ident) && !cx.types.enums.contains_key(ident) && !cx.types.cxx.contains(ident) && !cx.types.rust.contains(ident) { let msg = format!("unsupported type: {}", ident); cx.error(ident, msg); } } fn check_type_box(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { if cx.types.cxx.contains(&ident.rust) && !cx.types.aliases.contains_key(&ident.rust) && !cx.types.structs.contains_key(&ident.rust) && !cx.types.enums.contains_key(&ident.rust) { cx.error(ptr, error::BOX_CXX_TYPE.msg); } if Atom::from(&ident.rust).is_none() { return; } } cx.error(ptr, "unsupported target type of Box"); } fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { match &ty.inner { Type::Ident(ident) => { if cx.types.cxx.contains(&ident.rust) && !cx.types.aliases.contains_key(&ident.rust) && !cx.types.structs.contains_key(&ident.rust) && !cx.types.enums.contains_key(&ident.rust) { cx.error(ty, "Rust Vec containing C++ type is not supported yet"); return; } match Atom::from(&ident.rust) { None | Some( Bool | Char | U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | F32 | F64 | RustString, ) => return, Some(CxxString) => {} } } Type::Str(_) => return, _ => {} } cx.error(ty, "unsupported element type of Vec"); } fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { if cx.types.rust.contains(&ident.rust) { cx.error(ptr, "unique_ptr of a Rust type is not supported yet"); return; } match Atom::from(&ident.rust) { None | Some(CxxString) => return, _ => {} } } else if let Type::CxxVector(_) = &ptr.inner { return; } cx.error(ptr, "unsupported unique_ptr target type"); } fn check_type_shared_ptr(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { if cx.types.rust.contains(&ident.rust) { cx.error(ptr, "shared_ptr of a Rust type is not supported yet"); return; } match Atom::from(&ident.rust) { None | Some( Bool | U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | F32 | F64 | CxxString, ) => return, Some(Char | RustString) => {} } } else if let Type::CxxVector(_) = &ptr.inner { cx.error(ptr, "std::shared_ptr is not supported yet"); return; } cx.error(ptr, "unsupported shared_ptr target type"); } fn check_type_weak_ptr(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { if cx.types.rust.contains(&ident.rust) { cx.error(ptr, "weak_ptr of a Rust type is not supported yet"); return; } match Atom::from(&ident.rust) { None | Some( Bool | U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | F32 | F64 | CxxString, ) => return, Some(Char | RustString) => {} } } else if let Type::CxxVector(_) = &ptr.inner { cx.error(ptr, "std::weak_ptr is not supported yet"); return; } cx.error(ptr, "unsupported weak_ptr target type"); } fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { if cx.types.rust.contains(&ident.rust) { cx.error( ptr, "C++ vector containing a Rust type is not supported yet", ); return; } match Atom::from(&ident.rust) { None | Some( U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | F32 | F64 | CxxString, ) => return, Some(Char) => { /* todo */ } Some(Bool | RustString) => {} } } cx.error(ptr, "unsupported vector element type"); } fn check_type_ref(cx: &mut Check, ty: &Ref) { if ty.mutable && !ty.pinned { if let Some(requires_pin) = match &ty.inner { Type::Ident(ident) if ident.rust == CxxString || is_opaque_cxx(cx, &ident.rust) => { Some(ident.rust.to_string()) } Type::CxxVector(_) => Some("CxxVector<...>".to_owned()), _ => None, } { cx.error( ty, format!( "mutable reference to C++ type requires a pin -- use Pin<&mut {}>", requires_pin, ), ); } } match ty.inner { Type::Fn(_) | Type::Void(_) => {} Type::Ref(_) => { cx.error(ty, "C++ does not allow references to references"); return; } _ => return, } cx.error(ty, "unsupported reference type"); } fn check_type_ptr(cx: &mut Check, ty: &Ptr) { match ty.inner { Type::Fn(_) | Type::Void(_) => {} Type::Ref(_) => { cx.error(ty, "C++ does not allow pointer to reference as a type"); return; } _ => return, } cx.error(ty, "unsupported pointer type"); } fn check_type_slice_ref(cx: &mut Check, ty: &SliceRef) { let supported = !is_unsized(cx, &ty.inner) || match &ty.inner { Type::Ident(ident) => { cx.types.rust.contains(&ident.rust) || cx.types.aliases.contains_key(&ident.rust) } _ => false, }; if !supported { let mutable = if ty.mutable { "mut " } else { "" }; let mut msg = format!("unsupported &{}[T] element type", mutable); if let Type::Ident(ident) = &ty.inner { if is_opaque_cxx(cx, &ident.rust) { msg += ": opaque C++ type is not supported yet"; } } cx.error(ty, msg); } } fn check_type_array(cx: &mut Check, ty: &Array) { let supported = !is_unsized(cx, &ty.inner); if !supported { cx.error(ty, "unsupported array element type"); } } fn check_type_fn(cx: &mut Check, ty: &Signature) { if ty.throws { cx.error(ty, "function pointer returning Result is not supported yet"); } for arg in &ty.args { if let Type::Ptr(_) = arg.ty { if ty.unsafety.is_none() { cx.error( arg, "pointer argument requires that the function pointer be marked unsafe", ); } } } } fn check_api_struct(cx: &mut Check, strct: &Struct) { let name = &strct.name; check_reserved_name(cx, &name.rust); check_lifetimes(cx, &strct.generics); if strct.fields.is_empty() { let span = span_for_struct_error(strct); cx.error(span, "structs without any fields are not supported"); } if cx.types.cxx.contains(&name.rust) { if let Some(ety) = cx.types.untrusted.get(&name.rust) { let msg = "extern shared struct must be declared in an `unsafe extern` block"; cx.error(ety, msg); } } for derive in &strct.derives { if derive.what == Trait::ExternType { let msg = format!("derive({}) on shared struct is not supported", derive); cx.error(derive, msg); } } for field in &strct.fields { if let Type::Fn(_) = field.ty { cx.error( field, "function pointers in a struct field are not implemented yet", ); } else if is_unsized(cx, &field.ty) { let desc = describe(cx, &field.ty); let msg = format!("using {} by value is not supported", desc); cx.error(field, msg); } } } fn check_api_enum(cx: &mut Check, enm: &Enum) { check_reserved_name(cx, &enm.name.rust); check_lifetimes(cx, &enm.generics); if enm.variants.is_empty() && !enm.explicit_repr && !enm.variants_from_header { let span = span_for_enum_error(enm); cx.error( span, "explicit #[repr(...)] is required for enum without any variants", ); } for derive in &enm.derives { if derive.what == Trait::Default || derive.what == Trait::ExternType { let msg = format!("derive({}) on shared enum is not supported", derive); cx.error(derive, msg); } } } fn check_api_type(cx: &mut Check, ety: &ExternType) { check_reserved_name(cx, &ety.name.rust); check_lifetimes(cx, &ety.generics); for derive in &ety.derives { if derive.what == Trait::ExternType && ety.lang == Lang::Rust { continue; } let lang = match ety.lang { Lang::Rust => "Rust", Lang::Cxx => "C++", }; let msg = format!( "derive({}) on opaque {} type is not supported yet", derive, lang, ); cx.error(derive, msg); } if !ety.bounds.is_empty() { let bounds = &ety.bounds; let span = quote!(#(#bounds)*); cx.error(span, "extern type bounds are not implemented yet"); } if let Some(reasons) = cx.types.required_trivial.get(&ety.name.rust) { let msg = format!( "needs a cxx::ExternType impl in order to be used as {}", trivial::as_what(&ety.name, reasons), ); cx.error(ety, msg); } } fn check_api_fn(cx: &mut Check, efn: &ExternFn) { match efn.lang { Lang::Cxx => { if !efn.generics.params.is_empty() && !efn.trusted { let ref span = span_for_generics_error(efn); cx.error(span, "extern C++ function with lifetimes must be declared in `unsafe extern \"C++\"` block"); } } Lang::Rust => { if !efn.generics.params.is_empty() && efn.unsafety.is_none() { let ref span = span_for_generics_error(efn); let message = format!( "must be `unsafe fn {}` in order to expose explicit lifetimes to C++", efn.name.rust, ); cx.error(span, message); } } } check_generics(cx, &efn.sig.generics); if let Some(receiver) = &efn.receiver { let ref span = span_for_receiver_error(receiver); if receiver.ty.rust == "Self" { let mutability = match receiver.mutable { true => "mut ", false => "", }; let msg = format!( "unnamed receiver type is only allowed if the surrounding extern block contains exactly one extern type; use `self: &{mutability}TheType`", mutability = mutability, ); cx.error(span, msg); } else if cx.types.enums.contains_key(&receiver.ty.rust) { cx.error( span, "unsupported receiver type; C++ does not allow member functions on enums", ); } else if !cx.types.structs.contains_key(&receiver.ty.rust) && !cx.types.cxx.contains(&receiver.ty.rust) && !cx.types.rust.contains(&receiver.ty.rust) { cx.error(span, "unrecognized receiver type"); } else if receiver.mutable && !receiver.pinned && is_opaque_cxx(cx, &receiver.ty.rust) { cx.error( span, format!( "mutable reference to opaque C++ type requires a pin -- use `self: Pin<&mut {}>`", receiver.ty.rust, ), ); } } for arg in &efn.args { if let Type::Fn(_) = arg.ty { if efn.lang == Lang::Rust { cx.error( arg, "passing a function pointer from C++ to Rust is not implemented yet", ); } } else if let Type::Ptr(_) = arg.ty { if efn.sig.unsafety.is_none() { cx.error( arg, "pointer argument requires that the function be marked unsafe", ); } } else if is_unsized(cx, &arg.ty) { let desc = describe(cx, &arg.ty); let msg = format!("passing {} by value is not supported", desc); cx.error(arg, msg); } } if let Some(ty) = &efn.ret { if let Type::Fn(_) = ty { cx.error(ty, "returning a function pointer is not implemented yet"); } else if is_unsized(cx, ty) { let desc = describe(cx, ty); let msg = format!("returning {} by value is not supported", desc); cx.error(ty, msg); } } if efn.lang == Lang::Cxx { check_mut_return_restriction(cx, efn); } } fn check_api_type_alias(cx: &mut Check, alias: &TypeAlias) { check_lifetimes(cx, &alias.generics); for derive in &alias.derives { let msg = format!("derive({}) on extern type alias is not supported", derive); cx.error(derive, msg); } } fn check_api_impl(cx: &mut Check, imp: &Impl) { let ty = &imp.ty; check_lifetimes(cx, &imp.impl_generics); if let Some(negative) = imp.negative_token { let span = quote!(#negative #ty); cx.error(span, "negative impl is not supported yet"); return; } match ty { Type::RustBox(ty) | Type::RustVec(ty) | Type::UniquePtr(ty) | Type::SharedPtr(ty) | Type::WeakPtr(ty) | Type::CxxVector(ty) => { if let Type::Ident(inner) = &ty.inner { if Atom::from(&inner.rust).is_none() { return; } } } _ => {} } cx.error(imp, "unsupported Self type of explicit impl"); } fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) { if efn.sig.unsafety.is_some() { // Unrestricted as long as the function is made unsafe-to-call. return; } match &efn.ret { Some(Type::Ref(ty)) if ty.mutable => {} Some(Type::SliceRef(slice)) if slice.mutable => {} _ => return, } if let Some(receiver) = &efn.receiver { if receiver.mutable { return; } let Some(resolve) = cx.types.try_resolve(&receiver.ty) else { return; }; if !resolve.generics.lifetimes.is_empty() { return; } } struct FindLifetimeMut<'a> { cx: &'a Check<'a>, found: bool, } impl<'t, 'a> Visit<'t> for FindLifetimeMut<'a> { fn visit_type(&mut self, ty: &'t Type) { self.found |= match ty { Type::Ref(ty) => ty.mutable, Type::SliceRef(slice) => slice.mutable, Type::Ident(ident) if Atom::from(&ident.rust).is_none() => { match self.cx.types.try_resolve(ident) { Some(resolve) => !resolve.generics.lifetimes.is_empty(), None => true, } } _ => false, }; visit::visit_type(self, ty); } } let mut visitor = FindLifetimeMut { cx, found: false }; for arg in &efn.args { visitor.visit_type(&arg.ty); } if visitor.found { return; } cx.error( efn, "&mut return type is not allowed unless there is a &mut argument", ); } fn check_reserved_name(cx: &mut Check, ident: &Ident) { if ident == "Box" || ident == "UniquePtr" || ident == "SharedPtr" || ident == "WeakPtr" || ident == "Vec" || ident == "CxxVector" || ident == "str" || Atom::from(ident).is_some() { cx.error(ident, "reserved name"); } } fn check_reserved_lifetime(cx: &mut Check, lifetime: &Lifetime) { if lifetime.ident == "static" { match cx.generator { Generator::Macro => { /* rustc already reports this */ } Generator::Build => { cx.error(lifetime, error::RESERVED_LIFETIME); } } } } fn check_lifetimes(cx: &mut Check, generics: &Lifetimes) { for lifetime in &generics.lifetimes { check_reserved_lifetime(cx, lifetime); } } fn check_generics(cx: &mut Check, generics: &Generics) { for generic_param in &generics.params { if let GenericParam::Lifetime(def) = generic_param { check_reserved_lifetime(cx, &def.lifetime); } } } fn is_unsized(cx: &mut Check, ty: &Type) -> bool { match ty { Type::Ident(ident) => { let ident = &ident.rust; ident == CxxString || is_opaque_cxx(cx, ident) || cx.types.rust.contains(ident) } Type::Array(array) => is_unsized(cx, &array.inner), Type::CxxVector(_) | Type::Fn(_) | Type::Void(_) => true, Type::RustBox(_) | Type::RustVec(_) | Type::UniquePtr(_) | Type::SharedPtr(_) | Type::WeakPtr(_) | Type::Ref(_) | Type::Ptr(_) | Type::Str(_) | Type::SliceRef(_) => false, } } fn is_opaque_cxx(cx: &mut Check, ty: &Ident) -> bool { cx.types.cxx.contains(ty) && !cx.types.structs.contains_key(ty) && !cx.types.enums.contains_key(ty) && !(cx.types.aliases.contains_key(ty) && cx.types.required_trivial.contains_key(ty)) } fn span_for_struct_error(strct: &Struct) -> TokenStream { let struct_token = strct.struct_token; let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new()); brace_token.set_span(strct.brace_token.span.join()); quote!(#struct_token #brace_token) } fn span_for_enum_error(enm: &Enum) -> TokenStream { let enum_token = enm.enum_token; let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new()); brace_token.set_span(enm.brace_token.span.join()); quote!(#enum_token #brace_token) } fn span_for_receiver_error(receiver: &Receiver) -> TokenStream { let ampersand = receiver.ampersand; let lifetime = &receiver.lifetime; let mutability = receiver.mutability; if receiver.shorthand { let var = receiver.var; quote!(#ampersand #lifetime #mutability #var) } else { let ty = &receiver.ty; quote!(#ampersand #lifetime #mutability #ty) } } fn span_for_generics_error(efn: &ExternFn) -> TokenStream { let unsafety = efn.unsafety; let fn_token = efn.fn_token; let generics = &efn.generics; quote!(#unsafety #fn_token #generics) } fn describe(cx: &mut Check, ty: &Type) -> String { match ty { Type::Ident(ident) => { if cx.types.structs.contains_key(&ident.rust) { "struct".to_owned() } else if cx.types.enums.contains_key(&ident.rust) { "enum".to_owned() } else if cx.types.aliases.contains_key(&ident.rust) { "C++ type".to_owned() } else if cx.types.cxx.contains(&ident.rust) { "opaque C++ type".to_owned() } else if cx.types.rust.contains(&ident.rust) { "opaque Rust type".to_owned() } else if Atom::from(&ident.rust) == Some(CxxString) { "C++ string".to_owned() } else if Atom::from(&ident.rust) == Some(Char) { "C char".to_owned() } else { ident.rust.to_string() } } Type::RustBox(_) => "Box".to_owned(), Type::RustVec(_) => "Vec".to_owned(), Type::UniquePtr(_) => "unique_ptr".to_owned(), Type::SharedPtr(_) => "shared_ptr".to_owned(), Type::WeakPtr(_) => "weak_ptr".to_owned(), Type::Ref(_) => "reference".to_owned(), Type::Ptr(_) => "raw pointer".to_owned(), Type::Str(_) => "&str".to_owned(), Type::CxxVector(_) => "C++ vector".to_owned(), Type::SliceRef(_) => "slice".to_owned(), Type::Fn(_) => "function pointer".to_owned(), Type::Void(_) => "()".to_owned(), Type::Array(_) => "array".to_owned(), } } cxxbridge-macro-1.0.141/src/syntax/derive.rs000064400000000000000000000040171046102023000170110ustar 00000000000000use proc_macro2::{Ident, Span}; use std::fmt::{self, Display}; #[derive(Copy, Clone)] pub(crate) struct Derive { pub what: Trait, pub span: Span, } #[derive(Copy, Clone, PartialEq)] pub(crate) enum Trait { Clone, Copy, Debug, Default, Eq, ExternType, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize, } impl Derive { pub(crate) fn from(ident: &Ident) -> Option { let what = match ident.to_string().as_str() { "Clone" => Trait::Clone, "Copy" => Trait::Copy, "Debug" => Trait::Debug, "Default" => Trait::Default, "Eq" => Trait::Eq, "ExternType" => Trait::ExternType, "Hash" => Trait::Hash, "Ord" => Trait::Ord, "PartialEq" => Trait::PartialEq, "PartialOrd" => Trait::PartialOrd, "Serialize" => Trait::Serialize, "Deserialize" => Trait::Deserialize, _ => return None, }; let span = ident.span(); Some(Derive { what, span }) } } impl PartialEq for Derive { fn eq(&self, other: &Trait) -> bool { self.what == *other } } impl AsRef for Trait { fn as_ref(&self) -> &str { match self { Trait::Clone => "Clone", Trait::Copy => "Copy", Trait::Debug => "Debug", Trait::Default => "Default", Trait::Eq => "Eq", Trait::ExternType => "ExternType", Trait::Hash => "Hash", Trait::Ord => "Ord", Trait::PartialEq => "PartialEq", Trait::PartialOrd => "PartialOrd", Trait::Serialize => "Serialize", Trait::Deserialize => "Deserialize", } } } impl Display for Derive { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str(self.what.as_ref()) } } pub(crate) fn contains(derives: &[Derive], query: Trait) -> bool { derives.iter().any(|derive| derive.what == query) } cxxbridge-macro-1.0.141/src/syntax/discriminant.rs000064400000000000000000000236421046102023000202240ustar 00000000000000use crate::syntax::Atom::{self, *}; use proc_macro2::{Literal, Span, TokenStream}; use quote::ToTokens; use std::cmp::Ordering; use std::collections::BTreeSet; use std::fmt::{self, Display}; use std::str::FromStr; use syn::{Error, Expr, Lit, Result, Token, UnOp}; pub(crate) struct DiscriminantSet { repr: Option, values: BTreeSet, previous: Option, } #[derive(Copy, Clone, Eq, PartialEq)] pub(crate) struct Discriminant { sign: Sign, magnitude: u64, } #[derive(Copy, Clone, Eq, PartialEq)] enum Sign { Negative, Positive, } impl DiscriminantSet { pub(crate) fn new(repr: Option) -> Self { DiscriminantSet { repr, values: BTreeSet::new(), previous: None, } } pub(crate) fn insert(&mut self, expr: &Expr) -> Result { let (discriminant, repr) = expr_to_discriminant(expr)?; match (self.repr, repr) { (None, Some(new_repr)) => { if let Some(limits) = Limits::of(new_repr) { for &past in &self.values { if limits.min <= past && past <= limits.max { continue; } let msg = format!( "discriminant value `{}` is outside the limits of {}", past, new_repr, ); return Err(Error::new(Span::call_site(), msg)); } } self.repr = Some(new_repr); } (Some(prev), Some(repr)) if prev != repr => { let msg = format!("expected {}, found {}", prev, repr); return Err(Error::new(Span::call_site(), msg)); } _ => {} } insert(self, discriminant) } pub(crate) fn insert_next(&mut self) -> Result { let discriminant = match self.previous { None => Discriminant::zero(), Some(mut discriminant) => match discriminant.sign { Sign::Negative => { discriminant.magnitude -= 1; if discriminant.magnitude == 0 { discriminant.sign = Sign::Positive; } discriminant } Sign::Positive => { if discriminant.magnitude == u64::MAX { let msg = format!("discriminant overflow on value after {}", u64::MAX); return Err(Error::new(Span::call_site(), msg)); } discriminant.magnitude += 1; discriminant } }, }; insert(self, discriminant) } pub(crate) fn inferred_repr(&self) -> Result { if let Some(repr) = self.repr { return Ok(repr); } if self.values.is_empty() { return Ok(U8); } let min = *self.values.iter().next().unwrap(); let max = *self.values.iter().next_back().unwrap(); for limits in &LIMITS { if limits.min <= min && max <= limits.max { return Ok(limits.repr); } } let msg = "these discriminant values do not fit in any supported enum repr type"; Err(Error::new(Span::call_site(), msg)) } } fn expr_to_discriminant(expr: &Expr) -> Result<(Discriminant, Option)> { match expr { Expr::Lit(expr) => { if let Lit::Int(lit) = &expr.lit { let discriminant = lit.base10_parse::()?; let repr = parse_int_suffix(lit.suffix())?; return Ok((discriminant, repr)); } } Expr::Unary(unary) => { if let UnOp::Neg(_) = unary.op { let (mut discriminant, repr) = expr_to_discriminant(&unary.expr)?; discriminant.sign = match discriminant.sign { Sign::Positive => Sign::Negative, Sign::Negative => Sign::Positive, }; return Ok((discriminant, repr)); } } _ => {} } Err(Error::new_spanned( expr, "enums with non-integer literal discriminants are not supported yet", )) } fn insert(set: &mut DiscriminantSet, discriminant: Discriminant) -> Result { if let Some(expected_repr) = set.repr { if let Some(limits) = Limits::of(expected_repr) { if discriminant < limits.min || limits.max < discriminant { let msg = format!( "discriminant value `{}` is outside the limits of {}", discriminant, expected_repr, ); return Err(Error::new(Span::call_site(), msg)); } } } set.values.insert(discriminant); set.previous = Some(discriminant); Ok(discriminant) } impl Discriminant { pub(crate) const fn zero() -> Self { Discriminant { sign: Sign::Positive, magnitude: 0, } } const fn pos(u: u64) -> Self { Discriminant { sign: Sign::Positive, magnitude: u, } } const fn neg(i: i64) -> Self { Discriminant { sign: if i < 0 { Sign::Negative } else { Sign::Positive }, // This is `i.abs() as u64` but without overflow on MIN. Uses the // fact that MIN.wrapping_abs() wraps back to MIN whose binary // representation is 1<<63, and thus the `as u64` conversion // produces 1<<63 too which happens to be the correct unsigned // magnitude. magnitude: i.wrapping_abs() as u64, } } #[cfg(feature = "experimental-enum-variants-from-header")] pub(crate) const fn checked_succ(self) -> Option { match self.sign { Sign::Negative => { if self.magnitude == 1 { Some(Discriminant::zero()) } else { Some(Discriminant { sign: Sign::Negative, magnitude: self.magnitude - 1, }) } } Sign::Positive => match self.magnitude.checked_add(1) { Some(magnitude) => Some(Discriminant { sign: Sign::Positive, magnitude, }), None => None, }, } } } impl Display for Discriminant { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.sign == Sign::Negative { f.write_str("-")?; } write!(f, "{}", self.magnitude) } } impl ToTokens for Discriminant { fn to_tokens(&self, tokens: &mut TokenStream) { if self.sign == Sign::Negative { Token![-](Span::call_site()).to_tokens(tokens); } Literal::u64_unsuffixed(self.magnitude).to_tokens(tokens); } } impl FromStr for Discriminant { type Err = Error; fn from_str(mut s: &str) -> Result { let sign = if s.starts_with('-') { s = &s[1..]; Sign::Negative } else { Sign::Positive }; match s.parse::() { Ok(magnitude) => Ok(Discriminant { sign, magnitude }), Err(_) => Err(Error::new( Span::call_site(), "discriminant value outside of supported range", )), } } } impl Ord for Discriminant { fn cmp(&self, other: &Self) -> Ordering { use self::Sign::{Negative, Positive}; match (self.sign, other.sign) { (Negative, Negative) => self.magnitude.cmp(&other.magnitude).reverse(), (Negative, Positive) => Ordering::Less, // negative < positive (Positive, Negative) => Ordering::Greater, // positive > negative (Positive, Positive) => self.magnitude.cmp(&other.magnitude), } } } impl PartialOrd for Discriminant { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } fn parse_int_suffix(suffix: &str) -> Result> { if suffix.is_empty() { return Ok(None); } if let Some(atom) = Atom::from_str(suffix) { match atom { U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize => return Ok(Some(atom)), _ => {} } } let msg = format!("unrecognized integer suffix: `{}`", suffix); Err(Error::new(Span::call_site(), msg)) } #[derive(Copy, Clone)] struct Limits { repr: Atom, min: Discriminant, max: Discriminant, } impl Limits { fn of(repr: Atom) -> Option { for limits in &LIMITS { if limits.repr == repr { return Some(*limits); } } None } } const LIMITS: [Limits; 8] = [ Limits { repr: U8, min: Discriminant::zero(), max: Discriminant::pos(u8::MAX as u64), }, Limits { repr: I8, min: Discriminant::neg(i8::MIN as i64), max: Discriminant::pos(i8::MAX as u64), }, Limits { repr: U16, min: Discriminant::zero(), max: Discriminant::pos(u16::MAX as u64), }, Limits { repr: I16, min: Discriminant::neg(i16::MIN as i64), max: Discriminant::pos(i16::MAX as u64), }, Limits { repr: U32, min: Discriminant::zero(), max: Discriminant::pos(u32::MAX as u64), }, Limits { repr: I32, min: Discriminant::neg(i32::MIN as i64), max: Discriminant::pos(i32::MAX as u64), }, Limits { repr: U64, min: Discriminant::zero(), max: Discriminant::pos(u64::MAX), }, Limits { repr: I64, min: Discriminant::neg(i64::MIN), max: Discriminant::pos(i64::MAX as u64), }, ]; cxxbridge-macro-1.0.141/src/syntax/doc.rs000064400000000000000000000021101046102023000162700ustar 00000000000000use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::LitStr; pub(crate) struct Doc { pub hidden: bool, fragments: Vec, } impl Doc { pub(crate) fn new() -> Self { Doc { hidden: false, fragments: Vec::new(), } } pub(crate) fn push(&mut self, lit: LitStr) { self.fragments.push(lit); } #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub(crate) fn is_empty(&self) -> bool { self.fragments.is_empty() } #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub(crate) fn to_string(&self) -> String { let mut doc = String::new(); for lit in &self.fragments { doc += &lit.value(); doc.push('\n'); } doc } } impl ToTokens for Doc { fn to_tokens(&self, tokens: &mut TokenStream) { let fragments = &self.fragments; tokens.extend(quote! { #(#[doc = #fragments])* }); if self.hidden { tokens.extend(quote! { #[doc(hidden)] }); } } } cxxbridge-macro-1.0.141/src/syntax/error.rs000064400000000000000000000060601046102023000166640ustar 00000000000000use std::fmt::{self, Display}; #[derive(Copy, Clone)] pub(crate) struct Error { pub msg: &'static str, #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub label: Option<&'static str>, #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub note: Option<&'static str>, } impl Display for Error { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.msg.fmt(formatter) } } pub(crate) static ERRORS: &[Error] = &[ BOX_CXX_TYPE, CXXBRIDGE_RESERVED, CXX_STRING_BY_VALUE, CXX_TYPE_BY_VALUE, DISCRIMINANT_OVERFLOW, DOT_INCLUDE, DOUBLE_UNDERSCORE, RESERVED_LIFETIME, RUST_TYPE_BY_VALUE, UNSUPPORTED_TYPE, USE_NOT_ALLOWED, ]; pub(crate) static BOX_CXX_TYPE: Error = Error { msg: "Box of a C++ type is not supported yet", label: None, note: Some("hint: use UniquePtr<> or SharedPtr<>"), }; pub(crate) static CXXBRIDGE_RESERVED: Error = Error { msg: "identifiers starting with cxxbridge are reserved", label: Some("reserved identifier"), note: Some("identifiers starting with cxxbridge are reserved"), }; pub(crate) static CXX_STRING_BY_VALUE: Error = Error { msg: "C++ string by value is not supported", label: None, note: Some("hint: wrap it in a UniquePtr<>"), }; pub(crate) static CXX_TYPE_BY_VALUE: Error = Error { msg: "C++ type by value is not supported", label: None, note: Some("hint: wrap it in a UniquePtr<> or SharedPtr<>"), }; pub(crate) static DISCRIMINANT_OVERFLOW: Error = Error { msg: "discriminant overflow on value after ", label: Some("discriminant overflow"), note: Some("note: explicitly set `= 0` if that is desired outcome"), }; pub(crate) static DOT_INCLUDE: Error = Error { msg: "#include relative to `.` or `..` is not supported in Cargo builds", label: Some("#include relative to `.` or `..` is not supported in Cargo builds"), note: Some("note: use a path starting with the crate name"), }; pub(crate) static DOUBLE_UNDERSCORE: Error = Error { msg: "identifiers containing double underscore are reserved in C++", label: Some("reserved identifier"), note: Some("identifiers containing double underscore are reserved in C++"), }; pub(crate) static RESERVED_LIFETIME: Error = Error { msg: "invalid lifetime parameter name: `'static`", label: Some("'static is a reserved lifetime name"), note: None, }; pub(crate) static RUST_TYPE_BY_VALUE: Error = Error { msg: "opaque Rust type by value is not supported", label: None, note: Some("hint: wrap it in a Box<>"), }; pub(crate) static UNSUPPORTED_TYPE: Error = Error { msg: "unsupported type: ", label: Some("unsupported type"), note: None, }; pub(crate) static USE_NOT_ALLOWED: Error = Error { msg: "`use` items are not allowed within cxx bridge", label: Some("not allowed"), note: Some( "`use` items are not allowed within cxx bridge; only types defined\n\ within your bridge, primitive types, or types exported by the cxx\n\ crate may be used", ), }; cxxbridge-macro-1.0.141/src/syntax/file.rs000064400000000000000000000077361046102023000164650ustar 00000000000000use crate::syntax::cfg::CfgExpr; use crate::syntax::namespace::Namespace; use quote::quote; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::{ braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemImpl, ItemStruct, ItemUse, LitStr, Token, Visibility, }; pub(crate) struct Module { #[allow(dead_code)] pub cfg: CfgExpr, pub namespace: Namespace, pub attrs: Vec, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub vis: Visibility, pub unsafety: Option, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub mod_token: Token![mod], #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub ident: Ident, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub brace_token: token::Brace, pub content: Vec, } pub(crate) enum Item { Struct(ItemStruct), Enum(ItemEnum), ForeignMod(ItemForeignMod), Use(ItemUse), Impl(ItemImpl), Other(RustItem), } pub(crate) struct ItemForeignMod { pub attrs: Vec, pub unsafety: Option, pub abi: Abi, #[allow(dead_code)] pub brace_token: token::Brace, pub items: Vec, } impl Parse for Module { fn parse(input: ParseStream) -> Result { let cfg = CfgExpr::Unconditional; let namespace = Namespace::ROOT; let mut attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; let unsafety: Option = input.parse()?; let mod_token: Token![mod] = input.parse()?; let ident: Ident = input.parse()?; let semi: Option = input.parse()?; if let Some(semi) = semi { let span = quote!(#vis #mod_token #semi); return Err(Error::new_spanned( span, "#[cxx::bridge] module must have inline contents", )); } let content; let brace_token = braced!(content in input); attrs.extend(content.call(Attribute::parse_inner)?); let mut items = Vec::new(); while !content.is_empty() { items.push(content.parse()?); } Ok(Module { cfg, namespace, attrs, vis, unsafety, mod_token, ident, brace_token, content: items, }) } } impl Parse for Item { fn parse(input: ParseStream) -> Result { let attrs = input.call(Attribute::parse_outer)?; let ahead = input.fork(); let unsafety = if ahead.parse::>()?.is_some() && ahead.parse::>()?.is_some() && ahead.parse::>().is_ok() && ahead.peek(token::Brace) { Some(input.parse()?) } else { None }; let item = input.parse()?; match item { RustItem::Struct(mut item) => { item.attrs.splice(..0, attrs); Ok(Item::Struct(item)) } RustItem::Enum(mut item) => { item.attrs.splice(..0, attrs); Ok(Item::Enum(item)) } RustItem::ForeignMod(mut item) => { item.attrs.splice(..0, attrs); Ok(Item::ForeignMod(ItemForeignMod { attrs: item.attrs, unsafety, abi: item.abi, brace_token: item.brace_token, items: item.items, })) } RustItem::Impl(mut item) => { item.attrs.splice(..0, attrs); Ok(Item::Impl(item)) } RustItem::Use(mut item) => { item.attrs.splice(..0, attrs); Ok(Item::Use(item)) } other => Ok(Item::Other(other)), } } } cxxbridge-macro-1.0.141/src/syntax/ident.rs000064400000000000000000000033071046102023000166370ustar 00000000000000use crate::syntax::check::Check; use crate::syntax::{error, Api, Pair}; fn check(cx: &mut Check, name: &Pair) { for segment in &name.namespace { check_cxx_ident(cx, &segment.to_string()); } check_cxx_ident(cx, &name.cxx.to_string()); check_rust_ident(cx, &name.rust.to_string()); fn check_cxx_ident(cx: &mut Check, ident: &str) { if ident.starts_with("cxxbridge") { cx.error(ident, error::CXXBRIDGE_RESERVED.msg); } if ident.contains("__") { cx.error(ident, error::DOUBLE_UNDERSCORE.msg); } } fn check_rust_ident(cx: &mut Check, ident: &str) { if ident.starts_with("cxxbridge") { cx.error(ident, error::CXXBRIDGE_RESERVED.msg); } } } pub(crate) fn check_all(cx: &mut Check, apis: &[Api]) { for api in apis { match api { Api::Include(_) | Api::Impl(_) => {} Api::Struct(strct) => { check(cx, &strct.name); for field in &strct.fields { check(cx, &field.name); } } Api::Enum(enm) => { check(cx, &enm.name); for variant in &enm.variants { check(cx, &variant.name); } } Api::CxxType(ety) | Api::RustType(ety) => { check(cx, &ety.name); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { check(cx, &efn.name); for arg in &efn.args { check(cx, &arg.name); } } Api::TypeAlias(alias) => { check(cx, &alias.name); } } } } cxxbridge-macro-1.0.141/src/syntax/impls.rs000064400000000000000000000253321046102023000166620ustar 00000000000000use crate::syntax::{ Array, ExternFn, Include, Lifetimes, Ptr, Receiver, Ref, Signature, SliceRef, Ty1, Type, Var, }; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::{Deref, DerefMut}; impl PartialEq for Include { fn eq(&self, other: &Self) -> bool { let Include { cfg: _, path, kind, begin_span: _, end_span: _, } = self; let Include { cfg: _, path: path2, kind: kind2, begin_span: _, end_span: _, } = other; path == path2 && kind == kind2 } } impl Deref for ExternFn { type Target = Signature; fn deref(&self) -> &Self::Target { &self.sig } } impl DerefMut for ExternFn { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.sig } } impl Hash for Type { fn hash(&self, state: &mut H) { mem::discriminant(self).hash(state); match self { Type::Ident(t) => t.hash(state), Type::RustBox(t) => t.hash(state), Type::UniquePtr(t) => t.hash(state), Type::SharedPtr(t) => t.hash(state), Type::WeakPtr(t) => t.hash(state), Type::Ref(t) => t.hash(state), Type::Ptr(t) => t.hash(state), Type::Str(t) => t.hash(state), Type::RustVec(t) => t.hash(state), Type::CxxVector(t) => t.hash(state), Type::Fn(t) => t.hash(state), Type::SliceRef(t) => t.hash(state), Type::Array(t) => t.hash(state), Type::Void(_) => {} } } } impl Eq for Type {} impl PartialEq for Type { fn eq(&self, other: &Self) -> bool { match (self, other) { (Type::Ident(lhs), Type::Ident(rhs)) => lhs == rhs, (Type::RustBox(lhs), Type::RustBox(rhs)) => lhs == rhs, (Type::UniquePtr(lhs), Type::UniquePtr(rhs)) => lhs == rhs, (Type::SharedPtr(lhs), Type::SharedPtr(rhs)) => lhs == rhs, (Type::WeakPtr(lhs), Type::WeakPtr(rhs)) => lhs == rhs, (Type::Ref(lhs), Type::Ref(rhs)) => lhs == rhs, (Type::Str(lhs), Type::Str(rhs)) => lhs == rhs, (Type::RustVec(lhs), Type::RustVec(rhs)) => lhs == rhs, (Type::CxxVector(lhs), Type::CxxVector(rhs)) => lhs == rhs, (Type::Fn(lhs), Type::Fn(rhs)) => lhs == rhs, (Type::SliceRef(lhs), Type::SliceRef(rhs)) => lhs == rhs, (Type::Void(_), Type::Void(_)) => true, (_, _) => false, } } } impl Eq for Lifetimes {} impl PartialEq for Lifetimes { fn eq(&self, other: &Self) -> bool { let Lifetimes { lt_token: _, lifetimes, gt_token: _, } = self; let Lifetimes { lt_token: _, lifetimes: lifetimes2, gt_token: _, } = other; lifetimes.iter().eq(lifetimes2) } } impl Hash for Lifetimes { fn hash(&self, state: &mut H) { let Lifetimes { lt_token: _, lifetimes, gt_token: _, } = self; lifetimes.len().hash(state); for lifetime in lifetimes { lifetime.hash(state); } } } impl Eq for Ty1 {} impl PartialEq for Ty1 { fn eq(&self, other: &Self) -> bool { let Ty1 { name, langle: _, inner, rangle: _, } = self; let Ty1 { name: name2, langle: _, inner: inner2, rangle: _, } = other; name == name2 && inner == inner2 } } impl Hash for Ty1 { fn hash(&self, state: &mut H) { let Ty1 { name, langle: _, inner, rangle: _, } = self; name.hash(state); inner.hash(state); } } impl Eq for Ref {} impl PartialEq for Ref { fn eq(&self, other: &Self) -> bool { let Ref { pinned, ampersand: _, lifetime, mutable, inner, pin_tokens: _, mutability: _, } = self; let Ref { pinned: pinned2, ampersand: _, lifetime: lifetime2, mutable: mutable2, inner: inner2, pin_tokens: _, mutability: _, } = other; pinned == pinned2 && lifetime == lifetime2 && mutable == mutable2 && inner == inner2 } } impl Hash for Ref { fn hash(&self, state: &mut H) { let Ref { pinned, ampersand: _, lifetime, mutable, inner, pin_tokens: _, mutability: _, } = self; pinned.hash(state); lifetime.hash(state); mutable.hash(state); inner.hash(state); } } impl Eq for Ptr {} impl PartialEq for Ptr { fn eq(&self, other: &Ptr) -> bool { let Ptr { star: _, mutable, inner, mutability: _, constness: _, } = self; let Ptr { star: _, mutable: mutable2, inner: inner2, mutability: _, constness: _, } = other; mutable == mutable2 && inner == inner2 } } impl Hash for Ptr { fn hash(&self, state: &mut H) { let Ptr { star: _, mutable, inner, mutability: _, constness: _, } = self; mutable.hash(state); inner.hash(state); } } impl Eq for SliceRef {} impl PartialEq for SliceRef { fn eq(&self, other: &Self) -> bool { let SliceRef { ampersand: _, lifetime, mutable, bracket: _, inner, mutability: _, } = self; let SliceRef { ampersand: _, lifetime: lifetime2, mutable: mutable2, bracket: _, inner: inner2, mutability: _, } = other; lifetime == lifetime2 && mutable == mutable2 && inner == inner2 } } impl Hash for SliceRef { fn hash(&self, state: &mut H) { let SliceRef { ampersand: _, lifetime, mutable, bracket: _, inner, mutability: _, } = self; lifetime.hash(state); mutable.hash(state); inner.hash(state); } } impl Eq for Array {} impl PartialEq for Array { fn eq(&self, other: &Self) -> bool { let Array { bracket: _, inner, semi_token: _, len, len_token: _, } = self; let Array { bracket: _, inner: inner2, semi_token: _, len: len2, len_token: _, } = other; inner == inner2 && len == len2 } } impl Hash for Array { fn hash(&self, state: &mut H) { let Array { bracket: _, inner, semi_token: _, len, len_token: _, } = self; inner.hash(state); len.hash(state); } } impl Eq for Signature {} impl PartialEq for Signature { fn eq(&self, other: &Self) -> bool { let Signature { asyncness, unsafety, fn_token: _, generics: _, receiver, args, ret, throws, paren_token: _, throws_tokens: _, } = self; let Signature { asyncness: asyncness2, unsafety: unsafety2, fn_token: _, generics: _, receiver: receiver2, args: args2, ret: ret2, throws: throws2, paren_token: _, throws_tokens: _, } = other; asyncness.is_some() == asyncness2.is_some() && unsafety.is_some() == unsafety2.is_some() && receiver == receiver2 && ret == ret2 && throws == throws2 && args.len() == args2.len() && args.iter().zip(args2).all(|(arg, arg2)| { let Var { cfg: _, doc: _, attrs: _, visibility: _, name: _, colon_token: _, ty, } = arg; let Var { cfg: _, doc: _, attrs: _, visibility: _, name: _, colon_token: _, ty: ty2, } = arg2; ty == ty2 }) } } impl Hash for Signature { fn hash(&self, state: &mut H) { let Signature { asyncness, unsafety, fn_token: _, generics: _, receiver, args, ret, throws, paren_token: _, throws_tokens: _, } = self; asyncness.is_some().hash(state); unsafety.is_some().hash(state); receiver.hash(state); for arg in args { let Var { cfg: _, doc: _, attrs: _, visibility: _, name: _, colon_token: _, ty, } = arg; ty.hash(state); } ret.hash(state); throws.hash(state); } } impl Eq for Receiver {} impl PartialEq for Receiver { fn eq(&self, other: &Self) -> bool { let Receiver { pinned, ampersand: _, lifetime, mutable, var: _, colon_token: _, ty, shorthand: _, pin_tokens: _, mutability: _, } = self; let Receiver { pinned: pinned2, ampersand: _, lifetime: lifetime2, mutable: mutable2, var: _, colon_token: _, ty: ty2, shorthand: _, pin_tokens: _, mutability: _, } = other; pinned == pinned2 && lifetime == lifetime2 && mutable == mutable2 && ty == ty2 } } impl Hash for Receiver { fn hash(&self, state: &mut H) { let Receiver { pinned, ampersand: _, lifetime, mutable, var: _, colon_token: _, ty, shorthand: _, pin_tokens: _, mutability: _, } = self; pinned.hash(state); lifetime.hash(state); mutable.hash(state); ty.hash(state); } } cxxbridge-macro-1.0.141/src/syntax/improper.rs000064400000000000000000000025671046102023000174000ustar 00000000000000use self::ImproperCtype::*; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::{Type, Types}; use proc_macro2::Ident; pub(crate) enum ImproperCtype<'a> { Definite(bool), Depends(&'a Ident), } impl<'a> Types<'a> { // yes, no, maybe pub(crate) fn determine_improper_ctype(&self, ty: &Type) -> ImproperCtype<'a> { match ty { Type::Ident(ident) => { let ident = &ident.rust; if let Some(atom) = Atom::from(ident) { Definite(atom == RustString) } else if let Some(strct) = self.structs.get(ident) { Depends(&strct.name.rust) // iterate to fixed-point } else { Definite(self.rust.contains(ident) || self.aliases.contains_key(ident)) } } Type::RustBox(_) | Type::RustVec(_) | Type::Str(_) | Type::Fn(_) | Type::Void(_) | Type::SliceRef(_) => Definite(true), Type::UniquePtr(_) | Type::SharedPtr(_) | Type::WeakPtr(_) | Type::CxxVector(_) => { Definite(false) } Type::Ref(ty) => self.determine_improper_ctype(&ty.inner), Type::Ptr(ty) => self.determine_improper_ctype(&ty.inner), Type::Array(ty) => self.determine_improper_ctype(&ty.inner), } } } cxxbridge-macro-1.0.141/src/syntax/instantiate.rs000064400000000000000000000053321046102023000200570ustar 00000000000000use crate::syntax::{NamedType, Ty1, Type}; use proc_macro2::{Ident, Span}; use std::hash::{Hash, Hasher}; use syn::Token; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub(crate) enum ImplKey<'a> { RustBox(NamedImplKey<'a>), RustVec(NamedImplKey<'a>), UniquePtr(NamedImplKey<'a>), SharedPtr(NamedImplKey<'a>), WeakPtr(NamedImplKey<'a>), CxxVector(NamedImplKey<'a>), } #[derive(Copy, Clone)] pub(crate) struct NamedImplKey<'a> { #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub begin_span: Span, pub rust: &'a Ident, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub lt_token: Option, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub gt_token: Option]>, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub end_span: Span, } impl Type { pub(crate) fn impl_key(&self) -> Option { if let Type::RustBox(ty) = self { if let Type::Ident(ident) = &ty.inner { return Some(ImplKey::RustBox(NamedImplKey::new(ty, ident))); } } else if let Type::RustVec(ty) = self { if let Type::Ident(ident) = &ty.inner { return Some(ImplKey::RustVec(NamedImplKey::new(ty, ident))); } } else if let Type::UniquePtr(ty) = self { if let Type::Ident(ident) = &ty.inner { return Some(ImplKey::UniquePtr(NamedImplKey::new(ty, ident))); } } else if let Type::SharedPtr(ty) = self { if let Type::Ident(ident) = &ty.inner { return Some(ImplKey::SharedPtr(NamedImplKey::new(ty, ident))); } } else if let Type::WeakPtr(ty) = self { if let Type::Ident(ident) = &ty.inner { return Some(ImplKey::WeakPtr(NamedImplKey::new(ty, ident))); } } else if let Type::CxxVector(ty) = self { if let Type::Ident(ident) = &ty.inner { return Some(ImplKey::CxxVector(NamedImplKey::new(ty, ident))); } } None } } impl<'a> PartialEq for NamedImplKey<'a> { fn eq(&self, other: &Self) -> bool { PartialEq::eq(self.rust, other.rust) } } impl<'a> Eq for NamedImplKey<'a> {} impl<'a> Hash for NamedImplKey<'a> { fn hash(&self, hasher: &mut H) { self.rust.hash(hasher); } } impl<'a> NamedImplKey<'a> { fn new(outer: &Ty1, inner: &'a NamedType) -> Self { NamedImplKey { begin_span: outer.name.span(), rust: &inner.rust, lt_token: inner.generics.lt_token, gt_token: inner.generics.gt_token, end_span: outer.rangle.span, } } } cxxbridge-macro-1.0.141/src/syntax/mangle.rs000064400000000000000000000101631046102023000167750ustar 00000000000000// Mangled symbol arrangements: // // (a) One-off internal symbol. // pattern: {CXXBRIDGE} $ {NAME} // examples: // - cxxbridge1$exception // defining characteristics: // - 2 segments // - starts with cxxbridge // // (b) Behavior on a builtin binding without generic parameter. // pattern: {CXXBRIDGE} $ {TYPE} $ {NAME} // examples: // - cxxbridge1$string$len // defining characteristics: // - 3 segments // - starts with cxxbridge // // (c) Behavior on a builtin binding with generic parameter. // pattern: {CXXBRIDGE} $ {TYPE} $ {PARAM...} $ {NAME} // examples: // - cxxbridge1$box$org$rust$Struct$alloc // - cxxbridge1$unique_ptr$std$vector$u8$drop // defining characteristics: // - 4+ segments // - starts with cxxbridge // // (d) User-defined extern function. // pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {NAME} // examples: // - cxxbridge1$new_client // - org$rust$cxxbridge1$new_client // defining characteristics: // - cxxbridge is second from end // FIXME: conflict with (a) if they collide with one of our one-off symbol names in the global namespace // // (e) User-defined extern member function. // pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE} $ {NAME} // examples: // - org$cxxbridge1$Struct$get // defining characteristics: // - cxxbridge is third from end // FIXME: conflict with (b) if e.g. user binds a type in global namespace that collides with our builtin type names // // (f) Operator overload. // pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE} $ operator $ {NAME} // examples: // - org$rust$cxxbridge1$Struct$operator$eq // defining characteristics: // - second segment from end is `operator` (not possible in type or namespace names) // // (g) Closure trampoline. // pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE?} $ {NAME} $ {ARGUMENT} $ {DIRECTION} // examples: // - org$rust$cxxbridge1$Struct$invoke$f$0 // defining characteristics: // - last symbol is `0` (C half) or `1` (Rust half) which are not legal identifiers on their own // // // Mangled preprocessor variable arrangements: // // (A) One-off internal variable. // pattern: {CXXBRIDGE} _ {NAME} // examples: // - CXXBRIDGE1_PANIC // - CXXBRIDGE1_RUST_STRING // defining characteristics: // - NAME does not begin with STRUCT or ENUM // // (B) Guard around user-defined type. // pattern: {CXXBRIDGE} _ {STRUCT or ENUM} _ {NAMESPACE...} $ {TYPE} // examples: // - CXXBRIDGE1_STRUCT_org$rust$Struct // - CXXBRIDGE1_ENUM_Enabled use crate::syntax::symbol::{self, Symbol}; use crate::syntax::{ExternFn, Pair, Types}; const CXXBRIDGE: &str = "cxxbridge1"; macro_rules! join { ($($segment:expr),+ $(,)?) => { symbol::join(&[$(&$segment),+]) }; } pub(crate) fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol { match &efn.receiver { Some(receiver) => { let receiver_ident = types.resolve(&receiver.ty); join!( efn.name.namespace, CXXBRIDGE, receiver_ident.name.cxx, efn.name.rust, ) } None => join!(efn.name.namespace, CXXBRIDGE, efn.name.rust), } } pub(crate) fn operator(receiver: &Pair, operator: &'static str) -> Symbol { join!( receiver.namespace, CXXBRIDGE, receiver.cxx, "operator", operator, ) } // The C half of a function pointer trampoline. pub(crate) fn c_trampoline(efn: &ExternFn, var: &Pair, types: &Types) -> Symbol { join!(extern_fn(efn, types), var.rust, 0) } // The Rust half of a function pointer trampoline. pub(crate) fn r_trampoline(efn: &ExternFn, var: &Pair, types: &Types) -> Symbol { join!(extern_fn(efn, types), var.rust, 1) } cxxbridge-macro-1.0.141/src/syntax/map.rs000064400000000000000000000103361046102023000163110ustar 00000000000000use std::borrow::Borrow; use std::hash::Hash; use std::ops::Index; use std::slice; pub(crate) use self::ordered::OrderedMap; pub(crate) use self::unordered::UnorderedMap; pub(crate) use std::collections::hash_map::Entry; mod ordered { use super::{Entry, Iter, UnorderedMap}; use std::borrow::Borrow; use std::hash::Hash; use std::mem; pub(crate) struct OrderedMap { map: UnorderedMap, vec: Vec<(K, V)>, } impl OrderedMap { pub(crate) fn new() -> Self { OrderedMap { map: UnorderedMap::new(), vec: Vec::new(), } } pub(crate) fn iter(&self) -> Iter { Iter(self.vec.iter()) } #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub(crate) fn keys(&self) -> impl Iterator { self.vec.iter().map(|(k, _v)| k) } } impl OrderedMap where K: Copy + Hash + Eq, { pub(crate) fn insert(&mut self, key: K, value: V) -> Option { match self.map.entry(key) { Entry::Occupied(entry) => { let i = &mut self.vec[*entry.get()]; Some(mem::replace(&mut i.1, value)) } Entry::Vacant(entry) => { entry.insert(self.vec.len()); self.vec.push((key, value)); None } } } pub(crate) fn contains_key(&self, key: &Q) -> bool where K: Borrow, Q: ?Sized + Hash + Eq, { self.map.contains_key(key) } } impl<'a, K, V> IntoIterator for &'a OrderedMap { type Item = (&'a K, &'a V); type IntoIter = Iter<'a, K, V>; fn into_iter(self) -> Self::IntoIter { self.iter() } } } mod unordered { use crate::syntax::set::UnorderedSet; use std::borrow::Borrow; use std::collections::hash_map::{Entry, HashMap}; use std::hash::Hash; // Wrapper prohibits accidentally introducing iteration over the map, which // could lead to nondeterministic generated code. pub(crate) struct UnorderedMap(HashMap); impl UnorderedMap { pub(crate) fn new() -> Self { UnorderedMap(HashMap::new()) } } impl UnorderedMap where K: Hash + Eq, { pub(crate) fn insert(&mut self, key: K, value: V) -> Option { self.0.insert(key, value) } pub(crate) fn contains_key(&self, key: &Q) -> bool where K: Borrow, Q: ?Sized + Hash + Eq, { self.0.contains_key(key) } pub(crate) fn get(&self, key: &Q) -> Option<&V> where K: Borrow, Q: ?Sized + Hash + Eq, { self.0.get(key) } pub(crate) fn entry(&mut self, key: K) -> Entry { self.0.entry(key) } #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub(crate) fn remove(&mut self, key: &Q) -> Option where K: Borrow, Q: ?Sized + Hash + Eq, { self.0.remove(key) } pub(crate) fn keys(&self) -> UnorderedSet where K: Copy, { let mut set = UnorderedSet::new(); for key in self.0.keys() { set.insert(*key); } set } } } pub(crate) struct Iter<'a, K, V>(slice::Iter<'a, (K, V)>); impl<'a, K, V> Iterator for Iter<'a, K, V> { type Item = (&'a K, &'a V); fn next(&mut self) -> Option { let (k, v) = self.0.next()?; Some((k, v)) } fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } } impl Default for UnorderedMap { fn default() -> Self { UnorderedMap::new() } } impl Index<&Q> for UnorderedMap where K: Borrow + Hash + Eq, Q: ?Sized + Hash + Eq, { type Output = V; fn index(&self, key: &Q) -> &V { self.get(key).unwrap() } } cxxbridge-macro-1.0.141/src/syntax/mod.rs000064400000000000000000000225001046102023000163070ustar 00000000000000// Functionality that is shared between the cxxbridge macro and the cmd. pub(crate) mod atom; pub(crate) mod attrs; pub(crate) mod cfg; pub(crate) mod check; pub(crate) mod derive; mod discriminant; mod doc; pub(crate) mod error; pub(crate) mod file; pub(crate) mod ident; mod impls; mod improper; pub(crate) mod instantiate; pub(crate) mod mangle; pub(crate) mod map; mod names; pub(crate) mod namespace; mod parse; mod pod; pub(crate) mod qualified; pub(crate) mod report; pub(crate) mod resolve; pub(crate) mod set; pub(crate) mod symbol; mod tokens; mod toposort; pub(crate) mod trivial; pub(crate) mod types; mod visit; use self::attrs::OtherAttrs; use self::cfg::CfgExpr; use self::namespace::Namespace; use self::parse::kw; use self::symbol::Symbol; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; use syn::token::{Brace, Bracket, Paren}; use syn::{Attribute, Expr, Generics, Lifetime, LitInt, Token, Type as RustType}; pub(crate) use self::atom::Atom; pub(crate) use self::derive::{Derive, Trait}; pub(crate) use self::discriminant::Discriminant; pub(crate) use self::doc::Doc; pub(crate) use self::names::ForeignName; pub(crate) use self::parse::parse_items; pub(crate) use self::types::Types; pub(crate) enum Api { #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro Include(Include), Struct(Struct), Enum(Enum), CxxType(ExternType), CxxFunction(ExternFn), RustType(ExternType), RustFunction(ExternFn), TypeAlias(TypeAlias), Impl(Impl), } pub(crate) struct Include { pub cfg: CfgExpr, pub path: String, pub kind: IncludeKind, #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub begin_span: Span, #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub end_span: Span, } /// Whether to emit `#include "path"` or `#include `. #[derive(Copy, Clone, PartialEq, Debug)] pub enum IncludeKind { /// `#include "quoted/path/to"` Quoted, /// `#include ` Bracketed, } pub(crate) struct ExternType { #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub cfg: CfgExpr, pub lang: Lang, pub doc: Doc, pub derives: Vec, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub attrs: OtherAttrs, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub visibility: Token![pub], pub type_token: Token![type], pub name: Pair, pub generics: Lifetimes, #[allow(dead_code)] pub colon_token: Option, pub bounds: Vec, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub semi_token: Token![;], pub trusted: bool, } pub(crate) struct Struct { #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub cfg: CfgExpr, pub doc: Doc, pub derives: Vec, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub attrs: OtherAttrs, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub visibility: Token![pub], pub struct_token: Token![struct], pub name: Pair, pub generics: Lifetimes, pub brace_token: Brace, pub fields: Vec, } pub(crate) struct Enum { #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub cfg: CfgExpr, pub doc: Doc, pub derives: Vec, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub attrs: OtherAttrs, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub visibility: Token![pub], pub enum_token: Token![enum], pub name: Pair, pub generics: Lifetimes, pub brace_token: Brace, pub variants: Vec, pub variants_from_header: bool, #[allow(dead_code)] pub variants_from_header_attr: Option, pub repr: EnumRepr, pub explicit_repr: bool, } pub(crate) enum EnumRepr { Native { atom: Atom, repr_type: Type, }, #[cfg(feature = "experimental-enum-variants-from-header")] Foreign { rust_type: syn::Path, }, } pub(crate) struct ExternFn { #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub cfg: CfgExpr, pub lang: Lang, pub doc: Doc, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub attrs: OtherAttrs, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub visibility: Token![pub], pub name: Pair, pub sig: Signature, pub semi_token: Token![;], pub trusted: bool, } pub(crate) struct TypeAlias { #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub cfg: CfgExpr, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub doc: Doc, pub derives: Vec, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub attrs: OtherAttrs, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub visibility: Token![pub], pub type_token: Token![type], pub name: Pair, pub generics: Lifetimes, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub eq_token: Token![=], #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub ty: RustType, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub semi_token: Token![;], } pub(crate) struct Impl { #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub cfg: CfgExpr, pub impl_token: Token![impl], pub impl_generics: Lifetimes, #[allow(dead_code)] pub negative: bool, pub ty: Type, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub ty_generics: Lifetimes, pub brace_token: Brace, pub negative_token: Option, } #[derive(Clone, Default)] pub(crate) struct Lifetimes { pub lt_token: Option, pub lifetimes: Punctuated, pub gt_token: Option]>, } pub(crate) struct Signature { pub asyncness: Option, pub unsafety: Option, pub fn_token: Token![fn], pub generics: Generics, pub receiver: Option, pub args: Punctuated, pub ret: Option, pub throws: bool, pub paren_token: Paren, pub throws_tokens: Option<(kw::Result, Token![<], Token![>])>, } pub(crate) struct Var { #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub cfg: CfgExpr, pub doc: Doc, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub attrs: OtherAttrs, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub visibility: Token![pub], pub name: Pair, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub colon_token: Token![:], pub ty: Type, } pub(crate) struct Receiver { pub pinned: bool, pub ampersand: Token![&], pub lifetime: Option, pub mutable: bool, pub var: Token![self], pub ty: NamedType, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub colon_token: Token![:], pub shorthand: bool, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub pin_tokens: Option<(kw::Pin, Token![<], Token![>])>, pub mutability: Option, } pub(crate) struct Variant { #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro pub cfg: CfgExpr, pub doc: Doc, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub attrs: OtherAttrs, pub name: Pair, pub discriminant: Discriminant, #[allow(dead_code)] pub expr: Option, } pub(crate) enum Type { Ident(NamedType), RustBox(Box), RustVec(Box), UniquePtr(Box), SharedPtr(Box), WeakPtr(Box), Ref(Box), Ptr(Box), Str(Box), CxxVector(Box), Fn(Box), Void(Span), SliceRef(Box), Array(Box), } pub(crate) struct Ty1 { pub name: Ident, pub langle: Token![<], pub inner: Type, pub rangle: Token![>], } pub(crate) struct Ref { pub pinned: bool, pub ampersand: Token![&], pub lifetime: Option, pub mutable: bool, pub inner: Type, pub pin_tokens: Option<(kw::Pin, Token![<], Token![>])>, pub mutability: Option, } pub(crate) struct Ptr { pub star: Token![*], pub mutable: bool, pub inner: Type, pub mutability: Option, pub constness: Option, } pub(crate) struct SliceRef { pub ampersand: Token![&], pub lifetime: Option, pub mutable: bool, pub bracket: Bracket, pub inner: Type, pub mutability: Option, } pub(crate) struct Array { pub bracket: Bracket, pub inner: Type, pub semi_token: Token![;], pub len: usize, pub len_token: LitInt, } #[derive(Copy, Clone, PartialEq)] pub(crate) enum Lang { Cxx, Rust, } // An association of a defined Rust name with a fully resolved, namespace // qualified C++ name. #[derive(Clone)] pub(crate) struct Pair { pub namespace: Namespace, pub cxx: ForeignName, pub rust: Ident, } // Wrapper for a type which needs to be resolved before it can be printed in // C++. #[derive(PartialEq, Eq, Hash)] pub(crate) struct NamedType { pub rust: Ident, pub generics: Lifetimes, } cxxbridge-macro-1.0.141/src/syntax/names.rs000064400000000000000000000030751046102023000166410ustar 00000000000000use crate::syntax::symbol::Segment; use crate::syntax::{Lifetimes, NamedType, Pair, Symbol}; use proc_macro2::{Ident, Span}; use std::fmt::{self, Display}; use std::iter; use syn::ext::IdentExt; use syn::parse::{Error, Parser, Result}; use syn::punctuated::Punctuated; #[derive(Clone)] pub(crate) struct ForeignName { text: String, } impl Pair { pub(crate) fn to_symbol(&self) -> Symbol { let segments = self .namespace .iter() .map(|ident| ident as &dyn Segment) .chain(iter::once(&self.cxx as &dyn Segment)); Symbol::from_idents(segments) } } impl NamedType { pub(crate) fn new(rust: Ident) -> Self { let generics = Lifetimes { lt_token: None, lifetimes: Punctuated::new(), gt_token: None, }; NamedType { rust, generics } } } impl ForeignName { pub(crate) fn parse(text: &str, span: Span) -> Result { // TODO: support C++ names containing whitespace (`unsigned int`) or // non-alphanumeric characters (`operator++`). match Ident::parse_any.parse_str(text) { Ok(ident) => { let text = ident.to_string(); Ok(ForeignName { text }) } Err(err) => Err(Error::new(span, err)), } } } impl Display for ForeignName { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str(&self.text) } } impl PartialEq for ForeignName { fn eq(&self, rhs: &str) -> bool { self.text == rhs } } cxxbridge-macro-1.0.141/src/syntax/namespace.rs000064400000000000000000000061171046102023000174720ustar 00000000000000use crate::syntax::qualified::QualifiedName; use quote::IdentFragment; use std::fmt::{self, Display}; use std::slice::Iter; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::{Expr, Ident, Lit, Meta, Token}; mod kw { syn::custom_keyword!(namespace); } #[derive(Clone, Default)] pub(crate) struct Namespace { segments: Vec, } impl Namespace { pub(crate) const ROOT: Self = Namespace { segments: Vec::new(), }; pub(crate) fn iter(&self) -> Iter { self.segments.iter() } pub(crate) fn parse_bridge_attr_namespace(input: ParseStream) -> Result { if input.is_empty() { return Ok(Namespace::ROOT); } input.parse::()?; input.parse::()?; let namespace = input.parse::()?; input.parse::>()?; Ok(namespace) } pub(crate) fn parse_meta(meta: &Meta) -> Result { if let Meta::NameValue(meta) = meta { match &meta.value { Expr::Lit(expr) => { if let Lit::Str(lit) = &expr.lit { let segments = QualifiedName::parse_quoted(lit)?.segments; return Ok(Namespace { segments }); } } Expr::Path(expr) if expr.qself.is_none() && expr .path .segments .iter() .all(|segment| segment.arguments.is_none()) => { let segments = expr .path .segments .iter() .map(|segment| segment.ident.clone()) .collect(); return Ok(Namespace { segments }); } _ => {} } } Err(Error::new_spanned(meta, "unsupported namespace attribute")) } } impl Default for &Namespace { fn default() -> Self { const ROOT: &Namespace = &Namespace::ROOT; ROOT } } impl Parse for Namespace { fn parse(input: ParseStream) -> Result { let segments = QualifiedName::parse_quoted_or_unquoted(input)?.segments; Ok(Namespace { segments }) } } impl Display for Namespace { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for segment in self { write!(f, "{}$", segment)?; } Ok(()) } } impl IdentFragment for Namespace { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { Display::fmt(self, f) } } impl<'a> IntoIterator for &'a Namespace { type Item = &'a Ident; type IntoIter = Iter<'a, Ident>; fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a> FromIterator<&'a Ident> for Namespace { fn from_iter(idents: I) -> Self where I: IntoIterator, { let segments = idents.into_iter().cloned().collect(); Namespace { segments } } } cxxbridge-macro-1.0.141/src/syntax/parse.rs000064400000000000000000001355631046102023000166600ustar 00000000000000use crate::syntax::attrs::OtherAttrs; use crate::syntax::cfg::CfgExpr; use crate::syntax::discriminant::DiscriminantSet; use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ attrs, error, Api, Array, Derive, Doc, Enum, EnumRepr, ExternFn, ExternType, ForeignName, Impl, Include, IncludeKind, Lang, Lifetimes, NamedType, Namespace, Pair, Ptr, Receiver, Ref, Signature, SliceRef, Struct, Ty1, Type, TypeAlias, Var, Variant, }; use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; use std::mem; use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ Abi, Attribute, Error, Expr, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, GenericParam, Generics, Ident, ItemEnum, ItemImpl, ItemStruct, Lit, LitStr, Pat, PathArguments, Result, ReturnType, Signature as RustSignature, Token, TraitBound, TraitBoundModifier, Type as RustType, TypeArray, TypeBareFn, TypeParamBound, TypePath, TypePtr, TypeReference, Variant as RustVariant, Visibility, }; pub(crate) mod kw { syn::custom_keyword!(Pin); syn::custom_keyword!(Result); } pub(crate) fn parse_items( cx: &mut Errors, items: Vec, trusted: bool, namespace: &Namespace, ) -> Vec { let mut apis = Vec::new(); for item in items { match item { Item::Struct(item) => match parse_struct(cx, item, namespace) { Ok(strct) => apis.push(strct), Err(err) => cx.push(err), }, Item::Enum(item) => apis.push(parse_enum(cx, item, namespace)), Item::ForeignMod(foreign_mod) => { parse_foreign_mod(cx, foreign_mod, &mut apis, trusted, namespace); } Item::Impl(item) => match parse_impl(cx, item) { Ok(imp) => apis.push(imp), Err(err) => cx.push(err), }, Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED), Item::Other(item) => cx.error(item, "unsupported item"), } } apis } fn parse_struct(cx: &mut Errors, mut item: ItemStruct, namespace: &Namespace) -> Result { let mut cfg = CfgExpr::Unconditional; let mut doc = Doc::new(); let mut derives = Vec::new(); let mut namespace = namespace.clone(); let mut cxx_name = None; let mut rust_name = None; let attrs = attrs::parse( cx, mem::take(&mut item.attrs), attrs::Parser { cfg: Some(&mut cfg), doc: Some(&mut doc), derives: Some(&mut derives), namespace: Some(&mut namespace), cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), ..Default::default() }, ); let named_fields = match item.fields { Fields::Named(fields) => fields, Fields::Unit => return Err(Error::new_spanned(item, "unit structs are not supported")), Fields::Unnamed(_) => { return Err(Error::new_spanned(item, "tuple structs are not supported")); } }; let mut lifetimes = Punctuated::new(); let mut has_unsupported_generic_param = false; for pair in item.generics.params.into_pairs() { let (param, punct) = pair.into_tuple(); match param { GenericParam::Lifetime(param) => { if !param.bounds.is_empty() && !has_unsupported_generic_param { let msg = "lifetime parameter with bounds is not supported yet"; cx.error(¶m, msg); has_unsupported_generic_param = true; } lifetimes.push_value(param.lifetime); if let Some(punct) = punct { lifetimes.push_punct(punct); } } GenericParam::Type(param) => { if !has_unsupported_generic_param { let msg = "struct with generic type parameter is not supported yet"; cx.error(¶m, msg); has_unsupported_generic_param = true; } } GenericParam::Const(param) => { if !has_unsupported_generic_param { let msg = "struct with const generic parameter is not supported yet"; cx.error(¶m, msg); has_unsupported_generic_param = true; } } } } if let Some(where_clause) = &item.generics.where_clause { cx.error( where_clause, "struct with where-clause is not supported yet", ); } let mut fields = Vec::new(); for field in named_fields.named { let ident = field.ident.unwrap(); let mut cfg = CfgExpr::Unconditional; let mut doc = Doc::new(); let mut cxx_name = None; let mut rust_name = None; let attrs = attrs::parse( cx, field.attrs, attrs::Parser { cfg: Some(&mut cfg), doc: Some(&mut doc), cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), ..Default::default() }, ); let ty = match parse_type(&field.ty) { Ok(ty) => ty, Err(err) => { cx.push(err); continue; } }; let visibility = visibility_pub(&field.vis, ident.span()); let name = pair(Namespace::default(), &ident, cxx_name, rust_name); let colon_token = field.colon_token.unwrap(); fields.push(Var { cfg, doc, attrs, visibility, name, colon_token, ty, }); } let struct_token = item.struct_token; let visibility = visibility_pub(&item.vis, struct_token.span); let name = pair(namespace, &item.ident, cxx_name, rust_name); let generics = Lifetimes { lt_token: item.generics.lt_token, lifetimes, gt_token: item.generics.gt_token, }; let brace_token = named_fields.brace_token; Ok(Api::Struct(Struct { cfg, doc, derives, attrs, visibility, struct_token, name, generics, brace_token, fields, })) } fn parse_enum(cx: &mut Errors, item: ItemEnum, namespace: &Namespace) -> Api { let mut cfg = CfgExpr::Unconditional; let mut doc = Doc::new(); let mut derives = Vec::new(); let mut repr = None; let mut namespace = namespace.clone(); let mut cxx_name = None; let mut rust_name = None; let mut variants_from_header = None; let attrs = attrs::parse( cx, item.attrs, attrs::Parser { cfg: Some(&mut cfg), doc: Some(&mut doc), derives: Some(&mut derives), repr: Some(&mut repr), namespace: Some(&mut namespace), cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), variants_from_header: Some(&mut variants_from_header), ..Default::default() }, ); if !item.generics.params.is_empty() { let vis = &item.vis; let enum_token = item.enum_token; let ident = &item.ident; let generics = &item.generics; let span = quote!(#vis #enum_token #ident #generics); cx.error(span, "enum with generic parameters is not supported"); } else if let Some(where_clause) = &item.generics.where_clause { cx.error(where_clause, "enum with where-clause is not supported"); } let mut variants = Vec::new(); let mut discriminants = DiscriminantSet::new(repr); for variant in item.variants { match parse_variant(cx, variant, &mut discriminants) { Ok(variant) => variants.push(variant), Err(err) => cx.push(err), } } let enum_token = item.enum_token; let visibility = visibility_pub(&item.vis, enum_token.span); let brace_token = item.brace_token; let explicit_repr = repr.is_some(); let mut repr = U8; match discriminants.inferred_repr() { Ok(inferred) => repr = inferred, Err(err) => { let span = quote_spanned!(brace_token.span=> #enum_token {}); cx.error(span, err); variants.clear(); } } let name = pair(namespace, &item.ident, cxx_name, rust_name); let repr_ident = Ident::new(repr.as_ref(), Span::call_site()); let repr_type = Type::Ident(NamedType::new(repr_ident)); let repr = EnumRepr::Native { atom: repr, repr_type, }; let generics = Lifetimes { lt_token: None, lifetimes: Punctuated::new(), gt_token: None, }; let variants_from_header_attr = variants_from_header; let variants_from_header = variants_from_header_attr.is_some(); Api::Enum(Enum { cfg, doc, derives, attrs, visibility, enum_token, name, generics, brace_token, variants, variants_from_header, variants_from_header_attr, repr, explicit_repr, }) } fn parse_variant( cx: &mut Errors, mut variant: RustVariant, discriminants: &mut DiscriminantSet, ) -> Result { let mut cfg = CfgExpr::Unconditional; let mut doc = Doc::new(); let mut cxx_name = None; let mut rust_name = None; let attrs = attrs::parse( cx, mem::take(&mut variant.attrs), attrs::Parser { cfg: Some(&mut cfg), doc: Some(&mut doc), cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), ..Default::default() }, ); match variant.fields { Fields::Unit => {} _ => { let msg = "enums with data are not supported yet"; return Err(Error::new_spanned(variant, msg)); } } let expr = variant.discriminant.as_ref().map(|(_, expr)| expr); let try_discriminant = match &expr { Some(lit) => discriminants.insert(lit), None => discriminants.insert_next(), }; let discriminant = match try_discriminant { Ok(discriminant) => discriminant, Err(err) => return Err(Error::new_spanned(variant, err)), }; let name = pair(Namespace::ROOT, &variant.ident, cxx_name, rust_name); let expr = variant.discriminant.map(|(_, expr)| expr); Ok(Variant { cfg, doc, attrs, name, discriminant, expr, }) } fn parse_foreign_mod( cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec, trusted: bool, namespace: &Namespace, ) { let lang = match parse_lang(&foreign_mod.abi) { Ok(lang) => lang, Err(err) => return cx.push(err), }; match lang { Lang::Rust => { if foreign_mod.unsafety.is_some() { let unsafety = foreign_mod.unsafety; let abi = &foreign_mod.abi; let span = quote!(#unsafety #abi); cx.error(span, "extern \"Rust\" block does not need to be unsafe"); } } Lang::Cxx => {} } let trusted = trusted || foreign_mod.unsafety.is_some(); let mut cfg = CfgExpr::Unconditional; let mut namespace = namespace.clone(); let attrs = attrs::parse( cx, foreign_mod.attrs, attrs::Parser { cfg: Some(&mut cfg), namespace: Some(&mut namespace), ..Default::default() }, ); let mut items = Vec::new(); for foreign in foreign_mod.items { match foreign { ForeignItem::Type(foreign) => { let ety = parse_extern_type(cx, foreign, lang, trusted, &cfg, &namespace, &attrs); items.push(ety); } ForeignItem::Fn(foreign) => { match parse_extern_fn(cx, foreign, lang, trusted, &cfg, &namespace, &attrs) { Ok(efn) => items.push(efn), Err(err) => cx.push(err), } } ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => { match foreign.mac.parse_body_with(parse_include) { Ok(mut include) => { include.cfg = cfg.clone(); items.push(Api::Include(include)); } Err(err) => cx.push(err), } } ForeignItem::Verbatim(tokens) => { match parse_extern_verbatim(cx, tokens, lang, trusted, &cfg, &namespace, &attrs) { Ok(api) => items.push(api), Err(err) => cx.push(err), } } _ => cx.error(foreign, "unsupported foreign item"), } } if !trusted && items.iter().any(|api| match api { Api::CxxFunction(efn) => efn.unsafety.is_none(), _ => false, }) { cx.error( foreign_mod.abi, "block must be declared `unsafe extern \"C++\"` if it contains any safe-to-call C++ functions", ); } let mut types = items.iter().filter_map(|item| match item { Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.name), Api::TypeAlias(alias) => Some(&alias.name), _ => None, }); if let (Some(single_type), None) = (types.next(), types.next()) { let single_type = single_type.clone(); for item in &mut items { if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item { if let Some(receiver) = &mut efn.receiver { if receiver.ty.rust == "Self" { receiver.ty.rust = single_type.rust.clone(); } } } } } out.extend(items); } fn parse_lang(abi: &Abi) -> Result { let Some(name) = &abi.name else { return Err(Error::new_spanned( abi, "ABI name is required, extern \"C++\" or extern \"Rust\"", )); }; match name.value().as_str() { "C++" => Ok(Lang::Cxx), "Rust" => Ok(Lang::Rust), _ => Err(Error::new_spanned( abi, "unrecognized ABI, requires either \"C++\" or \"Rust\"", )), } } fn parse_extern_type( cx: &mut Errors, foreign_type: ForeignItemType, lang: Lang, trusted: bool, extern_block_cfg: &CfgExpr, namespace: &Namespace, attrs: &OtherAttrs, ) -> Api { let mut cfg = extern_block_cfg.clone(); let mut doc = Doc::new(); let mut derives = Vec::new(); let mut namespace = namespace.clone(); let mut cxx_name = None; let mut rust_name = None; let mut attrs = attrs.clone(); attrs.extend(attrs::parse( cx, foreign_type.attrs, attrs::Parser { cfg: Some(&mut cfg), doc: Some(&mut doc), derives: Some(&mut derives), namespace: Some(&mut namespace), cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), ..Default::default() }, )); let type_token = foreign_type.type_token; let visibility = visibility_pub(&foreign_type.vis, type_token.span); let name = pair(namespace, &foreign_type.ident, cxx_name, rust_name); let generics = extern_type_lifetimes(cx, foreign_type.generics); let colon_token = None; let bounds = Vec::new(); let semi_token = foreign_type.semi_token; (match lang { Lang::Cxx => Api::CxxType, Lang::Rust => Api::RustType, })(ExternType { cfg, lang, doc, derives, attrs, visibility, type_token, name, generics, colon_token, bounds, semi_token, trusted, }) } fn parse_extern_fn( cx: &mut Errors, mut foreign_fn: ForeignItemFn, lang: Lang, trusted: bool, extern_block_cfg: &CfgExpr, namespace: &Namespace, attrs: &OtherAttrs, ) -> Result { let mut cfg = extern_block_cfg.clone(); let mut doc = Doc::new(); let mut namespace = namespace.clone(); let mut cxx_name = None; let mut rust_name = None; let mut attrs = attrs.clone(); attrs.extend(attrs::parse( cx, mem::take(&mut foreign_fn.attrs), attrs::Parser { cfg: Some(&mut cfg), doc: Some(&mut doc), namespace: Some(&mut namespace), cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), ..Default::default() }, )); let generics = &foreign_fn.sig.generics; if generics.where_clause.is_some() || generics.params.iter().any(|param| match param { GenericParam::Lifetime(lifetime) => !lifetime.bounds.is_empty(), GenericParam::Type(_) | GenericParam::Const(_) => true, }) { return Err(Error::new_spanned( foreign_fn, "extern function with generic parameters is not supported yet", )); } if let Some(variadic) = &foreign_fn.sig.variadic { return Err(Error::new_spanned( variadic, "variadic function is not supported yet", )); } if foreign_fn.sig.asyncness.is_some() && !cfg!(feature = "experimental-async-fn") { return Err(Error::new_spanned( foreign_fn, "async function is not directly supported yet, but see https://cxx.rs/async.html \ for a working approach, and https://github.com/pcwalton/cxx-async for some helpers; \ eventually what you wrote will work but it isn't integrated into the cxx::bridge \ macro yet", )); } if foreign_fn.sig.constness.is_some() { return Err(Error::new_spanned( foreign_fn, "const extern function is not supported", )); } if let Some(abi) = &foreign_fn.sig.abi { return Err(Error::new_spanned( abi, "explicit ABI on extern function is not supported", )); } let mut receiver = None; let mut args = Punctuated::new(); for arg in foreign_fn.sig.inputs.pairs() { let (arg, comma) = arg.into_tuple(); match arg { FnArg::Receiver(arg) => { if let Some((ampersand, lifetime)) = &arg.reference { receiver = Some(Receiver { pinned: false, ampersand: *ampersand, lifetime: lifetime.clone(), mutable: arg.mutability.is_some(), var: arg.self_token, colon_token: Token![:](arg.self_token.span), ty: NamedType::new(Ident::new("Self", arg.self_token.span)), shorthand: true, pin_tokens: None, mutability: arg.mutability, }); continue; } if let Some(colon_token) = arg.colon_token { let ty = parse_type(&arg.ty)?; if let Type::Ref(reference) = ty { if let Type::Ident(ident) = reference.inner { receiver = Some(Receiver { pinned: reference.pinned, ampersand: reference.ampersand, lifetime: reference.lifetime, mutable: reference.mutable, var: Token![self](ident.rust.span()), colon_token, ty: ident, shorthand: false, pin_tokens: reference.pin_tokens, mutability: reference.mutability, }); continue; } } } return Err(Error::new_spanned(arg, "unsupported method receiver")); } FnArg::Typed(arg) => { let ident = match arg.pat.as_ref() { Pat::Ident(pat) => pat.ident.clone(), Pat::Wild(pat) => { Ident::new(&format!("arg{}", args.len()), pat.underscore_token.span) } _ => return Err(Error::new_spanned(arg, "unsupported signature")), }; let ty = parse_type(&arg.ty)?; let cfg = CfgExpr::Unconditional; let doc = Doc::new(); let attrs = OtherAttrs::none(); let visibility = Token![pub](ident.span()); let name = pair(Namespace::default(), &ident, None, None); let colon_token = arg.colon_token; args.push_value(Var { cfg, doc, attrs, visibility, name, colon_token, ty, }); if let Some(comma) = comma { args.push_punct(*comma); } } } } let mut throws_tokens = None; let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?; let throws = throws_tokens.is_some(); let asyncness = foreign_fn.sig.asyncness; let unsafety = foreign_fn.sig.unsafety; let fn_token = foreign_fn.sig.fn_token; let inherited_span = unsafety.map_or(fn_token.span, |unsafety| unsafety.span); let visibility = visibility_pub(&foreign_fn.vis, inherited_span); let name = pair(namespace, &foreign_fn.sig.ident, cxx_name, rust_name); let generics = generics.clone(); let paren_token = foreign_fn.sig.paren_token; let semi_token = foreign_fn.semi_token; Ok(match lang { Lang::Cxx => Api::CxxFunction, Lang::Rust => Api::RustFunction, }(ExternFn { cfg, lang, doc, attrs, visibility, name, sig: Signature { asyncness, unsafety, fn_token, generics, receiver, args, ret, throws, paren_token, throws_tokens, }, semi_token, trusted, })) } fn parse_extern_verbatim( cx: &mut Errors, tokens: TokenStream, lang: Lang, trusted: bool, extern_block_cfg: &CfgExpr, namespace: &Namespace, attrs: &OtherAttrs, ) -> Result { |input: ParseStream| -> Result { let unparsed_attrs = input.call(Attribute::parse_outer)?; let visibility: Visibility = input.parse()?; if input.peek(Token![type]) { parse_extern_verbatim_type( cx, unparsed_attrs, visibility, input, lang, trusted, extern_block_cfg, namespace, attrs, ) } else if input.peek(Token![fn]) { parse_extern_verbatim_fn(input) } else { let span = input.cursor().token_stream(); Err(Error::new_spanned( span, "unsupported foreign item, expected `type` or `fn`", )) } } .parse2(tokens) } fn parse_extern_verbatim_type( cx: &mut Errors, unparsed_attrs: Vec, visibility: Visibility, input: ParseStream, lang: Lang, trusted: bool, extern_block_cfg: &CfgExpr, namespace: &Namespace, attrs: &OtherAttrs, ) -> Result { let type_token: Token![type] = input.parse()?; let ident: Ident = input.parse()?; let generics: Generics = input.parse()?; let lifetimes = extern_type_lifetimes(cx, generics); let lookahead = input.lookahead1(); if lookahead.peek(Token![=]) { // type Alias = crate::path::to::Type; parse_type_alias( cx, unparsed_attrs, visibility, type_token, ident, lifetimes, input, lang, extern_block_cfg, namespace, attrs, ) } else if lookahead.peek(Token![:]) { // type Opaque: Bound2 + Bound2; parse_extern_type_bounded( cx, unparsed_attrs, visibility, type_token, ident, lifetimes, input, lang, trusted, extern_block_cfg, namespace, attrs, ) } else { Err(lookahead.error()) } } fn extern_type_lifetimes(cx: &mut Errors, generics: Generics) -> Lifetimes { let mut lifetimes = Punctuated::new(); let mut has_unsupported_generic_param = false; for pair in generics.params.into_pairs() { let (param, punct) = pair.into_tuple(); match param { GenericParam::Lifetime(param) => { if !param.bounds.is_empty() && !has_unsupported_generic_param { let msg = "lifetime parameter with bounds is not supported yet"; cx.error(¶m, msg); has_unsupported_generic_param = true; } lifetimes.push_value(param.lifetime); if let Some(punct) = punct { lifetimes.push_punct(punct); } } GenericParam::Type(param) => { if !has_unsupported_generic_param { let msg = "extern type with generic type parameter is not supported yet"; cx.error(¶m, msg); has_unsupported_generic_param = true; } } GenericParam::Const(param) => { if !has_unsupported_generic_param { let msg = "extern type with const generic parameter is not supported yet"; cx.error(¶m, msg); has_unsupported_generic_param = true; } } } } Lifetimes { lt_token: generics.lt_token, lifetimes, gt_token: generics.gt_token, } } fn parse_extern_verbatim_fn(input: ParseStream) -> Result { input.parse::()?; input.parse::()?; unreachable!() } fn parse_type_alias( cx: &mut Errors, unparsed_attrs: Vec, visibility: Visibility, type_token: Token![type], ident: Ident, generics: Lifetimes, input: ParseStream, lang: Lang, extern_block_cfg: &CfgExpr, namespace: &Namespace, attrs: &OtherAttrs, ) -> Result { let eq_token: Token![=] = input.parse()?; let ty: RustType = input.parse()?; let semi_token: Token![;] = input.parse()?; let mut cfg = extern_block_cfg.clone(); let mut doc = Doc::new(); let mut derives = Vec::new(); let mut namespace = namespace.clone(); let mut cxx_name = None; let mut rust_name = None; let mut attrs = attrs.clone(); attrs.extend(attrs::parse( cx, unparsed_attrs, attrs::Parser { cfg: Some(&mut cfg), doc: Some(&mut doc), derives: Some(&mut derives), namespace: Some(&mut namespace), cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), ..Default::default() }, )); if lang == Lang::Rust { let span = quote!(#type_token #semi_token); let msg = "type alias in extern \"Rust\" block is not supported"; return Err(Error::new_spanned(span, msg)); } let visibility = visibility_pub(&visibility, type_token.span); let name = pair(namespace, &ident, cxx_name, rust_name); Ok(Api::TypeAlias(TypeAlias { cfg, doc, derives, attrs, visibility, type_token, name, generics, eq_token, ty, semi_token, })) } fn parse_extern_type_bounded( cx: &mut Errors, unparsed_attrs: Vec, visibility: Visibility, type_token: Token![type], ident: Ident, generics: Lifetimes, input: ParseStream, lang: Lang, trusted: bool, extern_block_cfg: &CfgExpr, namespace: &Namespace, attrs: &OtherAttrs, ) -> Result { let mut bounds = Vec::new(); let colon_token: Option = input.parse()?; if colon_token.is_some() { loop { match input.parse()? { TypeParamBound::Trait(TraitBound { paren_token: None, modifier: TraitBoundModifier::None, lifetimes: None, path, }) if if let Some(derive) = path.get_ident().and_then(Derive::from) { bounds.push(derive); true } else { false } => {} bound => cx.error(bound, "unsupported trait"), } let lookahead = input.lookahead1(); if lookahead.peek(Token![+]) { input.parse::()?; } else if lookahead.peek(Token![;]) { break; } else { return Err(lookahead.error()); } } } let semi_token: Token![;] = input.parse()?; let mut cfg = extern_block_cfg.clone(); let mut doc = Doc::new(); let mut derives = Vec::new(); let mut namespace = namespace.clone(); let mut cxx_name = None; let mut rust_name = None; let mut attrs = attrs.clone(); attrs.extend(attrs::parse( cx, unparsed_attrs, attrs::Parser { cfg: Some(&mut cfg), doc: Some(&mut doc), derives: Some(&mut derives), namespace: Some(&mut namespace), cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), ..Default::default() }, )); let visibility = visibility_pub(&visibility, type_token.span); let name = pair(namespace, &ident, cxx_name, rust_name); Ok(match lang { Lang::Cxx => Api::CxxType, Lang::Rust => Api::RustType, }(ExternType { cfg, lang, doc, derives, attrs, visibility, type_token, name, generics, colon_token, bounds, semi_token, trusted, })) } fn parse_impl(cx: &mut Errors, imp: ItemImpl) -> Result { let impl_token = imp.impl_token; let mut cfg = CfgExpr::Unconditional; attrs::parse( cx, imp.attrs, attrs::Parser { cfg: Some(&mut cfg), ..Default::default() }, ); if !imp.items.is_empty() { let mut span = Group::new(Delimiter::Brace, TokenStream::new()); span.set_span(imp.brace_token.span.join()); return Err(Error::new_spanned(span, "expected an empty impl block")); } if let Some((bang, path, for_token)) = &imp.trait_ { let self_ty = &imp.self_ty; let span = quote!(#bang #path #for_token #self_ty); return Err(Error::new_spanned( span, "unexpected impl, expected something like `impl UniquePtr {}`", )); } if let Some(where_clause) = imp.generics.where_clause { return Err(Error::new_spanned( where_clause, "where-clause on an impl is not supported yet", )); } let mut impl_generics = Lifetimes { lt_token: imp.generics.lt_token, lifetimes: Punctuated::new(), gt_token: imp.generics.gt_token, }; for pair in imp.generics.params.into_pairs() { let (param, punct) = pair.into_tuple(); match param { GenericParam::Lifetime(def) if def.bounds.is_empty() => { impl_generics.lifetimes.push_value(def.lifetime); if let Some(punct) = punct { impl_generics.lifetimes.push_punct(punct); } } _ => { let span = quote!(#impl_token #impl_generics); return Err(Error::new_spanned( span, "generic parameter on an impl is not supported yet", )); } } } let mut negative_token = None; let mut self_ty = *imp.self_ty; if let RustType::Verbatim(ty) = &self_ty { let mut iter = ty.clone().into_iter(); if let Some(TokenTree::Punct(punct)) = iter.next() { if punct.as_char() == '!' { let ty = iter.collect::(); if !ty.is_empty() { negative_token = Some(Token![!](punct.span())); self_ty = syn::parse2(ty)?; } } } } let ty = parse_type(&self_ty)?; let ty_generics = match &ty { Type::RustBox(ty) | Type::RustVec(ty) | Type::UniquePtr(ty) | Type::SharedPtr(ty) | Type::WeakPtr(ty) | Type::CxxVector(ty) => match &ty.inner { Type::Ident(ident) => ident.generics.clone(), _ => Lifetimes::default(), }, Type::Ident(_) | Type::Ref(_) | Type::Ptr(_) | Type::Str(_) | Type::Fn(_) | Type::Void(_) | Type::SliceRef(_) | Type::Array(_) => Lifetimes::default(), }; let negative = negative_token.is_some(); let brace_token = imp.brace_token; Ok(Api::Impl(Impl { cfg, impl_token, impl_generics, negative, ty, ty_generics, brace_token, negative_token, })) } fn parse_include(input: ParseStream) -> Result { if input.peek(LitStr) { let lit: LitStr = input.parse()?; let span = lit.span(); return Ok(Include { cfg: CfgExpr::Unconditional, path: lit.value(), kind: IncludeKind::Quoted, begin_span: span, end_span: span, }); } if input.peek(Token![<]) { let mut path = String::new(); let langle: Token![<] = input.parse()?; while !input.is_empty() && !input.peek(Token![>]) { let token: TokenTree = input.parse()?; match token { TokenTree::Ident(token) => path += &token.to_string(), TokenTree::Literal(token) if token .to_string() .starts_with(|ch: char| ch.is_ascii_digit()) => { path += &token.to_string(); } TokenTree::Punct(token) => path.push(token.as_char()), _ => return Err(Error::new(token.span(), "unexpected token in include path")), } } let rangle: Token![>] = input.parse()?; return Ok(Include { cfg: CfgExpr::Unconditional, path, kind: IncludeKind::Bracketed, begin_span: langle.span, end_span: rangle.span, }); } Err(input.error("expected \"quoted/path/to\" or ")) } fn parse_type(ty: &RustType) -> Result { match ty { RustType::Reference(ty) => parse_type_reference(ty), RustType::Ptr(ty) => parse_type_ptr(ty), RustType::Path(ty) => parse_type_path(ty), RustType::Array(ty) => parse_type_array(ty), RustType::BareFn(ty) => parse_type_fn(ty), RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span.join())), _ => Err(Error::new_spanned(ty, "unsupported type")), } } fn parse_type_reference(ty: &TypeReference) -> Result { let ampersand = ty.and_token; let lifetime = ty.lifetime.clone(); let mutable = ty.mutability.is_some(); let mutability = ty.mutability; if let RustType::Slice(slice) = ty.elem.as_ref() { let inner = parse_type(&slice.elem)?; let bracket = slice.bracket_token; return Ok(Type::SliceRef(Box::new(SliceRef { ampersand, lifetime, mutable, bracket, inner, mutability, }))); } let inner = parse_type(&ty.elem)?; let pinned = false; let pin_tokens = None; Ok(match &inner { Type::Ident(ident) if ident.rust == "str" => { if ty.mutability.is_some() { return Err(Error::new_spanned(ty, "unsupported type")); } else { Type::Str } } _ => Type::Ref, }(Box::new(Ref { pinned, ampersand, lifetime, mutable, inner, pin_tokens, mutability, }))) } fn parse_type_ptr(ty: &TypePtr) -> Result { let star = ty.star_token; let mutable = ty.mutability.is_some(); let constness = ty.const_token; let mutability = ty.mutability; let inner = parse_type(&ty.elem)?; Ok(Type::Ptr(Box::new(Ptr { star, mutable, inner, mutability, constness, }))) } fn parse_type_path(ty: &TypePath) -> Result { let path = &ty.path; if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { let segment = &path.segments[0]; let ident = segment.ident.clone(); match &segment.arguments { PathArguments::None => return Ok(Type::Ident(NamedType::new(ident))), PathArguments::AngleBracketed(generic) => { if ident == "UniquePtr" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg)?; return Ok(Type::UniquePtr(Box::new(Ty1 { name: ident, langle: generic.lt_token, inner, rangle: generic.gt_token, }))); } } else if ident == "SharedPtr" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg)?; return Ok(Type::SharedPtr(Box::new(Ty1 { name: ident, langle: generic.lt_token, inner, rangle: generic.gt_token, }))); } } else if ident == "WeakPtr" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg)?; return Ok(Type::WeakPtr(Box::new(Ty1 { name: ident, langle: generic.lt_token, inner, rangle: generic.gt_token, }))); } } else if ident == "CxxVector" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg)?; return Ok(Type::CxxVector(Box::new(Ty1 { name: ident, langle: generic.lt_token, inner, rangle: generic.gt_token, }))); } } else if ident == "Box" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg)?; return Ok(Type::RustBox(Box::new(Ty1 { name: ident, langle: generic.lt_token, inner, rangle: generic.gt_token, }))); } } else if ident == "Vec" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg)?; return Ok(Type::RustVec(Box::new(Ty1 { name: ident, langle: generic.lt_token, inner, rangle: generic.gt_token, }))); } } else if ident == "Pin" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg)?; let pin_token = kw::Pin(ident.span()); if let Type::Ref(mut inner) = inner { inner.pinned = true; inner.pin_tokens = Some((pin_token, generic.lt_token, generic.gt_token)); return Ok(Type::Ref(inner)); } } } else { let mut lifetimes = Punctuated::new(); let mut only_lifetimes = true; for pair in generic.args.pairs() { let (param, punct) = pair.into_tuple(); if let GenericArgument::Lifetime(param) = param { lifetimes.push_value(param.clone()); if let Some(punct) = punct { lifetimes.push_punct(*punct); } } else { only_lifetimes = false; break; } } if only_lifetimes { return Ok(Type::Ident(NamedType { rust: ident, generics: Lifetimes { lt_token: Some(generic.lt_token), lifetimes, gt_token: Some(generic.gt_token), }, })); } } } PathArguments::Parenthesized(_) => {} } } if ty.qself.is_none() && path.segments.len() == 2 && path.segments[0].ident == "cxx" { return Err(Error::new_spanned( ty, "unexpected `cxx::` qualifier found in a `#[cxx::bridge]`", )); } Err(Error::new_spanned(ty, "unsupported type")) } fn parse_type_array(ty: &TypeArray) -> Result { let inner = parse_type(&ty.elem)?; let Expr::Lit(len_expr) = &ty.len else { let msg = "unsupported expression, array length must be an integer literal"; return Err(Error::new_spanned(&ty.len, msg)); }; let Lit::Int(len_token) = &len_expr.lit else { let msg = "array length must be an integer literal"; return Err(Error::new_spanned(len_expr, msg)); }; let len = len_token.base10_parse::()?; if len == 0 { let msg = "array with zero size is not supported"; return Err(Error::new_spanned(ty, msg)); } let bracket = ty.bracket_token; let semi_token = ty.semi_token; Ok(Type::Array(Box::new(Array { bracket, inner, semi_token, len, len_token: len_token.clone(), }))) } fn parse_type_fn(ty: &TypeBareFn) -> Result { if ty.lifetimes.is_some() { return Err(Error::new_spanned( ty, "function pointer with lifetime parameters is not supported yet", )); } if ty.variadic.is_some() { return Err(Error::new_spanned( ty, "variadic function pointer is not supported yet", )); } let args = ty .inputs .iter() .enumerate() .map(|(i, arg)| { let (ident, colon_token) = match &arg.name { Some((ident, colon_token)) => (ident.clone(), *colon_token), None => { let fn_span = ty.paren_token.span.join(); let ident = format_ident!("arg{}", i, span = fn_span); let colon_token = Token![:](fn_span); (ident, colon_token) } }; let ty = parse_type(&arg.ty)?; let cfg = CfgExpr::Unconditional; let doc = Doc::new(); let attrs = OtherAttrs::none(); let visibility = Token![pub](ident.span()); let name = pair(Namespace::default(), &ident, None, None); Ok(Var { cfg, doc, attrs, visibility, name, colon_token, ty, }) }) .collect::>()?; let mut throws_tokens = None; let ret = parse_return_type(&ty.output, &mut throws_tokens)?; let throws = throws_tokens.is_some(); let asyncness = None; let unsafety = ty.unsafety; let fn_token = ty.fn_token; let generics = Generics::default(); let receiver = None; let paren_token = ty.paren_token; Ok(Type::Fn(Box::new(Signature { asyncness, unsafety, fn_token, generics, receiver, args, ret, throws, paren_token, throws_tokens, }))) } fn parse_return_type( ty: &ReturnType, throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>, ) -> Result> { let mut ret = match ty { ReturnType::Default => return Ok(None), ReturnType::Type(_, ret) => ret.as_ref(), }; if let RustType::Path(ty) = ret { let path = &ty.path; if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { let segment = &path.segments[0]; let ident = segment.ident.clone(); if let PathArguments::AngleBracketed(generic) = &segment.arguments { if ident == "Result" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { ret = arg; *throws_tokens = Some((kw::Result(ident.span()), generic.lt_token, generic.gt_token)); } } } } } match parse_type(ret)? { Type::Void(_) => Ok(None), ty => Ok(Some(ty)), } } fn visibility_pub(vis: &Visibility, inherited: Span) -> Token![pub] { Token![pub](match vis { Visibility::Public(vis) => vis.span, Visibility::Restricted(vis) => vis.pub_token.span, Visibility::Inherited => inherited, }) } fn pair( namespace: Namespace, default: &Ident, cxx: Option, rust: Option, ) -> Pair { Pair { namespace, cxx: cxx .unwrap_or_else(|| ForeignName::parse(&default.to_string(), default.span()).unwrap()), rust: rust.unwrap_or_else(|| default.clone()), } } cxxbridge-macro-1.0.141/src/syntax/pod.rs000064400000000000000000000026211046102023000163140ustar 00000000000000use crate::syntax::atom::Atom::{self, *}; use crate::syntax::{derive, Trait, Type, Types}; impl<'a> Types<'a> { pub(crate) fn is_guaranteed_pod(&self, ty: &Type) -> bool { match ty { Type::Ident(ident) => { let ident = &ident.rust; if let Some(atom) = Atom::from(ident) { match atom { Bool | Char | U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | F32 | F64 => true, CxxString | RustString => false, } } else if let Some(strct) = self.structs.get(ident) { derive::contains(&strct.derives, Trait::Copy) || strct .fields .iter() .all(|field| self.is_guaranteed_pod(&field.ty)) } else { self.enums.contains_key(ident) } } Type::RustBox(_) | Type::RustVec(_) | Type::UniquePtr(_) | Type::SharedPtr(_) | Type::WeakPtr(_) | Type::CxxVector(_) | Type::Void(_) => false, Type::Ref(_) | Type::Str(_) | Type::Fn(_) | Type::SliceRef(_) | Type::Ptr(_) => true, Type::Array(array) => self.is_guaranteed_pod(&array.inner), } } } cxxbridge-macro-1.0.141/src/syntax/qualified.rs000064400000000000000000000040711046102023000174760ustar 00000000000000use syn::ext::IdentExt; use syn::parse::{Error, ParseStream, Result}; use syn::{Ident, LitStr, Token}; pub(crate) struct QualifiedName { pub segments: Vec, } impl QualifiedName { pub(crate) fn parse_quoted(lit: &LitStr) -> Result { if lit.value().is_empty() { let segments = Vec::new(); Ok(QualifiedName { segments }) } else { lit.parse_with(|input: ParseStream| { let allow_raw = false; parse_unquoted(input, allow_raw) }) } } pub(crate) fn parse_unquoted(input: ParseStream) -> Result { let allow_raw = true; parse_unquoted(input, allow_raw) } pub(crate) fn parse_quoted_or_unquoted(input: ParseStream) -> Result { if input.peek(LitStr) { let lit: LitStr = input.parse()?; Self::parse_quoted(&lit) } else { Self::parse_unquoted(input) } } } fn parse_unquoted(input: ParseStream, allow_raw: bool) -> Result { let mut segments = Vec::new(); let mut trailing_punct = true; let leading_colons: Option = input.parse()?; while trailing_punct && input.peek(Ident::peek_any) { let mut ident = Ident::parse_any(input)?; if let Some(unraw) = ident.to_string().strip_prefix("r#") { if !allow_raw { let msg = format!( "raw identifier `{}` is not allowed in a quoted namespace; use `{}`, or remove quotes", ident, unraw, ); return Err(Error::new(ident.span(), msg)); } ident = Ident::new(unraw, ident.span()); } segments.push(ident); let colons: Option = input.parse()?; trailing_punct = colons.is_some(); } if segments.is_empty() && leading_colons.is_none() { return Err(input.error("expected path")); } else if trailing_punct { return Err(input.error("expected path segment")); } Ok(QualifiedName { segments }) } cxxbridge-macro-1.0.141/src/syntax/report.rs000064400000000000000000000013471046102023000170510ustar 00000000000000use quote::ToTokens; use std::fmt::Display; use syn::{Error, Result}; pub(crate) struct Errors { errors: Vec, } impl Errors { pub(crate) fn new() -> Self { Errors { errors: Vec::new() } } pub(crate) fn error(&mut self, sp: impl ToTokens, msg: impl Display) { self.errors.push(Error::new_spanned(sp, msg)); } pub(crate) fn push(&mut self, error: Error) { self.errors.push(error); } pub(crate) fn propagate(&mut self) -> Result<()> { let mut iter = self.errors.drain(..); let Some(mut all_errors) = iter.next() else { return Ok(()); }; for err in iter { all_errors.combine(err); } Err(all_errors) } } cxxbridge-macro-1.0.141/src/syntax/resolve.rs000064400000000000000000000021001046102023000172010ustar 00000000000000use crate::syntax::instantiate::NamedImplKey; use crate::syntax::{Lifetimes, NamedType, Pair, Types}; use proc_macro2::Ident; #[derive(Copy, Clone)] pub(crate) struct Resolution<'a> { pub name: &'a Pair, pub generics: &'a Lifetimes, } impl<'a> Types<'a> { pub(crate) fn resolve(&self, ident: &impl UnresolvedName) -> Resolution<'a> { let ident = ident.ident(); match self.try_resolve(ident) { Some(resolution) => resolution, None => panic!("Unable to resolve type `{}`", ident), } } pub(crate) fn try_resolve(&self, ident: &impl UnresolvedName) -> Option> { let ident = ident.ident(); self.resolutions.get(ident).copied() } } pub(crate) trait UnresolvedName { fn ident(&self) -> &Ident; } impl UnresolvedName for Ident { fn ident(&self) -> &Ident { self } } impl UnresolvedName for NamedType { fn ident(&self) -> &Ident { &self.rust } } impl<'a> UnresolvedName for NamedImplKey<'a> { fn ident(&self) -> &Ident { self.rust } } cxxbridge-macro-1.0.141/src/syntax/set.rs000064400000000000000000000054031046102023000163260ustar 00000000000000use std::fmt::{self, Debug}; use std::slice; pub(crate) use self::ordered::OrderedSet; pub(crate) use self::unordered::UnorderedSet; mod ordered { use super::{Iter, UnorderedSet}; use std::hash::Hash; pub(crate) struct OrderedSet { set: UnorderedSet, vec: Vec, } impl<'a, T> OrderedSet<&'a T> where T: Hash + Eq, { pub(crate) fn new() -> Self { OrderedSet { set: UnorderedSet::new(), vec: Vec::new(), } } pub(crate) fn insert(&mut self, value: &'a T) -> bool { let new = self.set.insert(value); if new { self.vec.push(value); } new } } impl<'a, T> OrderedSet<&'a T> { pub(crate) fn is_empty(&self) -> bool { self.vec.is_empty() } pub(crate) fn iter(&self) -> Iter<'_, 'a, T> { Iter(self.vec.iter()) } } impl<'s, 'a, T> IntoIterator for &'s OrderedSet<&'a T> { type Item = &'a T; type IntoIter = Iter<'s, 'a, T>; fn into_iter(self) -> Self::IntoIter { self.iter() } } } mod unordered { use std::borrow::Borrow; use std::collections::HashSet; use std::hash::Hash; // Wrapper prohibits accidentally introducing iteration over the set, which // could lead to nondeterministic generated code. pub(crate) struct UnorderedSet(HashSet); impl UnorderedSet where T: Hash + Eq, { pub(crate) fn new() -> Self { UnorderedSet(HashSet::new()) } pub(crate) fn insert(&mut self, value: T) -> bool { self.0.insert(value) } pub(crate) fn contains(&self, value: &Q) -> bool where T: Borrow, Q: ?Sized + Hash + Eq, { self.0.contains(value) } #[allow(dead_code)] // only used by cxx-build, not cxxbridge-cmd pub(crate) fn get(&self, value: &Q) -> Option<&T> where T: Borrow, Q: ?Sized + Hash + Eq, { self.0.get(value) } pub(crate) fn retain(&mut self, f: impl FnMut(&T) -> bool) { self.0.retain(f); } } } pub(crate) struct Iter<'s, 'a, T>(slice::Iter<'s, &'a T>); impl<'s, 'a, T> Iterator for Iter<'s, 'a, T> { type Item = &'a T; fn next(&mut self) -> Option { self.0.next().copied() } fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } } impl Debug for OrderedSet<&T> where T: Debug, { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.debug_set().entries(self).finish() } } cxxbridge-macro-1.0.141/src/syntax/symbol.rs000064400000000000000000000050211046102023000170340ustar 00000000000000use crate::syntax::namespace::Namespace; use crate::syntax::{ForeignName, Pair}; use proc_macro2::{Ident, TokenStream}; use quote::ToTokens; use std::fmt::{self, Display, Write}; // A mangled symbol consisting of segments separated by '$'. // For example: cxxbridge1$string$new pub(crate) struct Symbol(String); impl Display for Symbol { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { Display::fmt(&self.0, formatter) } } impl ToTokens for Symbol { fn to_tokens(&self, tokens: &mut TokenStream) { ToTokens::to_tokens(&self.0, tokens); } } impl Symbol { fn push(&mut self, segment: &dyn Display) { let len_before = self.0.len(); if !self.0.is_empty() { self.0.push('$'); } self.0.write_fmt(format_args!("{}", segment)).unwrap(); assert!(self.0.len() > len_before); } pub(crate) fn from_idents<'a>(it: impl Iterator) -> Self { let mut symbol = Symbol(String::new()); for segment in it { segment.write(&mut symbol); } assert!(!symbol.0.is_empty()); symbol } } pub(crate) trait Segment { fn write(&self, symbol: &mut Symbol); } impl Segment for str { fn write(&self, symbol: &mut Symbol) { symbol.push(&self); } } impl Segment for usize { fn write(&self, symbol: &mut Symbol) { symbol.push(&self); } } impl Segment for Ident { fn write(&self, symbol: &mut Symbol) { symbol.push(&self); } } impl Segment for Symbol { fn write(&self, symbol: &mut Symbol) { symbol.push(&self); } } impl Segment for Namespace { fn write(&self, symbol: &mut Symbol) { for segment in self { symbol.push(segment); } } } impl Segment for Pair { fn write(&self, symbol: &mut Symbol) { self.namespace.write(symbol); self.cxx.write(symbol); } } impl Segment for ForeignName { fn write(&self, symbol: &mut Symbol) { // TODO: support C++ names containing whitespace (`unsigned int`) or // non-alphanumeric characters (`operator++`). self.to_string().write(symbol); } } impl Segment for &'_ T where T: ?Sized + Segment + Display, { fn write(&self, symbol: &mut Symbol) { (**self).write(symbol); } } pub(crate) fn join(segments: &[&dyn Segment]) -> Symbol { let mut symbol = Symbol(String::new()); for segment in segments { segment.write(&mut symbol); } assert!(!symbol.0.is_empty()); symbol } cxxbridge-macro-1.0.141/src/syntax/tokens.rs000064400000000000000000000216401046102023000170370ustar 00000000000000use crate::syntax::atom::Atom::*; use crate::syntax::{ Array, Atom, Derive, Enum, EnumRepr, ExternFn, ExternType, Impl, Lifetimes, NamedType, Ptr, Ref, Signature, SliceRef, Struct, Ty1, Type, TypeAlias, Var, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; use syn::{token, Token}; impl ToTokens for Type { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Type::Ident(ident) => { if ident.rust == Char { let span = ident.rust.span(); tokens.extend(quote_spanned!(span=> ::cxx::core::ffi::)); } else if ident.rust == CxxString { let span = ident.rust.span(); tokens.extend(quote_spanned!(span=> ::cxx::)); } else if ident.rust == RustString { let span = ident.rust.span(); tokens.extend(quote_spanned!(span=> ::cxx::alloc::string::)); } ident.to_tokens(tokens); } Type::RustBox(ty) | Type::UniquePtr(ty) | Type::SharedPtr(ty) | Type::WeakPtr(ty) | Type::CxxVector(ty) | Type::RustVec(ty) => ty.to_tokens(tokens), Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens), Type::Ptr(p) => p.to_tokens(tokens), Type::Array(a) => a.to_tokens(tokens), Type::Fn(f) => f.to_tokens(tokens), Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())), Type::SliceRef(r) => r.to_tokens(tokens), } } } impl ToTokens for Var { fn to_tokens(&self, tokens: &mut TokenStream) { let Var { cfg: _, doc: _, attrs: _, visibility: _, name, colon_token: _, ty, } = self; name.rust.to_tokens(tokens); Token![:](name.rust.span()).to_tokens(tokens); ty.to_tokens(tokens); } } impl ToTokens for Ty1 { fn to_tokens(&self, tokens: &mut TokenStream) { let Ty1 { name, langle, inner, rangle, } = self; let span = name.span(); match name.to_string().as_str() { "UniquePtr" | "SharedPtr" | "WeakPtr" | "CxxVector" => { tokens.extend(quote_spanned!(span=> ::cxx::)); } "Box" => { tokens.extend(quote_spanned!(span=> ::cxx::alloc::boxed::)); } "Vec" => { tokens.extend(quote_spanned!(span=> ::cxx::alloc::vec::)); } _ => {} } name.to_tokens(tokens); langle.to_tokens(tokens); inner.to_tokens(tokens); rangle.to_tokens(tokens); } } impl ToTokens for Ref { fn to_tokens(&self, tokens: &mut TokenStream) { let Ref { pinned: _, ampersand, lifetime, mutable: _, inner, pin_tokens, mutability, } = self; if let Some((pin, langle, _rangle)) = pin_tokens { tokens.extend(quote_spanned!(pin.span=> ::cxx::core::pin::Pin)); langle.to_tokens(tokens); } ampersand.to_tokens(tokens); lifetime.to_tokens(tokens); mutability.to_tokens(tokens); inner.to_tokens(tokens); if let Some((_pin, _langle, rangle)) = pin_tokens { rangle.to_tokens(tokens); } } } impl ToTokens for Ptr { fn to_tokens(&self, tokens: &mut TokenStream) { let Ptr { star, mutable: _, inner, mutability, constness, } = self; star.to_tokens(tokens); mutability.to_tokens(tokens); constness.to_tokens(tokens); inner.to_tokens(tokens); } } impl ToTokens for SliceRef { fn to_tokens(&self, tokens: &mut TokenStream) { let SliceRef { ampersand, lifetime, mutable: _, bracket, inner, mutability, } = self; ampersand.to_tokens(tokens); lifetime.to_tokens(tokens); mutability.to_tokens(tokens); bracket.surround(tokens, |tokens| { inner.to_tokens(tokens); }); } } impl ToTokens for Array { fn to_tokens(&self, tokens: &mut TokenStream) { let Array { bracket, inner, semi_token, len: _, len_token, } = self; bracket.surround(tokens, |tokens| { inner.to_tokens(tokens); semi_token.to_tokens(tokens); len_token.to_tokens(tokens); }); } } impl ToTokens for Atom { fn to_tokens(&self, tokens: &mut TokenStream) { Ident::new(self.as_ref(), Span::call_site()).to_tokens(tokens); } } impl ToTokens for Derive { fn to_tokens(&self, tokens: &mut TokenStream) { Ident::new(self.what.as_ref(), self.span).to_tokens(tokens); } } impl ToTokens for ExternType { fn to_tokens(&self, tokens: &mut TokenStream) { // Notional token range for error reporting purposes. self.type_token.to_tokens(tokens); self.name.rust.to_tokens(tokens); self.generics.to_tokens(tokens); } } impl ToTokens for TypeAlias { fn to_tokens(&self, tokens: &mut TokenStream) { // Notional token range for error reporting purposes. self.type_token.to_tokens(tokens); self.name.rust.to_tokens(tokens); self.generics.to_tokens(tokens); } } impl ToTokens for Struct { fn to_tokens(&self, tokens: &mut TokenStream) { // Notional token range for error reporting purposes. self.struct_token.to_tokens(tokens); self.name.rust.to_tokens(tokens); self.generics.to_tokens(tokens); } } impl ToTokens for Enum { fn to_tokens(&self, tokens: &mut TokenStream) { // Notional token range for error reporting purposes. self.enum_token.to_tokens(tokens); self.name.rust.to_tokens(tokens); self.generics.to_tokens(tokens); } } impl ToTokens for ExternFn { fn to_tokens(&self, tokens: &mut TokenStream) { // Notional token range for error reporting purposes. self.unsafety.to_tokens(tokens); self.sig.fn_token.to_tokens(tokens); self.semi_token.to_tokens(tokens); } } impl ToTokens for Impl { fn to_tokens(&self, tokens: &mut TokenStream) { let Impl { cfg: _, impl_token, impl_generics, negative: _, ty, ty_generics: _, brace_token, negative_token, } = self; impl_token.to_tokens(tokens); impl_generics.to_tokens(tokens); negative_token.to_tokens(tokens); ty.to_tokens(tokens); brace_token.surround(tokens, |_tokens| {}); } } impl ToTokens for Lifetimes { fn to_tokens(&self, tokens: &mut TokenStream) { let Lifetimes { lt_token, lifetimes, gt_token, } = self; lt_token.to_tokens(tokens); lifetimes.to_tokens(tokens); gt_token.to_tokens(tokens); } } impl ToTokens for Signature { fn to_tokens(&self, tokens: &mut TokenStream) { let Signature { asyncness: _, unsafety: _, fn_token, generics: _, receiver: _, args, ret, throws: _, paren_token, throws_tokens, } = self; fn_token.to_tokens(tokens); paren_token.surround(tokens, |tokens| { args.to_tokens(tokens); }); if let Some(ret) = ret { Token![->](paren_token.span.join()).to_tokens(tokens); if let Some((result, langle, rangle)) = throws_tokens { result.to_tokens(tokens); langle.to_tokens(tokens); ret.to_tokens(tokens); rangle.to_tokens(tokens); } else { ret.to_tokens(tokens); } } else if let Some((result, langle, rangle)) = throws_tokens { Token![->](paren_token.span.join()).to_tokens(tokens); result.to_tokens(tokens); langle.to_tokens(tokens); token::Paren(langle.span).surround(tokens, |_| ()); rangle.to_tokens(tokens); } } } impl ToTokens for EnumRepr { fn to_tokens(&self, tokens: &mut TokenStream) { match self { EnumRepr::Native { atom, repr_type: _ } => atom.to_tokens(tokens), #[cfg(feature = "experimental-enum-variants-from-header")] EnumRepr::Foreign { rust_type } => rust_type.to_tokens(tokens), } } } impl ToTokens for NamedType { fn to_tokens(&self, tokens: &mut TokenStream) { let NamedType { rust, generics } = self; rust.to_tokens(tokens); generics.to_tokens(tokens); } } cxxbridge-macro-1.0.141/src/syntax/toposort.rs000064400000000000000000000026601046102023000174260ustar 00000000000000use crate::syntax::map::{Entry, UnorderedMap as Map}; use crate::syntax::report::Errors; use crate::syntax::{Api, Struct, Type, Types}; enum Mark { Visiting, Visited, } pub(crate) fn sort<'a>(cx: &mut Errors, apis: &'a [Api], types: &Types<'a>) -> Vec<&'a Struct> { let mut sorted = Vec::new(); let ref mut marks = Map::new(); for api in apis { if let Api::Struct(strct) = api { let _ = visit(cx, strct, &mut sorted, marks, types); } } sorted } fn visit<'a>( cx: &mut Errors, strct: &'a Struct, sorted: &mut Vec<&'a Struct>, marks: &mut Map<*const Struct, Mark>, types: &Types<'a>, ) -> Result<(), ()> { match marks.entry(strct) { Entry::Occupied(entry) => match entry.get() { Mark::Visiting => return Err(()), // not a DAG Mark::Visited => return Ok(()), }, Entry::Vacant(entry) => { entry.insert(Mark::Visiting); } } let mut result = Ok(()); for field in &strct.fields { if let Type::Ident(ident) = &field.ty { if let Some(inner) = types.structs.get(&ident.rust) { if visit(cx, inner, sorted, marks, types).is_err() { cx.error(field, "unsupported cyclic data structure"); result = Err(()); } } } } marks.insert(strct, Mark::Visited); sorted.push(strct); result } cxxbridge-macro-1.0.141/src/syntax/trivial.rs000064400000000000000000000250711046102023000172100ustar 00000000000000use crate::syntax::map::UnorderedMap; use crate::syntax::set::{OrderedSet as Set, UnorderedSet}; use crate::syntax::{Api, Enum, ExternFn, NamedType, Pair, Struct, Type}; use proc_macro2::Ident; use std::fmt::{self, Display}; #[derive(Copy, Clone)] pub(crate) enum TrivialReason<'a> { StructField(&'a Struct), FunctionArgument(&'a ExternFn), FunctionReturn(&'a ExternFn), BoxTarget, VecElement, SliceElement { mutable: bool }, UnpinnedMut(&'a ExternFn), } pub(crate) fn required_trivial_reasons<'a>( apis: &'a [Api], all: &Set<&'a Type>, structs: &UnorderedMap<&'a Ident, &'a Struct>, enums: &UnorderedMap<&'a Ident, &'a Enum>, cxx: &UnorderedSet<&'a Ident>, ) -> UnorderedMap<&'a Ident, Vec>> { let mut required_trivial = UnorderedMap::new(); let mut insist_extern_types_are_trivial = |ident: &'a NamedType, reason| { if cxx.contains(&ident.rust) && !structs.contains_key(&ident.rust) && !enums.contains_key(&ident.rust) { required_trivial .entry(&ident.rust) .or_insert_with(Vec::new) .push(reason); } }; for api in apis { match api { Api::Struct(strct) => { for field in &strct.fields { if let Type::Ident(ident) = &field.ty { let reason = TrivialReason::StructField(strct); insist_extern_types_are_trivial(ident, reason); } } } Api::CxxFunction(efn) | Api::RustFunction(efn) => { if let Some(receiver) = &efn.receiver { if receiver.mutable && !receiver.pinned { let reason = TrivialReason::UnpinnedMut(efn); insist_extern_types_are_trivial(&receiver.ty, reason); } } for arg in &efn.args { match &arg.ty { Type::Ident(ident) => { let reason = TrivialReason::FunctionArgument(efn); insist_extern_types_are_trivial(ident, reason); } Type::Ref(ty) => { if ty.mutable && !ty.pinned { if let Type::Ident(ident) = &ty.inner { let reason = TrivialReason::UnpinnedMut(efn); insist_extern_types_are_trivial(ident, reason); } } } _ => {} } } if let Some(ret) = &efn.ret { match ret { Type::Ident(ident) => { let reason = TrivialReason::FunctionReturn(efn); insist_extern_types_are_trivial(ident, reason); } Type::Ref(ty) => { if ty.mutable && !ty.pinned { if let Type::Ident(ident) = &ty.inner { let reason = TrivialReason::UnpinnedMut(efn); insist_extern_types_are_trivial(ident, reason); } } } _ => {} } } } _ => {} } } for ty in all { match ty { Type::RustBox(ty) => { if let Type::Ident(ident) = &ty.inner { let reason = TrivialReason::BoxTarget; insist_extern_types_are_trivial(ident, reason); } } Type::RustVec(ty) => { if let Type::Ident(ident) = &ty.inner { let reason = TrivialReason::VecElement; insist_extern_types_are_trivial(ident, reason); } } Type::SliceRef(ty) => { if let Type::Ident(ident) = &ty.inner { let reason = TrivialReason::SliceElement { mutable: ty.mutable, }; insist_extern_types_are_trivial(ident, reason); } } _ => {} } } required_trivial } // Context: // "type {type} should be trivially move constructible and trivially destructible in C++ to be used as {what} in Rust" // "needs a cxx::ExternType impl in order to be used as {what}" pub(crate) fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display + 'a { struct Description<'a> { name: &'a Pair, reasons: &'a [TrivialReason<'a>], } impl<'a> Display for Description<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut field_of = Set::new(); let mut argument_of = Set::new(); let mut return_of = Set::new(); let mut box_target = false; let mut vec_element = false; let mut slice_shared_element = false; let mut slice_mut_element = false; let mut unpinned_mut = Set::new(); for reason in self.reasons { match reason { TrivialReason::StructField(strct) => { field_of.insert(&strct.name.rust); } TrivialReason::FunctionArgument(efn) => { argument_of.insert(&efn.name.rust); } TrivialReason::FunctionReturn(efn) => { return_of.insert(&efn.name.rust); } TrivialReason::BoxTarget => box_target = true, TrivialReason::VecElement => vec_element = true, TrivialReason::SliceElement { mutable } => { if *mutable { slice_mut_element = true; } else { slice_shared_element = true; } } TrivialReason::UnpinnedMut(efn) => { unpinned_mut.insert(&efn.name.rust); } } } let mut clauses = Vec::new(); if !field_of.is_empty() { clauses.push(Clause::Set { article: "a", desc: "field of", set: &field_of, }); } if !argument_of.is_empty() { clauses.push(Clause::Set { article: "an", desc: "argument of", set: &argument_of, }); } if !return_of.is_empty() { clauses.push(Clause::Set { article: "a", desc: "return value of", set: &return_of, }); } if box_target { clauses.push(Clause::Ty1 { article: "type", desc: "Box", param: self.name, }); } if vec_element { clauses.push(Clause::Ty1 { article: "a", desc: "vector element in Vec", param: self.name, }); } if slice_shared_element || slice_mut_element { clauses.push(Clause::Slice { article: "a", desc: "slice element in", shared: slice_shared_element, mutable: slice_mut_element, param: self.name, }); } if !unpinned_mut.is_empty() { clauses.push(Clause::Set { article: "a", desc: "non-pinned mutable reference in signature of", set: &unpinned_mut, }); } for (i, clause) in clauses.iter().enumerate() { if i == 0 { write!(f, "{} ", clause.article())?; } else if i + 1 < clauses.len() { write!(f, ", ")?; } else { write!(f, " or ")?; } clause.fmt(f)?; } Ok(()) } } enum Clause<'a> { Set { article: &'a str, desc: &'a str, set: &'a Set<&'a Ident>, }, Ty1 { article: &'a str, desc: &'a str, param: &'a Pair, }, Slice { article: &'a str, desc: &'a str, shared: bool, mutable: bool, param: &'a Pair, }, } impl<'a> Clause<'a> { fn article(&self) -> &'a str { match self { Clause::Set { article, .. } | Clause::Ty1 { article, .. } | Clause::Slice { article, .. } => article, } } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Clause::Set { article: _, desc, set, } => { write!(f, "{} ", desc)?; for (i, ident) in set.iter().take(3).enumerate() { if i > 0 { write!(f, ", ")?; } write!(f, "`{}`", ident)?; } Ok(()) } Clause::Ty1 { article: _, desc, param, } => write!(f, "{}<{}>", desc, param.rust), Clause::Slice { article: _, desc, shared, mutable, param, } => { write!(f, "{} ", desc)?; if *shared { write!(f, "&[{}]", param.rust)?; } if *shared && *mutable { write!(f, " and ")?; } if *mutable { write!(f, "&mut [{}]", param.rust)?; } Ok(()) } } } } Description { name, reasons } } cxxbridge-macro-1.0.141/src/syntax/types.rs000064400000000000000000000270251046102023000167030ustar 00000000000000use crate::syntax::improper::ImproperCtype; use crate::syntax::instantiate::ImplKey; use crate::syntax::map::{OrderedMap, UnorderedMap}; use crate::syntax::report::Errors; use crate::syntax::resolve::Resolution; use crate::syntax::set::{OrderedSet, UnorderedSet}; use crate::syntax::trivial::{self, TrivialReason}; use crate::syntax::visit::{self, Visit}; use crate::syntax::{ toposort, Api, Atom, Enum, EnumRepr, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias, }; use proc_macro2::Ident; use quote::ToTokens; pub(crate) struct Types<'a> { pub all: OrderedSet<&'a Type>, pub structs: UnorderedMap<&'a Ident, &'a Struct>, pub enums: UnorderedMap<&'a Ident, &'a Enum>, pub cxx: UnorderedSet<&'a Ident>, pub rust: UnorderedSet<&'a Ident>, pub aliases: UnorderedMap<&'a Ident, &'a TypeAlias>, pub untrusted: UnorderedMap<&'a Ident, &'a ExternType>, pub required_trivial: UnorderedMap<&'a Ident, Vec>>, pub impls: OrderedMap, Option<&'a Impl>>, pub resolutions: UnorderedMap<&'a Ident, Resolution<'a>>, pub struct_improper_ctypes: UnorderedSet<&'a Ident>, pub toposorted_structs: Vec<&'a Struct>, } impl<'a> Types<'a> { pub(crate) fn collect(cx: &mut Errors, apis: &'a [Api]) -> Self { let mut all = OrderedSet::new(); let mut structs = UnorderedMap::new(); let mut enums = UnorderedMap::new(); let mut cxx = UnorderedSet::new(); let mut rust = UnorderedSet::new(); let mut aliases = UnorderedMap::new(); let mut untrusted = UnorderedMap::new(); let mut impls = OrderedMap::new(); let mut resolutions = UnorderedMap::new(); let struct_improper_ctypes = UnorderedSet::new(); let toposorted_structs = Vec::new(); fn visit<'a>(all: &mut OrderedSet<&'a Type>, ty: &'a Type) { struct CollectTypes<'s, 'a>(&'s mut OrderedSet<&'a Type>); impl<'s, 'a> Visit<'a> for CollectTypes<'s, 'a> { fn visit_type(&mut self, ty: &'a Type) { self.0.insert(ty); visit::visit_type(self, ty); } } CollectTypes(all).visit_type(ty); } let mut add_resolution = |name: &'a Pair, generics: &'a Lifetimes| { resolutions.insert(&name.rust, Resolution { name, generics }); }; let mut type_names = UnorderedSet::new(); let mut function_names = UnorderedSet::new(); for api in apis { // The same identifier is permitted to be declared as both a shared // enum and extern C++ type, or shared struct and extern C++ type. // That indicates to not emit the C++ enum/struct definition because // it's defined by the included headers already. // // All other cases of duplicate identifiers are reported as an error. match api { Api::Include(_) => {} Api::Struct(strct) => { let ident = &strct.name.rust; if !type_names.insert(ident) && (!cxx.contains(ident) || structs.contains_key(ident) || enums.contains_key(ident)) { // If already declared as a struct or enum, or if // colliding with something other than an extern C++ // type, then error. duplicate_name(cx, strct, ident); } structs.insert(&strct.name.rust, strct); for field in &strct.fields { visit(&mut all, &field.ty); } add_resolution(&strct.name, &strct.generics); } Api::Enum(enm) => { match &enm.repr { EnumRepr::Native { atom: _, repr_type } => { all.insert(repr_type); } #[cfg(feature = "experimental-enum-variants-from-header")] EnumRepr::Foreign { rust_type: _ } => {} } let ident = &enm.name.rust; if !type_names.insert(ident) && (!cxx.contains(ident) || structs.contains_key(ident) || enums.contains_key(ident)) { // If already declared as a struct or enum, or if // colliding with something other than an extern C++ // type, then error. duplicate_name(cx, enm, ident); } enums.insert(ident, enm); if enm.variants_from_header { // #![variants_from_header] enums are implicitly extern // C++ type. cxx.insert(&enm.name.rust); } add_resolution(&enm.name, &enm.generics); } Api::CxxType(ety) => { let ident = &ety.name.rust; if !type_names.insert(ident) && (cxx.contains(ident) || !structs.contains_key(ident) && !enums.contains_key(ident)) { // If already declared as an extern C++ type, or if // colliding with something which is neither struct nor // enum, then error. duplicate_name(cx, ety, ident); } cxx.insert(ident); if !ety.trusted { untrusted.insert(ident, ety); } add_resolution(&ety.name, &ety.generics); } Api::RustType(ety) => { let ident = &ety.name.rust; if !type_names.insert(ident) { duplicate_name(cx, ety, ident); } rust.insert(ident); add_resolution(&ety.name, &ety.generics); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { // Note: duplication of the C++ name is fine because C++ has // function overloading. if !function_names.insert((&efn.receiver, &efn.name.rust)) { duplicate_name(cx, efn, &efn.name.rust); } for arg in &efn.args { visit(&mut all, &arg.ty); } if let Some(ret) = &efn.ret { visit(&mut all, ret); } } Api::TypeAlias(alias) => { let ident = &alias.name.rust; if !type_names.insert(ident) { duplicate_name(cx, alias, ident); } cxx.insert(ident); aliases.insert(ident, alias); add_resolution(&alias.name, &alias.generics); } Api::Impl(imp) => { visit(&mut all, &imp.ty); if let Some(key) = imp.ty.impl_key() { impls.insert(key, Some(imp)); } } } } for ty in &all { let Some(impl_key) = ty.impl_key() else { continue; }; let implicit_impl = match impl_key { ImplKey::RustBox(ident) | ImplKey::RustVec(ident) | ImplKey::UniquePtr(ident) | ImplKey::SharedPtr(ident) | ImplKey::WeakPtr(ident) | ImplKey::CxxVector(ident) => { Atom::from(ident.rust).is_none() && !aliases.contains_key(ident.rust) } }; if implicit_impl && !impls.contains_key(&impl_key) { impls.insert(impl_key, None); } } // All these APIs may contain types passed by value. We need to ensure // we check that this is permissible. We do this _after_ scanning all // the APIs above, in case some function or struct references a type // which is declared subsequently. let required_trivial = trivial::required_trivial_reasons(apis, &all, &structs, &enums, &cxx); let mut types = Types { all, structs, enums, cxx, rust, aliases, untrusted, required_trivial, impls, resolutions, struct_improper_ctypes, toposorted_structs, }; types.toposorted_structs = toposort::sort(cx, apis, &types); let mut unresolved_structs = types.structs.keys(); let mut new_information = true; while new_information { new_information = false; unresolved_structs.retain(|ident| { let mut retain = false; for var in &types.structs[ident].fields { if match types.determine_improper_ctype(&var.ty) { ImproperCtype::Depends(inner) => { retain = true; types.struct_improper_ctypes.contains(inner) } ImproperCtype::Definite(improper) => improper, } { types.struct_improper_ctypes.insert(ident); new_information = true; return false; } } // If all fields definite false, remove from unresolved_structs. retain }); } types } pub(crate) fn needs_indirect_abi(&self, ty: &Type) -> bool { match ty { Type::RustBox(_) | Type::UniquePtr(_) => false, Type::Array(_) => true, _ => !self.is_guaranteed_pod(ty), } } // Types that trigger rustc's default #[warn(improper_ctypes)] lint, even if // they may be otherwise unproblematic to mention in an extern signature. // For example in a signature like `extern "C" fn(*const String)`, rustc // refuses to believe that C could know how to supply us with a pointer to a // Rust String, even though C could easily have obtained that pointer // legitimately from a Rust call. #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub(crate) fn is_considered_improper_ctype(&self, ty: &Type) -> bool { match self.determine_improper_ctype(ty) { ImproperCtype::Definite(improper) => improper, ImproperCtype::Depends(ident) => self.struct_improper_ctypes.contains(ident), } } // Types which we need to assume could possibly exist by value on the Rust // side. pub(crate) fn is_maybe_trivial(&self, ty: &Ident) -> bool { self.structs.contains_key(ty) || self.enums.contains_key(ty) || self.aliases.contains_key(ty) } } impl<'t, 'a> IntoIterator for &'t Types<'a> { type Item = &'a Type; type IntoIter = crate::syntax::set::Iter<'t, 'a, Type>; fn into_iter(self) -> Self::IntoIter { self.all.into_iter() } } fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) { let msg = format!("the name `{}` is defined multiple times", ident); cx.error(sp, msg); } cxxbridge-macro-1.0.141/src/syntax/visit.rs000064400000000000000000000017211046102023000166700ustar 00000000000000use crate::syntax::Type; pub(crate) trait Visit<'a> { fn visit_type(&mut self, ty: &'a Type) { visit_type(self, ty); } } pub(crate) fn visit_type<'a, V>(visitor: &mut V, ty: &'a Type) where V: Visit<'a> + ?Sized, { match ty { Type::Ident(_) | Type::Str(_) | Type::Void(_) => {} Type::RustBox(ty) | Type::UniquePtr(ty) | Type::SharedPtr(ty) | Type::WeakPtr(ty) | Type::CxxVector(ty) | Type::RustVec(ty) => visitor.visit_type(&ty.inner), Type::Ref(r) => visitor.visit_type(&r.inner), Type::Ptr(p) => visitor.visit_type(&p.inner), Type::Array(a) => visitor.visit_type(&a.inner), Type::SliceRef(s) => visitor.visit_type(&s.inner), Type::Fn(fun) => { if let Some(ret) = &fun.ret { visitor.visit_type(ret); } for arg in &fun.args { visitor.visit_type(&arg.ty); } } } } cxxbridge-macro-1.0.141/src/tokens.rs000064400000000000000000000040361046102023000155110ustar 00000000000000use crate::syntax::Receiver; use proc_macro2::TokenStream; use quote::{quote_spanned, ToTokens}; use syn::Token; pub(crate) struct ReceiverType<'a>(&'a Receiver); pub(crate) struct ReceiverTypeSelf<'a>(&'a Receiver); impl Receiver { // &TheType pub(crate) fn ty(&self) -> ReceiverType { ReceiverType(self) } // &Self pub(crate) fn ty_self(&self) -> ReceiverTypeSelf { ReceiverTypeSelf(self) } } impl ToTokens for ReceiverType<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { let Receiver { pinned: _, ampersand, lifetime, mutable: _, var: _, colon_token: _, ty, shorthand: _, pin_tokens, mutability, } = &self.0; if let Some((pin, langle, _rangle)) = pin_tokens { tokens.extend(quote_spanned!(pin.span=> ::cxx::core::pin::Pin)); langle.to_tokens(tokens); } ampersand.to_tokens(tokens); lifetime.to_tokens(tokens); mutability.to_tokens(tokens); ty.to_tokens(tokens); if let Some((_pin, _langle, rangle)) = pin_tokens { rangle.to_tokens(tokens); } } } impl ToTokens for ReceiverTypeSelf<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { let Receiver { pinned: _, ampersand, lifetime, mutable: _, var: _, colon_token: _, ty, shorthand: _, pin_tokens, mutability, } = &self.0; if let Some((pin, langle, _rangle)) = pin_tokens { tokens.extend(quote_spanned!(pin.span=> ::cxx::core::pin::Pin)); langle.to_tokens(tokens); } ampersand.to_tokens(tokens); lifetime.to_tokens(tokens); mutability.to_tokens(tokens); Token![Self](ty.rust.span()).to_tokens(tokens); if let Some((_pin, _langle, rangle)) = pin_tokens { rangle.to_tokens(tokens); } } } cxxbridge-macro-1.0.141/src/type_id.rs000064400000000000000000000022771046102023000156500ustar 00000000000000use crate::syntax::qualified::QualifiedName; use proc_macro2::{TokenStream, TokenTree}; use quote::{format_ident, quote, ToTokens}; use syn::ext::IdentExt; pub(crate) enum Crate { Cxx, DollarCrate(TokenTree), } impl ToTokens for Crate { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Crate::Cxx => tokens.extend(quote!(::cxx)), Crate::DollarCrate(krate) => krate.to_tokens(tokens), } } } // "folly::File" => `(f, o, l, l, y, (), F, i, l, e)` pub(crate) fn expand(krate: Crate, arg: QualifiedName) -> TokenStream { let mut ids = Vec::new(); for word in arg.segments { if !ids.is_empty() { ids.push(quote!(())); } for ch in word.unraw().to_string().chars() { ids.push(match ch { 'A'..='Z' | 'a'..='z' => { let t = format_ident!("{}", ch); quote!(#krate::#t) } '0'..='9' | '_' => { let t = format_ident!("_{}", ch); quote!(#krate::#t) } _ => quote!([(); #ch as _]), }); } } quote! { (#(#ids,)*) } }