cxxbridge-macro-1.0.192/.cargo_vcs_info.json0000644000000001431046102023000143040ustar { "git": { "sha1": "0d80b351886a00af9a7120369f22a0b7f0affd72" }, "path_in_vcs": "macro" }cxxbridge-macro-1.0.192/Cargo.lock0000644000000171461046102023000122720ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "anstyle" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "cc" version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "shlex", ] [[package]] name = "clap" version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstyle", "clap_lex", "strsim", ] [[package]] name = "clap_lex" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "codespan-reporting" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" dependencies = [ "serde", "termcolor", "unicode-width", ] [[package]] name = "cxx" version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd46505a9588ea42961c833d8e20a8956741f26f4aa84f5248a60c2137bf0373" dependencies = [ "cc", "cxx-build", "cxxbridge-cmd", "cxxbridge-flags", "cxxbridge-macro 1.0.191", "foldhash", "link-cplusplus", ] [[package]] name = "cxx-build" version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b78549cfae183ba0391f186544fdf1f5d96e9d3cf2683ee1449a3aea066e4e32" dependencies = [ "cc", "codespan-reporting", "indexmap", "proc-macro2", "quote", "scratch", "syn", ] [[package]] name = "cxxbridge-cmd" version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5f593aa804e4cd4fda75ca4c56820c15a27ac667f02bffe626c2ed16e2d747d" dependencies = [ "clap", "codespan-reporting", "indexmap", "proc-macro2", "quote", "syn", ] [[package]] name = "cxxbridge-flags" version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5e77806f6ca008fe3cb35ac747db8f5cf30c038b81ae97077a6aecd7ca01a31" [[package]] name = "cxxbridge-macro" version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e751ebe2dee4a88a26df4f11c2baf6aa1e7f652af9a3bef5b4db5ae33397577d" dependencies = [ "indexmap", "proc-macro2", "quote", "syn", ] [[package]] name = "cxxbridge-macro" version = "1.0.192" dependencies = [ "cxx", "indexmap", "prettyplease", "proc-macro2", "quote", "syn", ] [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "find-msvc-tools" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "foldhash" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "indexmap" version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "link-cplusplus" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82" dependencies = [ "cc", ] [[package]] name = "prettyplease" version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", "syn", ] [[package]] name = "proc-macro2" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] [[package]] name = "scratch" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2" [[package]] name = "serde" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] [[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.111" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" 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.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-width" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "winapi-util" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ "windows-sys", ] [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] cxxbridge-macro-1.0.192/Cargo.toml0000644000000033421046102023000123060ustar # 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.82" name = "cxxbridge-macro" version = "1.0.192" 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] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = [ "--generate-link-to-definition", "--generate-macro-expansion", "--extern-html-root-url=core=https://doc.rust-lang.org", "--extern-html-root-url=alloc=https://doc.rust-lang.org", "--extern-html-root-url=std=https://doc.rust-lang.org", "--extern-html-root-url=proc_macro=https://doc.rust-lang.org", ] [lib] name = "cxxbridge_macro" path = "src/lib.rs" proc-macro = true [dependencies.indexmap] version = "2.9.0" [dependencies.proc-macro2] version = "1.0.74" [dependencies.quote] version = "1.0.35" [dependencies.syn] version = "2.0.46" features = ["full"] [dev-dependencies.cxx] version = "1.0" [dev-dependencies.prettyplease] version = "0.2.35" cxxbridge-macro-1.0.192/Cargo.toml.orig000064400000000000000000000020161046102023000157420ustar 00000000000000[package] name = "cxxbridge-macro" version = "1.0.192" 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.82" [lib] proc-macro = true [dependencies] indexmap = "2.9.0" proc-macro2 = "1.0.74" quote = "1.0.35" syn = { version = "2.0.46", features = ["full"] } [dev-dependencies] cxx = { version = "1.0", path = ".." } prettyplease = "0.2.35" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = [ "--generate-link-to-definition", "--generate-macro-expansion", "--extern-html-root-url=core=https://doc.rust-lang.org", "--extern-html-root-url=alloc=https://doc.rust-lang.org", "--extern-html-root-url=std=https://doc.rust-lang.org", "--extern-html-root-url=proc_macro=https://doc.rust-lang.org", ] cxxbridge-macro-1.0.192/LICENSE-APACHE000064400000000000000000000227731046102023000150130ustar 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.192/LICENSE-MIT000064400000000000000000000017771046102023000145240ustar 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.192/README.md000064400000000000000000000003121046102023000143270ustar 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.192/src/attrs.rs000064400000000000000000000032131046102023000153450ustar 00000000000000use crate::syntax::attrs::OtherAttrs; use proc_macro2::TokenStream; use quote::ToTokens; use syn::Attribute; impl OtherAttrs { pub(crate) fn all(&self) -> PrintOtherAttrs { PrintOtherAttrs { attrs: self, cfg: true, lint: true, passthrough: true, } } pub(crate) fn cfg(&self) -> PrintOtherAttrs { PrintOtherAttrs { attrs: self, cfg: true, lint: false, passthrough: false, } } pub(crate) fn cfg_and_lint(&self) -> PrintOtherAttrs { PrintOtherAttrs { attrs: self, cfg: true, lint: true, passthrough: false, } } } pub(crate) struct PrintOtherAttrs<'a> { attrs: &'a OtherAttrs, cfg: bool, lint: bool, passthrough: bool, } impl<'a> ToTokens for PrintOtherAttrs<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { if self.cfg { print_attrs_as_outer(&self.attrs.cfg, tokens); } if self.lint { print_attrs_as_outer(&self.attrs.lint, tokens); } if self.passthrough { print_attrs_as_outer(&self.attrs.passthrough, tokens); } } } fn print_attrs_as_outer(attrs: &[Attribute], tokens: &mut TokenStream) { for attr in attrs { 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.192/src/cfg.rs000064400000000000000000000067641046102023000147650ustar 00000000000000use crate::syntax::cfg::{CfgExpr, ComputedCfg}; use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream}; use quote::{ToTokens, TokenStreamExt as _}; use syn::{token, AttrStyle, Attribute, MacroDelimiter, Meta, MetaList, Path, Token}; impl<'a> ComputedCfg<'a> { pub(crate) fn into_attr(&self) -> Option { if let ComputedCfg::Leaf(CfgExpr::Unconditional) = self { None } else { let span = Span::call_site(); Some(Attribute { pound_token: Token![#](span), style: AttrStyle::Outer, bracket_token: token::Bracket(span), meta: Meta::List(MetaList { path: Path::from(Ident::new("cfg", span)), delimiter: MacroDelimiter::Paren(token::Paren(span)), tokens: self.as_meta().into_token_stream(), }), }) } } pub(crate) fn as_meta(&self) -> impl ToTokens + '_ { Print { cfg: self, span: Span::call_site(), } } } struct Print<'a, Cfg> { cfg: &'a Cfg, span: Span, } impl<'a> ToTokens for Print<'a, CfgExpr> { fn to_tokens(&self, tokens: &mut TokenStream) { let span = self.span; let print = |cfg| Print { cfg, span }; match self.cfg { CfgExpr::Unconditional => unreachable!(), CfgExpr::Eq(ident, value) => { ident.to_tokens(tokens); if let Some(value) = value { Token![=](span).to_tokens(tokens); value.to_tokens(tokens); } } CfgExpr::All(inner) => { tokens.append(Ident::new("all", span)); let mut group = TokenStream::new(); group.append_separated(inner.iter().map(print), Token![,](span)); tokens.append(Group::new(Delimiter::Parenthesis, group)); } CfgExpr::Any(inner) => { tokens.append(Ident::new("any", span)); let mut group = TokenStream::new(); group.append_separated(inner.iter().map(print), Token![,](span)); tokens.append(Group::new(Delimiter::Parenthesis, group)); } CfgExpr::Not(inner) => { tokens.append(Ident::new("not", span)); let group = print(inner).into_token_stream(); tokens.append(Group::new(Delimiter::Parenthesis, group)); } } } } impl<'a> ToTokens for Print<'a, ComputedCfg<'a>> { fn to_tokens(&self, tokens: &mut TokenStream) { let span = self.span; match *self.cfg { ComputedCfg::Leaf(cfg) => Print { cfg, span }.to_tokens(tokens), ComputedCfg::All(ref inner) => { tokens.append(Ident::new("all", span)); let mut group = TokenStream::new(); group.append_separated( inner.iter().map(|&cfg| Print { cfg, span }), Token![,](span), ); tokens.append(Group::new(Delimiter::Parenthesis, group)); } ComputedCfg::Any(ref inner) => { tokens.append(Ident::new("any", span)); let mut group = TokenStream::new(); group .append_separated(inner.iter().map(|cfg| Print { cfg, span }), Token![,](span)); tokens.append(Group::new(Delimiter::Parenthesis, group)); } } } } cxxbridge-macro-1.0.192/src/derive.rs000064400000000000000000000327741046102023000155040ustar 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::BitAnd => unreachable!(), Trait::BitOr => unreachable!(), Trait::BitXor => unreachable!(), 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::BitAnd => expanded.extend(enum_bitand(enm, span)), Trait::BitOr => expanded.extend(enum_bitor(enm, span)), Trait::BitXor => expanded.extend(enum_bitxor(enm, span)), 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 => expanded.extend(enum_default(enm, span)), 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; let cfg_and_lint_attrs = strct.attrs.cfg_and_lint(); quote_spanned! {span=> #cfg_and_lint_attrs #[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 cfg_and_lint_attrs = strct.attrs.cfg_and_lint(); 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=> #cfg_and_lint_attrs #[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 cfg_and_lint_attrs = strct.attrs.cfg_and_lint(); 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=> #cfg_and_lint_attrs #[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 cfg_and_lint_attrs = strct.attrs.cfg_and_lint(); let fields = strct.fields.iter().map(|field| &field.name.rust); quote_spanned! {span=> #cfg_and_lint_attrs #[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 cfg_and_lint_attrs = strct.attrs.cfg_and_lint(); let fields = strct.fields.iter().map(|field| &field.name.rust); quote_spanned! {span=> #cfg_and_lint_attrs #[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 cfg_and_lint_attrs = strct.attrs.cfg_and_lint(); 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=> #cfg_and_lint_attrs #[automatically_derived] impl #generics ::cxx::core::cmp::PartialOrd for #ident #generics { #[allow(clippy::non_canonical_partial_ord_impl)] fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> { #body } } } } fn enum_bitand(enm: &Enum, span: Span) -> TokenStream { let ident = &enm.name.rust; let cfg_and_lint_attrs = enm.attrs.cfg_and_lint(); quote_spanned! {span=> #cfg_and_lint_attrs #[automatically_derived] impl ::cxx::core::ops::BitAnd for #ident { type Output = #ident; fn bitand(self, rhs: Self) -> Self::Output { #ident { repr: self.repr & rhs.repr, } } } } } fn enum_bitor(enm: &Enum, span: Span) -> TokenStream { let ident = &enm.name.rust; let cfg_and_lint_attrs = enm.attrs.cfg_and_lint(); quote_spanned! {span=> #cfg_and_lint_attrs #[automatically_derived] impl ::cxx::core::ops::BitOr for #ident { type Output = #ident; fn bitor(self, rhs: Self) -> Self::Output { #ident { repr: self.repr | rhs.repr, } } } } } fn enum_bitxor(enm: &Enum, span: Span) -> TokenStream { let ident = &enm.name.rust; let cfg_and_lint_attrs = enm.attrs.cfg_and_lint(); quote_spanned! {span=> #cfg_and_lint_attrs #[automatically_derived] impl ::cxx::core::ops::BitXor for #ident { type Output = #ident; fn bitxor(self, rhs: Self) -> Self::Output { #ident { repr: self.repr ^ rhs.repr, } } } } } fn enum_copy(enm: &Enum, span: Span) -> TokenStream { let ident = &enm.name.rust; let cfg_and_lint_attrs = enm.attrs.cfg_and_lint(); quote_spanned! {span=> #cfg_and_lint_attrs #[automatically_derived] impl ::cxx::core::marker::Copy for #ident {} } } fn enum_clone(enm: &Enum, span: Span) -> TokenStream { let ident = &enm.name.rust; let cfg_and_lint_attrs = enm.attrs.cfg_and_lint(); quote_spanned! {span=> #cfg_and_lint_attrs #[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 cfg_and_lint_attrs = enm.attrs.cfg_and_lint(); 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=> #cfg_and_lint_attrs #[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_default(enm: &Enum, span: Span) -> TokenStream { let ident = &enm.name.rust; let cfg_and_lint_attrs = enm.attrs.cfg_and_lint(); for variant in &enm.variants { if variant.default { let variant = &variant.name.rust; return quote_spanned! {span=> #cfg_and_lint_attrs #[automatically_derived] impl ::cxx::core::default::Default for #ident { fn default() -> Self { #ident::#variant } } }; } } unreachable!("no #[default] variant"); } fn enum_ord(enm: &Enum, span: Span) -> TokenStream { let ident = &enm.name.rust; let cfg_and_lint_attrs = enm.attrs.cfg_and_lint(); quote_spanned! {span=> #cfg_and_lint_attrs #[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; let cfg_and_lint_attrs = enm.attrs.cfg_and_lint(); quote_spanned! {span=> #cfg_and_lint_attrs #[automatically_derived] impl ::cxx::core::cmp::PartialOrd for #ident { #[allow(clippy::non_canonical_partial_ord_impl)] 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.192/src/expand.rs000064400000000000000000002712521046102023000155010ustar 00000000000000use crate::syntax::atom::Atom::*; use crate::syntax::attrs::{self, OtherAttrs}; use crate::syntax::cfg::{CfgExpr, ComputedCfg}; use crate::syntax::file::Module; use crate::syntax::instantiate::{ImplKey, NamedImplKey}; use crate::syntax::map::OrderedMap; use crate::syntax::message::Message; use crate::syntax::namespace::Namespace; use crate::syntax::qualified::QualifiedName; use crate::syntax::report::Errors; use crate::syntax::set::UnorderedSet; use crate::syntax::symbol::Symbol; use crate::syntax::trivial::TrivialReason; use crate::syntax::types::ConditionalImpl; use crate::syntax::unpin::UnpinReason; use crate::syntax::{ self, check, mangle, Api, Doc, Enum, ExternFn, ExternType, FnKind, Lang, 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::fmt::{self, Display}; use std::mem; use syn::{parse_quote, GenericParam, Generics, Lifetime, Result, Token, Visibility}; 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); 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)); expanded.extend(expand_associated_functions(&strct.name.rust, types)); hidden.extend(expand_struct_nonempty(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) { hidden.extend(expand_extern_shared_struct(ety, &ffi)); } else if !types.enums.contains_key(ident) { expanded.extend(expand_cxx_type(ety)); expanded.extend(expand_associated_functions(&ety.name.rust, types)); hidden.extend(expand_cxx_type_assert_pinned(ety, types)); } } Api::CxxFunction(efn) => { if efn.self_type().is_none() { expanded.extend(expand_cxx_function_shim(efn, types)); } } Api::RustType(ety) => { expanded.extend(expand_rust_type_impl(ety)); expanded.extend(expand_associated_functions(&ety.name.rust, types)); 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)); expanded.extend(expand_associated_functions(&alias.name.rust, types)); hidden.extend(expand_type_alias_verify(alias, types)); } } } for (impl_key, conditional_impl) in &types.impls { match impl_key { ImplKey::RustBox(ident) => { hidden.extend(expand_rust_box(ident, types, conditional_impl)); } ImplKey::RustVec(ident) => { hidden.extend(expand_rust_vec(ident, types, conditional_impl)); } ImplKey::UniquePtr(ident) => { expanded.extend(expand_unique_ptr(ident, types, conditional_impl)); } ImplKey::SharedPtr(ident) => { expanded.extend(expand_shared_ptr(ident, types, conditional_impl)); } ImplKey::WeakPtr(ident) => { expanded.extend(expand_weak_ptr(ident, types, conditional_impl)); } ImplKey::CxxVector(ident) => { expanded.extend(expand_cxx_vector(ident, conditional_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 all_attrs = attrs.all(); 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 #all_attrs #[deny(improper_ctypes, improper_ctypes_definitions)] #[allow(clippy::unknown_lints)] #[allow( non_camel_case_types, non_snake_case, clippy::extra_unused_type_parameters, clippy::items_after_statements, clippy::no_effect_underscore_binding, clippy::unsafe_derive_deserialize, 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 all_attrs = strct.attrs.all(); let cfg_and_lint_attrs = strct.attrs.cfg_and_lint(); let generics = &strct.generics; let type_id = type_id(&strct.name); let fields = strct.fields.iter().map(|field| { let doc = &field.doc; let all_attrs = field.attrs.all(); // This span on the pub makes "private type in public interface" errors // appear in the right place. let vis = field.visibility; quote!(#doc #all_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,)* } }; let align = strct.align.as_ref().map(|align| quote!(, align(#align))); quote! { #doc #derives #all_attrs #[repr(C #align)] #struct_def #cfg_and_lint_attrs #[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_nonempty(strct: &Struct) -> TokenStream { let has_unconditional_field = strct .fields .iter() .any(|field| matches!(field.cfg, CfgExpr::Unconditional)); if has_unconditional_field { return TokenStream::new(); } let mut fields = strct.fields.iter(); let mut cfg = ComputedCfg::from(&fields.next().unwrap().cfg); fields.for_each(|field| cfg.merge_or(&field.cfg)); if let ComputedCfg::Leaf(CfgExpr::Unconditional) = cfg { // At least one field is unconditional, nothing to check. TokenStream::new() } else { let meta = cfg.as_meta(); let msg = "structs without any fields are not supported"; let error = syn::Error::new_spanned(strct, msg).into_compile_error(); quote! { #[cfg(not(#meta))] #error } } } fn expand_struct_operators(strct: &Struct) -> TokenStream { let ident = &strct.name.rust; let generics = &strct.generics; let cfg_and_lint_attrs = strct.attrs.cfg_and_lint(); 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=> #cfg_and_lint_attrs #[doc(hidden)] #[unsafe(export_name = #link_name)] extern "C" fn #local_name #generics(lhs: &#ident #generics, rhs: &#ident #generics) -> ::cxx::core::primitive::bool { let __fn = ::cxx::core::concat!("<", ::cxx::core::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=> #cfg_and_lint_attrs #[doc(hidden)] #[unsafe(export_name = #link_name)] extern "C" fn #local_name #generics(lhs: &#ident #generics, rhs: &#ident #generics) -> ::cxx::core::primitive::bool { let __fn = ::cxx::core::concat!("<", ::cxx::core::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=> #cfg_and_lint_attrs #[doc(hidden)] #[unsafe(export_name = #link_name)] extern "C" fn #local_name #generics(lhs: &#ident #generics, rhs: &#ident #generics) -> ::cxx::core::primitive::bool { let __fn = ::cxx::core::concat!("<", ::cxx::core::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=> #cfg_and_lint_attrs #[doc(hidden)] #[unsafe(export_name = #link_name)] extern "C" fn #local_name #generics(lhs: &#ident #generics, rhs: &#ident #generics) -> ::cxx::core::primitive::bool { let __fn = ::cxx::core::concat!("<", ::cxx::core::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=> #cfg_and_lint_attrs #[doc(hidden)] #[unsafe(export_name = #link_name)] extern "C" fn #local_name #generics(lhs: &#ident #generics, rhs: &#ident #generics) -> ::cxx::core::primitive::bool { let __fn = ::cxx::core::concat!("<", ::cxx::core::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=> #cfg_and_lint_attrs #[doc(hidden)] #[unsafe(export_name = #link_name)] extern "C" fn #local_name #generics(lhs: &#ident #generics, rhs: &#ident #generics) -> ::cxx::core::primitive::bool { let __fn = ::cxx::core::concat!("<", ::cxx::core::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=> #cfg_and_lint_attrs #[doc(hidden)] #[unsafe(export_name = #link_name)] #[allow(clippy::cast_possible_truncation)] extern "C" fn #local_name #generics(this: &#ident #generics) -> ::cxx::core::primitive::usize { let __fn = ::cxx::core::concat!("<", ::cxx::core::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 cfg_and_lint_attrs = strct.attrs.cfg_and_lint(); let span = ident.span(); let impl_token = Token![impl](strct.visibility.span); quote_spanned! {span=> #cfg_and_lint_attrs #[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 all_attrs = enm.attrs.all(); let cfg_and_lint_attrs = enm.attrs.cfg_and_lint(); let repr = &enm.repr; let type_id = type_id(&enm.name); let variants = enm.variants.iter().map(|variant| { let doc = &variant.doc; let all_attrs = variant.attrs.all(); let variant_ident = &variant.name.rust; let discriminant = &variant.discriminant; let span = variant_ident.span(); Some(quote_spanned! {span=> #doc #all_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 #all_attrs #[repr(transparent)] #enum_def #cfg_and_lint_attrs #[allow(non_upper_case_globals)] impl #ident { #(#variants)* } #cfg_and_lint_attrs #[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 all_attrs = ety.attrs.all(); let cfg_and_lint_attrs = ety.attrs.cfg_and_lint(); 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 #all_attrs #[repr(C)] #extern_type_def #cfg_and_lint_attrs #[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 cfg_and_lint_attrs = ety.attrs.cfg_and_lint(); let infer = Token![_](ident.span()); let resolve = types.resolve(ident); let lifetimes = resolve.generics.to_underscore_lifetimes(); quote! { #cfg_and_lint_attrs 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_extern_shared_struct(ety: &ExternType, ffi: &Module) -> TokenStream { let module = &ffi.ident; let name = &ety.name.rust; let namespaced_name = display_namespaced(&ety.name); let cfg_and_lint_attrs = ety.attrs.cfg_and_lint(); let visibility = match &ffi.vis { Visibility::Public(_) => "pub ".to_owned(), Visibility::Restricted(vis) => { format!( "pub(in {}) ", vis.path .segments .iter() .map(|segment| segment.ident.to_string()) .collect::>() .join("::"), ) } Visibility::Inherited => String::new(), }; let namespace_attr = if ety.name.namespace == Namespace::ROOT { String::new() } else { format!( "#[namespace = \"{}\"]\n ", ety.name .namespace .iter() .map(Ident::to_string) .collect::>() .join("::"), ) }; let message = format!( "\ \nShared struct redeclared as an unsafe extern C++ type is deprecated.\ \nIf this is intended to be a shared struct, remove this `type {name}`.\ \nIf this is intended to be an extern type, change it to:\ \n\ \n use cxx::ExternType;\ \n \ \n #[repr(C)]\ \n {visibility}struct {name} {{\ \n ...\ \n }}\ \n \ \n unsafe impl ExternType for {name} {{\ \n type Id = cxx::type_id!(\"{namespaced_name}\");\ \n type Kind = cxx::kind::Trivial;\ \n }}\ \n \ \n {visibility}mod {module} {{\ \n {namespace_attr}extern \"C++\" {{\ \n type {name} = crate::{name};\ \n }}\ \n ...\ \n }}", ); quote! { #cfg_and_lint_attrs #[deprecated = #message] struct #name {} #cfg_and_lint_attrs let _ = #name {}; } } fn expand_associated_functions(self_type: &Ident, types: &Types) -> TokenStream { let Some(functions) = types.associated_fn.get(self_type) else { return TokenStream::new(); }; let resolve = types.resolve(self_type); let self_type_cfg_attrs = resolve.attrs.cfg(); let elided_lifetime = Lifetime::new("'_", Span::call_site()); let mut group_by_lifetimes = OrderedMap::new(); let mut tokens = TokenStream::new(); for efn in functions { match efn.lang { Lang::Cxx | Lang::CxxUnwind => {} Lang::Rust => continue, } let mut impl_lifetimes = Vec::new(); let mut self_type_lifetimes = Vec::new(); let self_lt_token; let self_gt_token; match &efn.kind { FnKind::Method(receiver) if receiver.ty.generics.lt_token.is_some() => { for lifetime in &receiver.ty.generics.lifetimes { if lifetime.ident != "_" && efn .generics .lifetimes() .any(|param| param.lifetime == *lifetime) { impl_lifetimes.push(lifetime); } self_type_lifetimes.push(lifetime); } self_lt_token = receiver.ty.generics.lt_token; self_gt_token = receiver.ty.generics.gt_token; } _ => { self_type_lifetimes.resize(resolve.generics.lifetimes.len(), &elided_lifetime); self_lt_token = resolve.generics.lt_token; self_gt_token = resolve.generics.gt_token; } } if efn.undeclared_lifetimes().is_empty() && self_type_lifetimes.len() == resolve.generics.lifetimes.len() { group_by_lifetimes .entry((impl_lifetimes, self_type_lifetimes)) .or_insert_with(Vec::new) .push(efn); } else { let impl_token = Token![impl](efn.name.rust.span()); let impl_lt_token = efn.generics.lt_token; let impl_gt_token = efn.generics.gt_token; let self_type = efn.self_type().unwrap(); let function = expand_cxx_function_shim(efn, types); tokens.extend(quote! { #self_type_cfg_attrs #impl_token #impl_lt_token #(#impl_lifetimes),* #impl_gt_token #self_type #self_lt_token #(#self_type_lifetimes),* #self_gt_token { #function } }); } } for ((impl_lifetimes, self_type_lifetimes), functions) in &group_by_lifetimes { let functions = functions .iter() .map(|efn| expand_cxx_function_shim(efn, types)); tokens.extend(quote! { #self_type_cfg_attrs impl <#(#impl_lifetimes),*> #self_type <#(#self_type_lifetimes),*> { #(#functions)* } }); } tokens } fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream { let receiver = efn.receiver().into_iter().map(|receiver| { if types.is_considered_improper_ctype(&receiver.ty) { if receiver.mutable { quote!(_: *mut ::cxx::core::ffi::c_void) } else { quote!(_: *const ::cxx::core::ffi::c_void) } } else { 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, types, true, efn.lang) }; let mut outparam = None; if indirect_return(efn, types, efn.lang) { 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); let lt_token = efn.generics.lt_token.unwrap_or_default(); let undeclared_lifetimes = efn.undeclared_lifetimes().into_iter(); let declared_lifetimes = &efn.generics.params; let gt_token = efn.generics.gt_token.unwrap_or_default(); quote! { #[link_name = #link_name] fn #local_name #lt_token #(#undeclared_lifetimes,)* #declared_lifetimes #gt_token(#(#all_args,)* #outparam) #ret; } } fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { let doc = &efn.doc; let all_attrs = efn.attrs.all(); let decl = expand_cxx_function_decl(efn, types); let receiver = efn.receiver().into_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, efn.lang); let receiver_var = efn.receiver().into_iter().map(|receiver| { if types.is_considered_improper_ctype(&receiver.ty) { let var = receiver.var; let ty = &receiver.ty.rust; let resolve = types.resolve(ty); let lifetimes = resolve.generics.to_underscore_lifetimes(); if receiver.pinned { quote!(::cxx::core::ptr::from_mut::<#ty #lifetimes>(::cxx::core::pin::Pin::into_inner_unchecked(#var)).cast::<::cxx::core::ffi::c_void>()) } else if receiver.mutable { quote!(::cxx::core::ptr::from_mut::<#ty #lifetimes>(#var).cast::<::cxx::core::ffi::c_void>()) } else { quote!(::cxx::core::ptr::from_ref::<#ty #lifetimes>(#var).cast::<::cxx::core::ffi::c_void>()) } } else { 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().cast::<::cxx::private::RustString>().cast_const()) } 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().cast::<::cxx::private::RustVec<_>>().cast_const()), 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(_) => 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=> ::cxx::core::ptr::from_ref::<#inner>(#var).cast::<::cxx::core::ffi::c_void>()) } true => quote_spanned!(span=> ::cxx::core::ptr::from_mut::<#inner>(#var).cast::<::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 let Some(ret) = &efn.ret { expr = 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(_) => { 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(_) => 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)); } } else if efn.throws { expr = call; } else { expr = quote! { #call; }; } let dispatch = quote_spanned!(span=> unsafe { #setup #expr }); let visibility = efn.visibility; let unsafety = &efn.unsafety; let fn_token = efn.fn_token; let ident = &efn.name.rust; let lt_token = efn.generics.lt_token; let lifetimes = { let mut self_type_lifetimes = UnorderedSet::new(); if let FnKind::Method(receiver) = &efn.kind { self_type_lifetimes.extend(&receiver.ty.generics.lifetimes); } efn.generics .params .pairs() .filter(move |param| match param.value() { GenericParam::Lifetime(param) => !self_type_lifetimes.contains(¶m.lifetime), GenericParam::Type(_) | GenericParam::Const(_) => unreachable!(), }) }; let gt_token = efn.generics.gt_token; let arg_list = quote_spanned!(efn.paren_token.span=> (#(#all_args,)*)); let calling_conv = match efn.lang { Lang::Cxx => quote_spanned!(span=> "C"), Lang::CxxUnwind => quote_spanned!(span=> "C-unwind"), Lang::Rust => unreachable!(), }; quote_spanned! {span=> #doc #all_attrs #visibility #unsafety #fn_token #ident #lt_token #(#lifetimes)* #gt_token #arg_list #ret { unsafe extern #calling_conv { #decl } #trampolines #dispatch } } } 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 calling_conv = match efn.lang { Lang::Cxx => "C", Lang::CxxUnwind => "C-unwind", Lang::Rust => unreachable!(), }; let var = &var.rust; quote! { let #var = ::cxx::private::FatFunction { trampoline: { unsafe extern #calling_conv { #[link_name = #c_trampoline] fn trampoline(); } #shim trampoline as ::cxx::core::primitive::usize as *const ::cxx::core::ffi::c_void }, ptr: #var as ::cxx::core::primitive::usize as *const ::cxx::core::ffi::c_void, }; } } fn expand_rust_type_import(ety: &ExternType) -> TokenStream { let ident = &ety.name.rust; let all_attrs = ety.attrs.all(); let span = ident.span(); quote_spanned! {span=> #all_attrs use super::#ident; } } fn expand_rust_type_impl(ety: &ExternType) -> TokenStream { let ident = &ety.name.rust; let generics = &ety.generics; let cfg_and_lint_attrs = ety.attrs.cfg_and_lint(); let span = ident.span(); let unsafe_impl = quote_spanned!(ety.type_token.span=> unsafe impl); let mut impls = quote_spanned! {span=> #cfg_and_lint_attrs #[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=> #cfg_and_lint_attrs #[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 cfg_and_lint_attrs = ety.attrs.cfg_and_lint(); let resolve = types.resolve(ident); let lifetimes = resolve.generics.to_underscore_lifetimes(); quote_spanned! {ident.span()=> #cfg_and_lint_attrs const _: fn() = ::cxx::private::require_unpin::<#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 cfg_and_lint_attrs = ety.attrs.cfg_and_lint(); 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()=> #cfg_and_lint_attrs { #[doc(hidden)] #[allow(clippy::needless_maybe_sized)] fn __AssertSized() -> ::cxx::core::alloc::Layout { ::cxx::core::alloc::Layout::new::() } #[doc(hidden)] #[unsafe(export_name = #link_sizeof)] extern "C" fn #local_sizeof() -> ::cxx::core::primitive::usize { __AssertSized::<#ident #lifetimes>().size() } #[doc(hidden)] #[unsafe(export_name = #link_alignof)] extern "C" fn #local_alignof() -> ::cxx::core::primitive::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.self_type() { None => format_ident!("__{}", efn.name.rust), Some(self_type) => format_ident!("__{}__{}", self_type, efn.name.rust), }; let prevent_unwind_label = match efn.self_type() { None => format!("::{}", efn.name.rust), Some(self_type) => format!("::{}::{}", self_type, 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 all_attrs = attrs.all(); let generics = outer_generics.unwrap_or(&sig.generics); let receiver_var = sig .receiver() .map(|receiver| quote_spanned!(receiver.var.span=> __self)); let receiver = sig.receiver().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(_) => { requires_unsafe = true; 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(_) => 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 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 wrap_super = invoke.map(|invoke| { // If the wrapper function is being passed directly to prevent_unwind, // it must implement `FnOnce() -> R` and cannot be an unsafe fn. let unsafety = sig.unsafety.filter(|_| requires_closure); expand_rust_function_shim_super(sig, &local_name, invoke, unsafety) }); 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(_) => 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(_) => 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, Lang::Rust); 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, types, false, Lang::Rust) }; let pointer = match invoke { None => Some(quote_spanned!(span=> __extern: *const ())), Some(_) => None, }; quote_spanned! {span=> #all_attrs #[doc(hidden)] #[unsafe(export_name = #link_name)] unsafe extern "C" fn #local_name #generics(#(#all_args,)* #outparam #pointer) #ret { let __fn = ::cxx::core::concat!(::cxx::core::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, unsafety: Option, ) -> TokenStream { let generics = &sig.generics; let receiver_var = sig .receiver() .map(|receiver| Ident::new("__self", receiver.var.span)); let receiver = sig.receiver().into_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 = quote_spanned!(rangle.span=> ::cxx::core::fmt::Display + use<>>); 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.self_type() { None => quote_spanned!(span=> super::#invoke), Some(self_type) => quote_spanned!(span=> #self_type::#invoke), }; let mut body = quote_spanned!(span=> #call(#(#vars,)*)); let mut allow_unused_unsafe = None; if sig.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 all_attrs = alias.attrs.all(); 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 #all_attrs #visibility #type_token #ident #generics #eq_token #ty #semi_token } } fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream { let cfg_and_lint_attrs = alias.attrs.cfg_and_lint(); 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 resolve = types.resolve(ident); let lifetimes = resolve.generics.to_underscore_lifetimes(); let mut verify = quote! { #cfg_and_lint_attrs const _: fn() = #begin #ident #lifetimes, #type_id #end; }; let mut require_unpin = false; let mut require_box = false; let mut require_vec = false; let mut require_extern_type_trivial = false; let mut require_rust_type_or_trivial = None; if let Some(reasons) = types.required_trivial.get(&alias.name.rust) { for reason in reasons { match reason { TrivialReason::BoxTarget { local: true } | TrivialReason::VecElement { local: true } => require_unpin = true, TrivialReason::BoxTarget { local: false } => require_box = true, TrivialReason::VecElement { local: false } => require_vec = true, TrivialReason::StructField(_) | TrivialReason::FunctionArgument(_) | TrivialReason::FunctionReturn(_) => require_extern_type_trivial = true, TrivialReason::SliceElement(slice) => require_rust_type_or_trivial = Some(slice), } } } 'unpin: { if let Some(reason) = types.required_unpin.get(ident) { let ampersand; let reference_lifetime; let mutability; let mut inner; let generics; let shorthand; match reason { UnpinReason::Receiver(receiver) => { ampersand = &receiver.ampersand; reference_lifetime = &receiver.lifetime; mutability = &receiver.mutability; inner = receiver.ty.rust.clone(); generics = &receiver.ty.generics; shorthand = receiver.shorthand; if receiver.shorthand { inner.set_span(receiver.var.span); } } UnpinReason::Ref(mutable_reference) => { ampersand = &mutable_reference.ampersand; reference_lifetime = &mutable_reference.lifetime; mutability = &mutable_reference.mutability; let Type::Ident(inner_type) = &mutable_reference.inner else { unreachable!(); }; inner = inner_type.rust.clone(); generics = &inner_type.generics; shorthand = false; } UnpinReason::Slice(mutable_slice) => { ampersand = &mutable_slice.ampersand; mutability = &mutable_slice.mutability; let inner = quote_spanned!(mutable_slice.bracket.span=> [#ident #lifetimes]); let trait_name = format_ident!("SliceOfUnpin_{ident}"); let label = format!("requires `{ident}: Unpin`"); verify.extend(quote! { #cfg_and_lint_attrs let _ = { #[diagnostic::on_unimplemented( message = "mutable slice of pinned type is not supported", label = #label, )] trait #trait_name { fn check_unpin() {} } #[diagnostic::do_not_recommend] impl<'a, T: ?::cxx::core::marker::Sized + ::cxx::core::marker::Unpin> #trait_name for &'a #mutability T {} <#ampersand #mutability #inner as #trait_name>::check_unpin }; }); require_unpin = false; break 'unpin; } } let trait_name = format_ident!("ReferenceToUnpin_{ident}"); let message = format!("mutable reference to C++ type requires a pin -- use Pin<&mut {ident}>"); let label = { let mut label = Message::new(); write!(label, "use `"); if shorthand { write!(label, "self: "); } write!(label, "Pin<&"); if let Some(reference_lifetime) = reference_lifetime { write!(label, "{reference_lifetime} "); } write!(label, "mut {ident}"); if !generics.lifetimes.is_empty() { write!(label, "<"); for (i, lifetime) in generics.lifetimes.iter().enumerate() { if i > 0 { write!(label, ", "); } write!(label, "{lifetime}"); } write!(label, ">"); } else if shorthand && !alias.generics.lifetimes.is_empty() { write!(label, "<"); for i in 0..alias.generics.lifetimes.len() { if i > 0 { write!(label, ", "); } write!(label, "'_"); } write!(label, ">"); } write!(label, ">`"); label }; let lifetimes = generics.to_underscore_lifetimes(); verify.extend(quote! { #cfg_and_lint_attrs let _ = { #[diagnostic::on_unimplemented(message = #message, label = #label)] trait #trait_name { fn check_unpin() {} } #[diagnostic::do_not_recommend] impl<'a, T: ?::cxx::core::marker::Sized + ::cxx::core::marker::Unpin> #trait_name for &'a mut T {} <#ampersand #mutability #inner #lifetimes as #trait_name>::check_unpin }; }); require_unpin = false; } } if require_unpin { verify.extend(quote! { #cfg_and_lint_attrs const _: fn() = ::cxx::private::require_unpin::<#ident #lifetimes>; }); } if require_box { verify.extend(quote! { #cfg_and_lint_attrs const _: fn() = ::cxx::private::require_box::<#ident #lifetimes>; }); } if require_vec { verify.extend(quote! { #cfg_and_lint_attrs const _: fn() = ::cxx::private::require_vec::<#ident #lifetimes>; }); } if require_extern_type_trivial { let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<); verify.extend(quote! { #cfg_and_lint_attrs const _: fn() = #begin #ident #lifetimes, ::cxx::kind::Trivial #end; }); } else if let Some(slice_type) = require_rust_type_or_trivial { let ampersand = &slice_type.ampersand; let mutability = &slice_type.mutability; let inner = quote_spanned!(slice_type.bracket.span.join()=> [#ident #lifetimes]); verify.extend(quote! { #cfg_and_lint_attrs let _ = || ::cxx::private::with::<#ident #lifetimes>().check_slice::<#ampersand #mutability #inner>(); }); } 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, conditional_impl: &ConditionalImpl, ) -> TokenStream { let link_prefix = format!("cxxbridge1$box${}$", key.symbol); let link_alloc = format!("{}alloc", link_prefix); let link_dealloc = format!("{}dealloc", link_prefix); let link_drop = format!("{}drop", link_prefix); let (impl_generics, inner_with_generics) = generics::split_for_impl(key, conditional_impl, types); let cfg = conditional_impl.cfg.into_attr(); let begin_span = conditional_impl .explicit_impl .map_or(key.begin_span, |explicit| explicit.impl_token.span); let end_span = conditional_impl .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_type_label = generics::format_for_prevent_unwind_label(key.inner); quote_spanned!(end_span=> { #cfg #[automatically_derived] #[doc(hidden)] #unsafe_token impl #impl_generics ::cxx::private::ImplBox for #inner_with_generics {} #cfg #[doc(hidden)] #[unsafe(export_name = #link_alloc)] unsafe extern "C" fn __alloc #impl_generics() -> *mut ::cxx::core::mem::MaybeUninit<#inner_with_generics> { // No prevent_unwind: the global allocator is not allowed to panic. ::cxx::alloc::boxed::Box::into_raw(::cxx::alloc::boxed::Box::new_uninit()) } #cfg #[doc(hidden)] #[unsafe(export_name = #link_dealloc)] unsafe extern "C" fn __dealloc #impl_generics(ptr: *mut ::cxx::core::mem::MaybeUninit<#inner_with_generics>) { // No prevent_unwind: the global allocator is not allowed to panic. let _ = unsafe { ::cxx::alloc::boxed::Box::from_raw(ptr) }; } #cfg #[doc(hidden)] #[unsafe(export_name = #link_drop)] unsafe extern "C" fn __drop #impl_generics(this: *mut ::cxx::alloc::boxed::Box<#inner_with_generics>) { let __fn = ::cxx::core::concat!("<", #prevent_unwind_type_label, " as Drop>::drop"); ::cxx::private::prevent_unwind(__fn, || unsafe { ::cxx::core::ptr::drop_in_place(this) }); } }) } fn expand_rust_vec( key: &NamedImplKey, types: &Types, conditional_impl: &ConditionalImpl, ) -> TokenStream { let link_prefix = format!("cxxbridge1$rust_vec${}$", key.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 (impl_generics, inner_with_generics) = generics::split_for_impl(key, conditional_impl, types); let cfg = conditional_impl.cfg.into_attr(); let begin_span = conditional_impl .explicit_impl .map_or(key.begin_span, |explicit| explicit.impl_token.span); let end_span = conditional_impl .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_type_label = generics::format_for_prevent_unwind_label(key.inner); quote_spanned!(end_span=> { #cfg #[automatically_derived] #[doc(hidden)] #unsafe_token impl #impl_generics ::cxx::private::ImplVec for #inner_with_generics {} #cfg #[doc(hidden)] #[unsafe(export_name = #link_new)] unsafe extern "C" fn __new #impl_generics(this: *mut ::cxx::private::RustVec<#inner_with_generics>) { // No prevent_unwind: cannot panic. unsafe { ::cxx::core::ptr::write(this, ::cxx::private::RustVec::new()); } } #cfg #[doc(hidden)] #[unsafe(export_name = #link_drop)] unsafe extern "C" fn __drop #impl_generics(this: *mut ::cxx::private::RustVec<#inner_with_generics>) { let __fn = ::cxx::core::concat!("<", #prevent_unwind_type_label, " as Drop>::drop"); ::cxx::private::prevent_unwind( __fn, || unsafe { ::cxx::core::ptr::drop_in_place(this) }, ); } #cfg #[doc(hidden)] #[unsafe(export_name = #link_len)] unsafe extern "C" fn __len #impl_generics(this: *const ::cxx::private::RustVec<#inner_with_generics>) -> ::cxx::core::primitive::usize { // No prevent_unwind: cannot panic. unsafe { (*this).len() } } #cfg #[doc(hidden)] #[unsafe(export_name = #link_capacity)] unsafe extern "C" fn __capacity #impl_generics(this: *const ::cxx::private::RustVec<#inner_with_generics>) -> ::cxx::core::primitive::usize { // No prevent_unwind: cannot panic. unsafe { (*this).capacity() } } #cfg #[doc(hidden)] #[unsafe(export_name = #link_data)] unsafe extern "C" fn __data #impl_generics(this: *const ::cxx::private::RustVec<#inner_with_generics>) -> *const #inner_with_generics { // No prevent_unwind: cannot panic. unsafe { (*this).as_ptr() } } #cfg #[doc(hidden)] #[unsafe(export_name = #link_reserve_total)] unsafe extern "C" fn __reserve_total #impl_generics(this: *mut ::cxx::private::RustVec<#inner_with_generics>, new_cap: ::cxx::core::primitive::usize) { // No prevent_unwind: the global allocator is not allowed to panic. unsafe { (*this).reserve_total(new_cap); } } #cfg #[doc(hidden)] #[unsafe(export_name = #link_set_len)] unsafe extern "C" fn __set_len #impl_generics(this: *mut ::cxx::private::RustVec<#inner_with_generics>, len: ::cxx::core::primitive::usize) { // No prevent_unwind: cannot panic. unsafe { (*this).set_len(len); } } #cfg #[doc(hidden)] #[unsafe(export_name = #link_truncate)] unsafe extern "C" fn __truncate #impl_generics(this: *mut ::cxx::private::RustVec<#inner_with_generics>, len: ::cxx::core::primitive::usize) { let __fn = ::cxx::core::concat!("<", #prevent_unwind_type_label, " as Drop>::drop"); ::cxx::private::prevent_unwind( __fn, || unsafe { (*this).truncate(len) }, ); } }) } fn expand_unique_ptr( key: &NamedImplKey, types: &Types, conditional_impl: &ConditionalImpl, ) -> TokenStream { let prefix = format!("cxxbridge1$unique_ptr${}$", key.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 name = generics::concise_rust_name(key.inner); let (impl_generics, inner_with_generics) = generics::split_for_impl(key, conditional_impl, types); let can_construct_from_value = types.is_maybe_trivial(key.inner); let new_method = if can_construct_from_value { Some(quote! { fn __new(value: Self) -> ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void> { unsafe 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(&raw mut repr).cast::<#inner_with_generics>().write(value); } repr } }) } else { None }; let cfg = conditional_impl.cfg.into_attr(); let begin_span = conditional_impl .explicit_impl .map_or(key.begin_span, |explicit| explicit.impl_token.span); let end_span = conditional_impl .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=> #cfg #[automatically_derived] #unsafe_token impl #impl_generics ::cxx::memory::UniquePtrTarget for #inner_with_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> { unsafe 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(&raw mut repr); } repr } #new_method unsafe fn __raw(raw: *mut Self) -> ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void> { unsafe 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(&raw mut repr, raw.cast()); } repr } unsafe fn __get(repr: ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *const Self { unsafe 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(&raw const repr).cast() } } unsafe fn __release(mut repr: ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *mut Self { unsafe 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(&raw mut repr).cast() } } unsafe fn __drop(mut repr: ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) { unsafe extern "C" { #[link_name = #link_drop] fn __drop(this: *mut ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>); } unsafe { __drop(&raw mut repr); } } } } } fn expand_shared_ptr( key: &NamedImplKey, types: &Types, conditional_impl: &ConditionalImpl, ) -> TokenStream { let prefix = format!("cxxbridge1$shared_ptr${}$", key.symbol); let link_null = format!("{}null", prefix); let link_uninit = format!("{}uninit", prefix); let link_raw = format!("{}raw", prefix); let link_clone = format!("{}clone", prefix); let link_get = format!("{}get", prefix); let link_drop = format!("{}drop", prefix); let name = generics::concise_rust_name(key.inner); let (impl_generics, inner_with_generics) = generics::split_for_impl(key, conditional_impl, types); let can_construct_from_value = types.is_maybe_trivial(key.inner); let new_method = if can_construct_from_value { Some(quote! { unsafe fn __new(value: Self, new: *mut ::cxx::core::ffi::c_void) { unsafe 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::<#inner_with_generics>().write(value); } } }) } else { None }; let cfg = conditional_impl.cfg.into_attr(); let begin_span = conditional_impl .explicit_impl .map_or(key.begin_span, |explicit| explicit.impl_token.span); let end_span = conditional_impl .explicit_impl .map_or(key.end_span, |explicit| explicit.brace_token.span.join()); let unsafe_token = format_ident!("unsafe", span = begin_span); let not_destructible_err = format!( "{} is not destructible", generics::concise_cxx_name(key.inner, types), ); quote_spanned! {end_span=> #cfg #[automatically_derived] #unsafe_token impl #impl_generics ::cxx::memory::SharedPtrTarget for #inner_with_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) { unsafe extern "C" { #[link_name = #link_null] fn __null(new: *mut ::cxx::core::ffi::c_void); } unsafe { __null(new); } } #new_method #[track_caller] unsafe fn __raw(new: *mut ::cxx::core::ffi::c_void, raw: *mut Self) { unsafe extern "C" { #[link_name = #link_raw] fn __raw(new: *const ::cxx::core::ffi::c_void, raw: *mut ::cxx::core::ffi::c_void) -> ::cxx::core::primitive::bool; } if !unsafe { __raw(new, raw.cast::<::cxx::core::ffi::c_void>()) } { ::cxx::core::panic!(#not_destructible_err); } } unsafe fn __clone(this: *const ::cxx::core::ffi::c_void, new: *mut ::cxx::core::ffi::c_void) { unsafe 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 { unsafe 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) { unsafe 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, conditional_impl: &ConditionalImpl, ) -> TokenStream { let prefix = format!("cxxbridge1$weak_ptr${}$", key.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 name = generics::concise_rust_name(key.inner); let (impl_generics, inner_with_generics) = generics::split_for_impl(key, conditional_impl, types); let cfg = conditional_impl.cfg.into_attr(); let begin_span = conditional_impl .explicit_impl .map_or(key.begin_span, |explicit| explicit.impl_token.span); let end_span = conditional_impl .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=> #cfg #[automatically_derived] #unsafe_token impl #impl_generics ::cxx::memory::WeakPtrTarget for #inner_with_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) { unsafe 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) { unsafe 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) { unsafe 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) { unsafe 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) { unsafe extern "C" { #[link_name = #link_drop] fn __drop(this: *mut ::cxx::core::ffi::c_void); } unsafe { __drop(this); } } } } } fn expand_cxx_vector( key: &NamedImplKey, conditional_impl: &ConditionalImpl, types: &Types, ) -> TokenStream { let prefix = format!("cxxbridge1$std$vector${}$", key.symbol); let link_new = format!("{}new", prefix); let link_size = format!("{}size", prefix); let link_capacity = format!("{}capacity", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); let link_reserve = format!("{}reserve", 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${}$", key.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 name = generics::concise_rust_name(key.inner); let (impl_generics, inner_with_generics) = generics::split_for_impl(key, conditional_impl, types); let cfg = conditional_impl.cfg.into_attr(); let begin_span = conditional_impl .explicit_impl .map_or(key.begin_span, |explicit| explicit.impl_token.span); let end_span = conditional_impl .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(key.inner); 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, ) { unsafe extern "C" { #[link_name = #link_push_back] fn __push_back #impl_generics( this: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector<#inner_with_generics>>, value: *mut ::cxx::core::ffi::c_void, ); } unsafe { __push_back( this, ::cxx::core::ptr::from_mut::<::cxx::core::mem::ManuallyDrop>(value).cast::<::cxx::core::ffi::c_void>(), ); } } unsafe fn __pop_back( this: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector>, out: &mut ::cxx::core::mem::MaybeUninit, ) { unsafe extern "C" { #[link_name = #link_pop_back] fn __pop_back #impl_generics( this: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector<#inner_with_generics>>, out: *mut ::cxx::core::ffi::c_void, ); } unsafe { __pop_back( this, ::cxx::core::ptr::from_mut::<::cxx::core::mem::MaybeUninit>(out).cast::<::cxx::core::ffi::c_void>(), ); } } }) } else { None }; let not_move_constructible_err = format!( "{} is not move constructible", generics::concise_cxx_name(key.inner, types), ); quote_spanned! {end_span=> #cfg #[automatically_derived] #unsafe_token impl #impl_generics ::cxx::vector::VectorElement for #inner_with_generics { fn __typename(f: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result { f.write_str(#name) } fn __vector_new() -> *mut ::cxx::CxxVector { unsafe extern "C" { #[link_name = #link_new] fn __vector_new #impl_generics() -> *mut ::cxx::CxxVector<#inner_with_generics>; } unsafe { __vector_new() } } fn __vector_size(v: &::cxx::CxxVector) -> ::cxx::core::primitive::usize { unsafe extern "C" { #[link_name = #link_size] fn __vector_size #impl_generics(_: &::cxx::CxxVector<#inner_with_generics>) -> ::cxx::core::primitive::usize; } unsafe { __vector_size(v) } } fn __vector_capacity(v: &::cxx::CxxVector) -> ::cxx::core::primitive::usize { unsafe extern "C" { #[link_name = #link_capacity] fn __vector_capacity #impl_generics(_: &::cxx::CxxVector<#inner_with_generics>) -> ::cxx::core::primitive::usize; } unsafe { __vector_capacity(v) } } unsafe fn __get_unchecked(v: *mut ::cxx::CxxVector, pos: ::cxx::core::primitive::usize) -> *mut Self { unsafe extern "C" { #[link_name = #link_get_unchecked] fn __get_unchecked #impl_generics( v: *mut ::cxx::CxxVector<#inner_with_generics>, pos: ::cxx::core::primitive::usize, ) -> *mut ::cxx::core::ffi::c_void; } unsafe { __get_unchecked(v, pos).cast::() } } unsafe fn __reserve(v: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector>, new_cap: ::cxx::core::primitive::usize) { unsafe extern "C" { #[link_name = #link_reserve] fn __reserve #impl_generics( v: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector<#inner_with_generics>>, new_cap: ::cxx::core::primitive::usize, ) -> ::cxx::core::primitive::bool; } if !unsafe { __reserve(v, new_cap) } { ::cxx::core::panic!(#not_move_constructible_err); } } #by_value_methods fn __unique_ptr_null() -> ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void> { unsafe 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(&raw mut repr); } repr } unsafe fn __unique_ptr_raw(raw: *mut ::cxx::CxxVector) -> ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void> { unsafe 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<#inner_with_generics>); } let mut repr = ::cxx::core::mem::MaybeUninit::uninit(); unsafe { __unique_ptr_raw(&raw mut repr, raw); } repr } unsafe fn __unique_ptr_get(repr: ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *const ::cxx::CxxVector { unsafe 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<#inner_with_generics>; } unsafe { __unique_ptr_get(&raw const repr) } } unsafe fn __unique_ptr_release(mut repr: ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *mut ::cxx::CxxVector { unsafe 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<#inner_with_generics>; } unsafe { __unique_ptr_release(&raw mut repr) } } unsafe fn __unique_ptr_drop(mut repr: ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) { unsafe 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(&raw 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, lang: Lang) -> bool { sig.ret.as_ref().is_some_and(|ret| { sig.throws || types.needs_indirect_abi(ret) || match lang { Lang::Cxx | Lang::CxxUnwind => types.contains_elided_lifetime(ret), Lang::Rust => false, } }) } 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) => { // Replace Vec with ::cxx::private::RustVec. Both have the // same layout but only the latter has a predictable ABI. Note that // the overall size and alignment are independent of the element // type, but the field order inside of Vec may not be. let span = ty.name.span(); let langle = ty.langle; let elem = &ty.inner; 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 = &ty.inner; 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( sig: &Signature, types: &Types, proper: bool, lang: Lang, ) -> TokenStream { let ret = match &sig.ret { Some(ret) if !indirect_return(sig, types, lang) => ret, _ => return TokenStream::new(), }; let ty = expand_extern_type(ret, types, proper); quote!(-> #ty) } pub(crate) fn display_namespaced(name: &Pair) -> impl Display + '_ { struct Namespaced<'a>(&'a Pair); impl<'a> Display for Namespaced<'a> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { for segment in &self.0.namespace { write!(formatter, "{segment}::")?; } write!(formatter, "{}", self.0.cxx) } } Namespaced(name) } cxxbridge-macro-1.0.192/src/generics.rs000064400000000000000000000121111046102023000160040ustar 00000000000000use crate::expand::display_namespaced; use crate::syntax::instantiate::NamedImplKey; use crate::syntax::types::ConditionalImpl; use crate::syntax::{Lifetimes, Type, Types}; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{Lifetime, Token}; pub(crate) struct ResolvedGenericType<'a> { ty: &'a Type, explicit_impl: bool, types: &'a Types<'a>, } /// Gets `(impl_generics, inner_with_generics)` pair that can be used when /// generating an `impl` for a generic type: /// /// ```ignore /// quote! { impl #impl_generics SomeTrait for #inner_with_generics } /// ``` pub(crate) fn split_for_impl<'a>( key: &NamedImplKey<'a>, conditional_impl: &ConditionalImpl<'a>, types: &'a Types<'a>, ) -> (&'a Lifetimes, ResolvedGenericType<'a>) { let impl_generics = if let Some(explicit_impl) = conditional_impl.explicit_impl { &explicit_impl.impl_generics } else { get_impl_generics(key.inner, types) }; let ty_generics = ResolvedGenericType { ty: key.inner, explicit_impl: conditional_impl.explicit_impl.is_some(), types, }; (impl_generics, ty_generics) } impl<'a> ToTokens for ResolvedGenericType<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { match self.ty { Type::Ident(named_type) => { named_type.rust.to_tokens(tokens); if self.explicit_impl { named_type.generics.to_tokens(tokens); } else { let resolve = self.types.resolve(named_type); if !resolve.generics.lifetimes.is_empty() { let span = named_type.rust.span(); named_type .generics .lt_token .unwrap_or_else(|| Token![<](span)) .to_tokens(tokens); resolve.generics.lifetimes.to_tokens(tokens); named_type .generics .gt_token .unwrap_or_else(|| Token![>](span)) .to_tokens(tokens); } } } Type::RustBox(ty1) => { let inner = ResolvedGenericType { ty: &ty1.inner, explicit_impl: self.explicit_impl, types: self.types, }; tokens.extend(quote! { ::cxx::alloc::boxed::Box<#inner> }); } _ => unreachable!("syntax/check.rs should reject other types"), } } } fn get_impl_generics<'a>(ty: &Type, types: &Types<'a>) -> &'a Lifetimes { match ty { Type::Ident(named_type) => types.resolve(named_type).generics, Type::RustBox(ty1) => get_impl_generics(&ty1.inner, types), _ => unreachable!("syntax/check.rs should reject other types"), } } pub(crate) fn format_for_prevent_unwind_label(ty: &Type) -> TokenStream { match ty { Type::Ident(named_type) => { let rust_name = named_type.rust.to_string(); quote! { ::cxx::core::concat!(::cxx::core::module_path!(), "::", #rust_name) } } Type::RustBox(ty1) => { let inner = format_for_prevent_unwind_label(&ty1.inner); quote! { ::cxx::core::concat!("Box<", #inner, ">") } } _ => unreachable!("syntax/check.rs should reject other types"), } } pub(crate) fn concise_rust_name(ty: &Type) -> String { match ty { Type::Ident(named_type) => named_type.rust.to_string(), Type::RustBox(ty1) => { let inner = concise_rust_name(&ty1.inner); format!("Box<{inner}>") } _ => unreachable!("syntax/check.rs should reject other types"), } } pub(crate) fn concise_cxx_name(ty: &Type, types: &Types) -> String { match ty { Type::Ident(named_type) => { let res = types.resolve(&named_type.rust); display_namespaced(res.name).to_string() } Type::RustBox(ty1) => { let inner = concise_cxx_name(&ty1.inner, types); format!("rust::Box<{inner}>") } _ => unreachable!("syntax/check.rs should reject other types"), } } 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.192/src/lib.rs000064400000000000000000000056331046102023000147660ustar 00000000000000#![allow( clippy::cast_sign_loss, clippy::doc_markdown, clippy::elidable_lifetime_names, clippy::enum_glob_use, clippy::expl_impl_clone_on_copy, // https://github.com/rust-lang/rust-clippy/issues/15842 clippy::inherent_to_string, clippy::items_after_statements, clippy::match_bool, clippy::match_like_matches_macro, clippy::match_same_arms, clippy::needless_lifetimes, clippy::needless_pass_by_value, clippy::nonminimal_bool, clippy::precedence, clippy::redundant_else, clippy::ref_option, clippy::similar_names, clippy::single_match_else, clippy::struct_field_names, clippy::too_many_arguments, clippy::too_many_lines, clippy::toplevel_ref_arg, clippy::uninlined_format_args, clippy::wrong_self_convention )] #![cfg_attr(test, allow(dead_code, unfulfilled_lint_expectations))] #![allow(unknown_lints, mismatched_lifetime_syntaxes)] mod attrs; mod cfg; mod derive; mod expand; mod generics; mod syntax; #[cfg(test)] mod tests; mod tokens; mod type_id; 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.192/src/syntax/atom.rs000064400000000000000000000043141046102023000165010ustar 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.192/src/syntax/attrs.rs000064400000000000000000000240071046102023000166770ustar 00000000000000use crate::syntax::cfg::CfgExpr; use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::repr::Repr; use crate::syntax::{cfg, Derive, Doc, ForeignName}; use proc_macro2::Ident; 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 default: Option<&'a mut bool>, pub namespace: Option<&'a mut Namespace>, pub cxx_name: Option<&'a mut Option>, pub rust_name: Option<&'a mut Option>, pub self_type: 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: (), } #[must_use] pub(crate) fn parse(cx: &mut Errors, attrs: Vec, mut parser: Parser) -> OtherAttrs { let mut other_attrs = OtherAttrs::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::() { 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("default") { match parse_default_attribute(&attr.meta) { Ok(()) => { if let Some(default) = &mut parser.default { **default = true; 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_ident_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("Self") { match parse_rust_ident_attribute(&attr.meta) { Ok(attr) => { if let Some(self_type) = &mut parser.self_type { **self_type = 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_and(cfg_expr); other_attrs.cfg.push(attr); continue; } } Err(err) => { cx.push(err); break; } } } else if attr_path.is_ident("allow") || attr_path.is_ident("warn") || attr_path.is_ident("deny") || attr_path.is_ident("forbid") { other_attrs.lint.push(attr); continue; } else if attr_path.is_ident("deprecated") || attr_path.is_ident("must_use") || attr_path.is_ident("serde") { other_attrs.passthrough.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" { other_attrs.lint.push(attr); continue; } } if !parser.ignore_unrecognized { cx.error(attr, "unsupported attribute"); break; } } other_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_default_attribute(meta: &Meta) -> Result<()> { let error_span = match meta { Meta::Path(_) => return Ok(()), Meta::List(meta) => meta.delimiter.span().open(), Meta::NameValue(meta) => meta.eq_token.span, }; Err(Error::new( error_span, "#[default] attribute does not accept an argument", )) } 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_ident_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, format!( "unsupported `{}` attribute", meta.path().get_ident().unwrap(), ), )) } #[derive(Clone)] pub(crate) struct OtherAttrs { pub cfg: Vec, pub lint: Vec, pub passthrough: Vec, } impl OtherAttrs { pub(crate) fn new() -> Self { OtherAttrs { cfg: Vec::new(), lint: Vec::new(), passthrough: Vec::new(), } } pub(crate) fn extend(&mut self, other: Self) { self.cfg.extend(other.cfg); self.lint.extend(other.lint); self.passthrough.extend(other.passthrough); } } cxxbridge-macro-1.0.192/src/syntax/cfg.rs000064400000000000000000000145531046102023000163060ustar 00000000000000use indexmap::{indexset as set, IndexSet as Set}; use proc_macro2::Ident; use std::hash::{Hash, Hasher}; use std::iter; use std::mem; use syn::parse::{Error, ParseStream, Result}; use syn::{parenthesized, token, Attribute, LitStr, Token}; #[derive(Clone)] pub(crate) enum CfgExpr { Unconditional, Eq(Ident, Option), All(Vec), Any(Vec), Not(Box), } #[derive(Clone)] pub(crate) enum ComputedCfg<'a> { Leaf(&'a CfgExpr), All(Set<&'a CfgExpr>), Any(Set>), } impl CfgExpr { pub(crate) fn merge_and(&mut self, expr: CfgExpr) { if let CfgExpr::Unconditional = self { *self = expr; } else if let CfgExpr::Unconditional = expr { // drop } else if let CfgExpr::All(list) = self { list.push(expr); } else { let prev = mem::replace(self, CfgExpr::Unconditional); *self = CfgExpr::All(vec![prev, expr]); } } } impl<'a> ComputedCfg<'a> { pub(crate) fn all(one: &'a CfgExpr, two: &'a CfgExpr) -> Self { if let (cfg, CfgExpr::Unconditional) | (CfgExpr::Unconditional, cfg) = (one, two) { ComputedCfg::Leaf(cfg) } else if one == two { ComputedCfg::Leaf(one) } else { ComputedCfg::All(set![one, two]) } } pub(crate) fn merge_or(&mut self, other: impl Into>) { let other = other.into(); if let ComputedCfg::Leaf(CfgExpr::Unconditional) = self { // drop } else if let ComputedCfg::Leaf(CfgExpr::Unconditional) = other { *self = other; } else if *self == other { // drop } else if let ComputedCfg::Any(list) = self { list.insert(other); } else { let prev = mem::replace(self, ComputedCfg::Any(Set::new())); let ComputedCfg::Any(list) = self else { unreachable!(); }; list.extend([prev, other]); } } } impl<'a> From<&'a CfgExpr> for ComputedCfg<'a> { fn from(cfg: &'a CfgExpr) -> Self { ComputedCfg::Leaf(cfg) } } impl Eq for CfgExpr {} impl PartialEq for CfgExpr { fn eq(&self, other: &Self) -> bool { match (self, other) { (CfgExpr::Unconditional, CfgExpr::Unconditional) => true, (CfgExpr::Eq(this_ident, None), CfgExpr::Eq(other_ident, None)) => { this_ident == other_ident } ( CfgExpr::Eq(this_ident, Some(this_value)), CfgExpr::Eq(other_ident, Some(other_value)), ) => { this_ident == other_ident && this_value.token().to_string() == other_value.token().to_string() } (CfgExpr::All(this), CfgExpr::All(other)) | (CfgExpr::Any(this), CfgExpr::Any(other)) => this == other, (CfgExpr::Not(this), CfgExpr::Not(other)) => this == other, (_, _) => false, } } } impl Hash for CfgExpr { fn hash(&self, hasher: &mut H) { mem::discriminant(self).hash(hasher); match self { CfgExpr::Unconditional => {} CfgExpr::Eq(ident, value) => { ident.hash(hasher); // syn::LitStr does not have its own Hash impl value.as_ref().map(LitStr::value).hash(hasher); } CfgExpr::All(inner) | CfgExpr::Any(inner) => inner.hash(hasher), CfgExpr::Not(inner) => inner.hash(hasher), } } } impl<'a> Eq for ComputedCfg<'a> {} impl<'a> PartialEq for ComputedCfg<'a> { fn eq(&self, other: &Self) -> bool { match (self, other) { (ComputedCfg::Leaf(this), ComputedCfg::Leaf(other)) => this == other, // For the purpose of deduplicating the contents of an `all` or // `any`, we only consider sets equal if they contain the same cfgs // in the same order. (ComputedCfg::All(this), ComputedCfg::All(other)) => { this.len() == other.len() && iter::zip(this, other).all(|(this, other)| this == other) } (ComputedCfg::Any(this), ComputedCfg::Any(other)) => { this.len() == other.len() && iter::zip(this, other).all(|(this, other)| this == other) } (_, _) => false, } } } impl<'a> Hash for ComputedCfg<'a> { fn hash(&self, hasher: &mut H) { mem::discriminant(self).hash(hasher); match self { ComputedCfg::Leaf(cfg) => cfg.hash(hasher), ComputedCfg::All(inner) => inner.iter().for_each(|cfg| cfg.hash(hasher)), ComputedCfg::Any(inner) => inner.iter().for_each(|cfg| cfg.hash(hasher)), } } } 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.192/src/syntax/check.rs000064400000000000000000000642101046102023000166170ustar 00000000000000use crate::syntax::atom::Atom::{self, *}; use crate::syntax::message::Message; use crate::syntax::report::Errors; use crate::syntax::visit::{self, Visit}; use crate::syntax::{ error, ident, trivial, Api, Array, Enum, ExternFn, ExternType, FnKind, 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. #[cfg_attr(proc_macro, expect(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. #[cfg_attr(not(proc_macro), expect(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, Type::RustBox(ty1) => { check_type_box(cx, ty1); 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 || (cx.types.cxx.contains(&ident.rust) && !cx.types.structs.contains_key(&ident.rust) && !cx.types.enums.contains_key(&ident.rust) && !cx.types.aliases.contains_key(&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.types, &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 cx.types.cxx.contains(&ident.rust) && !cx.types.structs.contains_key(&ident.rust) && !cx.types.enums.contains_key(&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.types, &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 { match derive.what { Trait::Clone | Trait::Copy | Trait::Debug | Trait::Default | Trait::Eq | Trait::Hash | Trait::Ord | Trait::PartialEq | Trait::PartialOrd | Trait::Serialize | Trait::Deserialize => {} Trait::BitAnd | Trait::BitOr | Trait::BitXor => { let msg = format!( "derive({}) is currently only supported on enums, not structs", derive, ); cx.error(derive, msg); } 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.types, &field.ty) { let desc = describe(cx.types, &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 { let span = span_for_enum_error(enm); cx.error( span, "explicit #[repr(...)] is required for enum without any variants", ); } for derive in &enm.derives { match derive.what { Trait::BitAnd | Trait::BitOr | Trait::BitXor | Trait::Clone | Trait::Copy | Trait::Debug | Trait::Eq | Trait::Hash | Trait::Ord | Trait::PartialEq | Trait::PartialOrd | Trait::Serialize | Trait::Deserialize => {} Trait::Default => { let default_variants = enm.variants.iter().filter(|v| v.default).count(); if default_variants != 1 { let mut msg = Message::new(); write!(msg, "derive(Default) on enum requires exactly one variant to be marked with #[default]"); if default_variants > 0 { write!(msg, " (found {})", default_variants); } cx.error(derive, msg); } } Trait::ExternType => { let msg = "derive(ExternType) on shared enum is not supported"; 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 | Lang::CxxUnwind => "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 | Lang::CxxUnwind => { 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.generics); match &efn.kind { FnKind::Method(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 && cx.types.cxx.contains(&receiver.ty.rust) && !cx.types.structs.contains_key(&receiver.ty.rust) && !cx.types.aliases.contains_key(&receiver.ty.rust) { cx.error( span, format!( "mutable reference to opaque C++ type requires a pin -- use `self: Pin<&mut {}>`", receiver.ty.rust, ), ); } } FnKind::Assoc(self_type) => { if cx.types.enums.contains_key(self_type) { cx.error( self_type, "unsupported self type; C++ does not allow member functions on enums", ); } else if !cx.types.structs.contains_key(self_type) && !cx.types.cxx.contains(self_type) && !cx.types.rust.contains(self_type) { cx.error(self_type, "unrecognized self type"); } } FnKind::Free => {} } 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.unsafety.is_none() { cx.error( arg, "pointer argument requires that the function be marked unsafe", ); } } else if is_unsized(cx.types, &arg.ty) { let desc = describe(cx.types, &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.types, ty) { let desc = describe(cx.types, 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 { // Reject `impl Vec` and other built-in impls. if Atom::from(&inner.rust).is_some() { cx.error(imp, "unsupported Self type of explicit impl"); } } } // Reject `impl fn() -> &S {}`, `impl [S]`, etc. _ => cx.error(imp, "unsupported Self type of explicit impl"), } } fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) { if efn.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(types: &Types, ty: &Type) -> bool { match ty { Type::Ident(ident) => { let ident = &ident.rust; ident == CxxString || (types.cxx.contains(ident) && !types.structs.contains_key(ident) && !types.enums.contains_key(ident) && !(types.aliases.contains_key(ident) && types.required_trivial.contains_key(ident))) || types.rust.contains(ident) } Type::Array(array) => is_unsized(types, &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 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(types: &Types, ty: &Type) -> String { match ty { Type::Ident(ident) => { if types.structs.contains_key(&ident.rust) { "struct".to_owned() } else if types.enums.contains_key(&ident.rust) { "enum".to_owned() } else if types.aliases.contains_key(&ident.rust) { "C++ type".to_owned() } else if types.cxx.contains(&ident.rust) { "opaque C++ type".to_owned() } else if 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.192/src/syntax/derive.rs000064400000000000000000000044301046102023000170160ustar 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 { BitAnd, BitOr, BitXor, 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() { "BitAnd" => Trait::BitAnd, "BitOr" => Trait::BitOr, "BitXor" => Trait::BitXor, "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::BitAnd => "BitAnd", Trait::BitOr => "BitOr", Trait::BitXor => "BitXor", 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.192/src/syntax/discriminant.rs000064400000000000000000000223031046102023000202230ustar 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, } } } 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)] pub(crate) struct Limits { pub repr: Atom, pub min: Discriminant, pub max: Discriminant, } impl Limits { pub(crate) 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.192/src/syntax/doc.rs000064400000000000000000000020301046102023000162770ustar 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); } #[cfg_attr(proc_macro, expect(dead_code))] pub(crate) fn is_empty(&self) -> bool { self.fragments.is_empty() } #[cfg_attr(proc_macro, expect(dead_code))] 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.192/src/syntax/error.rs000064400000000000000000000060001046102023000166640ustar 00000000000000use std::fmt::{self, Display}; #[derive(Copy, Clone)] pub(crate) struct Error { pub msg: &'static str, #[cfg_attr(proc_macro, expect(dead_code))] pub label: Option<&'static str>, #[cfg_attr(proc_macro, expect(dead_code))] 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.192/src/syntax/file.rs000064400000000000000000000076241046102023000164670ustar 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 { #[expect(dead_code)] pub cfg: CfgExpr, pub namespace: Namespace, pub attrs: Vec, #[cfg_attr(not(proc_macro), expect(dead_code))] pub vis: Visibility, pub unsafety: Option, #[cfg_attr(not(proc_macro), expect(dead_code))] pub mod_token: Token![mod], #[cfg_attr(not(proc_macro), expect(dead_code))] pub ident: Ident, #[cfg_attr(not(proc_macro), expect(dead_code))] 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, #[expect(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.192/src/syntax/ident.rs000064400000000000000000000033071046102023000166450ustar 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.192/src/syntax/impls.rs000064400000000000000000000252761046102023000166770ustar 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: _, kind, args, ret, throws, paren_token: _, throws_tokens: _, } = self; let Signature { asyncness: asyncness2, unsafety: unsafety2, fn_token: _, generics: _, kind: kind2, args: args2, ret: ret2, throws: throws2, paren_token: _, throws_tokens: _, } = other; asyncness.is_some() == asyncness2.is_some() && unsafety.is_some() == unsafety2.is_some() && kind == kind2 && 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: _, kind, args, ret, throws, paren_token: _, throws_tokens: _, } = self; asyncness.is_some().hash(state); unsafety.is_some().hash(state); kind.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.192/src/syntax/improper.rs000064400000000000000000000027621046102023000174030ustar 00000000000000use self::ImproperCtype::*; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::query::TypeQuery; use crate::syntax::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: impl Into>, ) -> ImproperCtype<'a> { match ty.into() { TypeQuery::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)) } } TypeQuery::RustBox | TypeQuery::RustVec | TypeQuery::Str | TypeQuery::Fn | TypeQuery::Void | TypeQuery::SliceRef => Definite(true), TypeQuery::UniquePtr | TypeQuery::SharedPtr | TypeQuery::WeakPtr | TypeQuery::CxxVector => Definite(false), TypeQuery::Ref(ty) => self.determine_improper_ctype(&ty.inner), TypeQuery::Ptr(ty) => self.determine_improper_ctype(&ty.inner), TypeQuery::Array(ty) => self.determine_improper_ctype(&ty.inner), } } } cxxbridge-macro-1.0.192/src/syntax/instantiate.rs000064400000000000000000000071641046102023000200720ustar 00000000000000use crate::syntax::map::UnorderedMap; use crate::syntax::resolve::Resolution; use crate::syntax::types::Types; use crate::syntax::{mangle, Symbol, Ty1, Type}; use proc_macro2::{Ident, Span}; use std::hash::{Hash, Hasher}; #[derive(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>), } impl<'a> ImplKey<'a> { /// Whether to produce FFI symbols instantiating the given generic type even /// when an explicit `impl Foo {}` is not present in the current bridge. /// /// The main consideration is that the same instantiation must not be /// present in two places, which is accomplished using trait impls and the /// orphan rule. Every instantiation of a C++ template like `CxxVector` /// and Rust generic type like `Vec` requires the implementation of /// traits defined by the `cxx` crate for some local type or for a /// fundamental type like `Box`. pub(crate) fn is_implicit_impl_ok(&self, types: &Types) -> bool { // TODO: relax this for Rust generics to allow Vec> etc. types.is_local(self.inner()) } /// Returns the type argument in the generic instantiation described by /// `self`. For example, if `self` represents `UniquePtr` then this /// will return `u32`. fn inner(&self) -> &'a Type { let named_impl_key = match self { ImplKey::RustBox(key) | ImplKey::RustVec(key) | ImplKey::UniquePtr(key) | ImplKey::SharedPtr(key) | ImplKey::WeakPtr(key) | ImplKey::CxxVector(key) => key, }; named_impl_key.inner } } pub(crate) struct NamedImplKey<'a> { #[cfg_attr(not(proc_macro), expect(dead_code))] pub begin_span: Span, /// Mangled form of the `inner` type. pub symbol: Symbol, /// Generic type - e.g. `UniquePtr`. #[cfg_attr(proc_macro, expect(dead_code))] pub outer: &'a Type, /// Generic type argument - e.g. `u8` from `UniquePtr`. pub inner: &'a Type, #[cfg_attr(not(proc_macro), expect(dead_code))] pub end_span: Span, } impl Type { pub(crate) fn impl_key(&self, res: &UnorderedMap<&Ident, Resolution>) -> Option { match self { Type::RustBox(ty) => Some(ImplKey::RustBox(NamedImplKey::new(self, ty, res)?)), Type::RustVec(ty) => Some(ImplKey::RustVec(NamedImplKey::new(self, ty, res)?)), Type::UniquePtr(ty) => Some(ImplKey::UniquePtr(NamedImplKey::new(self, ty, res)?)), Type::SharedPtr(ty) => Some(ImplKey::SharedPtr(NamedImplKey::new(self, ty, res)?)), Type::WeakPtr(ty) => Some(ImplKey::WeakPtr(NamedImplKey::new(self, ty, res)?)), Type::CxxVector(ty) => Some(ImplKey::CxxVector(NamedImplKey::new(self, ty, res)?)), _ => None, } } } impl<'a> PartialEq for NamedImplKey<'a> { fn eq(&self, other: &Self) -> bool { PartialEq::eq(&self.symbol, &other.symbol) } } impl<'a> Eq for NamedImplKey<'a> {} impl<'a> Hash for NamedImplKey<'a> { fn hash(&self, hasher: &mut H) { self.symbol.hash(hasher); } } impl<'a> NamedImplKey<'a> { fn new(outer: &'a Type, ty1: &'a Ty1, res: &UnorderedMap<&Ident, Resolution>) -> Option { let inner = &ty1.inner; Some(NamedImplKey { symbol: mangle::typename(inner, res)?, begin_span: ty1.name.span(), outer, inner, end_span: ty1.rangle.span, }) } } cxxbridge-macro-1.0.192/src/syntax/mangle.rs000064400000000000000000000121471046102023000170070ustar 00000000000000// Mangled symbol arrangements: // // (a) One-off internal symbol. // pattern: {CXXBRIDGE} $ {NAME} // examples: // - cxxbridge1$exception // defining characteristics: // - 2 segments, none an integer // // (b) Behavior on a builtin binding without generic parameter. // pattern: {CXXBRIDGE} $ {TYPE} $ {NAME} // examples: // - cxxbridge1$string$len // defining characteristics: // - 3 segments, none an integer // // (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, none an integer // // (d) User-defined extern function. // pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {CXXVERSION} $ {NAME} // examples: // - cxxbridge1$189$new_client // - org$rust$cxxbridge1$189$new_client // defining characteristics: // - second segment from end is an integer // // (e) User-defined extern member function. // pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {CXXVERSION} $ {TYPE} $ {NAME} // examples: // - org$cxxbridge1$189$Struct$get // defining characteristics: // - third segment from end is an integer // // (f) Operator overload. // pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {CXXVERSION} $ {TYPE} $ operator $ {NAME} // examples: // - org$rust$cxxbridge1$189$Struct$operator$eq // defining characteristics: // - second segment from end is `operator` (not possible in type or namespace names) // // (g) Closure trampoline. // pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {CXXVERSION} $ {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::map::UnorderedMap; use crate::syntax::resolve::Resolution; use crate::syntax::symbol::{self, Symbol}; use crate::syntax::{ExternFn, Pair, Type, Types}; use proc_macro2::Ident; const CXXBRIDGE: &str = "cxxbridge1"; const CXXVERSION: &str = env!("CARGO_PKG_VERSION_PATCH"); macro_rules! join { ($($segment:expr),+ $(,)?) => { symbol::join(&[$(&$segment),+]) }; } pub(crate) fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol { match efn.self_type() { Some(self_type) => { let self_type_ident = types.resolve(self_type); join!( efn.name.namespace, CXXBRIDGE, CXXVERSION, self_type_ident.name.cxx, efn.name.rust, ) } None => join!(efn.name.namespace, CXXBRIDGE, CXXVERSION, efn.name.rust), } } pub(crate) fn operator(receiver: &Pair, operator: &'static str) -> Symbol { join!( receiver.namespace, CXXBRIDGE, CXXVERSION, 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) } /// Mangles the given type (e.g. `Box`) into a symbol /// fragment (`box$org$rust$Struct`) to be used in the name of generic /// instantiations (`cxxbridge1$box$org$rust$Struct$alloc`) pertaining to that /// type. /// /// Generic instantiation is not supported for all types in full generality. /// This function must handle unsupported types gracefully by returning `None` /// because it is used early during construction of the data structures that are /// the input to 'syntax/check.rs', and unsupported generic instantiations are /// only reported as an error later. pub(crate) fn typename(t: &Type, res: &UnorderedMap<&Ident, Resolution>) -> Option { match t { Type::Ident(named_type) => res.get(&named_type.rust).map(|res| res.name.to_symbol()), Type::CxxVector(ty1) => typename(&ty1.inner, res).map(|s| join!("std", "vector", s)), Type::RustBox(ty1) => typename(&ty1.inner, res).map(|s| join!("box", s)), _ => None, } } cxxbridge-macro-1.0.192/src/syntax/map.rs000064400000000000000000000064211046102023000163170ustar 00000000000000use std::borrow::Borrow; use std::hash::Hash; use std::ops::Index; pub(crate) use self::ordered::OrderedMap; pub(crate) use self::unordered::UnorderedMap; pub(crate) use std::collections::hash_map::Entry; mod ordered { use indexmap::Equivalent; use std::hash::Hash; pub(crate) struct OrderedMap(indexmap::IndexMap); impl OrderedMap { pub(crate) fn new() -> Self { OrderedMap(indexmap::IndexMap::new()) } pub(crate) fn keys(&self) -> indexmap::map::Keys { self.0.keys() } pub(crate) fn contains_key(&self, key: &Q) -> bool where Q: ?Sized + Hash + Equivalent, { self.0.contains_key(key) } } impl OrderedMap where K: Hash + Eq, { pub(crate) fn insert(&mut self, key: K, value: V) -> Option { self.0.insert(key, value) } pub(crate) fn entry(&mut self, key: K) -> indexmap::map::Entry { self.0.entry(key) } } impl<'a, K, V> IntoIterator for &'a OrderedMap { type Item = (&'a K, &'a V); type IntoIter = indexmap::map::Iter<'a, K, V>; fn into_iter(self) -> Self::IntoIter { self.0.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 } } } 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.192/src/syntax/message.rs000064400000000000000000000011021046102023000171550ustar 00000000000000use proc_macro2::TokenStream; use quote::ToTokens; use std::fmt::{self, Display}; pub(crate) struct Message(String); impl Message { pub fn new() -> Self { Message(String::new()) } pub fn write_fmt(&mut self, args: fmt::Arguments) { fmt::Write::write_fmt(&mut self.0, args).unwrap(); } } impl Display for Message { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(formatter) } } impl ToTokens for Message { fn to_tokens(&self, tokens: &mut TokenStream) { self.0.to_tokens(tokens); } } cxxbridge-macro-1.0.192/src/syntax/mod.rs000064400000000000000000000205761046102023000163300ustar 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; pub(crate) 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; pub(crate) mod message; mod names; pub(crate) mod namespace; mod parse; mod pod; pub(crate) mod primitive; pub(crate) mod qualified; pub(crate) mod query; pub(crate) mod report; pub(crate) mod repr; pub(crate) mod resolve; pub(crate) mod set; mod signature; pub(crate) mod symbol; mod tokens; mod toposort; pub(crate) mod trivial; pub(crate) mod types; pub(crate) mod unpin; 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::{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 { #[cfg_attr(proc_macro, expect(dead_code))] 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, #[cfg_attr(proc_macro, expect(dead_code))] pub begin_span: Span, #[cfg_attr(proc_macro, expect(dead_code))] 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 { #[cfg_attr(proc_macro, expect(dead_code))] pub cfg: CfgExpr, pub lang: Lang, pub doc: Doc, pub derives: Vec, pub attrs: OtherAttrs, #[cfg_attr(not(proc_macro), expect(dead_code))] pub visibility: Token![pub], pub type_token: Token![type], pub name: Pair, pub generics: Lifetimes, #[expect(dead_code)] pub colon_token: Option, pub bounds: Vec, #[cfg_attr(not(proc_macro), expect(dead_code))] pub semi_token: Token![;], pub trusted: bool, } pub(crate) struct Struct { pub cfg: CfgExpr, pub doc: Doc, pub derives: Vec, pub align: Option, pub attrs: OtherAttrs, #[cfg_attr(not(proc_macro), expect(dead_code))] 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 { pub cfg: CfgExpr, pub doc: Doc, pub derives: Vec, pub attrs: OtherAttrs, #[cfg_attr(not(proc_macro), expect(dead_code))] pub visibility: Token![pub], pub enum_token: Token![enum], pub name: Pair, pub generics: Lifetimes, pub brace_token: Brace, pub variants: Vec, pub repr: EnumRepr, pub explicit_repr: bool, } pub(crate) struct EnumRepr { pub atom: Atom, pub repr_type: Type, } pub(crate) struct ExternFn { pub cfg: CfgExpr, pub lang: Lang, pub doc: Doc, #[cfg_attr(not(proc_macro), expect(dead_code))] pub attrs: OtherAttrs, #[cfg_attr(not(proc_macro), expect(dead_code))] pub visibility: Token![pub], pub name: Pair, pub sig: Signature, pub semi_token: Token![;], pub trusted: bool, } pub(crate) struct TypeAlias { #[cfg_attr(proc_macro, expect(dead_code))] pub cfg: CfgExpr, #[cfg_attr(not(proc_macro), expect(dead_code))] pub doc: Doc, pub derives: Vec, pub attrs: OtherAttrs, #[cfg_attr(not(proc_macro), expect(dead_code))] pub visibility: Token![pub], pub type_token: Token![type], pub name: Pair, pub generics: Lifetimes, #[cfg_attr(not(proc_macro), expect(dead_code))] pub eq_token: Token![=], #[cfg_attr(not(proc_macro), expect(dead_code))] pub ty: RustType, #[cfg_attr(not(proc_macro), expect(dead_code))] pub semi_token: Token![;], } pub(crate) struct Impl { pub cfg: CfgExpr, #[expect(dead_code)] pub attrs: OtherAttrs, pub impl_token: Token![impl], pub impl_generics: Lifetimes, #[expect(dead_code)] pub negative: bool, pub ty: Type, 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 kind: FnKind, pub args: Punctuated, pub ret: Option, pub throws: bool, pub paren_token: Paren, pub throws_tokens: Option<(kw::Result, Token![<], Token![>])>, } #[derive(PartialEq, Hash)] pub(crate) enum FnKind { /// Rust method or C++ non-static member function. Method(Receiver), /// Rust associated function or C++ static member function. Assoc(Ident), /// Non-member function. Free, } pub(crate) struct Var { pub cfg: CfgExpr, pub doc: Doc, #[cfg_attr(not(proc_macro), expect(dead_code))] pub attrs: OtherAttrs, #[cfg_attr(not(proc_macro), expect(dead_code))] pub visibility: Token![pub], pub name: Pair, #[cfg_attr(not(proc_macro), expect(dead_code))] 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, #[cfg_attr(not(proc_macro), expect(dead_code))] pub colon_token: Token![:], pub shorthand: bool, #[cfg_attr(not(proc_macro), expect(dead_code))] pub pin_tokens: Option<(kw::Pin, Token![<], Token![>])>, pub mutability: Option, } pub(crate) struct Variant { #[cfg_attr(proc_macro, expect(dead_code))] pub cfg: CfgExpr, pub doc: Doc, pub default: bool, #[cfg_attr(not(proc_macro), expect(dead_code))] pub attrs: OtherAttrs, pub name: Pair, pub discriminant: Discriminant, #[expect(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, CxxUnwind, 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.192/src/syntax/names.rs000064400000000000000000000030751046102023000166470ustar 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.192/src/syntax/namespace.rs000064400000000000000000000053341046102023000175000ustar 00000000000000use crate::syntax::qualified::QualifiedName; 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, PartialEq)] 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<'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.192/src/syntax/parse.rs000064400000000000000000001364341046102023000166640ustar 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::repr::Repr; use crate::syntax::Atom::*; use crate::syntax::{ attrs, error, Api, Array, Derive, Doc, Enum, EnumRepr, ExternFn, ExternType, FnKind, 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 repr = None; 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), repr: Some(&mut repr), namespace: Some(&mut namespace), cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), ..Default::default() }, ); let align = match repr { Some(Repr::Align(align)) => Some(align), Some(Repr::Atom(_atom, span)) => { cx.push(Error::new(span, "unsupported alignment on a struct")); None } None => None, }; 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, align, 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 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), ..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 repr = match repr { Some(Repr::Atom(atom, _span)) => Some(atom), Some(Repr::Align(align)) => { cx.error(align, "C++ does not support custom alignment on an enum"); None } None => None, }; 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 { atom: repr, repr_type, }; let generics = Lifetimes { lt_token: None, lifetimes: Punctuated::new(), gt_token: None, }; Api::Enum(Enum { cfg, doc, derives, attrs, visibility, enum_token, name, generics, brace_token, variants, 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 default = false; 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), default: Some(&mut default), 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, default, 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 | Lang::CxxUnwind => {} } 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) = efn.sig.receiver_mut() { 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), "C++-unwind" => Ok(Lang::CxxUnwind), "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 | Lang::CxxUnwind => 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 self_type = 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), self_type: Some(&mut self_type), ..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() { 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::new(); 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 kind = match (self_type, receiver) { (None, None) => FnKind::Free, (Some(self_type), None) => FnKind::Assoc(self_type), (None, Some(receiver)) => FnKind::Method(receiver), (Some(self_type), Some(receiver)) => { let msg = "function with Self type must not have a `self` argument"; cx.error(self_type, msg); FnKind::Method(receiver) } }; 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 | Lang::CxxUnwind => Api::CxxFunction, Lang::Rust => Api::RustFunction, }(ExternFn { cfg, lang, doc, attrs, visibility, name, sig: Signature { asyncness, unsafety, fn_token, generics, kind, 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 | Lang::CxxUnwind => 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; let attrs = 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 negative = negative_token.is_some(); let brace_token = imp.brace_token; Ok(Api::Impl(Impl { cfg, attrs, impl_token, impl_generics, negative, ty, 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::new(); 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 kind = FnKind::Free; let paren_token = ty.paren_token; Ok(Type::Fn(Box::new(Signature { asyncness, unsafety, fn_token, generics, kind, 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.192/src/syntax/pod.rs000064400000000000000000000027261046102023000163300ustar 00000000000000use crate::syntax::atom::Atom::{self, *}; use crate::syntax::query::TypeQuery; use crate::syntax::{primitive, Types}; impl<'a> Types<'a> { pub(crate) fn is_guaranteed_pod(&self, ty: impl Into>) -> bool { match ty.into() { TypeQuery::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) { strct.fields.iter().all(|field| { primitive::kind(&field.ty).is_none() && self.is_guaranteed_pod(&field.ty) }) } else { self.enums.contains_key(ident) } } TypeQuery::RustBox | TypeQuery::RustVec | TypeQuery::UniquePtr | TypeQuery::SharedPtr | TypeQuery::WeakPtr | TypeQuery::CxxVector | TypeQuery::Void => false, TypeQuery::Ref(_) | TypeQuery::Str | TypeQuery::Fn | TypeQuery::SliceRef | TypeQuery::Ptr(_) => true, TypeQuery::Array(array) => self.is_guaranteed_pod(&array.inner), } } } cxxbridge-macro-1.0.192/src/syntax/primitive.rs000064400000000000000000000011751046102023000175530ustar 00000000000000use crate::syntax::atom::Atom::{self, *}; use crate::syntax::Type; pub(crate) enum PrimitiveKind { Boolean, Number, Pointer, } pub(crate) fn kind(ty: &Type) -> Option { match ty { Type::Ident(ident) => Atom::from(&ident.rust).and_then(|atom| match atom { Bool => Some(PrimitiveKind::Boolean), Char | U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | F32 | F64 => { Some(PrimitiveKind::Number) } CxxString | RustString => None, }), Type::Ptr(_) => Some(PrimitiveKind::Pointer), _ => None, } } cxxbridge-macro-1.0.192/src/syntax/qualified.rs000064400000000000000000000040711046102023000175040ustar 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.192/src/syntax/query.rs000064400000000000000000000024461046102023000167120ustar 00000000000000use crate::syntax::{Array, NamedType, Ptr, Ref, Type}; #[derive(Copy, Clone)] pub(crate) enum TypeQuery<'a> { Ident(&'a NamedType), RustBox, RustVec, UniquePtr, SharedPtr, WeakPtr, Ref(&'a Ref), Ptr(&'a Ptr), Str, CxxVector, Fn, Void, SliceRef, Array(&'a Array), } impl<'a> From<&'a NamedType> for TypeQuery<'a> { fn from(query: &'a NamedType) -> Self { TypeQuery::Ident(query) } } impl<'a> From<&'a Type> for TypeQuery<'a> { fn from(query: &'a Type) -> Self { match query { Type::Ident(query) => TypeQuery::Ident(query), Type::RustBox(_) => TypeQuery::RustBox, Type::RustVec(_) => TypeQuery::RustVec, Type::UniquePtr(_) => TypeQuery::UniquePtr, Type::SharedPtr(_) => TypeQuery::SharedPtr, Type::WeakPtr(_) => TypeQuery::WeakPtr, Type::Ref(query) => TypeQuery::Ref(query), Type::Ptr(query) => TypeQuery::Ptr(query), Type::Str(_) => TypeQuery::Str, Type::CxxVector(_) => TypeQuery::CxxVector, Type::Fn(_) => TypeQuery::Fn, Type::Void(_) => TypeQuery::Void, Type::SliceRef(_) => TypeQuery::SliceRef, Type::Array(query) => TypeQuery::Array(query), } } } cxxbridge-macro-1.0.192/src/syntax/report.rs000064400000000000000000000013471046102023000170570ustar 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.192/src/syntax/repr.rs000064400000000000000000000034561046102023000165170ustar 00000000000000use crate::syntax::Atom::{self, *}; use proc_macro2::{Ident, Span}; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::{parenthesized, Expr, LitInt}; pub(crate) enum Repr { Align(LitInt), Atom(Atom, Span), } impl Parse for Repr { fn parse(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(Repr::Atom(atom, ident.span())); } _ => {} } } else if ident == "align" { let content; parenthesized!(content in input); let align_expr: Expr = content.fork().parse()?; if !matches!(align_expr, Expr::Lit(_)) { return Err(Error::new_spanned( align_expr, "invalid repr(align) attribute: an arithmetic expression is not supported", )); } let align_lit: LitInt = content.parse()?; let align: u32 = align_lit.base10_parse()?; if !align.is_power_of_two() { return Err(Error::new_spanned( align_lit, "invalid repr(align) attribute: not a power of two", )); } if align > 2u32.pow(13) { return Err(Error::new_spanned( align_lit, "invalid repr(align) attribute: larger than 2^13", )); } return Ok(Repr::Align(align_lit)); } Err(Error::new_spanned( begin.token_stream(), "unrecognized repr", )) } } cxxbridge-macro-1.0.192/src/syntax/resolve.rs000064400000000000000000000020411046102023000172130ustar 00000000000000use crate::syntax::attrs::OtherAttrs; use crate::syntax::{Lifetimes, NamedType, Pair, Types}; use proc_macro2::Ident; #[derive(Copy, Clone)] pub(crate) struct Resolution<'a> { pub name: &'a Pair, #[cfg_attr(not(proc_macro), expect(dead_code))] pub attrs: &'a OtherAttrs, 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 } } cxxbridge-macro-1.0.192/src/syntax/set.rs000064400000000000000000000063261046102023000163410ustar 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() } } impl<'a, T> IntoIterator for OrderedSet<&'a T> { type Item = &'a T; type IntoIter = as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.vec.into_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); } #[cfg_attr(not(proc_macro), expect(dead_code))] pub(crate) fn extend(&mut self, iter: impl IntoIterator) { for value in iter { self.insert(value); } } } } 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.192/src/syntax/signature.rs000064400000000000000000000075071046102023000175510ustar 00000000000000use crate::syntax::set::{OrderedSet, UnorderedSet}; use crate::syntax::{FnKind, Receiver, Signature, Type}; use proc_macro2::Ident; use syn::Lifetime; impl Signature { pub fn receiver(&self) -> Option<&Receiver> { match &self.kind { FnKind::Method(receiver) => Some(receiver), FnKind::Assoc(_) | FnKind::Free => None, } } pub fn receiver_mut(&mut self) -> Option<&mut Receiver> { match &mut self.kind { FnKind::Method(receiver) => Some(receiver), FnKind::Assoc(_) | FnKind::Free => None, } } pub fn self_type(&self) -> Option<&Ident> { match &self.kind { FnKind::Method(receiver) => Some(&receiver.ty.rust), FnKind::Assoc(self_type) => Some(self_type), FnKind::Free => None, } } #[cfg_attr(not(proc_macro), allow(dead_code))] pub fn undeclared_lifetimes<'a>(&'a self) -> OrderedSet<&'a Lifetime> { let mut declared_lifetimes = UnorderedSet::new(); for param in self.generics.lifetimes() { declared_lifetimes.insert(¶m.lifetime); } let mut undeclared_lifetimes = OrderedSet::new(); let mut collect_lifetime = |lifetime: &'a Lifetime| { if lifetime.ident != "_" && lifetime.ident != "static" && !declared_lifetimes.contains(lifetime) { undeclared_lifetimes.insert(lifetime); } }; match &self.kind { FnKind::Method(receiver) => { if let Some(lifetime) = &receiver.lifetime { collect_lifetime(lifetime); } for lifetime in &receiver.ty.generics.lifetimes { collect_lifetime(lifetime); } } FnKind::Assoc(self_type) => { // If support is added for explicit lifetimes in the Self type // of static member functions, that needs to be handled here. let _: &Ident = self_type; } FnKind::Free => {} } fn collect_type<'a>(collect_lifetime: &mut impl FnMut(&'a Lifetime), ty: &'a Type) { match ty { Type::Ident(named_type) => { for lifetime in &named_type.generics.lifetimes { collect_lifetime(lifetime); } } Type::RustBox(ty1) | Type::RustVec(ty1) | Type::UniquePtr(ty1) | Type::SharedPtr(ty1) | Type::WeakPtr(ty1) | Type::CxxVector(ty1) => collect_type(collect_lifetime, &ty1.inner), Type::Ref(ty) | Type::Str(ty) => { if let Some(lifetime) = &ty.lifetime { collect_lifetime(lifetime); } collect_type(collect_lifetime, &ty.inner); } Type::Ptr(ty) => collect_type(collect_lifetime, &ty.inner), Type::Fn(signature) => { for lifetime in signature.undeclared_lifetimes() { collect_lifetime(lifetime); } } Type::Void(_) => {} Type::SliceRef(ty) => { if let Some(lifetime) = &ty.lifetime { collect_lifetime(lifetime); } collect_type(collect_lifetime, &ty.inner); } Type::Array(ty) => collect_type(collect_lifetime, &ty.inner), } } for arg in &self.args { collect_type(&mut collect_lifetime, &arg.ty); } if let Some(ret) = &self.ret { collect_type(&mut collect_lifetime, ret); } undeclared_lifetimes } } cxxbridge-macro-1.0.192/src/syntax/symbol.rs000064400000000000000000000052641046102023000170530ustar 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 '$'. // Example: cxxbridge1$string$new #[derive(Eq, Hash, PartialEq)] 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 } #[cfg_attr(proc_macro, expect(dead_code))] pub(crate) fn contains(&self, ch: char) -> bool { self.0.contains(ch) } } 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.192/src/syntax/tokens.rs000064400000000000000000000213451046102023000170470ustar 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.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: _, attrs: _, impl_token, impl_generics, negative: _, ty, 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: _, kind: _, 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) { let EnumRepr { atom, repr_type: _ } = self; atom.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.192/src/syntax/toposort.rs000064400000000000000000000026601046102023000174340ustar 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.192/src/syntax/trivial.rs000064400000000000000000000236711046102023000172220ustar 00000000000000use crate::syntax::cfg::ComputedCfg; use crate::syntax::instantiate::ImplKey; use crate::syntax::map::{OrderedMap, UnorderedMap}; use crate::syntax::resolve::Resolution; use crate::syntax::set::{OrderedSet as Set, UnorderedSet}; use crate::syntax::types::ConditionalImpl; use crate::syntax::{Api, Enum, ExternFn, NamedType, Pair, SliceRef, Struct, Type, TypeAlias}; 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 { // Whether the extern functions used by rust::Box are being produced // within this cxx::bridge expansion, as opposed to the boxed type being // a type alias from a different module. #[cfg_attr(not(proc_macro), expect(dead_code))] local: bool, }, VecElement { #[cfg_attr(not(proc_macro), expect(dead_code))] local: bool, }, SliceElement(&'a SliceRef), } pub(crate) fn required_trivial_reasons<'a>( apis: &'a [Api], all: &OrderedMap<&'a Type, ComputedCfg>, structs: &UnorderedMap<&'a Ident, &'a Struct>, enums: &UnorderedMap<&'a Ident, &'a Enum>, cxx: &UnorderedSet<&'a Ident>, aliases: &UnorderedMap<&'a Ident, &'a TypeAlias>, impls: &OrderedMap, ConditionalImpl<'a>>, resolutions: &UnorderedMap<&Ident, Resolution>, ) -> 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) => { for arg in &efn.args { if let Type::Ident(ident) = &arg.ty { let reason = TrivialReason::FunctionArgument(efn); insist_extern_types_are_trivial(ident, reason); } } if let Some(Type::Ident(ident)) = &efn.ret { let reason = TrivialReason::FunctionReturn(efn); insist_extern_types_are_trivial(ident, reason); } } _ => {} } } for (ty, _cfg) in all { // Ignore cfg. For now if any use of an extern type requires it to be // trivial, we enforce that it is trivial in all configurations. This // can potentially be relaxed if there is a motivating use case. match ty { Type::RustBox(ty1) => { if let Type::Ident(ident) = &ty1.inner { let local = !aliases.contains_key(&ident.rust) || impls.contains_key(&ty.impl_key(resolutions).unwrap()); let reason = TrivialReason::BoxTarget { local }; insist_extern_types_are_trivial(ident, reason); } } Type::RustVec(ty1) => { if let Type::Ident(ident) = &ty1.inner { let local = !aliases.contains_key(&ident.rust) || impls.contains_key(&ty.impl_key(resolutions).unwrap()); let reason = TrivialReason::VecElement { local }; insist_extern_types_are_trivial(ident, reason); } } Type::SliceRef(ty) => { if let Type::Ident(ident) = &ty.inner { let reason = TrivialReason::SliceElement(ty); 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; 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(slice) => { if slice.mutable { slice_mut_element = true; } else { slice_shared_element = true; } } } } 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, }); } 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.192/src/syntax/types.rs000064400000000000000000000421351046102023000167100ustar 00000000000000use crate::syntax::attrs::OtherAttrs; use crate::syntax::cfg::ComputedCfg; use crate::syntax::improper::ImproperCtype; use crate::syntax::instantiate::ImplKey; use crate::syntax::map::{OrderedMap, UnorderedMap}; use crate::syntax::query::TypeQuery; use crate::syntax::report::Errors; use crate::syntax::resolve::Resolution; use crate::syntax::set::UnorderedSet; use crate::syntax::trivial::{self, TrivialReason}; use crate::syntax::unpin::{self, UnpinReason}; use crate::syntax::visit::{self, Visit}; use crate::syntax::{ toposort, Api, Atom, Enum, ExternFn, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias, }; use indexmap::map::Entry; use proc_macro2::Ident; use quote::ToTokens; pub(crate) struct Types<'a> { pub all: OrderedMap<&'a Type, ComputedCfg<'a>>, 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>>, #[cfg_attr(not(proc_macro), expect(dead_code))] pub required_unpin: UnorderedMap<&'a Ident, UnpinReason<'a>>, pub impls: OrderedMap, ConditionalImpl<'a>>, pub resolutions: UnorderedMap<&'a Ident, Resolution<'a>>, #[cfg_attr(not(proc_macro), expect(dead_code))] pub associated_fn: UnorderedMap<&'a Ident, Vec<&'a ExternFn>>, pub struct_improper_ctypes: UnorderedSet<&'a Ident>, pub toposorted_structs: Vec<&'a Struct>, } pub(crate) struct ConditionalImpl<'a> { pub cfg: ComputedCfg<'a>, // None for implicit impls, which arise from using a generic type // instantiation in a struct field or function signature. #[cfg_attr(not(proc_macro), expect(dead_code))] pub explicit_impl: Option<&'a Impl>, } impl<'a> Types<'a> { pub(crate) fn collect(cx: &mut Errors, apis: &'a [Api]) -> Self { let mut all = OrderedMap::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 mut associated_fn = UnorderedMap::new(); let struct_improper_ctypes = UnorderedSet::new(); let toposorted_structs = Vec::new(); fn visit<'a>( all: &mut OrderedMap<&'a Type, ComputedCfg<'a>>, ty: &'a Type, cfg: impl Into>, ) { struct CollectTypes<'s, 'a> { all: &'s mut OrderedMap<&'a Type, ComputedCfg<'a>>, cfg: ComputedCfg<'a>, } impl<'s, 'a> Visit<'a> for CollectTypes<'s, 'a> { fn visit_type(&mut self, ty: &'a Type) { match self.all.entry(ty) { Entry::Vacant(entry) => { entry.insert(self.cfg.clone()); } Entry::Occupied(mut entry) => entry.get_mut().merge_or(self.cfg.clone()), } visit::visit_type(self, ty); } } let mut visitor = CollectTypes { all, cfg: cfg.into(), }; visitor.visit_type(ty); } let mut add_resolution = |name: &'a Pair, attrs: &'a OtherAttrs, generics: &'a Lifetimes| { resolutions.insert( &name.rust, Resolution { name, attrs, 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, ItemName::Type(ident)); } structs.insert(&strct.name.rust, strct); for field in &strct.fields { let cfg = ComputedCfg::all(&strct.cfg, &field.cfg); visit(&mut all, &field.ty, cfg); } add_resolution(&strct.name, &strct.attrs, &strct.generics); } Api::Enum(enm) => { match all.entry(&enm.repr.repr_type) { Entry::Vacant(entry) => { entry.insert(ComputedCfg::Leaf(&enm.cfg)); } Entry::Occupied(mut entry) => entry.get_mut().merge_or(&enm.cfg), } 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, ItemName::Type(ident)); } enums.insert(ident, enm); add_resolution(&enm.name, &enm.attrs, &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, ItemName::Type(ident)); } cxx.insert(ident); if !ety.trusted { untrusted.insert(ident, ety); } add_resolution(&ety.name, &ety.attrs, &ety.generics); } Api::RustType(ety) => { let ident = &ety.name.rust; if !type_names.insert(ident) { duplicate_name(cx, ety, ItemName::Type(ident)); } rust.insert(ident); add_resolution(&ety.name, &ety.attrs, &ety.generics); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { // Note: duplication of the C++ name is fine because C++ has // function overloading. let self_type = efn.self_type(); if let Some(self_type) = self_type { associated_fn .entry(self_type) .or_insert_with(Vec::new) .push(efn); } if !self_type.is_some_and(|self_type| self_type == "Self") && !function_names.insert((self_type, &efn.name.rust)) { duplicate_name(cx, efn, ItemName::Function(self_type, &efn.name.rust)); } for arg in &efn.args { visit(&mut all, &arg.ty, &efn.cfg); } if let Some(ret) = &efn.ret { visit(&mut all, ret, &efn.cfg); } } Api::TypeAlias(alias) => { let ident = &alias.name.rust; if !type_names.insert(ident) { duplicate_name(cx, alias, ItemName::Type(ident)); } cxx.insert(ident); aliases.insert(ident, alias); add_resolution(&alias.name, &alias.attrs, &alias.generics); } Api::Impl(imp) => { visit(&mut all, &imp.ty, &imp.cfg); } } } for api in apis { if let Api::Impl(imp) = api { if let Some(key) = imp.ty.impl_key(&resolutions) { impls.insert(key, ConditionalImpl::from(imp)); } } } // 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, &aliases, &impls, &resolutions, ); let required_unpin = unpin::required_unpin_reasons(apis, &all, &structs, &enums, &cxx, &aliases); let mut types = Types { all, structs, enums, cxx, rust, aliases, untrusted, required_trivial, required_unpin, impls, resolutions, associated_fn, struct_improper_ctypes, toposorted_structs, }; types.toposorted_structs = toposort::sort(cx, apis, &types); for (ty, cfg) in &types.all { let Some(impl_key) = ty.impl_key(&types.resolutions) else { continue; }; if impl_key.is_implicit_impl_ok(&types) { match types.impls.entry(impl_key) { Entry::Vacant(entry) => { entry.insert(ConditionalImpl::from(cfg.clone())); } Entry::Occupied(mut entry) => entry.get_mut().cfg.merge_or(cfg.clone()), } } } 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: impl Into>) -> bool { let ty = ty.into(); match ty { TypeQuery::RustBox | TypeQuery::UniquePtr | TypeQuery::Ref(_) | TypeQuery::Ptr(_) | TypeQuery::Str | TypeQuery::Fn | TypeQuery::SliceRef => false, TypeQuery::Array(_) => true, _ => !self.is_guaranteed_pod(ty) || self.is_considered_improper_ctype(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. pub(crate) fn is_considered_improper_ctype(&self, ty: impl Into>) -> 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: &Type) -> bool { match ty { Type::Ident(named_type) => { let ident = &named_type.rust; self.structs.contains_key(ident) || self.enums.contains_key(ident) || self.aliases.contains_key(ident) } Type::CxxVector(_) => false, // No other type can appear as the inner type of CxxVector, // UniquePtr, or SharedPtr. _ => unreachable!("syntax/check.rs should reject other types"), } } pub(crate) fn contains_elided_lifetime(&self, ty: &Type) -> bool { match ty { Type::Ident(ty) => { Atom::from(&ty.rust).is_none() && ty.generics.lifetimes.len() != self.resolve(&ty.rust).generics.lifetimes.len() } Type::RustBox(ty) | Type::RustVec(ty) | Type::UniquePtr(ty) | Type::SharedPtr(ty) | Type::WeakPtr(ty) | Type::CxxVector(ty) => self.contains_elided_lifetime(&ty.inner), Type::Ref(ty) => ty.lifetime.is_none() || self.contains_elided_lifetime(&ty.inner), Type::Ptr(ty) => self.contains_elided_lifetime(&ty.inner), Type::Str(ty) => ty.lifetime.is_none(), Type::SliceRef(ty) => ty.lifetime.is_none() || self.contains_elided_lifetime(&ty.inner), Type::Array(ty) => self.contains_elided_lifetime(&ty.inner), Type::Fn(_) | Type::Void(_) => false, } } /// Whether the current module is responsible for generic type /// instantiations pertaining to the given type. pub(crate) fn is_local(&self, ty: &Type) -> bool { match ty { Type::Ident(ident) => { Atom::from(&ident.rust).is_none() && !self.aliases.contains_key(&ident.rust) } Type::RustBox(ty1) => { // https://doc.rust-lang.org/reference/glossary.html#fundamental-type-constructors // "Any time a type T is considered local [...] Box [... is] // also considered local." self.is_local(&ty1.inner) } Type::Array(_) | Type::CxxVector(_) | Type::Fn(_) | Type::Void(_) | Type::RustVec(_) | Type::UniquePtr(_) | Type::SharedPtr(_) | Type::WeakPtr(_) | Type::Ref(_) | Type::Ptr(_) | Type::Str(_) | Type::SliceRef(_) => false, } } } impl<'t, 'a> IntoIterator for &'t Types<'a> { type Item = &'a Type; type IntoIter = std::iter::Copied>>; fn into_iter(self) -> Self::IntoIter { self.all.keys().copied() } } impl<'a> From> for ConditionalImpl<'a> { fn from(cfg: ComputedCfg<'a>) -> Self { ConditionalImpl { cfg, explicit_impl: None, } } } impl<'a> From<&'a Impl> for ConditionalImpl<'a> { fn from(imp: &'a Impl) -> Self { ConditionalImpl { cfg: ComputedCfg::Leaf(&imp.cfg), explicit_impl: Some(imp), } } } enum ItemName<'a> { Type(&'a Ident), Function(Option<&'a Ident>, &'a Ident), } fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, name: ItemName) { let description = match name { ItemName::Type(name) => format!("type `{}`", name), ItemName::Function(Some(self_type), name) => { format!("associated function `{}::{}`", self_type, name) } ItemName::Function(None, name) => format!("function `{}`", name), }; let msg = format!("the {} is defined multiple times", description); cx.error(sp, msg); } cxxbridge-macro-1.0.192/src/syntax/unpin.rs000064400000000000000000000037771046102023000167060ustar 00000000000000use crate::syntax::cfg::ComputedCfg; use crate::syntax::map::{OrderedMap, UnorderedMap}; use crate::syntax::set::UnorderedSet; use crate::syntax::{Api, Enum, NamedType, Receiver, Ref, SliceRef, Struct, Type, TypeAlias}; use proc_macro2::Ident; #[cfg_attr(not(proc_macro), expect(dead_code))] pub(crate) enum UnpinReason<'a> { Receiver(&'a Receiver), Ref(&'a Ref), Slice(&'a SliceRef), } pub(crate) fn required_unpin_reasons<'a>( apis: &'a [Api], all: &OrderedMap<&'a Type, ComputedCfg>, structs: &UnorderedMap<&'a Ident, &'a Struct>, enums: &UnorderedMap<&'a Ident, &'a Enum>, cxx: &UnorderedSet<&'a Ident>, aliases: &UnorderedMap<&'a Ident, &'a TypeAlias>, ) -> UnorderedMap<&'a Ident, UnpinReason<'a>> { let mut reasons = UnorderedMap::new(); let is_extern_type_alias = |ty: &NamedType| -> bool { cxx.contains(&ty.rust) && !structs.contains_key(&ty.rust) && !enums.contains_key(&ty.rust) && aliases.contains_key(&ty.rust) }; for (ty, _cfgs) in all { if let Type::SliceRef(slice) = ty { if let Type::Ident(inner) = &slice.inner { if slice.mutable && is_extern_type_alias(inner) { reasons.insert(&inner.rust, UnpinReason::Slice(slice)); } } } } for api in apis { if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api { if let Some(receiver) = efn.receiver() { if receiver.mutable && !receiver.pinned && is_extern_type_alias(&receiver.ty) { reasons.insert(&receiver.ty.rust, UnpinReason::Receiver(receiver)); } } } } for (ty, _cfg) in all { if let Type::Ref(ty) = ty { if let Type::Ident(inner) = &ty.inner { if ty.mutable && !ty.pinned && is_extern_type_alias(inner) { reasons.insert(&inner.rust, UnpinReason::Ref(ty)); } } } } reasons } cxxbridge-macro-1.0.192/src/syntax/visit.rs000064400000000000000000000017211046102023000166760ustar 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.192/src/tests.rs000064400000000000000000000142731046102023000153620ustar 00000000000000use crate::expand; use crate::syntax::file::Module; use proc_macro2::TokenStream; use quote::quote; use syn::File; fn bridge(cxx_bridge: TokenStream) -> String { let module = syn::parse2::(cxx_bridge).unwrap(); let tokens = expand::bridge(module).unwrap(); let file = match syn::parse2::(tokens.clone()) { Ok(file) => file, Err(err) => { eprintln!("The code below is syntactically invalid: {err}:"); eprintln!("{tokens}"); panic!("`expand::bridge` should generate syntactically valid code"); } }; let pretty = prettyplease::unparse(&file); eprintln!("{0:/<80}\n{pretty}{0:/<80}", ""); pretty } #[test] fn test_unique_ptr_with_elided_lifetime_implicit_impl() { let rs = bridge(quote! { mod ffi { unsafe extern "C++" { type Borrowed<'a>; fn borrowed(arg: &i32) -> UniquePtr; } } }); // It is okay that the return type elides Borrowed's lifetime parameter. assert!(rs.contains("pub fn borrowed(arg: &i32) -> ::cxx::UniquePtr")); // But in impl blocks, the lifetime parameter needs to be present. assert!(rs.contains("unsafe impl<'a> ::cxx::ExternType for Borrowed<'a> {")); assert!(rs.contains("unsafe impl<'a> ::cxx::memory::UniquePtrTarget for Borrowed<'a> {")); // Wrong. assert!(!rs.contains("unsafe impl ::cxx::ExternType for Borrowed {")); assert!(!rs.contains("unsafe impl ::cxx::memory::UniquePtrTarget for Borrowed {")); // Potentially okay, but not what we currently do. assert!(!rs.contains("unsafe impl ::cxx::ExternType for Borrowed<'_> {")); assert!(!rs.contains("unsafe impl ::cxx::memory::UniquePtrTarget for Borrowed<'_> {")); } #[test] fn test_unique_ptr_lifetimes_from_explicit_impl() { let rs = bridge(quote! { mod ffi { unsafe extern "C++" { type Borrowed<'a>; } impl<'b> UniquePtr> {} } }); // Lifetimes use the name from the extern type. assert!(rs.contains("unsafe impl<'a> ::cxx::ExternType for Borrowed<'a>")); // Lifetimes use the names written in the explicit impl if one is present. assert!(rs.contains("unsafe impl<'b> ::cxx::memory::UniquePtrTarget for Borrowed<'c>")); } #[test] fn test_vec_string() { let rs = bridge(quote! { mod ffi { extern "Rust" { fn foo() -> Vec; } } }); // No substitution of String <=> ::cxx::private::RustString. assert!(rs.contains("__return: *mut ::cxx::private::RustVec<::cxx::alloc::string::String>")); assert!(rs.contains("fn __foo() -> ::cxx::alloc::vec::Vec<::cxx::alloc::string::String>")); let rs = bridge(quote! { mod ffi { extern "Rust" { fn foo(v: &Vec); } } }); // No substitution of String <=> ::cxx::private::RustString. assert!(rs.contains("v: &::cxx::private::RustVec<::cxx::alloc::string::String>")); assert!(rs.contains("fn __foo(v: &::cxx::alloc::vec::Vec<::cxx::alloc::string::String>)")); } #[test] fn test_mangling_covers_cpp_namespace_of_vec_elements() { let rs = bridge(quote! { mod ffi { #[namespace = "test_namespace"] struct Context { x: i32 } impl Vec {} } }); // Mangling must include Context's C++ namespace to avoid colliding the // symbol names for two identically named structs in different namespaces. assert!(rs.contains("export_name = \"cxxbridge1$rust_vec$test_namespace$Context$set_len\"")); } #[test] fn test_struct_with_lifetime() { let rs = bridge(quote! { mod ffi { struct StructWithLifetime<'a> { s: &'a str, } extern "Rust" { fn f(_: UniquePtr>); } } }); // Regression test for // which generated this invalid code: // // impl<'a> ::cxx::memory::UniquePtrTarget for StructWithLifetime < > < 'a > { // // Invalid syntax in the output code would already have caused the test // helper `bridge` to panic above. But for completeness this assertion // verifies the intended code has been generated. assert!(rs.contains("impl<'a> ::cxx::memory::UniquePtrTarget for StructWithLifetime<'a> {")); // Assertions for other places that refer to `StructWithLifetime`. assert!(rs.contains("pub struct StructWithLifetime<'a> {")); assert!(rs.contains("cast::>()")); assert!(rs.contains("fn __f(arg0: ::cxx::UniquePtr) {")); assert!(rs.contains("impl<'a> self::Drop for super::StructWithLifetime<'a>")); } #[test] fn test_original_lifetimes_used_in_impls() { let rs = bridge(quote! { mod ffi { struct Context<'sess> { session: &'sess str, } struct Server<'srv> { ctx: UniquePtr>, } struct Client<'clt> { ctx: UniquePtr>, } } }); // Verify which lifetime name ('sess, 'srv, 'clt) gets used for this impl. assert!(rs.contains("impl<'sess> ::cxx::memory::UniquePtrTarget for Context<'sess> {")); } /// This test covers implicit impl of `Vec>`. #[test] fn test_vec_of_box() { let rs = bridge(quote! { mod ffi { extern "Rust" { type R; fn foo() -> Vec>; } } }); assert!(rs.contains("unsafe impl ::cxx::private::ImplBox for R {}")); assert!(rs.contains("export_name = \"cxxbridge1$box$R$drop\"")); assert!(rs.contains("unsafe impl ::cxx::private::ImplVec for ::cxx::alloc::boxed::Box {}")); assert!(rs.contains("export_name = \"cxxbridge1$rust_vec$box$R$set_len\"")); // Not supposed to be `RustVec<*mut R>` (which happened in an early draft). assert!(rs.contains("__return: *mut ::cxx::private::RustVec<::cxx::alloc::boxed::Box>")); assert!(rs.contains("fn __foo() -> ::cxx::alloc::vec::Vec<::cxx::alloc::boxed::Box>")); } cxxbridge-macro-1.0.192/src/tokens.rs000064400000000000000000000040361046102023000155170ustar 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.192/src/type_id.rs000064400000000000000000000022771046102023000156560ustar 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,)*) } }