--- /dev/null
+{
+ "git": {
+ "sha1": "6d7299e7b59b3ea6b2f64acb619c23a4507172e7"
+ },
+ "path_in_vcs": "gen/cmd"
+}
\ No newline at end of file
--- /dev/null
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "clap"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d17bf219fcd37199b9a29e00ba65dfb8cd5b2688b7297ec14ff829c40ac50ca9"
+dependencies = [
+ "bitflags",
+ "indexmap",
+ "os_str_bytes",
+ "strsim",
+ "textwrap",
+]
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "cxxbridge-cmd"
+version = "1.0.62"
+dependencies = [
+ "clap",
+ "codespan-reporting",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+
+[[package]]
+name = "indexmap"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "memchr"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+
+[[package]]
+name = "os_str_bytes"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "syn"
+version = "1.0.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
--- /dev/null
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+rust-version = "1.54"
+name = "cxxbridge-cmd"
+version = "1.0.62"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+exclude = ["build.rs"]
+description = "C++ code generator for integrating `cxx` crate into a non-Cargo build."
+homepage = "https://cxx.rs"
+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"]
+
+[[bin]]
+name = "cxxbridge"
+path = "src/main.rs"
+[dependencies.clap]
+version = "3.0"
+features = ["std", "suggestions"]
+default-features = false
+
+[dependencies.codespan-reporting]
+version = "0.11"
+
+[dependencies.proc-macro2]
+version = "1.0.26"
+features = ["span-locations"]
+default-features = false
+
+[dependencies.quote]
+version = "1.0"
+default-features = false
+
+[dependencies.syn]
+version = "1.0.70"
+features = ["parsing", "printing", "clone-impls", "full"]
+default-features = false
--- /dev/null
+[package]
+name = "cxxbridge-cmd"
+version = "1.0.62"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+edition = "2018"
+rust-version = "1.54"
+license = "MIT OR Apache-2.0"
+description = "C++ code generator for integrating `cxx` crate into a non-Cargo build."
+repository = "https://github.com/dtolnay/cxx"
+homepage = "https://cxx.rs"
+exclude = ["build.rs"]
+keywords = ["ffi"]
+categories = ["development-tools::ffi"]
+
+[[bin]]
+name = "cxxbridge"
+path = "src/main.rs"
+
+[dependencies]
+clap = { version = "3.0", default-features = false, features = ["std", "suggestions"] }
+codespan-reporting = "0.11"
+proc-macro2 = { version = "1.0.26", default-features = false, features = ["span-locations"] }
+quote = { version = "1.0", default-features = false }
+syn = { version = "1.0.70", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] }
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
--- /dev/null
+ 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
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
--- /dev/null
+Permission 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.
--- /dev/null
+#[cfg(test)]
+#[path = "test.rs"]
+mod test;
+
+use super::{Opt, Output};
+use crate::gen::include::Include;
+use crate::syntax::IncludeKind;
+use clap::{App, AppSettings, Arg};
+use std::ffi::OsStr;
+use std::path::PathBuf;
+
+const USAGE: &str = "\
+ cxxbridge <input>.rs Emit .cc file for bridge to stdout
+ cxxbridge <input>.rs --header Emit .h file for bridge to stdout
+ cxxbridge --header Emit \"rust/cxx.h\" header to stdout\
+";
+
+const TEMPLATE: &str = "\
+{bin} {version}
+David Tolnay <dtolnay@gmail.com>
+https://github.com/dtolnay/cxx
+
+USAGE:
+ {usage}
+
+ARGS:
+{positionals}
+
+OPTIONS:
+{options}\
+";
+
+fn app() -> App<'static> {
+ let mut app = App::new("cxxbridge")
+ .override_usage(USAGE)
+ .help_template(TEMPLATE)
+ .setting(AppSettings::NextLineHelp)
+ .arg(arg_input())
+ .arg(arg_cxx_impl_annotations())
+ .arg(arg_header())
+ .arg(arg_include())
+ .arg(arg_output())
+ .mut_arg("help", |a| a.help("Print help information."));
+ if let Some(version) = option_env!("CARGO_PKG_VERSION") {
+ app = app
+ .version(version)
+ .mut_arg("version", |a| a.help("Print version information."));
+ }
+ app
+}
+
+const INPUT: &str = "input";
+const CXX_IMPL_ANNOTATIONS: &str = "cxx-impl-annotations";
+const HEADER: &str = "header";
+const INCLUDE: &str = "include";
+const OUTPUT: &str = "output";
+
+pub(super) fn from_args() -> Opt {
+ let matches = app().get_matches();
+
+ let input = matches.value_of_os(INPUT).map(PathBuf::from);
+ let cxx_impl_annotations = matches.value_of(CXX_IMPL_ANNOTATIONS).map(str::to_owned);
+ let header = matches.is_present(HEADER);
+ let include = matches
+ .values_of(INCLUDE)
+ .unwrap_or_default()
+ .map(|include| {
+ if include.starts_with('<') && include.ends_with('>') {
+ Include {
+ path: include[1..include.len() - 1].to_owned(),
+ kind: IncludeKind::Bracketed,
+ }
+ } else {
+ Include {
+ path: include.to_owned(),
+ kind: IncludeKind::Quoted,
+ }
+ }
+ })
+ .collect();
+
+ let mut outputs = Vec::new();
+ for path in matches.values_of_os(OUTPUT).unwrap_or_default() {
+ outputs.push(if path == "-" {
+ Output::Stdout
+ } else {
+ Output::File(PathBuf::from(path))
+ });
+ }
+ if outputs.is_empty() {
+ outputs.push(Output::Stdout);
+ }
+
+ Opt {
+ input,
+ header,
+ cxx_impl_annotations,
+ include,
+ outputs,
+ }
+}
+
+fn validate_utf8(arg: &OsStr) -> Result<(), &'static str> {
+ if arg.to_str().is_some() {
+ Ok(())
+ } else {
+ Err("invalid utf-8 sequence")
+ }
+}
+
+fn arg_input() -> Arg<'static> {
+ Arg::new(INPUT)
+ .help("Input Rust source file containing #[cxx::bridge].")
+ .required_unless_present(HEADER)
+ .allow_invalid_utf8(true)
+}
+
+fn arg_cxx_impl_annotations() -> Arg<'static> {
+ const HELP: &str = "\
+Optional annotation for implementations of C++ function wrappers
+that may be exposed to Rust. You may for example need to provide
+__declspec(dllexport) or __attribute__((visibility(\"default\")))
+if Rust code from one shared object or executable depends on
+these C++ functions in another.";
+ Arg::new(CXX_IMPL_ANNOTATIONS)
+ .long(CXX_IMPL_ANNOTATIONS)
+ .takes_value(true)
+ .value_name("annotation")
+ .allow_invalid_utf8(true)
+ .validator_os(validate_utf8)
+ .help(HELP)
+}
+
+fn arg_header() -> Arg<'static> {
+ const HELP: &str = "\
+Emit header with declarations only. Optional if using `-o` with
+a path ending in `.h`.";
+ Arg::new(HEADER).long(HEADER).help(HELP)
+}
+
+fn arg_include() -> Arg<'static> {
+ const HELP: &str = "\
+Any additional headers to #include. The cxxbridge tool does not
+parse or even require the given paths to exist; they simply go
+into the generated C++ code as #include lines.";
+ Arg::new(INCLUDE)
+ .long(INCLUDE)
+ .short('i')
+ .takes_value(true)
+ .multiple_occurrences(true)
+ .allow_invalid_utf8(true)
+ .validator_os(validate_utf8)
+ .help(HELP)
+}
+
+fn arg_output() -> Arg<'static> {
+ const HELP: &str = "\
+Path of file to write as output. Output goes to stdout if -o is
+not specified.";
+ Arg::new(OUTPUT)
+ .long(OUTPUT)
+ .short('o')
+ .takes_value(true)
+ .multiple_occurrences(true)
+ .allow_invalid_utf8(true)
+ .validator_os(validate_utf8)
+ .help(HELP)
+}
--- /dev/null
+use proc_macro2::Ident;
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum Block<'a> {
+ AnonymousNamespace,
+ Namespace(&'static str),
+ UserDefinedNamespace(&'a Ident),
+ InlineNamespace(&'static str),
+ ExternC,
+}
+
+impl<'a> Block<'a> {
+ pub fn write_begin(self, out: &mut String) {
+ if let Block::InlineNamespace(_) = self {
+ out.push_str("inline ");
+ }
+ self.write_common(out);
+ out.push_str(" {\n");
+ }
+
+ pub fn write_end(self, out: &mut String) {
+ out.push_str("} // ");
+ self.write_common(out);
+ out.push('\n');
+ }
+
+ fn write_common(self, out: &mut String) {
+ match self {
+ Block::AnonymousNamespace => out.push_str("namespace"),
+ Block::Namespace(name) => {
+ out.push_str("namespace ");
+ out.push_str(name);
+ }
+ Block::UserDefinedNamespace(name) => {
+ out.push_str("namespace ");
+ out.push_str(&name.to_string());
+ }
+ Block::InlineNamespace(name) => {
+ out.push_str("namespace ");
+ out.push_str(name);
+ }
+ Block::ExternC => out.push_str("extern \"C\""),
+ }
+ }
+}
--- /dev/null
+use crate::gen::block::Block;
+use crate::gen::ifndef;
+use crate::gen::out::{Content, OutFile};
+
+#[derive(Default, PartialEq)]
+pub struct Builtins<'a> {
+ pub panic: bool,
+ pub rust_string: bool,
+ pub rust_str: bool,
+ pub rust_slice: bool,
+ pub rust_box: bool,
+ pub rust_vec: bool,
+ pub rust_fn: bool,
+ pub rust_isize: bool,
+ pub opaque: bool,
+ pub layout: bool,
+ pub unsafe_bitcopy: bool,
+ pub unsafe_bitcopy_t: bool,
+ pub rust_error: bool,
+ pub manually_drop: bool,
+ pub maybe_uninit: bool,
+ pub trycatch: bool,
+ pub ptr_len: bool,
+ pub repr_fat: bool,
+ pub rust_str_new_unchecked: bool,
+ pub rust_str_repr: bool,
+ pub rust_slice_new: bool,
+ pub rust_slice_repr: bool,
+ pub exception: bool,
+ pub relocatable: bool,
+ pub friend_impl: bool,
+ pub is_complete: bool,
+ pub destroy: bool,
+ pub deleter_if: bool,
+ pub content: Content<'a>,
+}
+
+impl<'a> Builtins<'a> {
+ pub fn new() -> Self {
+ Builtins::default()
+ }
+}
+
+pub(super) fn write(out: &mut OutFile) {
+ if out.builtin == Default::default() {
+ return;
+ }
+
+ let include = &mut out.include;
+ let builtin = &mut out.builtin;
+ let out = &mut builtin.content;
+
+ if builtin.rust_string {
+ include.array = true;
+ include.cstdint = true;
+ include.string = true;
+ }
+
+ if builtin.rust_str {
+ include.array = true;
+ include.cstdint = true;
+ include.string = true;
+ builtin.friend_impl = true;
+ }
+
+ if builtin.rust_vec {
+ include.algorithm = true;
+ include.array = true;
+ include.cassert = true;
+ include.cstddef = true;
+ include.cstdint = true;
+ include.initializer_list = true;
+ include.iterator = true;
+ include.new = true;
+ include.stdexcept = true;
+ include.type_traits = true;
+ include.utility = true;
+ builtin.panic = true;
+ builtin.rust_slice = true;
+ builtin.unsafe_bitcopy_t = true;
+ }
+
+ if builtin.rust_slice {
+ include.array = true;
+ include.cassert = true;
+ include.cstddef = true;
+ include.cstdint = true;
+ include.iterator = true;
+ include.stdexcept = true;
+ include.type_traits = true;
+ builtin.friend_impl = true;
+ builtin.layout = true;
+ builtin.panic = true;
+ }
+
+ if builtin.rust_box {
+ include.new = true;
+ include.type_traits = true;
+ include.utility = true;
+ }
+
+ if builtin.rust_fn {
+ include.utility = true;
+ }
+
+ if builtin.rust_error {
+ include.exception = true;
+ builtin.friend_impl = true;
+ }
+
+ if builtin.rust_isize {
+ include.basetsd = true;
+ include.sys_types = true;
+ }
+
+ if builtin.relocatable {
+ include.type_traits = true;
+ }
+
+ if builtin.layout {
+ include.type_traits = true;
+ include.cstddef = true;
+ builtin.is_complete = true;
+ }
+
+ if builtin.is_complete {
+ include.cstddef = true;
+ include.type_traits = true;
+ }
+
+ if builtin.unsafe_bitcopy {
+ builtin.unsafe_bitcopy_t = true;
+ }
+
+ out.begin_block(Block::Namespace("rust"));
+ out.begin_block(Block::InlineNamespace("cxxbridge1"));
+
+ let cxx_header = include.has_cxx_header();
+ if !cxx_header {
+ writeln!(out, "// #include \"rust/cxx.h\"");
+
+ ifndef::write(out, builtin.panic, "CXXBRIDGE1_PANIC");
+
+ if builtin.rust_string {
+ out.next_section();
+ writeln!(out, "struct unsafe_bitcopy_t;");
+ }
+
+ if builtin.friend_impl {
+ out.begin_block(Block::AnonymousNamespace);
+ writeln!(out, "template <typename T>");
+ writeln!(out, "class impl;");
+ out.end_block(Block::AnonymousNamespace);
+ }
+
+ out.next_section();
+ if builtin.rust_str && !builtin.rust_string {
+ writeln!(out, "class String;");
+ }
+ if builtin.layout && !builtin.opaque {
+ writeln!(out, "class Opaque;");
+ }
+
+ if builtin.rust_slice {
+ out.next_section();
+ writeln!(out, "template <typename T>");
+ writeln!(out, "::std::size_t size_of();");
+ writeln!(out, "template <typename T>");
+ writeln!(out, "::std::size_t align_of();");
+ }
+
+ ifndef::write(out, builtin.rust_string, "CXXBRIDGE1_RUST_STRING");
+ ifndef::write(out, builtin.rust_str, "CXXBRIDGE1_RUST_STR");
+ ifndef::write(out, builtin.rust_slice, "CXXBRIDGE1_RUST_SLICE");
+ ifndef::write(out, builtin.rust_box, "CXXBRIDGE1_RUST_BOX");
+ ifndef::write(out, builtin.unsafe_bitcopy_t, "CXXBRIDGE1_RUST_BITCOPY_T");
+ ifndef::write(out, builtin.unsafe_bitcopy, "CXXBRIDGE1_RUST_BITCOPY");
+ ifndef::write(out, builtin.rust_vec, "CXXBRIDGE1_RUST_VEC");
+ ifndef::write(out, builtin.rust_fn, "CXXBRIDGE1_RUST_FN");
+ ifndef::write(out, builtin.rust_error, "CXXBRIDGE1_RUST_ERROR");
+ ifndef::write(out, builtin.rust_isize, "CXXBRIDGE1_RUST_ISIZE");
+ ifndef::write(out, builtin.opaque, "CXXBRIDGE1_RUST_OPAQUE");
+ ifndef::write(out, builtin.is_complete, "CXXBRIDGE1_IS_COMPLETE");
+ ifndef::write(out, builtin.layout, "CXXBRIDGE1_LAYOUT");
+ ifndef::write(out, builtin.relocatable, "CXXBRIDGE1_RELOCATABLE");
+ }
+
+ if builtin.rust_str_new_unchecked {
+ out.next_section();
+ writeln!(out, "class Str::uninit {{}};");
+ writeln!(out, "inline Str::Str(uninit) noexcept {{}}");
+ }
+
+ if builtin.rust_slice_new {
+ out.next_section();
+ writeln!(out, "template <typename T>");
+ writeln!(out, "class Slice<T>::uninit {{}};");
+ writeln!(out, "template <typename T>");
+ writeln!(out, "inline Slice<T>::Slice(uninit) noexcept {{}}");
+ }
+
+ out.begin_block(Block::Namespace("detail"));
+
+ if builtin.maybe_uninit {
+ include.cstddef = true;
+ include.new = true;
+ out.next_section();
+ writeln!(out, "template <typename T, typename = void *>");
+ writeln!(out, "struct operator_new {{");
+ writeln!(
+ out,
+ " void *operator()(::std::size_t sz) {{ return ::operator new(sz); }}",
+ );
+ writeln!(out, "}};");
+ out.next_section();
+ writeln!(out, "template <typename T>");
+ writeln!(
+ out,
+ "struct operator_new<T, decltype(T::operator new(sizeof(T)))> {{",
+ );
+ writeln!(
+ out,
+ " void *operator()(::std::size_t sz) {{ return T::operator new(sz); }}",
+ );
+ writeln!(out, "}};");
+ }
+
+ out.end_block(Block::Namespace("detail"));
+
+ if builtin.manually_drop {
+ out.next_section();
+ include.utility = true;
+ writeln!(out, "template <typename T>");
+ writeln!(out, "union ManuallyDrop {{");
+ writeln!(out, " T value;");
+ writeln!(
+ out,
+ " ManuallyDrop(T &&value) : value(::std::move(value)) {{}}",
+ );
+ writeln!(out, " ~ManuallyDrop() {{}}");
+ writeln!(out, "}};");
+ }
+
+ if builtin.maybe_uninit {
+ include.cstddef = true;
+ out.next_section();
+ writeln!(out, "template <typename T>");
+ writeln!(out, "union MaybeUninit {{");
+ writeln!(out, " T value;");
+ writeln!(
+ out,
+ " void *operator new(::std::size_t sz) {{ return detail::operator_new<T>{{}}(sz); }}",
+ );
+ writeln!(out, " MaybeUninit() {{}}");
+ writeln!(out, " ~MaybeUninit() {{}}");
+ writeln!(out, "}};");
+ }
+
+ out.begin_block(Block::AnonymousNamespace);
+
+ if builtin.repr_fat {
+ include.array = true;
+ include.cstdint = true;
+ out.next_section();
+ out.begin_block(Block::Namespace("repr"));
+ writeln!(out, "using Fat = ::std::array<::std::uintptr_t, 2>;");
+ out.end_block(Block::Namespace("repr"));
+ }
+
+ if builtin.ptr_len {
+ include.cstddef = true;
+ out.next_section();
+ out.begin_block(Block::Namespace("repr"));
+ writeln!(out, "struct PtrLen final {{");
+ writeln!(out, " void *ptr;");
+ writeln!(out, " ::std::size_t len;");
+ writeln!(out, "}};");
+ out.end_block(Block::Namespace("repr"));
+ }
+
+ if builtin.rust_str_new_unchecked || builtin.rust_str_repr {
+ out.next_section();
+ writeln!(out, "template <>");
+ writeln!(out, "class impl<Str> final {{");
+ writeln!(out, "public:");
+ if builtin.rust_str_new_unchecked {
+ writeln!(
+ out,
+ " static Str new_unchecked(repr::Fat repr) noexcept {{",
+ );
+ writeln!(out, " Str str = Str::uninit{{}};");
+ writeln!(out, " str.repr = repr;");
+ writeln!(out, " return str;");
+ writeln!(out, " }}");
+ }
+ if builtin.rust_str_repr {
+ writeln!(out, " static repr::Fat repr(Str str) noexcept {{");
+ writeln!(out, " return str.repr;");
+ writeln!(out, " }}");
+ }
+ writeln!(out, "}};");
+ }
+
+ if builtin.rust_slice_new || builtin.rust_slice_repr {
+ out.next_section();
+ writeln!(out, "template <typename T>");
+ writeln!(out, "class impl<Slice<T>> final {{");
+ writeln!(out, "public:");
+ if builtin.rust_slice_new {
+ writeln!(out, " static Slice<T> slice(repr::Fat repr) noexcept {{");
+ writeln!(out, " Slice<T> slice = typename Slice<T>::uninit{{}};");
+ writeln!(out, " slice.repr = repr;");
+ writeln!(out, " return slice;");
+ writeln!(out, " }}");
+ }
+ if builtin.rust_slice_repr {
+ writeln!(out, " static repr::Fat repr(Slice<T> slice) noexcept {{");
+ writeln!(out, " return slice.repr;");
+ writeln!(out, " }}");
+ }
+ writeln!(out, "}};");
+ }
+
+ if builtin.rust_error {
+ out.next_section();
+ writeln!(out, "template <>");
+ writeln!(out, "class impl<Error> final {{");
+ writeln!(out, "public:");
+ writeln!(out, " static Error error(repr::PtrLen repr) noexcept {{");
+ writeln!(out, " Error error;");
+ writeln!(out, " error.msg = static_cast<const char *>(repr.ptr);");
+ writeln!(out, " error.len = repr.len;");
+ writeln!(out, " return error;");
+ writeln!(out, " }}");
+ writeln!(out, "}};");
+ }
+
+ if builtin.destroy {
+ out.next_section();
+ writeln!(out, "template <typename T>");
+ writeln!(out, "void destroy(T *ptr) {{");
+ writeln!(out, " ptr->~T();");
+ writeln!(out, "}}");
+ }
+
+ if builtin.deleter_if {
+ out.next_section();
+ writeln!(out, "template <bool> struct deleter_if {{");
+ writeln!(out, " template <typename T> void operator()(T *) {{}}");
+ writeln!(out, "}};");
+ out.next_section();
+ writeln!(out, "template <> struct deleter_if<true> {{");
+ writeln!(
+ out,
+ " template <typename T> void operator()(T *ptr) {{ ptr->~T(); }}",
+ );
+ writeln!(out, "}};");
+ }
+
+ out.end_block(Block::AnonymousNamespace);
+ out.end_block(Block::InlineNamespace("cxxbridge1"));
+
+ if builtin.trycatch {
+ out.begin_block(Block::Namespace("behavior"));
+ include.exception = true;
+ include.type_traits = true;
+ include.utility = true;
+ writeln!(out, "class missing {{}};");
+ writeln!(out, "missing trycatch(...);");
+ writeln!(out);
+ writeln!(out, "template <typename Try, typename Fail>");
+ writeln!(out, "static typename ::std::enable_if<");
+ writeln!(
+ out,
+ " ::std::is_same<decltype(trycatch(::std::declval<Try>(), ::std::declval<Fail>())),",
+ );
+ writeln!(out, " missing>::value>::type");
+ writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{");
+ writeln!(out, " func();");
+ writeln!(out, "}} catch (const ::std::exception &e) {{");
+ writeln!(out, " fail(e.what());");
+ writeln!(out, "}}");
+ out.end_block(Block::Namespace("behavior"));
+ }
+
+ out.end_block(Block::Namespace("rust"));
+
+ if builtin.exception {
+ include.cstddef = true;
+ out.begin_block(Block::ExternC);
+ writeln!(
+ out,
+ "const char *cxxbridge1$exception(const char *, ::std::size_t);",
+ );
+ out.end_block(Block::ExternC);
+ }
+}
--- /dev/null
+use crate::gen::Opt;
+use crate::syntax::report::Errors;
+use crate::syntax::{error, Api};
+use quote::{quote, quote_spanned};
+use std::path::{Component, Path};
+
+pub(super) use crate::syntax::check::{typecheck, Generator};
+
+pub(super) fn precheck(cx: &mut Errors, apis: &[Api], opt: &Opt) {
+ if !opt.allow_dot_includes {
+ check_dot_includes(cx, apis);
+ }
+}
+
+fn check_dot_includes(cx: &mut Errors, apis: &[Api]) {
+ for api in apis {
+ if let Api::Include(include) = api {
+ let first_component = Path::new(&include.path).components().next();
+ if let Some(Component::CurDir) | Some(Component::ParentDir) = first_component {
+ let begin = quote_spanned!(include.begin_span=> .);
+ let end = quote_spanned!(include.end_span=> .);
+ let span = quote!(#begin #end);
+ cx.error(span, error::DOT_INCLUDE.msg);
+ }
+ }
+ }
+}
--- /dev/null
+use crate::gen::fs;
+use crate::syntax;
+use codespan_reporting::diagnostic::{Diagnostic, Label};
+use codespan_reporting::files::SimpleFiles;
+use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor};
+use codespan_reporting::term::{self, Config};
+use std::borrow::Cow;
+use std::error::Error as StdError;
+use std::fmt::{self, Display};
+use std::io::{self, Write};
+use std::ops::Range;
+use std::path::{Path, PathBuf};
+use std::process;
+use std::str::Utf8Error;
+
+pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
+
+#[derive(Debug)]
+pub(crate) enum Error {
+ NoBridgeMod,
+ Fs(fs::Error),
+ Utf8(PathBuf, Utf8Error),
+ Syn(syn::Error),
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"),
+ Error::Fs(err) => err.fmt(f),
+ Error::Utf8(path, _) => write!(f, "Failed to read file `{}`", path.display()),
+ Error::Syn(err) => err.fmt(f),
+ }
+ }
+}
+
+impl StdError for Error {
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ match self {
+ Error::Fs(err) => err.source(),
+ Error::Utf8(_, err) => Some(err),
+ Error::Syn(err) => err.source(),
+ _ => None,
+ }
+ }
+}
+
+impl From<fs::Error> for Error {
+ fn from(err: fs::Error) -> Self {
+ Error::Fs(err)
+ }
+}
+
+impl From<syn::Error> for Error {
+ fn from(err: syn::Error) -> Self {
+ Error::Syn(err)
+ }
+}
+
+pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! {
+ match error {
+ Error::Syn(syn_error) => {
+ let syn_error = sort_syn_errors(syn_error);
+ let writer = StandardStream::stderr(ColorChoice::Auto);
+ let ref mut stderr = writer.lock();
+ for error in syn_error {
+ let _ = writeln!(stderr);
+ display_syn_error(stderr, path, source, error);
+ }
+ }
+ Error::NoBridgeMod => {
+ let _ = writeln!(
+ io::stderr(),
+ "cxxbridge: no #[cxx::bridge] module found in {}",
+ path.display(),
+ );
+ }
+ _ => {
+ let _ = writeln!(io::stderr(), "cxxbridge: {}", report(error));
+ }
+ }
+ process::exit(1);
+}
+
+pub(crate) fn report(error: impl StdError) -> impl Display {
+ struct Report<E>(E);
+
+ impl<E: StdError> Display for Report<E> {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ write!(formatter, "{}", self.0)?;
+ let mut error: &dyn StdError = &self.0;
+
+ while let Some(cause) = error.source() {
+ write!(formatter, "\n\nCaused by:\n {}", cause)?;
+ error = cause;
+ }
+
+ Ok(())
+ }
+ }
+
+ Report(error)
+}
+
+fn sort_syn_errors(error: syn::Error) -> Vec<syn::Error> {
+ let mut errors: Vec<_> = error.into_iter().collect();
+ errors.sort_by_key(|e| {
+ let start = e.span().start();
+ (start.line, start.column)
+ });
+ errors
+}
+
+fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, error: syn::Error) {
+ let span = error.span();
+ let start = span.start();
+ let end = span.end();
+
+ let mut start_offset = 0;
+ for _ in 1..start.line {
+ start_offset += source[start_offset..].find('\n').unwrap() + 1;
+ }
+ let start_column = source[start_offset..]
+ .chars()
+ .take(start.column)
+ .map(char::len_utf8)
+ .sum::<usize>();
+ start_offset += start_column;
+
+ let mut end_offset = start_offset;
+ if start.line == end.line {
+ end_offset -= start_column;
+ } else {
+ for _ in 0..end.line - start.line {
+ end_offset += source[end_offset..].find('\n').unwrap() + 1;
+ }
+ }
+ end_offset += source[end_offset..]
+ .chars()
+ .take(end.column)
+ .map(char::len_utf8)
+ .sum::<usize>();
+
+ let mut path = path.to_string_lossy();
+ if path == "-" {
+ path = Cow::Borrowed(if cfg!(unix) { "/dev/stdin" } else { "stdin" });
+ }
+
+ let mut files = SimpleFiles::new();
+ let file = files.add(path, source);
+
+ let diagnostic = diagnose(file, start_offset..end_offset, error);
+
+ let config = Config::default();
+ let _ = term::emit(stderr, &config, &files, &diagnostic);
+}
+
+fn diagnose(file: usize, range: Range<usize>, error: syn::Error) -> Diagnostic<usize> {
+ let message = error.to_string();
+ let info = syntax::error::ERRORS
+ .iter()
+ .find(|e| message.contains(e.msg));
+ let mut diagnostic = Diagnostic::error().with_message(&message);
+ let mut label = Label::primary(file, range);
+ if let Some(info) = info {
+ label.message = info.label.map_or(message, str::to_owned);
+ diagnostic.labels.push(label);
+ diagnostic.notes.extend(info.note.map(str::to_owned));
+ } else {
+ label.message = message;
+ diagnostic.labels.push(label);
+ }
+ diagnostic.code = Some("cxxbridge".to_owned());
+ diagnostic
+}
--- /dev/null
+use crate::syntax::file::Module;
+use crate::syntax::namespace::Namespace;
+use syn::parse::discouraged::Speculative;
+use syn::parse::{Error, Parse, ParseStream, Result};
+use syn::{braced, Attribute, Ident, Item, Token, Visibility};
+
+pub struct File {
+ pub modules: Vec<Module>,
+}
+
+impl Parse for File {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let mut modules = Vec::new();
+ input.call(Attribute::parse_inner)?;
+ parse(input, &mut modules)?;
+ Ok(File { modules })
+ }
+}
+
+fn parse(input: ParseStream, modules: &mut Vec<Module>) -> Result<()> {
+ while !input.is_empty() {
+ let mut cxx_bridge = false;
+ let mut namespace = Namespace::ROOT;
+ let mut attrs = input.call(Attribute::parse_outer)?;
+ for attr in &attrs {
+ let path = &attr.path.segments;
+ if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" {
+ cxx_bridge = true;
+ namespace = parse_args(attr)?;
+ break;
+ }
+ }
+
+ let ahead = input.fork();
+ ahead.parse::<Visibility>()?;
+ ahead.parse::<Option<Token![unsafe]>>()?;
+ if !ahead.peek(Token![mod]) {
+ let item: Item = input.parse()?;
+ if cxx_bridge {
+ return Err(Error::new_spanned(item, "expected a module"));
+ }
+ continue;
+ }
+
+ if cxx_bridge {
+ let mut module: Module = input.parse()?;
+ module.namespace = namespace;
+ attrs.extend(module.attrs);
+ module.attrs = attrs;
+ modules.push(module);
+ } else {
+ input.advance_to(&ahead);
+ input.parse::<Token![mod]>()?;
+ input.parse::<Ident>()?;
+ let semi: Option<Token![;]> = input.parse()?;
+ if semi.is_none() {
+ let content;
+ braced!(content in input);
+ parse(&content, modules)?;
+ }
+ }
+ }
+ Ok(())
+}
+
+fn parse_args(attr: &Attribute) -> Result<Namespace> {
+ if attr.tokens.is_empty() {
+ Ok(Namespace::ROOT)
+ } else {
+ attr.parse_args_with(Namespace::parse_bridge_attr_namespace)
+ }
+}
--- /dev/null
+#![allow(dead_code)]
+
+use std::error::Error as StdError;
+use std::fmt::{self, Display};
+use std::io::{self, Read};
+use std::path::{Path, PathBuf};
+
+pub(crate) type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Debug)]
+pub(crate) struct Error {
+ source: Option<io::Error>,
+ message: String,
+}
+
+impl Error {
+ pub fn kind(&self) -> io::ErrorKind {
+ match &self.source {
+ Some(io_error) => io_error.kind(),
+ None => io::ErrorKind::Other,
+ }
+ }
+}
+
+impl Display for Error {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str(&self.message)
+ }
+}
+
+impl StdError for Error {
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ let source = self.source.as_ref()?;
+ Some(source)
+ }
+}
+
+macro_rules! err {
+ ($io_error:expr, $fmt:expr $(, $path:expr)* $(,)?) => {
+ Err(Error {
+ source: Option::from($io_error),
+ message: format!($fmt $(, $path.display())*),
+ })
+ }
+}
+
+pub(crate) fn copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<u64> {
+ let from = from.as_ref();
+ let to = to.as_ref();
+ match std::fs::copy(from, to) {
+ Ok(n) => Ok(n),
+ Err(e) => err!(e, "Failed to copy `{}` -> `{}`", from, to),
+ }
+}
+
+pub(crate) fn create_dir_all(path: impl AsRef<Path>) -> Result<()> {
+ let path = path.as_ref();
+ match std::fs::create_dir_all(path) {
+ Ok(()) => Ok(()),
+ Err(e) => err!(e, "Failed to create directory `{}`", path),
+ }
+}
+
+pub(crate) fn current_dir() -> Result<PathBuf> {
+ match std::env::current_dir() {
+ Ok(dir) => Ok(dir),
+ Err(e) => err!(e, "Failed to determine current directory"),
+ }
+}
+
+pub(crate) fn read(path: impl AsRef<Path>) -> Result<Vec<u8>> {
+ let path = path.as_ref();
+ match std::fs::read(path) {
+ Ok(string) => Ok(string),
+ Err(e) => err!(e, "Failed to read file `{}`", path),
+ }
+}
+
+pub(crate) fn read_stdin() -> Result<Vec<u8>> {
+ let mut bytes = Vec::new();
+ match io::stdin().read_to_end(&mut bytes) {
+ Ok(_len) => Ok(bytes),
+ Err(e) => err!(e, "Failed to read input from stdin"),
+ }
+}
+
+pub(crate) fn remove_file(path: impl AsRef<Path>) -> Result<()> {
+ let path = path.as_ref();
+ match std::fs::remove_file(path) {
+ Ok(()) => Ok(()),
+ Err(e) => err!(e, "Failed to remove file `{}`", path),
+ }
+}
+
+pub(crate) fn remove_dir(path: impl AsRef<Path>) -> Result<()> {
+ let path = path.as_ref();
+ match std::fs::remove_dir(path) {
+ Ok(()) => Ok(()),
+ Err(e) => err!(e, "Failed to remove directory `{}`", path),
+ }
+}
+
+fn symlink<'a>(
+ original: &'a Path,
+ link: &'a Path,
+ fun: fn(&'a Path, &'a Path) -> io::Result<()>,
+) -> Result<()> {
+ match fun(original, link) {
+ Ok(()) => Ok(()),
+ Err(e) => err!(
+ e,
+ "Failed to create symlink `{}` pointing to `{}`",
+ link,
+ original,
+ ),
+ }
+}
+
+pub(crate) fn symlink_fail(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
+ err!(
+ None,
+ "Failed to create symlink `{}` pointing to `{}`",
+ link.as_ref(),
+ original.as_ref(),
+ )
+}
+
+#[cfg(unix)]
+#[allow(unused_imports)]
+pub(crate) use self::symlink_file as symlink_dir;
+
+#[cfg(not(any(unix, windows)))]
+#[allow(unused_imports)]
+pub(crate) use self::symlink_fail as symlink_dir;
+
+#[cfg(unix)]
+pub(crate) fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
+ symlink(original.as_ref(), link.as_ref(), std::os::unix::fs::symlink)
+}
+
+#[cfg(windows)]
+pub(crate) fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
+ symlink(
+ original.as_ref(),
+ link.as_ref(),
+ std::os::windows::fs::symlink_file,
+ )
+}
+
+#[cfg(windows)]
+pub(crate) fn symlink_dir(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
+ symlink(
+ original.as_ref(),
+ link.as_ref(),
+ std::os::windows::fs::symlink_dir,
+ )
+}
+
+pub(crate) fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> Result<()> {
+ let path = path.as_ref();
+ match std::fs::write(path, contents) {
+ Ok(()) => Ok(()),
+ Err(e) => err!(e, "Failed to write file `{}`", path),
+ }
+}
--- /dev/null
+use crate::gen::include::HEADER;
+use crate::gen::out::Content;
+
+pub(super) fn write(out: &mut Content, needed: bool, guard: &str) {
+ let ifndef = format!("#ifndef {}", guard);
+ let define = format!("#define {}", guard);
+ let endif = format!("#endif // {}", guard);
+
+ let mut offset = 0;
+ loop {
+ let begin = find_line(offset, &ifndef);
+ let end = find_line(offset, &endif);
+ if let (Some(begin), Some(end)) = (begin, end) {
+ if !needed {
+ return;
+ }
+ out.next_section();
+ if offset == 0 {
+ writeln!(out, "{}", ifndef);
+ writeln!(out, "{}", define);
+ }
+ for line in HEADER[begin + ifndef.len()..end].trim().lines() {
+ if line != define && !line.trim_start().starts_with("//") {
+ writeln!(out, "{}", line);
+ }
+ }
+ offset = end + endif.len();
+ } else if offset == 0 {
+ panic!("not found in cxx.h header: {}", guard)
+ } else {
+ writeln!(out, "{}", endif);
+ return;
+ }
+ }
+}
+
+fn find_line(mut offset: usize, line: &str) -> Option<usize> {
+ loop {
+ offset += HEADER[offset..].find(line)?;
+ let rest = &HEADER[offset + line.len()..];
+ if rest.starts_with('\n') || rest.starts_with('\r') {
+ return Some(offset);
+ }
+ offset += line.len();
+ }
+}
--- /dev/null
+use crate::gen::out::{Content, OutFile};
+use crate::syntax::{self, IncludeKind};
+use std::ops::{Deref, DerefMut};
+
+/// The complete contents of the "rust/cxx.h" header.
+pub static HEADER: &str = include_str!("include/cxx.h");
+
+/// A header to #include.
+///
+/// The cxxbridge tool does not parse or even require the given paths to exist;
+/// they simply go into the generated C++ code as #include lines.
+#[derive(Clone, PartialEq, Debug)]
+pub struct Include {
+ /// The header's path, not including the enclosing quotation marks or angle
+ /// brackets.
+ pub path: String,
+ /// Whether to emit `#include "path"` or `#include <path>`.
+ pub kind: IncludeKind,
+}
+
+#[derive(Default, PartialEq)]
+pub struct Includes<'a> {
+ pub custom: Vec<Include>,
+ pub algorithm: bool,
+ pub array: bool,
+ pub cassert: bool,
+ pub cstddef: bool,
+ pub cstdint: bool,
+ pub cstring: bool,
+ pub exception: bool,
+ pub functional: bool,
+ pub initializer_list: bool,
+ pub iterator: bool,
+ pub memory: bool,
+ pub new: bool,
+ pub stdexcept: bool,
+ pub string: bool,
+ pub type_traits: bool,
+ pub utility: bool,
+ pub vector: bool,
+ pub basetsd: bool,
+ pub sys_types: bool,
+ pub content: Content<'a>,
+}
+
+impl<'a> Includes<'a> {
+ pub fn new() -> Self {
+ Includes::default()
+ }
+
+ pub fn insert(&mut self, include: impl Into<Include>) {
+ self.custom.push(include.into());
+ }
+
+ pub fn has_cxx_header(&self) -> bool {
+ self.custom
+ .iter()
+ .any(|header| header.path == "rust/cxx.h" || header.path == "rust\\cxx.h")
+ }
+}
+
+pub(super) fn write(out: &mut OutFile) {
+ let header = out.header;
+ let include = &mut out.include;
+ let cxx_header = include.has_cxx_header();
+ let out = &mut include.content;
+
+ if header {
+ writeln!(out, "#pragma once");
+ }
+
+ for include in &include.custom {
+ match include.kind {
+ IncludeKind::Quoted => {
+ writeln!(out, "#include \"{}\"", include.path.escape_default());
+ }
+ IncludeKind::Bracketed => {
+ writeln!(out, "#include <{}>", include.path);
+ }
+ }
+ }
+
+ let Includes {
+ custom: _,
+ algorithm,
+ array,
+ cassert,
+ cstddef,
+ cstdint,
+ cstring,
+ exception,
+ functional,
+ initializer_list,
+ iterator,
+ memory,
+ new,
+ stdexcept,
+ string,
+ type_traits,
+ utility,
+ vector,
+ basetsd,
+ sys_types,
+ content: _,
+ } = *include;
+
+ if algorithm && !cxx_header {
+ writeln!(out, "#include <algorithm>");
+ }
+ if array && !cxx_header {
+ writeln!(out, "#include <array>");
+ }
+ if cassert && !cxx_header {
+ writeln!(out, "#include <cassert>");
+ }
+ if cstddef && !cxx_header {
+ writeln!(out, "#include <cstddef>");
+ }
+ if cstdint && !cxx_header {
+ writeln!(out, "#include <cstdint>");
+ }
+ if cstring {
+ writeln!(out, "#include <cstring>");
+ }
+ if exception && !cxx_header {
+ writeln!(out, "#include <exception>");
+ }
+ if functional {
+ writeln!(out, "#include <functional>");
+ }
+ if initializer_list && !cxx_header {
+ writeln!(out, "#include <initializer_list>");
+ }
+ if iterator && !cxx_header {
+ writeln!(out, "#include <iterator>");
+ }
+ if memory {
+ writeln!(out, "#include <memory>");
+ }
+ if new && !cxx_header {
+ writeln!(out, "#include <new>");
+ }
+ if stdexcept && !cxx_header {
+ writeln!(out, "#include <stdexcept>");
+ }
+ if string && !cxx_header {
+ writeln!(out, "#include <string>");
+ }
+ if type_traits && !cxx_header {
+ writeln!(out, "#include <type_traits>");
+ }
+ if utility && !cxx_header {
+ writeln!(out, "#include <utility>");
+ }
+ if vector && !cxx_header {
+ writeln!(out, "#include <vector>");
+ }
+ if basetsd && !cxx_header {
+ writeln!(out, "#if defined(_WIN32)");
+ writeln!(out, "#include <basetsd.h>");
+ }
+ if sys_types && !cxx_header {
+ if basetsd {
+ writeln!(out, "#else");
+ } else {
+ writeln!(out, "#if not defined(_WIN32)");
+ }
+ }
+ if sys_types && !cxx_header {
+ writeln!(out, "#include <sys/types.h>");
+ }
+ if (basetsd || sys_types) && !cxx_header {
+ writeln!(out, "#endif");
+ }
+}
+
+impl<'i, 'a> Extend<&'i Include> for Includes<'a> {
+ fn extend<I: IntoIterator<Item = &'i Include>>(&mut self, iter: I) {
+ self.custom.extend(iter.into_iter().cloned());
+ }
+}
+
+impl<'i> From<&'i syntax::Include> for Include {
+ fn from(include: &syntax::Include) -> Self {
+ Include {
+ path: include.path.clone(),
+ kind: include.kind,
+ }
+ }
+}
+
+impl<'a> Deref for Includes<'a> {
+ type Target = Content<'a>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.content
+ }
+}
+
+impl<'a> DerefMut for Includes<'a> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.content
+ }
+}
--- /dev/null
+#pragma once
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <exception>
+#include <initializer_list>
+#include <iosfwd>
+#include <iterator>
+#include <new>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+#if defined(_WIN32)
+#include <basetsd.h>
+#else
+#include <sys/types.h>
+#endif
+
+namespace rust {
+inline namespace cxxbridge1 {
+
+struct unsafe_bitcopy_t;
+
+namespace {
+template <typename T>
+class impl;
+}
+
+#ifndef CXXBRIDGE1_RUST_STRING
+#define CXXBRIDGE1_RUST_STRING
+// https://cxx.rs/binding/string.html
+class String final {
+public:
+ String() noexcept;
+ String(const String &) noexcept;
+ String(String &&) noexcept;
+ ~String() noexcept;
+
+ String(const std::string &);
+ String(const char *);
+ String(const char *, std::size_t);
+ String(const char16_t *);
+ String(const char16_t *, std::size_t);
+
+ // Replace invalid Unicode data with the replacement character (U+FFFD).
+ static String lossy(const std::string &) noexcept;
+ static String lossy(const char *) noexcept;
+ static String lossy(const char *, std::size_t) noexcept;
+ static String lossy(const char16_t *) noexcept;
+ static String lossy(const char16_t *, std::size_t) noexcept;
+
+ String &operator=(const String &) &noexcept;
+ String &operator=(String &&) &noexcept;
+
+ explicit operator std::string() const;
+
+ // Note: no null terminator.
+ const char *data() const noexcept;
+ std::size_t size() const noexcept;
+ std::size_t length() const noexcept;
+ bool empty() const noexcept;
+
+ const char *c_str() noexcept;
+
+ std::size_t capacity() const noexcept;
+ void reserve(size_t new_cap) noexcept;
+
+ using iterator = char *;
+ iterator begin() noexcept;
+ iterator end() noexcept;
+
+ using const_iterator = const char *;
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept;
+ const_iterator cbegin() const noexcept;
+ const_iterator cend() const noexcept;
+
+ bool operator==(const String &) const noexcept;
+ bool operator!=(const String &) const noexcept;
+ bool operator<(const String &) const noexcept;
+ bool operator<=(const String &) const noexcept;
+ bool operator>(const String &) const noexcept;
+ bool operator>=(const String &) const noexcept;
+
+ void swap(String &) noexcept;
+
+ // Internal API only intended for the cxxbridge code generator.
+ String(unsafe_bitcopy_t, const String &) noexcept;
+
+private:
+ struct lossy_t;
+ String(lossy_t, const char *, std::size_t) noexcept;
+ String(lossy_t, const char16_t *, std::size_t) noexcept;
+ friend void swap(String &lhs, String &rhs) noexcept { lhs.swap(rhs); }
+
+ // Size and alignment statically verified by rust_string.rs.
+ std::array<std::uintptr_t, 3> repr;
+};
+#endif // CXXBRIDGE1_RUST_STRING
+
+#ifndef CXXBRIDGE1_RUST_STR
+#define CXXBRIDGE1_RUST_STR
+// https://cxx.rs/binding/str.html
+class Str final {
+public:
+ Str() noexcept;
+ Str(const String &) noexcept;
+ Str(const std::string &);
+ Str(const char *);
+ Str(const char *, std::size_t);
+
+ Str &operator=(const Str &) &noexcept = default;
+
+ explicit operator std::string() const;
+
+ // Note: no null terminator.
+ const char *data() const noexcept;
+ std::size_t size() const noexcept;
+ std::size_t length() const noexcept;
+ bool empty() const noexcept;
+
+ // Important in order for System V ABI to pass in registers.
+ Str(const Str &) noexcept = default;
+ ~Str() noexcept = default;
+
+ using iterator = const char *;
+ using const_iterator = const char *;
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept;
+ const_iterator cbegin() const noexcept;
+ const_iterator cend() const noexcept;
+
+ bool operator==(const Str &) const noexcept;
+ bool operator!=(const Str &) const noexcept;
+ bool operator<(const Str &) const noexcept;
+ bool operator<=(const Str &) const noexcept;
+ bool operator>(const Str &) const noexcept;
+ bool operator>=(const Str &) const noexcept;
+
+ void swap(Str &) noexcept;
+
+private:
+ class uninit;
+ Str(uninit) noexcept;
+ friend impl<Str>;
+
+ std::array<std::uintptr_t, 2> repr;
+};
+#endif // CXXBRIDGE1_RUST_STR
+
+#ifndef CXXBRIDGE1_RUST_SLICE
+namespace detail {
+template <bool>
+struct copy_assignable_if {};
+
+template <>
+struct copy_assignable_if<false> {
+ copy_assignable_if() noexcept = default;
+ copy_assignable_if(const copy_assignable_if &) noexcept = default;
+ copy_assignable_if &operator=(const copy_assignable_if &) &noexcept = delete;
+ copy_assignable_if &operator=(copy_assignable_if &&) &noexcept = default;
+};
+} // namespace detail
+
+// https://cxx.rs/binding/slice.html
+template <typename T>
+class Slice final
+ : private detail::copy_assignable_if<std::is_const<T>::value> {
+public:
+ using value_type = T;
+
+ Slice() noexcept;
+ Slice(T *, std::size_t count) noexcept;
+
+ Slice &operator=(const Slice<T> &) &noexcept = default;
+ Slice &operator=(Slice<T> &&) &noexcept = default;
+
+ T *data() const noexcept;
+ std::size_t size() const noexcept;
+ std::size_t length() const noexcept;
+ bool empty() const noexcept;
+
+ T &operator[](std::size_t n) const noexcept;
+ T &at(std::size_t n) const;
+ T &front() const noexcept;
+ T &back() const noexcept;
+
+ // Important in order for System V ABI to pass in registers.
+ Slice(const Slice<T> &) noexcept = default;
+ ~Slice() noexcept = default;
+
+ class iterator;
+ iterator begin() const noexcept;
+ iterator end() const noexcept;
+
+ void swap(Slice &) noexcept;
+
+private:
+ class uninit;
+ Slice(uninit) noexcept;
+ friend impl<Slice>;
+ friend void sliceInit(void *, const void *, std::size_t) noexcept;
+ friend void *slicePtr(const void *) noexcept;
+ friend std::size_t sliceLen(const void *) noexcept;
+
+ std::array<std::uintptr_t, 2> repr;
+};
+
+template <typename T>
+class Slice<T>::iterator final {
+public:
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = typename std::add_pointer<T>::type;
+ using reference = typename std::add_lvalue_reference<T>::type;
+
+ reference operator*() const noexcept;
+ pointer operator->() const noexcept;
+ reference operator[](difference_type) const noexcept;
+
+ iterator &operator++() noexcept;
+ iterator operator++(int) noexcept;
+ iterator &operator--() noexcept;
+ iterator operator--(int) noexcept;
+
+ iterator &operator+=(difference_type) noexcept;
+ iterator &operator-=(difference_type) noexcept;
+ iterator operator+(difference_type) const noexcept;
+ iterator operator-(difference_type) const noexcept;
+ difference_type operator-(const iterator &) const noexcept;
+
+ bool operator==(const iterator &) const noexcept;
+ bool operator!=(const iterator &) const noexcept;
+ bool operator<(const iterator &) const noexcept;
+ bool operator<=(const iterator &) const noexcept;
+ bool operator>(const iterator &) const noexcept;
+ bool operator>=(const iterator &) const noexcept;
+
+private:
+ friend class Slice;
+ void *pos;
+ std::size_t stride;
+};
+#endif // CXXBRIDGE1_RUST_SLICE
+
+#ifndef CXXBRIDGE1_RUST_BOX
+// https://cxx.rs/binding/box.html
+template <typename T>
+class Box final {
+public:
+ using element_type = T;
+ using const_pointer =
+ typename std::add_pointer<typename std::add_const<T>::type>::type;
+ using pointer = typename std::add_pointer<T>::type;
+
+ Box() = delete;
+ Box(Box &&) noexcept;
+ ~Box() noexcept;
+
+ explicit Box(const T &);
+ explicit Box(T &&);
+
+ Box &operator=(Box &&) &noexcept;
+
+ const T *operator->() const noexcept;
+ const T &operator*() const noexcept;
+ T *operator->() noexcept;
+ T &operator*() noexcept;
+
+ template <typename... Fields>
+ static Box in_place(Fields &&...);
+
+ void swap(Box &) noexcept;
+
+ // Important: requires that `raw` came from an into_raw call. Do not pass a
+ // pointer from `new` or any other source.
+ static Box from_raw(T *) noexcept;
+
+ T *into_raw() noexcept;
+
+ /* Deprecated */ using value_type = element_type;
+
+private:
+ class uninit;
+ class allocation;
+ Box(uninit) noexcept;
+ void drop() noexcept;
+
+ friend void swap(Box &lhs, Box &rhs) noexcept { lhs.swap(rhs); }
+
+ T *ptr;
+};
+#endif // CXXBRIDGE1_RUST_BOX
+
+#ifndef CXXBRIDGE1_RUST_VEC
+// https://cxx.rs/binding/vec.html
+template <typename T>
+class Vec final {
+public:
+ using value_type = T;
+
+ Vec() noexcept;
+ Vec(std::initializer_list<T>);
+ Vec(const Vec &);
+ Vec(Vec &&) noexcept;
+ ~Vec() noexcept;
+
+ Vec &operator=(Vec &&) &noexcept;
+ Vec &operator=(const Vec &) &;
+
+ std::size_t size() const noexcept;
+ bool empty() const noexcept;
+ const T *data() const noexcept;
+ T *data() noexcept;
+ std::size_t capacity() const noexcept;
+
+ const T &operator[](std::size_t n) const noexcept;
+ const T &at(std::size_t n) const;
+ const T &front() const noexcept;
+ const T &back() const noexcept;
+
+ T &operator[](std::size_t n) noexcept;
+ T &at(std::size_t n);
+ T &front() noexcept;
+ T &back() noexcept;
+
+ void reserve(std::size_t new_cap);
+ void push_back(const T &value);
+ void push_back(T &&value);
+ template <typename... Args>
+ void emplace_back(Args &&...args);
+ void truncate(std::size_t len);
+ void clear();
+
+ using iterator = typename Slice<T>::iterator;
+ iterator begin() noexcept;
+ iterator end() noexcept;
+
+ using const_iterator = typename Slice<const T>::iterator;
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept;
+ const_iterator cbegin() const noexcept;
+ const_iterator cend() const noexcept;
+
+ void swap(Vec &) noexcept;
+
+ // Internal API only intended for the cxxbridge code generator.
+ Vec(unsafe_bitcopy_t, const Vec &) noexcept;
+
+private:
+ void reserve_total(std::size_t new_cap) noexcept;
+ void set_len(std::size_t len) noexcept;
+ void drop() noexcept;
+
+ friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); }
+
+ // Size and alignment statically verified by rust_vec.rs.
+ std::array<std::uintptr_t, 3> repr;
+};
+#endif // CXXBRIDGE1_RUST_VEC
+
+#ifndef CXXBRIDGE1_RUST_FN
+// https://cxx.rs/binding/fn.html
+template <typename Signature>
+class Fn;
+
+template <typename Ret, typename... Args>
+class Fn<Ret(Args...)> final {
+public:
+ Ret operator()(Args... args) const noexcept;
+ Fn operator*() const noexcept;
+
+private:
+ Ret (*trampoline)(Args..., void *fn) noexcept;
+ void *fn;
+};
+#endif // CXXBRIDGE1_RUST_FN
+
+#ifndef CXXBRIDGE1_RUST_ERROR
+#define CXXBRIDGE1_RUST_ERROR
+// https://cxx.rs/binding/result.html
+class Error final : public std::exception {
+public:
+ Error(const Error &);
+ Error(Error &&) noexcept;
+ ~Error() noexcept override;
+
+ Error &operator=(const Error &) &;
+ Error &operator=(Error &&) &noexcept;
+
+ const char *what() const noexcept override;
+
+private:
+ Error() noexcept = default;
+ friend impl<Error>;
+ const char *msg;
+ std::size_t len;
+};
+#endif // CXXBRIDGE1_RUST_ERROR
+
+#ifndef CXXBRIDGE1_RUST_ISIZE
+#define CXXBRIDGE1_RUST_ISIZE
+#if defined(_WIN32)
+using isize = SSIZE_T;
+#else
+using isize = ssize_t;
+#endif
+#endif // CXXBRIDGE1_RUST_ISIZE
+
+std::ostream &operator<<(std::ostream &, const String &);
+std::ostream &operator<<(std::ostream &, const Str &);
+
+#ifndef CXXBRIDGE1_RUST_OPAQUE
+#define CXXBRIDGE1_RUST_OPAQUE
+// Base class of generated opaque Rust types.
+class Opaque {
+public:
+ Opaque() = delete;
+ Opaque(const Opaque &) = delete;
+ ~Opaque() = delete;
+};
+#endif // CXXBRIDGE1_RUST_OPAQUE
+
+template <typename T>
+std::size_t size_of();
+template <typename T>
+std::size_t align_of();
+
+// IsRelocatable<T> is used in assertions that a C++ type passed by value
+// between Rust and C++ is soundly relocatable by Rust.
+//
+// There may be legitimate reasons to opt out of the check for support of types
+// that the programmer knows are soundly Rust-movable despite not being
+// recognized as such by the C++ type system due to a move constructor or
+// destructor. To opt out of the relocatability check, do either of the
+// following things in any header used by `include!` in the bridge.
+//
+// --- if you define the type:
+// struct MyType {
+// ...
+// + using IsRelocatable = std::true_type;
+// };
+//
+// --- otherwise:
+// + template <>
+// + struct rust::IsRelocatable<MyType> : std::true_type {};
+template <typename T>
+struct IsRelocatable;
+
+using u8 = std::uint8_t;
+using u16 = std::uint16_t;
+using u32 = std::uint32_t;
+using u64 = std::uint64_t;
+using usize = std::size_t; // see static asserts in cxx.cc
+using i8 = std::int8_t;
+using i16 = std::int16_t;
+using i32 = std::int32_t;
+using i64 = std::int64_t;
+using f32 = float;
+using f64 = double;
+
+// Snake case aliases for use in code that uses this style for type names.
+using string = String;
+using str = Str;
+template <typename T>
+using slice = Slice<T>;
+template <typename T>
+using box = Box<T>;
+template <typename T>
+using vec = Vec<T>;
+using error = Error;
+template <typename Signature>
+using fn = Fn<Signature>;
+template <typename T>
+using is_relocatable = IsRelocatable<T>;
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+/// end public API, begin implementation details
+
+#ifndef CXXBRIDGE1_PANIC
+#define CXXBRIDGE1_PANIC
+template <typename Exception>
+void panic [[noreturn]] (const char *msg);
+#endif // CXXBRIDGE1_PANIC
+
+#ifndef CXXBRIDGE1_RUST_FN
+#define CXXBRIDGE1_RUST_FN
+template <typename Ret, typename... Args>
+Ret Fn<Ret(Args...)>::operator()(Args... args) const noexcept {
+ return (*this->trampoline)(std::forward<Args>(args)..., this->fn);
+}
+
+template <typename Ret, typename... Args>
+Fn<Ret(Args...)> Fn<Ret(Args...)>::operator*() const noexcept {
+ return *this;
+}
+#endif // CXXBRIDGE1_RUST_FN
+
+#ifndef CXXBRIDGE1_RUST_BITCOPY_T
+#define CXXBRIDGE1_RUST_BITCOPY_T
+struct unsafe_bitcopy_t final {
+ explicit unsafe_bitcopy_t() = default;
+};
+#endif // CXXBRIDGE1_RUST_BITCOPY_T
+
+#ifndef CXXBRIDGE1_RUST_BITCOPY
+#define CXXBRIDGE1_RUST_BITCOPY
+constexpr unsafe_bitcopy_t unsafe_bitcopy{};
+#endif // CXXBRIDGE1_RUST_BITCOPY
+
+#ifndef CXXBRIDGE1_RUST_SLICE
+#define CXXBRIDGE1_RUST_SLICE
+template <typename T>
+Slice<T>::Slice() noexcept {
+ sliceInit(this, reinterpret_cast<void *>(align_of<T>()), 0);
+}
+
+template <typename T>
+Slice<T>::Slice(T *s, std::size_t count) noexcept {
+ assert(s != nullptr || count == 0);
+ sliceInit(this,
+ s == nullptr && count == 0
+ ? reinterpret_cast<void *>(align_of<T>())
+ : const_cast<typename std::remove_const<T>::type *>(s),
+ count);
+}
+
+template <typename T>
+T *Slice<T>::data() const noexcept {
+ return reinterpret_cast<T *>(slicePtr(this));
+}
+
+template <typename T>
+std::size_t Slice<T>::size() const noexcept {
+ return sliceLen(this);
+}
+
+template <typename T>
+std::size_t Slice<T>::length() const noexcept {
+ return this->size();
+}
+
+template <typename T>
+bool Slice<T>::empty() const noexcept {
+ return this->size() == 0;
+}
+
+template <typename T>
+T &Slice<T>::operator[](std::size_t n) const noexcept {
+ assert(n < this->size());
+ auto ptr = static_cast<char *>(slicePtr(this)) + size_of<T>() * n;
+ return *reinterpret_cast<T *>(ptr);
+}
+
+template <typename T>
+T &Slice<T>::at(std::size_t n) const {
+ if (n >= this->size()) {
+ panic<std::out_of_range>("rust::Slice index out of range");
+ }
+ return (*this)[n];
+}
+
+template <typename T>
+T &Slice<T>::front() const noexcept {
+ assert(!this->empty());
+ return (*this)[0];
+}
+
+template <typename T>
+T &Slice<T>::back() const noexcept {
+ assert(!this->empty());
+ return (*this)[this->size() - 1];
+}
+
+template <typename T>
+typename Slice<T>::iterator::reference
+Slice<T>::iterator::operator*() const noexcept {
+ return *static_cast<T *>(this->pos);
+}
+
+template <typename T>
+typename Slice<T>::iterator::pointer
+Slice<T>::iterator::operator->() const noexcept {
+ return static_cast<T *>(this->pos);
+}
+
+template <typename T>
+typename Slice<T>::iterator::reference Slice<T>::iterator::operator[](
+ typename Slice<T>::iterator::difference_type n) const noexcept {
+ auto ptr = static_cast<char *>(this->pos) + this->stride * n;
+ return *reinterpret_cast<T *>(ptr);
+}
+
+template <typename T>
+typename Slice<T>::iterator &Slice<T>::iterator::operator++() noexcept {
+ this->pos = static_cast<char *>(this->pos) + this->stride;
+ return *this;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::iterator::operator++(int) noexcept {
+ auto ret = iterator(*this);
+ this->pos = static_cast<char *>(this->pos) + this->stride;
+ return ret;
+}
+
+template <typename T>
+typename Slice<T>::iterator &Slice<T>::iterator::operator--() noexcept {
+ this->pos = static_cast<char *>(this->pos) - this->stride;
+ return *this;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::iterator::operator--(int) noexcept {
+ auto ret = iterator(*this);
+ this->pos = static_cast<char *>(this->pos) - this->stride;
+ return ret;
+}
+
+template <typename T>
+typename Slice<T>::iterator &Slice<T>::iterator::operator+=(
+ typename Slice<T>::iterator::difference_type n) noexcept {
+ this->pos = static_cast<char *>(this->pos) + this->stride * n;
+ return *this;
+}
+
+template <typename T>
+typename Slice<T>::iterator &Slice<T>::iterator::operator-=(
+ typename Slice<T>::iterator::difference_type n) noexcept {
+ this->pos = static_cast<char *>(this->pos) - this->stride * n;
+ return *this;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::iterator::operator+(
+ typename Slice<T>::iterator::difference_type n) const noexcept {
+ auto ret = iterator(*this);
+ ret.pos = static_cast<char *>(this->pos) + this->stride * n;
+ return ret;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::iterator::operator-(
+ typename Slice<T>::iterator::difference_type n) const noexcept {
+ auto ret = iterator(*this);
+ ret.pos = static_cast<char *>(this->pos) - this->stride * n;
+ return ret;
+}
+
+template <typename T>
+typename Slice<T>::iterator::difference_type
+Slice<T>::iterator::operator-(const iterator &other) const noexcept {
+ auto diff = std::distance(static_cast<char *>(other.pos),
+ static_cast<char *>(this->pos));
+ return diff / this->stride;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator==(const iterator &other) const noexcept {
+ return this->pos == other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator!=(const iterator &other) const noexcept {
+ return this->pos != other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator<(const iterator &other) const noexcept {
+ return this->pos < other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator<=(const iterator &other) const noexcept {
+ return this->pos <= other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator>(const iterator &other) const noexcept {
+ return this->pos > other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator>=(const iterator &other) const noexcept {
+ return this->pos >= other.pos;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::begin() const noexcept {
+ iterator it;
+ it.pos = slicePtr(this);
+ it.stride = size_of<T>();
+ return it;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::end() const noexcept {
+ iterator it = this->begin();
+ it.pos = static_cast<char *>(it.pos) + it.stride * this->size();
+ return it;
+}
+
+template <typename T>
+void Slice<T>::swap(Slice &rhs) noexcept {
+ std::swap(*this, rhs);
+}
+#endif // CXXBRIDGE1_RUST_SLICE
+
+#ifndef CXXBRIDGE1_RUST_BOX
+#define CXXBRIDGE1_RUST_BOX
+template <typename T>
+class Box<T>::uninit {};
+
+template <typename T>
+class Box<T>::allocation {
+ static T *alloc() noexcept;
+ static void dealloc(T *) noexcept;
+
+public:
+ allocation() noexcept : ptr(alloc()) {}
+ ~allocation() noexcept {
+ if (this->ptr) {
+ dealloc(this->ptr);
+ }
+ }
+ T *ptr;
+};
+
+template <typename T>
+Box<T>::Box(Box &&other) noexcept : ptr(other.ptr) {
+ other.ptr = nullptr;
+}
+
+template <typename T>
+Box<T>::Box(const T &val) {
+ allocation alloc;
+ ::new (alloc.ptr) T(val);
+ this->ptr = alloc.ptr;
+ alloc.ptr = nullptr;
+}
+
+template <typename T>
+Box<T>::Box(T &&val) {
+ allocation alloc;
+ ::new (alloc.ptr) T(std::move(val));
+ this->ptr = alloc.ptr;
+ alloc.ptr = nullptr;
+}
+
+template <typename T>
+Box<T>::~Box() noexcept {
+ if (this->ptr) {
+ this->drop();
+ }
+}
+
+template <typename T>
+Box<T> &Box<T>::operator=(Box &&other) &noexcept {
+ if (this->ptr) {
+ this->drop();
+ }
+ this->ptr = other.ptr;
+ other.ptr = nullptr;
+ return *this;
+}
+
+template <typename T>
+const T *Box<T>::operator->() const noexcept {
+ return this->ptr;
+}
+
+template <typename T>
+const T &Box<T>::operator*() const noexcept {
+ return *this->ptr;
+}
+
+template <typename T>
+T *Box<T>::operator->() noexcept {
+ return this->ptr;
+}
+
+template <typename T>
+T &Box<T>::operator*() noexcept {
+ return *this->ptr;
+}
+
+template <typename T>
+template <typename... Fields>
+Box<T> Box<T>::in_place(Fields &&...fields) {
+ allocation alloc;
+ auto ptr = alloc.ptr;
+ ::new (ptr) T{std::forward<Fields>(fields)...};
+ alloc.ptr = nullptr;
+ return from_raw(ptr);
+}
+
+template <typename T>
+void Box<T>::swap(Box &rhs) noexcept {
+ using std::swap;
+ swap(this->ptr, rhs.ptr);
+}
+
+template <typename T>
+Box<T> Box<T>::from_raw(T *raw) noexcept {
+ Box box = uninit{};
+ box.ptr = raw;
+ return box;
+}
+
+template <typename T>
+T *Box<T>::into_raw() noexcept {
+ T *raw = this->ptr;
+ this->ptr = nullptr;
+ return raw;
+}
+
+template <typename T>
+Box<T>::Box(uninit) noexcept {}
+#endif // CXXBRIDGE1_RUST_BOX
+
+#ifndef CXXBRIDGE1_RUST_VEC
+#define CXXBRIDGE1_RUST_VEC
+template <typename T>
+Vec<T>::Vec(std::initializer_list<T> init) : Vec{} {
+ this->reserve_total(init.size());
+ std::move(init.begin(), init.end(), std::back_inserter(*this));
+}
+
+template <typename T>
+Vec<T>::Vec(const Vec &other) : Vec() {
+ this->reserve_total(other.size());
+ std::copy(other.begin(), other.end(), std::back_inserter(*this));
+}
+
+template <typename T>
+Vec<T>::Vec(Vec &&other) noexcept : repr(other.repr) {
+ new (&other) Vec();
+}
+
+template <typename T>
+Vec<T>::~Vec() noexcept {
+ this->drop();
+}
+
+template <typename T>
+Vec<T> &Vec<T>::operator=(Vec &&other) &noexcept {
+ this->drop();
+ this->repr = other.repr;
+ new (&other) Vec();
+ return *this;
+}
+
+template <typename T>
+Vec<T> &Vec<T>::operator=(const Vec &other) & {
+ if (this != &other) {
+ this->drop();
+ new (this) Vec(other);
+ }
+ return *this;
+}
+
+template <typename T>
+bool Vec<T>::empty() const noexcept {
+ return this->size() == 0;
+}
+
+template <typename T>
+T *Vec<T>::data() noexcept {
+ return const_cast<T *>(const_cast<const Vec<T> *>(this)->data());
+}
+
+template <typename T>
+const T &Vec<T>::operator[](std::size_t n) const noexcept {
+ assert(n < this->size());
+ auto data = reinterpret_cast<const char *>(this->data());
+ return *reinterpret_cast<const T *>(data + n * size_of<T>());
+}
+
+template <typename T>
+const T &Vec<T>::at(std::size_t n) const {
+ if (n >= this->size()) {
+ panic<std::out_of_range>("rust::Vec index out of range");
+ }
+ return (*this)[n];
+}
+
+template <typename T>
+const T &Vec<T>::front() const noexcept {
+ assert(!this->empty());
+ return (*this)[0];
+}
+
+template <typename T>
+const T &Vec<T>::back() const noexcept {
+ assert(!this->empty());
+ return (*this)[this->size() - 1];
+}
+
+template <typename T>
+T &Vec<T>::operator[](std::size_t n) noexcept {
+ assert(n < this->size());
+ auto data = reinterpret_cast<char *>(this->data());
+ return *reinterpret_cast<T *>(data + n * size_of<T>());
+}
+
+template <typename T>
+T &Vec<T>::at(std::size_t n) {
+ if (n >= this->size()) {
+ panic<std::out_of_range>("rust::Vec index out of range");
+ }
+ return (*this)[n];
+}
+
+template <typename T>
+T &Vec<T>::front() noexcept {
+ assert(!this->empty());
+ return (*this)[0];
+}
+
+template <typename T>
+T &Vec<T>::back() noexcept {
+ assert(!this->empty());
+ return (*this)[this->size() - 1];
+}
+
+template <typename T>
+void Vec<T>::reserve(std::size_t new_cap) {
+ this->reserve_total(new_cap);
+}
+
+template <typename T>
+void Vec<T>::push_back(const T &value) {
+ this->emplace_back(value);
+}
+
+template <typename T>
+void Vec<T>::push_back(T &&value) {
+ this->emplace_back(std::move(value));
+}
+
+template <typename T>
+template <typename... Args>
+void Vec<T>::emplace_back(Args &&...args) {
+ auto size = this->size();
+ this->reserve_total(size + 1);
+ ::new (reinterpret_cast<T *>(reinterpret_cast<char *>(this->data()) +
+ size * size_of<T>()))
+ T(std::forward<Args>(args)...);
+ this->set_len(size + 1);
+}
+
+template <typename T>
+void Vec<T>::clear() {
+ this->truncate(0);
+}
+
+template <typename T>
+typename Vec<T>::iterator Vec<T>::begin() noexcept {
+ return Slice<T>(this->data(), this->size()).begin();
+}
+
+template <typename T>
+typename Vec<T>::iterator Vec<T>::end() noexcept {
+ return Slice<T>(this->data(), this->size()).end();
+}
+
+template <typename T>
+typename Vec<T>::const_iterator Vec<T>::begin() const noexcept {
+ return this->cbegin();
+}
+
+template <typename T>
+typename Vec<T>::const_iterator Vec<T>::end() const noexcept {
+ return this->cend();
+}
+
+template <typename T>
+typename Vec<T>::const_iterator Vec<T>::cbegin() const noexcept {
+ return Slice<const T>(this->data(), this->size()).begin();
+}
+
+template <typename T>
+typename Vec<T>::const_iterator Vec<T>::cend() const noexcept {
+ return Slice<const T>(this->data(), this->size()).end();
+}
+
+template <typename T>
+void Vec<T>::swap(Vec &rhs) noexcept {
+ using std::swap;
+ swap(this->repr, rhs.repr);
+}
+
+// Internal API only intended for the cxxbridge code generator.
+template <typename T>
+Vec<T>::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {}
+#endif // CXXBRIDGE1_RUST_VEC
+
+#ifndef CXXBRIDGE1_IS_COMPLETE
+#define CXXBRIDGE1_IS_COMPLETE
+namespace detail {
+namespace {
+template <typename T, typename = std::size_t>
+struct is_complete : std::false_type {};
+template <typename T>
+struct is_complete<T, decltype(sizeof(T))> : std::true_type {};
+} // namespace
+} // namespace detail
+#endif // CXXBRIDGE1_IS_COMPLETE
+
+#ifndef CXXBRIDGE1_LAYOUT
+#define CXXBRIDGE1_LAYOUT
+class layout {
+ template <typename T>
+ friend std::size_t size_of();
+ template <typename T>
+ friend std::size_t align_of();
+ template <typename T>
+ static typename std::enable_if<std::is_base_of<Opaque, T>::value,
+ std::size_t>::type
+ do_size_of() {
+ return T::layout::size();
+ }
+ template <typename T>
+ static typename std::enable_if<!std::is_base_of<Opaque, T>::value,
+ std::size_t>::type
+ do_size_of() {
+ return sizeof(T);
+ }
+ template <typename T>
+ static
+ typename std::enable_if<detail::is_complete<T>::value, std::size_t>::type
+ size_of() {
+ return do_size_of<T>();
+ }
+ template <typename T>
+ static typename std::enable_if<std::is_base_of<Opaque, T>::value,
+ std::size_t>::type
+ do_align_of() {
+ return T::layout::align();
+ }
+ template <typename T>
+ static typename std::enable_if<!std::is_base_of<Opaque, T>::value,
+ std::size_t>::type
+ do_align_of() {
+ return alignof(T);
+ }
+ template <typename T>
+ static
+ typename std::enable_if<detail::is_complete<T>::value, std::size_t>::type
+ align_of() {
+ return do_align_of<T>();
+ }
+};
+
+template <typename T>
+std::size_t size_of() {
+ return layout::size_of<T>();
+}
+
+template <typename T>
+std::size_t align_of() {
+ return layout::align_of<T>();
+}
+#endif // CXXBRIDGE1_LAYOUT
+
+#ifndef CXXBRIDGE1_RELOCATABLE
+#define CXXBRIDGE1_RELOCATABLE
+namespace detail {
+template <typename... Ts>
+struct make_void {
+ using type = void;
+};
+
+template <typename... Ts>
+using void_t = typename make_void<Ts...>::type;
+
+template <typename Void, template <typename...> class, typename...>
+struct detect : std::false_type {};
+template <template <typename...> class T, typename... A>
+struct detect<void_t<T<A...>>, T, A...> : std::true_type {};
+
+template <template <typename...> class T, typename... A>
+using is_detected = detect<void, T, A...>;
+
+template <typename T>
+using detect_IsRelocatable = typename T::IsRelocatable;
+
+template <typename T>
+struct get_IsRelocatable
+ : std::is_same<typename T::IsRelocatable, std::true_type> {};
+} // namespace detail
+
+template <typename T>
+struct IsRelocatable
+ : std::conditional<
+ detail::is_detected<detail::detect_IsRelocatable, T>::value,
+ detail::get_IsRelocatable<T>,
+ std::integral_constant<
+ bool, std::is_trivially_move_constructible<T>::value &&
+ std::is_trivially_destructible<T>::value>>::type {};
+#endif // CXXBRIDGE1_RELOCATABLE
+
+} // namespace cxxbridge1
+} // namespace rust
--- /dev/null
+// Functionality that is shared between the cxx_build::bridge entry point and
+// the cxxbridge CLI command.
+
+mod block;
+mod builtin;
+mod check;
+pub(super) mod error;
+mod file;
+pub(super) mod fs;
+mod ifndef;
+pub(super) mod include;
+mod namespace;
+mod nested;
+pub(super) mod out;
+mod write;
+
+pub(super) use self::error::Error;
+use self::error::{format_err, Result};
+use self::file::File;
+use self::include::Include;
+use crate::syntax::report::Errors;
+use crate::syntax::{self, Types};
+use std::path::Path;
+
+/// Options for C++ code generation.
+///
+/// We expect options to be added over time, so this is a non-exhaustive struct.
+/// To instantiate one you need to crate a default value and mutate those fields
+/// that you want to modify.
+///
+/// ```
+/// # use cxx_gen::Opt;
+/// #
+/// let impl_annotations = r#"__attribute__((visibility("default")))"#.to_owned();
+///
+/// let mut opt = Opt::default();
+/// opt.cxx_impl_annotations = Some(impl_annotations);
+/// ```
+#[non_exhaustive]
+pub struct Opt {
+ /// Any additional headers to #include. The cxxbridge tool does not parse or
+ /// even require the given paths to exist; they simply go into the generated
+ /// C++ code as #include lines.
+ pub include: Vec<Include>,
+ /// Optional annotation for implementations of C++ function wrappers that
+ /// may be exposed to Rust. You may for example need to provide
+ /// `__declspec(dllexport)` or `__attribute__((visibility("default")))` if
+ /// Rust code from one shared object or executable depends on these C++
+ /// functions in another.
+ pub cxx_impl_annotations: Option<String>,
+
+ pub(super) gen_header: bool,
+ pub(super) gen_implementation: bool,
+ pub(super) allow_dot_includes: bool,
+}
+
+/// Results of code generation.
+#[derive(Default)]
+pub struct GeneratedCode {
+ /// The bytes of a C++ header file.
+ pub header: Vec<u8>,
+ /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.)
+ pub implementation: Vec<u8>,
+}
+
+impl Default for Opt {
+ fn default() -> Self {
+ Opt {
+ include: Vec::new(),
+ cxx_impl_annotations: None,
+ gen_header: true,
+ gen_implementation: true,
+ allow_dot_includes: true,
+ }
+ }
+}
+
+pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode {
+ let source = match read_to_string(path) {
+ Ok(source) => source,
+ Err(err) => format_err(path, "", err),
+ };
+ match generate_from_string(&source, opt) {
+ Ok(out) => out,
+ Err(err) => format_err(path, &source, err),
+ }
+}
+
+fn read_to_string(path: &Path) -> Result<String> {
+ let bytes = if path == Path::new("-") {
+ fs::read_stdin()
+ } else {
+ fs::read(path)
+ }?;
+ match String::from_utf8(bytes) {
+ Ok(string) => Ok(string),
+ Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())),
+ }
+}
+
+fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
+ let mut source = source;
+ if source.starts_with("#!") && !source.starts_with("#![") {
+ let shebang_end = source.find('\n').unwrap_or(source.len());
+ source = &source[shebang_end..];
+ }
+ proc_macro2::fallback::force();
+ let syntax: File = syn::parse_str(source)?;
+ generate(syntax, opt)
+}
+
+pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
+ if syntax.modules.is_empty() {
+ return Err(Error::NoBridgeMod);
+ }
+
+ let ref mut apis = Vec::new();
+ let ref mut errors = Errors::new();
+ for bridge in syntax.modules {
+ let ref namespace = bridge.namespace;
+ let trusted = bridge.unsafety.is_some();
+ apis.extend(syntax::parse_items(
+ errors,
+ bridge.content,
+ trusted,
+ namespace,
+ ));
+ }
+
+ let ref types = Types::collect(errors, apis);
+ check::precheck(errors, apis, opt);
+ errors.propagate()?;
+ let generator = check::Generator::Build;
+ check::typecheck(errors, apis, types, generator);
+ errors.propagate()?;
+
+ // Some callers may wish to generate both header and implementation from the
+ // same token stream to avoid parsing twice. Others only need to generate
+ // one or the other.
+ let (mut header, mut implementation) = Default::default();
+ if opt.gen_header {
+ header = write::gen(apis, types, opt, true);
+ }
+ if opt.gen_implementation {
+ implementation = write::gen(apis, types, opt, false);
+ }
+ Ok(GeneratedCode {
+ header,
+ implementation,
+ })
+}
--- /dev/null
+use crate::syntax::namespace::Namespace;
+use crate::syntax::Api;
+
+impl Api {
+ pub fn namespace(&self) -> &Namespace {
+ match self {
+ Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.name.namespace,
+ Api::CxxType(ety) | Api::RustType(ety) => &ety.name.namespace,
+ Api::Enum(enm) => &enm.name.namespace,
+ Api::Struct(strct) => &strct.name.namespace,
+ Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => Default::default(),
+ }
+ }
+}
--- /dev/null
+use crate::syntax::map::UnorderedMap as Map;
+use crate::syntax::Api;
+use proc_macro2::Ident;
+
+pub struct NamespaceEntries<'a> {
+ direct: Vec<&'a Api>,
+ nested: Vec<(&'a Ident, NamespaceEntries<'a>)>,
+}
+
+impl<'a> NamespaceEntries<'a> {
+ pub fn new(apis: Vec<&'a Api>) -> Self {
+ sort_by_inner_namespace(apis, 0)
+ }
+
+ pub fn direct_content(&self) -> &[&'a Api] {
+ &self.direct
+ }
+
+ pub fn nested_content(&self) -> impl Iterator<Item = (&'a Ident, &NamespaceEntries<'a>)> {
+ self.nested.iter().map(|(k, entries)| (*k, entries))
+ }
+}
+
+fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries {
+ let mut direct = Vec::new();
+ let mut nested_namespaces = Vec::new();
+ let mut index_of_namespace = Map::new();
+
+ for api in &apis {
+ if let Some(first_ns_elem) = api.namespace().iter().nth(depth) {
+ match index_of_namespace.get(first_ns_elem) {
+ None => {
+ index_of_namespace.insert(first_ns_elem, nested_namespaces.len());
+ nested_namespaces.push((first_ns_elem, vec![*api]));
+ }
+ Some(&index) => nested_namespaces[index].1.push(*api),
+ }
+ continue;
+ }
+ direct.push(*api);
+ }
+
+ let nested = nested_namespaces
+ .into_iter()
+ .map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1)))
+ .collect();
+
+ NamespaceEntries { direct, nested }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::NamespaceEntries;
+ use crate::syntax::attrs::OtherAttrs;
+ use crate::syntax::namespace::Namespace;
+ use crate::syntax::{Api, Doc, ExternType, ForeignName, Lang, Lifetimes, Pair};
+ use proc_macro2::{Ident, Span};
+ use std::iter::FromIterator;
+ use syn::punctuated::Punctuated;
+ use syn::Token;
+
+ #[test]
+ fn test_ns_entries_sort() {
+ let apis = &[
+ make_api(None, "C"),
+ make_api(None, "A"),
+ make_api(Some("G"), "E"),
+ make_api(Some("D"), "F"),
+ make_api(Some("G"), "H"),
+ make_api(Some("D::K"), "L"),
+ make_api(Some("D::K"), "M"),
+ make_api(None, "B"),
+ make_api(Some("D"), "I"),
+ make_api(Some("D"), "J"),
+ ];
+
+ let root = NamespaceEntries::new(Vec::from_iter(apis));
+
+ // ::
+ let root_direct = root.direct_content();
+ assert_eq!(root_direct.len(), 3);
+ assert_ident(root_direct[0], "C");
+ assert_ident(root_direct[1], "A");
+ assert_ident(root_direct[2], "B");
+
+ let mut root_nested = root.nested_content();
+ let (id, g) = root_nested.next().unwrap();
+ assert_eq!(id, "G");
+ let (id, d) = root_nested.next().unwrap();
+ assert_eq!(id, "D");
+ assert!(root_nested.next().is_none());
+
+ // ::G
+ let g_direct = g.direct_content();
+ assert_eq!(g_direct.len(), 2);
+ assert_ident(g_direct[0], "E");
+ assert_ident(g_direct[1], "H");
+
+ let mut g_nested = g.nested_content();
+ assert!(g_nested.next().is_none());
+
+ // ::D
+ let d_direct = d.direct_content();
+ assert_eq!(d_direct.len(), 3);
+ assert_ident(d_direct[0], "F");
+ assert_ident(d_direct[1], "I");
+ assert_ident(d_direct[2], "J");
+
+ let mut d_nested = d.nested_content();
+ let (id, k) = d_nested.next().unwrap();
+ assert_eq!(id, "K");
+
+ // ::D::K
+ let k_direct = k.direct_content();
+ assert_eq!(k_direct.len(), 2);
+ assert_ident(k_direct[0], "L");
+ assert_ident(k_direct[1], "M");
+ }
+
+ fn assert_ident(api: &Api, expected: &str) {
+ if let Api::CxxType(cxx_type) = api {
+ assert_eq!(cxx_type.name.cxx.to_string(), expected);
+ } else {
+ unreachable!()
+ }
+ }
+
+ fn make_api(ns: Option<&str>, ident: &str) -> Api {
+ let ns = ns.map_or(Namespace::ROOT, |ns| syn::parse_str(ns).unwrap());
+ Api::CxxType(ExternType {
+ lang: Lang::Rust,
+ doc: Doc::new(),
+ derives: Vec::new(),
+ attrs: OtherAttrs::none(),
+ visibility: Token),
+ type_token: Token),
+ name: Pair {
+ namespace: ns,
+ cxx: ForeignName::parse(ident, Span::call_site()).unwrap(),
+ rust: Ident::new(ident, Span::call_site()),
+ },
+ generics: Lifetimes {
+ lt_token: None,
+ lifetimes: Punctuated::new(),
+ gt_token: None,
+ },
+ colon_token: None,
+ bounds: Vec::new(),
+ semi_token: Token),
+ trusted: false,
+ })
+ }
+}
--- /dev/null
+use crate::gen::block::Block;
+use crate::gen::builtin::Builtins;
+use crate::gen::include::Includes;
+use crate::gen::Opt;
+use crate::syntax::namespace::Namespace;
+use crate::syntax::Types;
+use std::cell::RefCell;
+use std::fmt::{self, Arguments, Write};
+
+pub(crate) struct OutFile<'a> {
+ pub header: bool,
+ pub opt: &'a Opt,
+ pub types: &'a Types<'a>,
+ pub include: Includes<'a>,
+ pub builtin: Builtins<'a>,
+ content: RefCell<Content<'a>>,
+}
+
+#[derive(Default)]
+pub struct Content<'a> {
+ bytes: String,
+ namespace: &'a Namespace,
+ blocks: Vec<BlockBoundary<'a>>,
+ section_pending: bool,
+ blocks_pending: usize,
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+enum BlockBoundary<'a> {
+ Begin(Block<'a>),
+ End(Block<'a>),
+}
+
+impl<'a> OutFile<'a> {
+ pub fn new(header: bool, opt: &'a Opt, types: &'a Types) -> Self {
+ OutFile {
+ header,
+ opt,
+ types,
+ include: Includes::new(),
+ builtin: Builtins::new(),
+ content: RefCell::new(Content::new()),
+ }
+ }
+
+ // Write a blank line if the preceding section had any contents.
+ pub fn next_section(&mut self) {
+ self.content.get_mut().next_section();
+ }
+
+ pub fn begin_block(&mut self, block: Block<'a>) {
+ self.content.get_mut().begin_block(block);
+ }
+
+ pub fn end_block(&mut self, block: Block<'a>) {
+ self.content.get_mut().end_block(block);
+ }
+
+ pub fn set_namespace(&mut self, namespace: &'a Namespace) {
+ self.content.get_mut().set_namespace(namespace);
+ }
+
+ pub fn write_fmt(&self, args: Arguments) {
+ let content = &mut *self.content.borrow_mut();
+ Write::write_fmt(content, args).unwrap();
+ }
+
+ pub fn content(&mut self) -> Vec<u8> {
+ self.flush();
+ let include = &self.include.content.bytes;
+ let builtin = &self.builtin.content.bytes;
+ let content = &self.content.get_mut().bytes;
+ let len = include.len() + builtin.len() + content.len() + 2;
+ let mut out = String::with_capacity(len);
+ out.push_str(include);
+ if !out.is_empty() && !builtin.is_empty() {
+ out.push('\n');
+ }
+ out.push_str(builtin);
+ if !out.is_empty() && !content.is_empty() {
+ out.push('\n');
+ }
+ out.push_str(content);
+ if out.is_empty() {
+ out.push_str("// empty\n");
+ }
+ out.into_bytes()
+ }
+
+ fn flush(&mut self) {
+ self.include.content.flush();
+ self.builtin.content.flush();
+ self.content.get_mut().flush();
+ }
+}
+
+impl<'a> Write for Content<'a> {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ self.write(s);
+ Ok(())
+ }
+}
+
+impl<'a> PartialEq for Content<'a> {
+ fn eq(&self, _other: &Self) -> bool {
+ true
+ }
+}
+
+impl<'a> Content<'a> {
+ fn new() -> Self {
+ Content::default()
+ }
+
+ pub fn next_section(&mut self) {
+ self.section_pending = true;
+ }
+
+ pub fn begin_block(&mut self, block: Block<'a>) {
+ self.push_block_boundary(BlockBoundary::Begin(block));
+ }
+
+ pub fn end_block(&mut self, block: Block<'a>) {
+ self.push_block_boundary(BlockBoundary::End(block));
+ }
+
+ pub fn set_namespace(&mut self, namespace: &'a Namespace) {
+ for name in self.namespace.iter().rev() {
+ self.end_block(Block::UserDefinedNamespace(name));
+ }
+ for name in namespace {
+ self.begin_block(Block::UserDefinedNamespace(name));
+ }
+ self.namespace = namespace;
+ }
+
+ pub fn write_fmt(&mut self, args: Arguments) {
+ Write::write_fmt(self, args).unwrap();
+ }
+
+ fn write(&mut self, b: &str) {
+ if !b.is_empty() {
+ if self.blocks_pending > 0 {
+ self.flush_blocks();
+ }
+ if self.section_pending && !self.bytes.is_empty() {
+ self.bytes.push('\n');
+ }
+ self.bytes.push_str(b);
+ self.section_pending = false;
+ self.blocks_pending = 0;
+ }
+ }
+
+ fn push_block_boundary(&mut self, boundary: BlockBoundary<'a>) {
+ if self.blocks_pending > 0 && boundary == self.blocks.last().unwrap().rev() {
+ self.blocks.pop();
+ self.blocks_pending -= 1;
+ } else {
+ self.blocks.push(boundary);
+ self.blocks_pending += 1;
+ }
+ }
+
+ fn flush(&mut self) {
+ self.set_namespace(Default::default());
+ if self.blocks_pending > 0 {
+ self.flush_blocks();
+ }
+ }
+
+ fn flush_blocks(&mut self) {
+ self.section_pending = !self.bytes.is_empty();
+ let mut read = self.blocks.len() - self.blocks_pending;
+ let mut write = read;
+
+ while read < self.blocks.len() {
+ match self.blocks[read] {
+ BlockBoundary::Begin(begin_block) => {
+ if self.section_pending {
+ self.bytes.push('\n');
+ self.section_pending = false;
+ }
+ Block::write_begin(begin_block, &mut self.bytes);
+ self.blocks[write] = BlockBoundary::Begin(begin_block);
+ write += 1;
+ }
+ BlockBoundary::End(end_block) => {
+ write = write.checked_sub(1).unwrap();
+ let begin_block = self.blocks[write];
+ assert_eq!(begin_block, BlockBoundary::Begin(end_block));
+ Block::write_end(end_block, &mut self.bytes);
+ self.section_pending = true;
+ }
+ }
+ read += 1;
+ }
+
+ self.blocks.truncate(write);
+ }
+}
+
+impl<'a> BlockBoundary<'a> {
+ fn rev(self) -> BlockBoundary<'a> {
+ match self {
+ BlockBoundary::Begin(block) => BlockBoundary::End(block),
+ BlockBoundary::End(block) => BlockBoundary::Begin(block),
+ }
+ }
+}
--- /dev/null
+use crate::gen::block::Block;
+use crate::gen::nested::NamespaceEntries;
+use crate::gen::out::OutFile;
+use crate::gen::{builtin, include, Opt};
+use crate::syntax::atom::Atom::{self, *};
+use crate::syntax::instantiate::{ImplKey, NamedImplKey};
+use crate::syntax::map::UnorderedMap as Map;
+use crate::syntax::set::UnorderedSet;
+use crate::syntax::symbol::Symbol;
+use crate::syntax::trivial::{self, TrivialReason};
+use crate::syntax::{
+ derive, mangle, Api, Doc, Enum, EnumRepr, ExternFn, ExternType, Pair, Signature, Struct, Trait,
+ Type, TypeAlias, Types, Var,
+};
+use proc_macro2::Ident;
+
+pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec<u8> {
+ let mut out_file = OutFile::new(header, opt, types);
+ let out = &mut out_file;
+
+ pick_includes_and_builtins(out, apis);
+ out.include.extend(&opt.include);
+
+ write_forward_declarations(out, apis);
+ write_data_structures(out, apis);
+ write_functions(out, apis);
+ write_generic_instantiations(out);
+
+ builtin::write(out);
+ include::write(out);
+
+ out_file.content()
+}
+
+fn write_forward_declarations(out: &mut OutFile, apis: &[Api]) {
+ let needs_forward_declaration = |api: &&Api| match api {
+ Api::Struct(_) | Api::CxxType(_) | Api::RustType(_) => true,
+ Api::Enum(enm) => !out.types.cxx.contains(&enm.name.rust),
+ _ => false,
+ };
+
+ let apis_by_namespace =
+ NamespaceEntries::new(apis.iter().filter(needs_forward_declaration).collect());
+
+ write(out, &apis_by_namespace, 0);
+
+ fn write(out: &mut OutFile, ns_entries: &NamespaceEntries, indent: usize) {
+ let apis = ns_entries.direct_content();
+
+ for api in apis {
+ write!(out, "{:1$}", "", indent);
+ match api {
+ Api::Struct(strct) => write_struct_decl(out, &strct.name),
+ Api::Enum(enm) => write_enum_decl(out, enm),
+ Api::CxxType(ety) => write_struct_using(out, &ety.name),
+ Api::RustType(ety) => write_struct_decl(out, &ety.name),
+ _ => unreachable!(),
+ }
+ }
+
+ for (namespace, nested_ns_entries) in ns_entries.nested_content() {
+ writeln!(out, "{:2$}namespace {} {{", "", namespace, indent);
+ write(out, nested_ns_entries, indent + 2);
+ writeln!(out, "{:1$}}}", "", indent);
+ }
+ }
+}
+
+fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
+ let mut methods_for_type = Map::new();
+ for api in apis {
+ if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api {
+ if let Some(receiver) = &efn.sig.receiver {
+ methods_for_type
+ .entry(&receiver.ty.rust)
+ .or_insert_with(Vec::new)
+ .push(efn);
+ }
+ }
+ }
+
+ let mut structs_written = UnorderedSet::new();
+ let mut toposorted_structs = out.types.toposorted_structs.iter();
+ for api in apis {
+ match api {
+ Api::Struct(strct) if !structs_written.contains(&strct.name.rust) => {
+ for next in &mut toposorted_structs {
+ if !out.types.cxx.contains(&strct.name.rust) {
+ out.next_section();
+ let methods = methods_for_type
+ .get(&strct.name.rust)
+ .map(Vec::as_slice)
+ .unwrap_or_default();
+ write_struct(out, next, methods);
+ }
+ structs_written.insert(&next.name.rust);
+ if next.name.rust == strct.name.rust {
+ break;
+ }
+ }
+ }
+ Api::Enum(enm) => {
+ out.next_section();
+ if !out.types.cxx.contains(&enm.name.rust) {
+ write_enum(out, enm);
+ } else if !enm.variants_from_header {
+ check_enum(out, enm);
+ }
+ }
+ Api::RustType(ety) => {
+ out.next_section();
+ let methods = methods_for_type
+ .get(&ety.name.rust)
+ .map(Vec::as_slice)
+ .unwrap_or_default();
+ write_opaque_type(out, ety, methods);
+ }
+ _ => {}
+ }
+ }
+
+ if out.header {
+ return;
+ }
+
+ out.set_namespace(Default::default());
+
+ out.next_section();
+ for api in apis {
+ if let Api::TypeAlias(ety) = api {
+ if let Some(reasons) = out.types.required_trivial.get(&ety.name.rust) {
+ check_trivial_extern_type(out, ety, reasons)
+ }
+ }
+ }
+}
+
+fn write_functions<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
+ if !out.header {
+ for api in apis {
+ match api {
+ Api::Struct(strct) => write_struct_operator_decls(out, strct),
+ Api::RustType(ety) => write_opaque_type_layout_decls(out, ety),
+ Api::CxxFunction(efn) => write_cxx_function_shim(out, efn),
+ Api::RustFunction(efn) => write_rust_function_decl(out, efn),
+ _ => {}
+ }
+ }
+
+ write_std_specializations(out, apis);
+ }
+
+ for api in apis {
+ match api {
+ Api::Struct(strct) => write_struct_operators(out, strct),
+ Api::RustType(ety) => write_opaque_type_layout(out, ety),
+ Api::RustFunction(efn) => {
+ out.next_section();
+ write_rust_function_shim(out, efn);
+ }
+ _ => {}
+ }
+ }
+}
+
+fn write_std_specializations(out: &mut OutFile, apis: &[Api]) {
+ out.set_namespace(Default::default());
+ out.begin_block(Block::Namespace("std"));
+
+ for api in apis {
+ if let Api::Struct(strct) = api {
+ if derive::contains(&strct.derives, Trait::Hash) {
+ out.next_section();
+ out.include.cstddef = true;
+ out.include.functional = true;
+ let qualified = strct.name.to_fully_qualified();
+ writeln!(out, "template <> struct hash<{}> {{", qualified);
+ writeln!(
+ out,
+ " ::std::size_t operator()(const {} &self) const noexcept {{",
+ qualified,
+ );
+ let link_name = mangle::operator(&strct.name, "hash");
+ write!(out, " return ::");
+ for name in &strct.name.namespace {
+ write!(out, "{}::", name);
+ }
+ writeln!(out, "{}(self);", link_name);
+ writeln!(out, " }}");
+ writeln!(out, "}};");
+ }
+ }
+ }
+
+ out.end_block(Block::Namespace("std"));
+}
+
+fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) {
+ for api in apis {
+ if let Api::Include(include) = api {
+ out.include.insert(include);
+ }
+ }
+
+ for ty in out.types {
+ match ty {
+ Type::Ident(ident) => match Atom::from(&ident.rust) {
+ Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32)
+ | Some(I64) => out.include.cstdint = true,
+ Some(Usize) => out.include.cstddef = true,
+ Some(Isize) => out.builtin.rust_isize = true,
+ Some(CxxString) => out.include.string = true,
+ Some(RustString) => out.builtin.rust_string = true,
+ Some(Bool) | Some(Char) | Some(F32) | Some(F64) | None => {}
+ },
+ Type::RustBox(_) => out.builtin.rust_box = true,
+ Type::RustVec(_) => out.builtin.rust_vec = true,
+ Type::UniquePtr(_) => out.include.memory = true,
+ Type::SharedPtr(_) | Type::WeakPtr(_) => out.include.memory = true,
+ Type::Str(_) => out.builtin.rust_str = true,
+ Type::CxxVector(_) => out.include.vector = true,
+ Type::Fn(_) => out.builtin.rust_fn = true,
+ Type::SliceRef(_) => out.builtin.rust_slice = true,
+ Type::Array(_) => out.include.array = true,
+ Type::Ref(_) | Type::Void(_) | Type::Ptr(_) => {}
+ }
+ }
+}
+
+fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct, methods: &[&ExternFn]) {
+ let operator_eq = derive::contains(&strct.derives, Trait::PartialEq);
+ let operator_ord = derive::contains(&strct.derives, Trait::PartialOrd);
+
+ out.set_namespace(&strct.name.namespace);
+ let guard = format!("CXXBRIDGE1_STRUCT_{}", strct.name.to_symbol());
+ writeln!(out, "#ifndef {}", guard);
+ writeln!(out, "#define {}", guard);
+ for line in strct.doc.to_string().lines() {
+ writeln!(out, "//{}", line);
+ }
+ writeln!(out, "struct {} final {{", strct.name.cxx);
+
+ for field in &strct.fields {
+ for line in field.doc.to_string().lines() {
+ writeln!(out, " //{}", line);
+ }
+ write!(out, " ");
+ write_type_space(out, &field.ty);
+ writeln!(out, "{};", field.name.cxx);
+ }
+
+ out.next_section();
+
+ for method in methods {
+ if !method.doc.is_empty() {
+ out.next_section();
+ }
+ for line in method.doc.to_string().lines() {
+ writeln!(out, " //{}", line);
+ }
+ write!(out, " ");
+ let sig = &method.sig;
+ let local_name = method.name.cxx.to_string();
+ let indirect_call = false;
+ write_rust_function_shim_decl(out, &local_name, sig, indirect_call);
+ writeln!(out, ";");
+ if !method.doc.is_empty() {
+ out.next_section();
+ }
+ }
+
+ if operator_eq {
+ writeln!(
+ out,
+ " bool operator==(const {} &) const noexcept;",
+ strct.name.cxx,
+ );
+ writeln!(
+ out,
+ " bool operator!=(const {} &) const noexcept;",
+ strct.name.cxx,
+ );
+ }
+
+ if operator_ord {
+ writeln!(
+ out,
+ " bool operator<(const {} &) const noexcept;",
+ strct.name.cxx,
+ );
+ writeln!(
+ out,
+ " bool operator<=(const {} &) const noexcept;",
+ strct.name.cxx,
+ );
+ writeln!(
+ out,
+ " bool operator>(const {} &) const noexcept;",
+ strct.name.cxx,
+ );
+ writeln!(
+ out,
+ " bool operator>=(const {} &) const noexcept;",
+ strct.name.cxx,
+ );
+ }
+
+ out.include.type_traits = true;
+ writeln!(out, " using IsRelocatable = ::std::true_type;");
+
+ writeln!(out, "}};");
+ writeln!(out, "#endif // {}", guard);
+}
+
+fn write_struct_decl(out: &mut OutFile, ident: &Pair) {
+ writeln!(out, "struct {};", ident.cxx);
+}
+
+fn write_enum_decl(out: &mut OutFile, enm: &Enum) {
+ let repr = match &enm.repr {
+ EnumRepr::Foreign { .. } => return,
+ EnumRepr::Native { atom, .. } => *atom,
+ };
+ write!(out, "enum class {} : ", enm.name.cxx);
+ write_atom(out, repr);
+ writeln!(out, ";");
+}
+
+fn write_struct_using(out: &mut OutFile, ident: &Pair) {
+ writeln!(out, "using {} = {};", ident.cxx, ident.to_fully_qualified());
+}
+
+fn write_opaque_type<'a>(out: &mut OutFile<'a>, ety: &'a ExternType, methods: &[&ExternFn]) {
+ out.set_namespace(&ety.name.namespace);
+ let guard = format!("CXXBRIDGE1_STRUCT_{}", ety.name.to_symbol());
+ writeln!(out, "#ifndef {}", guard);
+ writeln!(out, "#define {}", guard);
+ for line in ety.doc.to_string().lines() {
+ writeln!(out, "//{}", line);
+ }
+
+ out.builtin.opaque = true;
+ writeln!(
+ out,
+ "struct {} final : public ::rust::Opaque {{",
+ ety.name.cxx,
+ );
+
+ for (i, method) in methods.iter().enumerate() {
+ if i > 0 && !method.doc.is_empty() {
+ out.next_section();
+ }
+ for line in method.doc.to_string().lines() {
+ writeln!(out, " //{}", line);
+ }
+ write!(out, " ");
+ let sig = &method.sig;
+ let local_name = method.name.cxx.to_string();
+ let indirect_call = false;
+ write_rust_function_shim_decl(out, &local_name, sig, indirect_call);
+ writeln!(out, ";");
+ if !method.doc.is_empty() {
+ out.next_section();
+ }
+ }
+
+ writeln!(out, " ~{}() = delete;", ety.name.cxx);
+ writeln!(out);
+
+ out.builtin.layout = true;
+ out.include.cstddef = true;
+ writeln!(out, "private:");
+ writeln!(out, " friend ::rust::layout;");
+ writeln!(out, " struct layout {{");
+ writeln!(out, " static ::std::size_t size() noexcept;");
+ writeln!(out, " static ::std::size_t align() noexcept;");
+ writeln!(out, " }};");
+ writeln!(out, "}};");
+ writeln!(out, "#endif // {}", guard);
+}
+
+fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) {
+ let repr = match &enm.repr {
+ EnumRepr::Foreign { .. } => return,
+ EnumRepr::Native { atom, .. } => *atom,
+ };
+ out.set_namespace(&enm.name.namespace);
+ let guard = format!("CXXBRIDGE1_ENUM_{}", enm.name.to_symbol());
+ writeln!(out, "#ifndef {}", guard);
+ writeln!(out, "#define {}", guard);
+ for line in enm.doc.to_string().lines() {
+ writeln!(out, "//{}", line);
+ }
+ write!(out, "enum class {} : ", enm.name.cxx);
+ write_atom(out, repr);
+ writeln!(out, " {{");
+ for variant in &enm.variants {
+ for line in variant.doc.to_string().lines() {
+ writeln!(out, " //{}", line);
+ }
+ writeln!(out, " {} = {},", variant.name.cxx, variant.discriminant);
+ }
+ writeln!(out, "}};");
+ writeln!(out, "#endif // {}", guard);
+}
+
+fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) {
+ let repr = match &enm.repr {
+ EnumRepr::Foreign { .. } => return,
+ EnumRepr::Native { atom, .. } => *atom,
+ };
+ out.set_namespace(&enm.name.namespace);
+ out.include.type_traits = true;
+ writeln!(
+ out,
+ "static_assert(::std::is_enum<{}>::value, \"expected enum\");",
+ enm.name.cxx,
+ );
+ write!(out, "static_assert(sizeof({}) == sizeof(", enm.name.cxx);
+ write_atom(out, repr);
+ writeln!(out, "), \"incorrect size\");");
+ for variant in &enm.variants {
+ write!(out, "static_assert(static_cast<");
+ write_atom(out, repr);
+ writeln!(
+ out,
+ ">({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");",
+ enm.name.cxx, variant.name.cxx, variant.discriminant,
+ );
+ }
+}
+
+fn check_trivial_extern_type(out: &mut OutFile, alias: &TypeAlias, reasons: &[TrivialReason]) {
+ // NOTE: The following static assertion is just nice-to-have and not
+ // necessary for soundness. That's because triviality is always declared by
+ // the user in the form of an unsafe impl of cxx::ExternType:
+ //
+ // unsafe impl ExternType for MyType {
+ // type Id = cxx::type_id!("...");
+ // type Kind = cxx::kind::Trivial;
+ // }
+ //
+ // Since the user went on the record with their unsafe impl to unsafely
+ // claim they KNOW that the type is trivial, it's fine for that to be on
+ // them if that were wrong. However, in practice correctly reasoning about
+ // the relocatability of C++ types is challenging, particularly if the type
+ // definition were to change over time, so for now we add this check.
+ //
+ // There may be legitimate reasons to opt out of this assertion for support
+ // of types that the programmer knows are soundly Rust-movable despite not
+ // being recognized as such by the C++ type system due to a move constructor
+ // or destructor. To opt out of the relocatability check, they need to do
+ // one of the following things in any header used by `include!` in their
+ // bridge.
+ //
+ // --- if they define the type:
+ // struct MyType {
+ // ...
+ // + using IsRelocatable = std::true_type;
+ // };
+ //
+ // --- otherwise:
+ // + template <>
+ // + struct rust::IsRelocatable<MyType> : std::true_type {};
+ //
+
+ let id = alias.name.to_fully_qualified();
+ out.builtin.relocatable = true;
+ writeln!(out, "static_assert(");
+ writeln!(out, " ::rust::IsRelocatable<{}>::value,", id);
+ writeln!(
+ out,
+ " \"type {} should be trivially move constructible and trivially destructible in C++ to be used as {} in Rust\");",
+ id.trim_start_matches("::"),
+ trivial::as_what(&alias.name, reasons),
+ );
+}
+
+fn write_struct_operator_decls<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
+ out.set_namespace(&strct.name.namespace);
+ out.begin_block(Block::ExternC);
+
+ if derive::contains(&strct.derives, Trait::PartialEq) {
+ let link_name = mangle::operator(&strct.name, "eq");
+ writeln!(
+ out,
+ "bool {}(const {1} &, const {1} &) noexcept;",
+ link_name, strct.name.cxx,
+ );
+
+ if !derive::contains(&strct.derives, Trait::Eq) {
+ let link_name = mangle::operator(&strct.name, "ne");
+ writeln!(
+ out,
+ "bool {}(const {1} &, const {1} &) noexcept;",
+ link_name, strct.name.cxx,
+ );
+ }
+ }
+
+ if derive::contains(&strct.derives, Trait::PartialOrd) {
+ let link_name = mangle::operator(&strct.name, "lt");
+ writeln!(
+ out,
+ "bool {}(const {1} &, const {1} &) noexcept;",
+ link_name, strct.name.cxx,
+ );
+
+ let link_name = mangle::operator(&strct.name, "le");
+ writeln!(
+ out,
+ "bool {}(const {1} &, const {1} &) noexcept;",
+ link_name, strct.name.cxx,
+ );
+
+ if !derive::contains(&strct.derives, Trait::Ord) {
+ let link_name = mangle::operator(&strct.name, "gt");
+ writeln!(
+ out,
+ "bool {}(const {1} &, const {1} &) noexcept;",
+ link_name, strct.name.cxx,
+ );
+
+ let link_name = mangle::operator(&strct.name, "ge");
+ writeln!(
+ out,
+ "bool {}(const {1} &, const {1} &) noexcept;",
+ link_name, strct.name.cxx,
+ );
+ }
+ }
+
+ if derive::contains(&strct.derives, Trait::Hash) {
+ out.include.cstddef = true;
+ let link_name = mangle::operator(&strct.name, "hash");
+ writeln!(
+ out,
+ "::std::size_t {}(const {} &) noexcept;",
+ link_name, strct.name.cxx,
+ );
+ }
+
+ out.end_block(Block::ExternC);
+}
+
+fn write_struct_operators<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
+ if out.header {
+ return;
+ }
+
+ out.set_namespace(&strct.name.namespace);
+
+ if derive::contains(&strct.derives, Trait::PartialEq) {
+ out.next_section();
+ writeln!(
+ out,
+ "bool {0}::operator==(const {0} &rhs) const noexcept {{",
+ strct.name.cxx,
+ );
+ let link_name = mangle::operator(&strct.name, "eq");
+ writeln!(out, " return {}(*this, rhs);", link_name);
+ writeln!(out, "}}");
+
+ out.next_section();
+ writeln!(
+ out,
+ "bool {0}::operator!=(const {0} &rhs) const noexcept {{",
+ strct.name.cxx,
+ );
+ if derive::contains(&strct.derives, Trait::Eq) {
+ writeln!(out, " return !(*this == rhs);");
+ } else {
+ let link_name = mangle::operator(&strct.name, "ne");
+ writeln!(out, " return {}(*this, rhs);", link_name);
+ }
+ writeln!(out, "}}");
+ }
+
+ if derive::contains(&strct.derives, Trait::PartialOrd) {
+ out.next_section();
+ writeln!(
+ out,
+ "bool {0}::operator<(const {0} &rhs) const noexcept {{",
+ strct.name.cxx,
+ );
+ let link_name = mangle::operator(&strct.name, "lt");
+ writeln!(out, " return {}(*this, rhs);", link_name);
+ writeln!(out, "}}");
+
+ out.next_section();
+ writeln!(
+ out,
+ "bool {0}::operator<=(const {0} &rhs) const noexcept {{",
+ strct.name.cxx,
+ );
+ let link_name = mangle::operator(&strct.name, "le");
+ writeln!(out, " return {}(*this, rhs);", link_name);
+ writeln!(out, "}}");
+
+ out.next_section();
+ writeln!(
+ out,
+ "bool {0}::operator>(const {0} &rhs) const noexcept {{",
+ strct.name.cxx,
+ );
+ if derive::contains(&strct.derives, Trait::Ord) {
+ writeln!(out, " return !(*this <= rhs);");
+ } else {
+ let link_name = mangle::operator(&strct.name, "gt");
+ writeln!(out, " return {}(*this, rhs);", link_name);
+ }
+ writeln!(out, "}}");
+
+ out.next_section();
+ writeln!(
+ out,
+ "bool {0}::operator>=(const {0} &rhs) const noexcept {{",
+ strct.name.cxx,
+ );
+ if derive::contains(&strct.derives, Trait::Ord) {
+ writeln!(out, " return !(*this < rhs);");
+ } else {
+ let link_name = mangle::operator(&strct.name, "ge");
+ writeln!(out, " return {}(*this, rhs);", link_name);
+ }
+ writeln!(out, "}}");
+ }
+}
+
+fn write_opaque_type_layout_decls<'a>(out: &mut OutFile<'a>, ety: &'a ExternType) {
+ out.set_namespace(&ety.name.namespace);
+ out.begin_block(Block::ExternC);
+
+ let link_name = mangle::operator(&ety.name, "sizeof");
+ writeln!(out, "::std::size_t {}() noexcept;", link_name);
+
+ let link_name = mangle::operator(&ety.name, "alignof");
+ writeln!(out, "::std::size_t {}() noexcept;", link_name);
+
+ out.end_block(Block::ExternC);
+}
+
+fn write_opaque_type_layout<'a>(out: &mut OutFile<'a>, ety: &'a ExternType) {
+ if out.header {
+ return;
+ }
+
+ out.set_namespace(&ety.name.namespace);
+
+ out.next_section();
+ let link_name = mangle::operator(&ety.name, "sizeof");
+ writeln!(
+ out,
+ "::std::size_t {}::layout::size() noexcept {{",
+ ety.name.cxx,
+ );
+ writeln!(out, " return {}();", link_name);
+ writeln!(out, "}}");
+
+ out.next_section();
+ let link_name = mangle::operator(&ety.name, "alignof");
+ writeln!(
+ out,
+ "::std::size_t {}::layout::align() noexcept {{",
+ ety.name.cxx,
+ );
+ writeln!(out, " return {}();", link_name);
+ writeln!(out, "}}");
+}
+
+fn begin_function_definition(out: &mut OutFile) {
+ if let Some(annotation) = &out.opt.cxx_impl_annotations {
+ write!(out, "{} ", annotation);
+ }
+}
+
+fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
+ out.next_section();
+ out.set_namespace(&efn.name.namespace);
+ out.begin_block(Block::ExternC);
+ begin_function_definition(out);
+ if efn.throws {
+ out.builtin.ptr_len = true;
+ write!(out, "::rust::repr::PtrLen ");
+ } else {
+ write_extern_return_type_space(out, &efn.ret);
+ }
+ let mangled = mangle::extern_fn(efn, out.types);
+ write!(out, "{}(", mangled);
+ if let Some(receiver) = &efn.receiver {
+ if !receiver.mutable {
+ write!(out, "const ");
+ }
+ write!(
+ out,
+ "{} &self",
+ out.types.resolve(&receiver.ty).name.to_fully_qualified(),
+ );
+ }
+ for (i, arg) in efn.args.iter().enumerate() {
+ if i > 0 || efn.receiver.is_some() {
+ write!(out, ", ");
+ }
+ if arg.ty == RustString {
+ write!(out, "const ");
+ } else if let Type::RustVec(_) = arg.ty {
+ write!(out, "const ");
+ }
+ write_extern_arg(out, arg);
+ }
+ let indirect_return = indirect_return(efn, out.types);
+ if indirect_return {
+ if !efn.args.is_empty() || efn.receiver.is_some() {
+ write!(out, ", ");
+ }
+ write_indirect_return_type_space(out, efn.ret.as_ref().unwrap());
+ write!(out, "*return$");
+ }
+ writeln!(out, ") noexcept {{");
+ write!(out, " ");
+ write_return_type(out, &efn.ret);
+ match &efn.receiver {
+ None => write!(out, "(*{}$)(", efn.name.rust),
+ Some(receiver) => write!(
+ out,
+ "({}::*{}$)(",
+ out.types.resolve(&receiver.ty).name.to_fully_qualified(),
+ efn.name.rust,
+ ),
+ }
+ for (i, arg) in efn.args.iter().enumerate() {
+ if i > 0 {
+ write!(out, ", ");
+ }
+ write_type(out, &arg.ty);
+ }
+ write!(out, ")");
+ if let Some(receiver) = &efn.receiver {
+ if !receiver.mutable {
+ write!(out, " const");
+ }
+ }
+ write!(out, " = ");
+ match &efn.receiver {
+ None => write!(out, "{}", efn.name.to_fully_qualified()),
+ Some(receiver) => write!(
+ out,
+ "&{}::{}",
+ out.types.resolve(&receiver.ty).name.to_fully_qualified(),
+ efn.name.cxx,
+ ),
+ }
+ writeln!(out, ";");
+ write!(out, " ");
+ if efn.throws {
+ out.builtin.ptr_len = true;
+ out.builtin.trycatch = true;
+ writeln!(out, "::rust::repr::PtrLen throw$;");
+ writeln!(out, " ::rust::behavior::trycatch(");
+ writeln!(out, " [&] {{");
+ write!(out, " ");
+ }
+ if indirect_return {
+ out.include.new = true;
+ write!(out, "new (return$) ");
+ write_indirect_return_type(out, efn.ret.as_ref().unwrap());
+ write!(out, "(");
+ } else if efn.ret.is_some() {
+ write!(out, "return ");
+ }
+ match &efn.ret {
+ Some(Type::Ref(_)) => write!(out, "&"),
+ Some(Type::Str(_)) if !indirect_return => {
+ out.builtin.rust_str_repr = true;
+ write!(out, "::rust::impl<::rust::Str>::repr(");
+ }
+ Some(ty @ Type::SliceRef(_)) if !indirect_return => {
+ out.builtin.rust_slice_repr = true;
+ write!(out, "::rust::impl<");
+ write_type(out, ty);
+ write!(out, ">::repr(");
+ }
+ _ => {}
+ }
+ match &efn.receiver {
+ None => write!(out, "{}$(", efn.name.rust),
+ Some(_) => write!(out, "(self.*{}$)(", efn.name.rust),
+ }
+ for (i, arg) in efn.args.iter().enumerate() {
+ if i > 0 {
+ write!(out, ", ");
+ }
+ if let Type::RustBox(_) = &arg.ty {
+ write_type(out, &arg.ty);
+ write!(out, "::from_raw({})", arg.name.cxx);
+ } else if let Type::UniquePtr(_) = &arg.ty {
+ write_type(out, &arg.ty);
+ write!(out, "({})", arg.name.cxx);
+ } else if arg.ty == RustString {
+ out.builtin.unsafe_bitcopy = true;
+ write!(
+ out,
+ "::rust::String(::rust::unsafe_bitcopy, *{})",
+ arg.name.cxx,
+ );
+ } else if let Type::RustVec(_) = arg.ty {
+ out.builtin.unsafe_bitcopy = true;
+ write_type(out, &arg.ty);
+ write!(out, "(::rust::unsafe_bitcopy, *{})", arg.name.cxx);
+ } else if out.types.needs_indirect_abi(&arg.ty) {
+ out.include.utility = true;
+ write!(out, "::std::move(*{})", arg.name.cxx);
+ } else {
+ write!(out, "{}", arg.name.cxx);
+ }
+ }
+ write!(out, ")");
+ match &efn.ret {
+ Some(Type::RustBox(_)) => write!(out, ".into_raw()"),
+ Some(Type::UniquePtr(_)) => write!(out, ".release()"),
+ Some(Type::Str(_)) | Some(Type::SliceRef(_)) if !indirect_return => write!(out, ")"),
+ _ => {}
+ }
+ if indirect_return {
+ write!(out, ")");
+ }
+ writeln!(out, ";");
+ if efn.throws {
+ out.include.cstring = true;
+ out.builtin.exception = true;
+ writeln!(out, " throw$.ptr = nullptr;");
+ writeln!(out, " }},");
+ writeln!(out, " [&](const char *catch$) noexcept {{");
+ writeln!(out, " throw$.len = ::std::strlen(catch$);");
+ writeln!(
+ out,
+ " throw$.ptr = const_cast<char *>(::cxxbridge1$exception(catch$, throw$.len));",
+ );
+ writeln!(out, " }});");
+ writeln!(out, " return throw$;");
+ }
+ writeln!(out, "}}");
+ for arg in &efn.args {
+ if let Type::Fn(f) = &arg.ty {
+ let var = &arg.name;
+ write_function_pointer_trampoline(out, efn, var, f);
+ }
+ }
+ out.end_block(Block::ExternC);
+}
+
+fn write_function_pointer_trampoline(out: &mut OutFile, efn: &ExternFn, var: &Pair, f: &Signature) {
+ let r_trampoline = mangle::r_trampoline(efn, var, out.types);
+ let indirect_call = true;
+ write_rust_function_decl_impl(out, &r_trampoline, f, indirect_call);
+
+ out.next_section();
+ let c_trampoline = mangle::c_trampoline(efn, var, out.types).to_string();
+ let doc = Doc::new();
+ write_rust_function_shim_impl(out, &c_trampoline, f, &doc, &r_trampoline, indirect_call);
+}
+
+fn write_rust_function_decl<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
+ out.set_namespace(&efn.name.namespace);
+ out.begin_block(Block::ExternC);
+ let link_name = mangle::extern_fn(efn, out.types);
+ let indirect_call = false;
+ write_rust_function_decl_impl(out, &link_name, efn, indirect_call);
+ out.end_block(Block::ExternC);
+}
+
+fn write_rust_function_decl_impl(
+ out: &mut OutFile,
+ link_name: &Symbol,
+ sig: &Signature,
+ indirect_call: bool,
+) {
+ out.next_section();
+ if sig.throws {
+ out.builtin.ptr_len = true;
+ write!(out, "::rust::repr::PtrLen ");
+ } else {
+ write_extern_return_type_space(out, &sig.ret);
+ }
+ write!(out, "{}(", link_name);
+ let mut needs_comma = false;
+ if let Some(receiver) = &sig.receiver {
+ if !receiver.mutable {
+ write!(out, "const ");
+ }
+ write!(
+ out,
+ "{} &self",
+ out.types.resolve(&receiver.ty).name.to_fully_qualified(),
+ );
+ needs_comma = true;
+ }
+ for arg in &sig.args {
+ if needs_comma {
+ write!(out, ", ");
+ }
+ write_extern_arg(out, arg);
+ needs_comma = true;
+ }
+ if indirect_return(sig, out.types) {
+ if needs_comma {
+ write!(out, ", ");
+ }
+ match sig.ret.as_ref().unwrap() {
+ Type::Ref(ret) => {
+ write_pointee_type(out, &ret.inner, ret.mutable);
+ write!(out, " *");
+ }
+ ret => write_type_space(out, ret),
+ }
+ write!(out, "*return$");
+ needs_comma = true;
+ }
+ if indirect_call {
+ if needs_comma {
+ write!(out, ", ");
+ }
+ write!(out, "void *");
+ }
+ writeln!(out, ") noexcept;");
+}
+
+fn write_rust_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
+ out.set_namespace(&efn.name.namespace);
+ let local_name = match &efn.sig.receiver {
+ None => efn.name.cxx.to_string(),
+ Some(receiver) => format!(
+ "{}::{}",
+ out.types.resolve(&receiver.ty).name.cxx,
+ efn.name.cxx,
+ ),
+ };
+ let doc = &efn.doc;
+ let invoke = mangle::extern_fn(efn, out.types);
+ let indirect_call = false;
+ write_rust_function_shim_impl(out, &local_name, efn, doc, &invoke, indirect_call);
+}
+
+fn write_rust_function_shim_decl(
+ out: &mut OutFile,
+ local_name: &str,
+ sig: &Signature,
+ indirect_call: bool,
+) {
+ begin_function_definition(out);
+ write_return_type(out, &sig.ret);
+ write!(out, "{}(", local_name);
+ for (i, arg) in sig.args.iter().enumerate() {
+ if i > 0 {
+ write!(out, ", ");
+ }
+ write_type_space(out, &arg.ty);
+ write!(out, "{}", arg.name.cxx);
+ }
+ if indirect_call {
+ if !sig.args.is_empty() {
+ write!(out, ", ");
+ }
+ write!(out, "void *extern$");
+ }
+ write!(out, ")");
+ if let Some(receiver) = &sig.receiver {
+ if !receiver.mutable {
+ write!(out, " const");
+ }
+ }
+ if !sig.throws {
+ write!(out, " noexcept");
+ }
+}
+
+fn write_rust_function_shim_impl(
+ out: &mut OutFile,
+ local_name: &str,
+ sig: &Signature,
+ doc: &Doc,
+ invoke: &Symbol,
+ indirect_call: bool,
+) {
+ if out.header && sig.receiver.is_some() {
+ // We've already defined this inside the struct.
+ return;
+ }
+ if sig.receiver.is_none() {
+ // Member functions already documented at their declaration.
+ for line in doc.to_string().lines() {
+ writeln!(out, "//{}", line);
+ }
+ }
+ write_rust_function_shim_decl(out, local_name, sig, indirect_call);
+ if out.header {
+ writeln!(out, ";");
+ return;
+ }
+ writeln!(out, " {{");
+ for arg in &sig.args {
+ if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) {
+ out.include.utility = true;
+ out.builtin.manually_drop = true;
+ write!(out, " ::rust::ManuallyDrop<");
+ write_type(out, &arg.ty);
+ writeln!(out, "> {}$(::std::move({0}));", arg.name.cxx);
+ }
+ }
+ write!(out, " ");
+ let indirect_return = indirect_return(sig, out.types);
+ if indirect_return {
+ out.builtin.maybe_uninit = true;
+ write!(out, "::rust::MaybeUninit<");
+ match sig.ret.as_ref().unwrap() {
+ Type::Ref(ret) => {
+ write_pointee_type(out, &ret.inner, ret.mutable);
+ write!(out, " *");
+ }
+ ret => write_type(out, ret),
+ }
+ writeln!(out, "> return$;");
+ write!(out, " ");
+ } else if let Some(ret) = &sig.ret {
+ write!(out, "return ");
+ match ret {
+ Type::RustBox(_) => {
+ write_type(out, ret);
+ write!(out, "::from_raw(");
+ }
+ Type::UniquePtr(_) => {
+ write_type(out, ret);
+ write!(out, "(");
+ }
+ Type::Ref(_) => write!(out, "*"),
+ Type::Str(_) => {
+ out.builtin.rust_str_new_unchecked = true;
+ write!(out, "::rust::impl<::rust::Str>::new_unchecked(");
+ }
+ Type::SliceRef(_) => {
+ out.builtin.rust_slice_new = true;
+ write!(out, "::rust::impl<");
+ write_type(out, ret);
+ write!(out, ">::slice(");
+ }
+ _ => {}
+ }
+ }
+ if sig.throws {
+ out.builtin.ptr_len = true;
+ write!(out, "::rust::repr::PtrLen error$ = ");
+ }
+ write!(out, "{}(", invoke);
+ let mut needs_comma = false;
+ if sig.receiver.is_some() {
+ write!(out, "*this");
+ needs_comma = true;
+ }
+ for arg in &sig.args {
+ if needs_comma {
+ write!(out, ", ");
+ }
+ if out.types.needs_indirect_abi(&arg.ty) {
+ write!(out, "&");
+ }
+ write!(out, "{}", arg.name.cxx);
+ match &arg.ty {
+ Type::RustBox(_) => write!(out, ".into_raw()"),
+ Type::UniquePtr(_) => write!(out, ".release()"),
+ ty if ty != RustString && out.types.needs_indirect_abi(ty) => write!(out, "$.value"),
+ _ => {}
+ }
+ needs_comma = true;
+ }
+ if indirect_return {
+ if needs_comma {
+ write!(out, ", ");
+ }
+ write!(out, "&return$.value");
+ needs_comma = true;
+ }
+ if indirect_call {
+ if needs_comma {
+ write!(out, ", ");
+ }
+ write!(out, "extern$");
+ }
+ write!(out, ")");
+ if !indirect_return {
+ if let Some(ret) = &sig.ret {
+ if let Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::SliceRef(_) = ret {
+ write!(out, ")");
+ }
+ }
+ }
+ writeln!(out, ";");
+ if sig.throws {
+ out.builtin.rust_error = true;
+ writeln!(out, " if (error$.ptr) {{");
+ writeln!(out, " throw ::rust::impl<::rust::Error>::error(error$);");
+ writeln!(out, " }}");
+ }
+ if indirect_return {
+ write!(out, " return ");
+ match sig.ret.as_ref().unwrap() {
+ Type::Ref(_) => write!(out, "*return$.value"),
+ _ => {
+ out.include.utility = true;
+ write!(out, "::std::move(return$.value)");
+ }
+ }
+ writeln!(out, ";");
+ }
+ writeln!(out, "}}");
+}
+
+fn write_return_type(out: &mut OutFile, ty: &Option<Type>) {
+ match ty {
+ None => write!(out, "void "),
+ Some(ty) => write_type_space(out, ty),
+ }
+}
+
+fn indirect_return(sig: &Signature, types: &Types) -> bool {
+ sig.ret
+ .as_ref()
+ .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret))
+}
+
+fn write_indirect_return_type(out: &mut OutFile, ty: &Type) {
+ match ty {
+ Type::RustBox(ty) | Type::UniquePtr(ty) => {
+ write_type_space(out, &ty.inner);
+ write!(out, "*");
+ }
+ Type::Ref(ty) => {
+ if !ty.mutable {
+ write!(out, "const ");
+ }
+ write_type(out, &ty.inner);
+ write!(out, " *");
+ }
+ _ => write_type(out, ty),
+ }
+}
+
+fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) {
+ write_indirect_return_type(out, ty);
+ match ty {
+ Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {}
+ Type::Str(_) | Type::SliceRef(_) => write!(out, " "),
+ _ => write_space_after_type(out, ty),
+ }
+}
+
+fn write_extern_return_type_space(out: &mut OutFile, ty: &Option<Type>) {
+ match ty {
+ Some(Type::RustBox(ty)) | Some(Type::UniquePtr(ty)) => {
+ write_type_space(out, &ty.inner);
+ write!(out, "*");
+ }
+ Some(Type::Ref(ty)) => {
+ if !ty.mutable {
+ write!(out, "const ");
+ }
+ write_type(out, &ty.inner);
+ write!(out, " *");
+ }
+ Some(Type::Str(_)) | Some(Type::SliceRef(_)) => {
+ out.builtin.repr_fat = true;
+ write!(out, "::rust::repr::Fat ");
+ }
+ Some(ty) if out.types.needs_indirect_abi(ty) => write!(out, "void "),
+ _ => write_return_type(out, ty),
+ }
+}
+
+fn write_extern_arg(out: &mut OutFile, arg: &Var) {
+ match &arg.ty {
+ Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) => {
+ write_type_space(out, &ty.inner);
+ write!(out, "*");
+ }
+ _ => write_type_space(out, &arg.ty),
+ }
+ if out.types.needs_indirect_abi(&arg.ty) {
+ write!(out, "*");
+ }
+ write!(out, "{}", arg.name.cxx);
+}
+
+fn write_type(out: &mut OutFile, ty: &Type) {
+ match ty {
+ Type::Ident(ident) => match Atom::from(&ident.rust) {
+ Some(atom) => write_atom(out, atom),
+ None => write!(
+ out,
+ "{}",
+ out.types.resolve(ident).name.to_fully_qualified(),
+ ),
+ },
+ Type::RustBox(ty) => {
+ write!(out, "::rust::Box<");
+ write_type(out, &ty.inner);
+ write!(out, ">");
+ }
+ Type::RustVec(ty) => {
+ write!(out, "::rust::Vec<");
+ write_type(out, &ty.inner);
+ write!(out, ">");
+ }
+ Type::UniquePtr(ptr) => {
+ write!(out, "::std::unique_ptr<");
+ write_type(out, &ptr.inner);
+ write!(out, ">");
+ }
+ Type::SharedPtr(ptr) => {
+ write!(out, "::std::shared_ptr<");
+ write_type(out, &ptr.inner);
+ write!(out, ">");
+ }
+ Type::WeakPtr(ptr) => {
+ write!(out, "::std::weak_ptr<");
+ write_type(out, &ptr.inner);
+ write!(out, ">");
+ }
+ Type::CxxVector(ty) => {
+ write!(out, "::std::vector<");
+ write_type(out, &ty.inner);
+ write!(out, ">");
+ }
+ Type::Ref(r) => {
+ write_pointee_type(out, &r.inner, r.mutable);
+ write!(out, " &");
+ }
+ Type::Ptr(p) => {
+ write_pointee_type(out, &p.inner, p.mutable);
+ write!(out, " *");
+ }
+ Type::Str(_) => {
+ write!(out, "::rust::Str");
+ }
+ Type::SliceRef(slice) => {
+ write!(out, "::rust::Slice<");
+ if slice.mutability.is_none() {
+ write!(out, "const ");
+ }
+ write_type(out, &slice.inner);
+ write!(out, ">");
+ }
+ Type::Fn(f) => {
+ write!(out, "::rust::Fn<");
+ match &f.ret {
+ Some(ret) => write_type(out, ret),
+ None => write!(out, "void"),
+ }
+ write!(out, "(");
+ for (i, arg) in f.args.iter().enumerate() {
+ if i > 0 {
+ write!(out, ", ");
+ }
+ write_type(out, &arg.ty);
+ }
+ write!(out, ")>");
+ }
+ Type::Array(a) => {
+ write!(out, "::std::array<");
+ write_type(out, &a.inner);
+ write!(out, ", {}>", &a.len);
+ }
+ Type::Void(_) => unreachable!(),
+ }
+}
+
+// Write just the T type behind a &T or &mut T or *const T or *mut T.
+fn write_pointee_type(out: &mut OutFile, inner: &Type, mutable: bool) {
+ if let Type::Ptr(_) = inner {
+ write_type_space(out, inner);
+ if !mutable {
+ write!(out, "const");
+ }
+ } else {
+ if !mutable {
+ write!(out, "const ");
+ }
+ write_type(out, inner);
+ }
+}
+
+fn write_atom(out: &mut OutFile, atom: Atom) {
+ match atom {
+ Bool => write!(out, "bool"),
+ Char => write!(out, "char"),
+ U8 => write!(out, "::std::uint8_t"),
+ U16 => write!(out, "::std::uint16_t"),
+ U32 => write!(out, "::std::uint32_t"),
+ U64 => write!(out, "::std::uint64_t"),
+ Usize => write!(out, "::std::size_t"),
+ I8 => write!(out, "::std::int8_t"),
+ I16 => write!(out, "::std::int16_t"),
+ I32 => write!(out, "::std::int32_t"),
+ I64 => write!(out, "::std::int64_t"),
+ Isize => write!(out, "::rust::isize"),
+ F32 => write!(out, "float"),
+ F64 => write!(out, "double"),
+ CxxString => write!(out, "::std::string"),
+ RustString => write!(out, "::rust::String"),
+ }
+}
+
+fn write_type_space(out: &mut OutFile, ty: &Type) {
+ write_type(out, ty);
+ write_space_after_type(out, ty);
+}
+
+fn write_space_after_type(out: &mut OutFile, ty: &Type) {
+ match ty {
+ Type::Ident(_)
+ | Type::RustBox(_)
+ | Type::UniquePtr(_)
+ | Type::SharedPtr(_)
+ | Type::WeakPtr(_)
+ | Type::Str(_)
+ | Type::CxxVector(_)
+ | Type::RustVec(_)
+ | Type::SliceRef(_)
+ | Type::Fn(_)
+ | Type::Array(_) => write!(out, " "),
+ Type::Ref(_) | Type::Ptr(_) => {}
+ Type::Void(_) => unreachable!(),
+ }
+}
+
+#[derive(Copy, Clone)]
+enum UniquePtr<'a> {
+ Ident(&'a Ident),
+ CxxVector(&'a Ident),
+}
+
+trait ToTypename {
+ fn to_typename(&self, types: &Types) -> String;
+}
+
+impl ToTypename for Ident {
+ fn to_typename(&self, types: &Types) -> String {
+ types.resolve(self).name.to_fully_qualified()
+ }
+}
+
+impl<'a> ToTypename for UniquePtr<'a> {
+ fn to_typename(&self, types: &Types) -> String {
+ match self {
+ UniquePtr::Ident(ident) => ident.to_typename(types),
+ UniquePtr::CxxVector(element) => {
+ format!("::std::vector<{}>", element.to_typename(types))
+ }
+ }
+ }
+}
+
+trait ToMangled {
+ fn to_mangled(&self, types: &Types) -> Symbol;
+}
+
+impl ToMangled for Ident {
+ fn to_mangled(&self, types: &Types) -> Symbol {
+ types.resolve(self).name.to_symbol()
+ }
+}
+
+impl<'a> ToMangled for UniquePtr<'a> {
+ fn to_mangled(&self, types: &Types) -> Symbol {
+ match self {
+ UniquePtr::Ident(ident) => ident.to_mangled(types),
+ UniquePtr::CxxVector(element) => element.to_mangled(types).prefix_with("std$vector$"),
+ }
+ }
+}
+
+fn write_generic_instantiations(out: &mut OutFile) {
+ if out.header {
+ return;
+ }
+
+ out.next_section();
+ out.set_namespace(Default::default());
+ out.begin_block(Block::ExternC);
+ for impl_key in out.types.impls.keys() {
+ out.next_section();
+ match *impl_key {
+ ImplKey::RustBox(ident) => write_rust_box_extern(out, ident),
+ ImplKey::RustVec(ident) => write_rust_vec_extern(out, ident),
+ ImplKey::UniquePtr(ident) => write_unique_ptr(out, ident),
+ ImplKey::SharedPtr(ident) => write_shared_ptr(out, ident),
+ ImplKey::WeakPtr(ident) => write_weak_ptr(out, ident),
+ ImplKey::CxxVector(ident) => write_cxx_vector(out, ident),
+ }
+ }
+ out.end_block(Block::ExternC);
+
+ out.begin_block(Block::Namespace("rust"));
+ out.begin_block(Block::InlineNamespace("cxxbridge1"));
+ for impl_key in out.types.impls.keys() {
+ match *impl_key {
+ ImplKey::RustBox(ident) => write_rust_box_impl(out, ident),
+ ImplKey::RustVec(ident) => write_rust_vec_impl(out, ident),
+ _ => {}
+ }
+ }
+ out.end_block(Block::InlineNamespace("cxxbridge1"));
+ out.end_block(Block::Namespace("rust"));
+}
+
+fn write_rust_box_extern(out: &mut OutFile, key: NamedImplKey) {
+ let resolve = out.types.resolve(&key);
+ let inner = resolve.name.to_fully_qualified();
+ let instance = resolve.name.to_symbol();
+
+ writeln!(
+ out,
+ "{} *cxxbridge1$box${}$alloc() noexcept;",
+ inner, instance,
+ );
+ writeln!(
+ out,
+ "void cxxbridge1$box${}$dealloc({} *) noexcept;",
+ instance, inner,
+ );
+ writeln!(
+ out,
+ "void cxxbridge1$box${}$drop(::rust::Box<{}> *ptr) noexcept;",
+ instance, inner,
+ );
+}
+
+fn write_rust_vec_extern(out: &mut OutFile, key: NamedImplKey) {
+ let element = key.rust;
+ let inner = element.to_typename(out.types);
+ let instance = element.to_mangled(out.types);
+
+ out.include.cstddef = true;
+
+ writeln!(
+ out,
+ "void cxxbridge1$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;",
+ instance, inner,
+ );
+ writeln!(
+ out,
+ "void cxxbridge1$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;",
+ instance, inner,
+ );
+ writeln!(
+ out,
+ "::std::size_t cxxbridge1$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;",
+ instance, inner,
+ );
+ writeln!(
+ out,
+ "::std::size_t cxxbridge1$rust_vec${}$capacity(const ::rust::Vec<{}> *ptr) noexcept;",
+ instance, inner,
+ );
+ writeln!(
+ out,
+ "const {} *cxxbridge1$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;",
+ inner, instance,
+ );
+ writeln!(
+ out,
+ "void cxxbridge1$rust_vec${}$reserve_total(::rust::Vec<{}> *ptr, ::std::size_t new_cap) noexcept;",
+ instance, inner,
+ );
+ writeln!(
+ out,
+ "void cxxbridge1$rust_vec${}$set_len(::rust::Vec<{}> *ptr, ::std::size_t len) noexcept;",
+ instance, inner,
+ );
+ writeln!(
+ out,
+ "void cxxbridge1$rust_vec${}$truncate(::rust::Vec<{}> *ptr, ::std::size_t len) noexcept;",
+ instance, inner,
+ );
+}
+
+fn write_rust_box_impl(out: &mut OutFile, key: NamedImplKey) {
+ let resolve = out.types.resolve(&key);
+ let inner = resolve.name.to_fully_qualified();
+ let instance = resolve.name.to_symbol();
+
+ writeln!(out, "template <>");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "{} *Box<{}>::allocation::alloc() noexcept {{",
+ inner, inner,
+ );
+ writeln!(out, " return cxxbridge1$box${}$alloc();", instance);
+ writeln!(out, "}}");
+
+ writeln!(out, "template <>");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void Box<{}>::allocation::dealloc({} *ptr) noexcept {{",
+ inner, inner,
+ );
+ writeln!(out, " cxxbridge1$box${}$dealloc(ptr);", instance);
+ writeln!(out, "}}");
+
+ writeln!(out, "template <>");
+ begin_function_definition(out);
+ writeln!(out, "void Box<{}>::drop() noexcept {{", inner);
+ writeln!(out, " cxxbridge1$box${}$drop(this);", instance);
+ writeln!(out, "}}");
+}
+
+fn write_rust_vec_impl(out: &mut OutFile, key: NamedImplKey) {
+ let element = key.rust;
+ let inner = element.to_typename(out.types);
+ let instance = element.to_mangled(out.types);
+
+ out.include.cstddef = true;
+
+ writeln!(out, "template <>");
+ begin_function_definition(out);
+ writeln!(out, "Vec<{}>::Vec() noexcept {{", inner);
+ writeln!(out, " cxxbridge1$rust_vec${}$new(this);", instance);
+ writeln!(out, "}}");
+
+ writeln!(out, "template <>");
+ begin_function_definition(out);
+ writeln!(out, "void Vec<{}>::drop() noexcept {{", inner);
+ writeln!(out, " return cxxbridge1$rust_vec${}$drop(this);", instance);
+ writeln!(out, "}}");
+
+ writeln!(out, "template <>");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "::std::size_t Vec<{}>::size() const noexcept {{",
+ inner,
+ );
+ writeln!(out, " return cxxbridge1$rust_vec${}$len(this);", instance);
+ writeln!(out, "}}");
+
+ writeln!(out, "template <>");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "::std::size_t Vec<{}>::capacity() const noexcept {{",
+ inner,
+ );
+ writeln!(
+ out,
+ " return cxxbridge1$rust_vec${}$capacity(this);",
+ instance,
+ );
+ writeln!(out, "}}");
+
+ writeln!(out, "template <>");
+ begin_function_definition(out);
+ writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner);
+ writeln!(out, " return cxxbridge1$rust_vec${}$data(this);", instance);
+ writeln!(out, "}}");
+
+ writeln!(out, "template <>");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void Vec<{}>::reserve_total(::std::size_t new_cap) noexcept {{",
+ inner,
+ );
+ writeln!(
+ out,
+ " return cxxbridge1$rust_vec${}$reserve_total(this, new_cap);",
+ instance,
+ );
+ writeln!(out, "}}");
+
+ writeln!(out, "template <>");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void Vec<{}>::set_len(::std::size_t len) noexcept {{",
+ inner,
+ );
+ writeln!(
+ out,
+ " return cxxbridge1$rust_vec${}$set_len(this, len);",
+ instance,
+ );
+ writeln!(out, "}}");
+
+ writeln!(out, "template <>");
+ begin_function_definition(out);
+ writeln!(out, "void Vec<{}>::truncate(::std::size_t len) {{", inner,);
+ writeln!(
+ out,
+ " return cxxbridge1$rust_vec${}$truncate(this, len);",
+ instance,
+ );
+ writeln!(out, "}}");
+}
+
+fn write_unique_ptr(out: &mut OutFile, key: NamedImplKey) {
+ let ty = UniquePtr::Ident(key.rust);
+ write_unique_ptr_common(out, ty);
+}
+
+// Shared by UniquePtr<T> and UniquePtr<CxxVector<T>>.
+fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) {
+ out.include.new = true;
+ out.include.utility = true;
+ let inner = ty.to_typename(out.types);
+ let instance = ty.to_mangled(out.types);
+
+ let can_construct_from_value = match ty {
+ // Some aliases are to opaque types; some are to trivial types. We can't
+ // know at code generation time, so we generate both C++ and Rust side
+ // bindings for a "new" method anyway. But the Rust code can't be called
+ // for Opaque types because the 'new' method is not implemented.
+ UniquePtr::Ident(ident) => out.types.is_maybe_trivial(ident),
+ UniquePtr::CxxVector(_) => false,
+ };
+
+ let conditional_delete = match ty {
+ UniquePtr::Ident(ident) => {
+ !out.types.structs.contains_key(ident) && !out.types.enums.contains_key(ident)
+ }
+ UniquePtr::CxxVector(_) => false,
+ };
+
+ if conditional_delete {
+ out.builtin.is_complete = true;
+ let definition = match ty {
+ UniquePtr::Ident(ty) => &out.types.resolve(ty).name.cxx,
+ UniquePtr::CxxVector(_) => unreachable!(),
+ };
+ writeln!(
+ out,
+ "static_assert(::rust::detail::is_complete<{}>::value, \"definition of {} is required\");",
+ inner, definition,
+ );
+ }
+ writeln!(
+ out,
+ "static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");",
+ inner,
+ );
+ writeln!(
+ out,
+ "static_assert(alignof(::std::unique_ptr<{}>) == alignof(void *), \"\");",
+ inner,
+ );
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void cxxbridge1$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{",
+ instance, inner,
+ );
+ writeln!(out, " ::new (ptr) ::std::unique_ptr<{}>();", inner);
+ writeln!(out, "}}");
+ if can_construct_from_value {
+ out.builtin.maybe_uninit = true;
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "{} *cxxbridge1$unique_ptr${}$uninit(::std::unique_ptr<{}> *ptr) noexcept {{",
+ inner, instance, inner,
+ );
+ writeln!(
+ out,
+ " {} *uninit = reinterpret_cast<{} *>(new ::rust::MaybeUninit<{}>);",
+ inner, inner, inner,
+ );
+ writeln!(out, " ::new (ptr) ::std::unique_ptr<{}>(uninit);", inner);
+ writeln!(out, " return uninit;");
+ writeln!(out, "}}");
+ }
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void cxxbridge1$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{",
+ instance, inner, inner,
+ );
+ writeln!(out, " ::new (ptr) ::std::unique_ptr<{}>(raw);", inner);
+ writeln!(out, "}}");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "const {} *cxxbridge1$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{",
+ inner, instance, inner,
+ );
+ writeln!(out, " return ptr.get();");
+ writeln!(out, "}}");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "{} *cxxbridge1$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{",
+ inner, instance, inner,
+ );
+ writeln!(out, " return ptr.release();");
+ writeln!(out, "}}");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void cxxbridge1$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{",
+ instance, inner,
+ );
+ if conditional_delete {
+ out.builtin.deleter_if = true;
+ writeln!(
+ out,
+ " ::rust::deleter_if<::rust::detail::is_complete<{}>::value>{{}}(ptr);",
+ inner,
+ );
+ } else {
+ writeln!(out, " ptr->~unique_ptr();");
+ }
+ writeln!(out, "}}");
+}
+
+fn write_shared_ptr(out: &mut OutFile, key: NamedImplKey) {
+ let ident = key.rust;
+ let resolve = out.types.resolve(ident);
+ let inner = resolve.name.to_fully_qualified();
+ let instance = resolve.name.to_symbol();
+
+ out.include.new = true;
+ out.include.utility = true;
+
+ // Some aliases are to opaque types; some are to trivial types. We can't
+ // know at code generation time, so we generate both C++ and Rust side
+ // bindings for a "new" method anyway. But the Rust code can't be called for
+ // Opaque types because the 'new' method is not implemented.
+ let can_construct_from_value = out.types.is_maybe_trivial(ident);
+
+ writeln!(
+ out,
+ "static_assert(sizeof(::std::shared_ptr<{}>) == 2 * sizeof(void *), \"\");",
+ inner,
+ );
+ writeln!(
+ out,
+ "static_assert(alignof(::std::shared_ptr<{}>) == alignof(void *), \"\");",
+ inner,
+ );
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void cxxbridge1$shared_ptr${}$null(::std::shared_ptr<{}> *ptr) noexcept {{",
+ instance, inner,
+ );
+ writeln!(out, " ::new (ptr) ::std::shared_ptr<{}>();", inner);
+ writeln!(out, "}}");
+ if can_construct_from_value {
+ out.builtin.maybe_uninit = true;
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "{} *cxxbridge1$shared_ptr${}$uninit(::std::shared_ptr<{}> *ptr) noexcept {{",
+ inner, instance, inner,
+ );
+ writeln!(
+ out,
+ " {} *uninit = reinterpret_cast<{} *>(new ::rust::MaybeUninit<{}>);",
+ inner, inner, inner,
+ );
+ writeln!(out, " ::new (ptr) ::std::shared_ptr<{}>(uninit);", inner);
+ writeln!(out, " return uninit;");
+ writeln!(out, "}}");
+ }
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void cxxbridge1$shared_ptr${}$clone(const ::std::shared_ptr<{}>& self, ::std::shared_ptr<{}> *ptr) noexcept {{",
+ instance, inner, inner,
+ );
+ writeln!(out, " ::new (ptr) ::std::shared_ptr<{}>(self);", inner);
+ writeln!(out, "}}");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "const {} *cxxbridge1$shared_ptr${}$get(const ::std::shared_ptr<{}>& self) noexcept {{",
+ inner, instance, inner,
+ );
+ writeln!(out, " return self.get();");
+ writeln!(out, "}}");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void cxxbridge1$shared_ptr${}$drop(::std::shared_ptr<{}> *self) noexcept {{",
+ instance, inner,
+ );
+ writeln!(out, " self->~shared_ptr();");
+ writeln!(out, "}}");
+}
+
+fn write_weak_ptr(out: &mut OutFile, key: NamedImplKey) {
+ let resolve = out.types.resolve(&key);
+ let inner = resolve.name.to_fully_qualified();
+ let instance = resolve.name.to_symbol();
+
+ out.include.new = true;
+ out.include.utility = true;
+
+ writeln!(
+ out,
+ "static_assert(sizeof(::std::weak_ptr<{}>) == 2 * sizeof(void *), \"\");",
+ inner,
+ );
+ writeln!(
+ out,
+ "static_assert(alignof(::std::weak_ptr<{}>) == alignof(void *), \"\");",
+ inner,
+ );
+ writeln!(
+ out,
+ "void cxxbridge1$weak_ptr${}$null(::std::weak_ptr<{}> *ptr) noexcept {{",
+ instance, inner,
+ );
+ writeln!(out, " ::new (ptr) ::std::weak_ptr<{}>();", inner);
+ writeln!(out, "}}");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void cxxbridge1$weak_ptr${}$clone(const ::std::weak_ptr<{}>& self, ::std::weak_ptr<{}> *ptr) noexcept {{",
+ instance, inner, inner,
+ );
+ writeln!(out, " ::new (ptr) ::std::weak_ptr<{}>(self);", inner);
+ writeln!(out, "}}");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void cxxbridge1$weak_ptr${}$downgrade(const ::std::shared_ptr<{}>& shared, ::std::weak_ptr<{}> *weak) noexcept {{",
+ instance, inner, inner,
+ );
+ writeln!(out, " ::new (weak) ::std::weak_ptr<{}>(shared);", inner);
+ writeln!(out, "}}");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void cxxbridge1$weak_ptr${}$upgrade(const ::std::weak_ptr<{}>& weak, ::std::shared_ptr<{}> *shared) noexcept {{",
+ instance, inner, inner,
+ );
+ writeln!(
+ out,
+ " ::new (shared) ::std::shared_ptr<{}>(weak.lock());",
+ inner,
+ );
+ writeln!(out, "}}");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void cxxbridge1$weak_ptr${}$drop(::std::weak_ptr<{}> *self) noexcept {{",
+ instance, inner,
+ );
+ writeln!(out, " self->~weak_ptr();");
+ writeln!(out, "}}");
+}
+
+fn write_cxx_vector(out: &mut OutFile, key: NamedImplKey) {
+ let element = key.rust;
+ let inner = element.to_typename(out.types);
+ let instance = element.to_mangled(out.types);
+
+ out.include.cstddef = true;
+ out.include.utility = true;
+ out.builtin.destroy = true;
+
+ writeln!(
+ out,
+ "::std::size_t cxxbridge1$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{",
+ instance, inner,
+ );
+ writeln!(out, " return s.size();");
+ writeln!(out, "}}");
+
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "{} *cxxbridge1$std$vector${}$get_unchecked(::std::vector<{}> *s, ::std::size_t pos) noexcept {{",
+ inner, instance, inner,
+ );
+ writeln!(out, " return &(*s)[pos];");
+ writeln!(out, "}}");
+
+ if out.types.is_maybe_trivial(element) {
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void cxxbridge1$std$vector${}$push_back(::std::vector<{}> *v, {} *value) noexcept {{",
+ instance, inner, inner,
+ );
+ writeln!(out, " v->push_back(::std::move(*value));");
+ writeln!(out, " ::rust::destroy(value);");
+ writeln!(out, "}}");
+
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void cxxbridge1$std$vector${}$pop_back(::std::vector<{}> *v, {} *out) noexcept {{",
+ instance, inner, inner,
+ );
+ writeln!(out, " ::new (out) {}(::std::move(v->back()));", inner);
+ writeln!(out, " v->pop_back();");
+ writeln!(out, "}}");
+ }
+
+ out.include.memory = true;
+ write_unique_ptr_common(out, UniquePtr::CxxVector(element));
+}
--- /dev/null
+#![allow(
+ clippy::cast_sign_loss,
+ clippy::cognitive_complexity,
+ clippy::default_trait_access,
+ clippy::enum_glob_use,
+ clippy::if_same_then_else,
+ clippy::inherent_to_string,
+ clippy::items_after_statements,
+ clippy::large_enum_variant,
+ clippy::match_bool,
+ clippy::match_on_vec_items,
+ clippy::match_same_arms,
+ clippy::module_name_repetitions,
+ clippy::needless_pass_by_value,
+ clippy::new_without_default,
+ clippy::nonminimal_bool,
+ clippy::option_if_let_else,
+ clippy::or_fun_call,
+ clippy::redundant_else,
+ clippy::shadow_unrelated,
+ clippy::similar_names,
+ clippy::single_match_else,
+ clippy::struct_excessive_bools,
+ clippy::too_many_arguments,
+ clippy::too_many_lines,
+ clippy::toplevel_ref_arg,
+ // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6983
+ clippy::wrong_self_convention
+)]
+
+mod app;
+mod gen;
+mod output;
+mod syntax;
+
+use crate::gen::error::{report, Result};
+use crate::gen::fs;
+use crate::gen::include::{self, Include};
+use crate::output::Output;
+use std::io::{self, Write};
+use std::path::PathBuf;
+use std::process;
+
+#[derive(Debug)]
+struct Opt {
+ input: Option<PathBuf>,
+ header: bool,
+ cxx_impl_annotations: Option<String>,
+ include: Vec<Include>,
+ outputs: Vec<Output>,
+}
+
+fn main() {
+ if let Err(err) = try_main() {
+ let _ = writeln!(io::stderr(), "cxxbridge: {}", report(err));
+ process::exit(1);
+ }
+}
+
+enum Kind {
+ GeneratedHeader,
+ GeneratedImplementation,
+ Header,
+}
+
+fn try_main() -> Result<()> {
+ let opt = app::from_args();
+
+ let mut outputs = Vec::new();
+ let mut gen_header = false;
+ let mut gen_implementation = false;
+ for output in opt.outputs {
+ let kind = if opt.input.is_none() {
+ Kind::Header
+ } else if opt.header
+ || output.ends_with(".h")
+ || output.ends_with(".hh")
+ || output.ends_with(".hpp")
+ {
+ gen_header = true;
+ Kind::GeneratedHeader
+ } else {
+ gen_implementation = true;
+ Kind::GeneratedImplementation
+ };
+ outputs.push((output, kind));
+ }
+
+ let gen = gen::Opt {
+ include: opt.include,
+ cxx_impl_annotations: opt.cxx_impl_annotations,
+ gen_header,
+ gen_implementation,
+ ..Default::default()
+ };
+
+ let generated_code = if let Some(input) = opt.input {
+ gen::generate_from_path(&input, &gen)
+ } else {
+ Default::default()
+ };
+
+ for (output, kind) in outputs {
+ let content = match kind {
+ Kind::GeneratedHeader => &generated_code.header,
+ Kind::GeneratedImplementation => &generated_code.implementation,
+ Kind::Header => include::HEADER.as_bytes(),
+ };
+ match output {
+ Output::Stdout => drop(io::stdout().write_all(content)),
+ Output::File(path) => fs::write(path, content)?,
+ }
+ }
+
+ Ok(())
+}
--- /dev/null
+use std::path::PathBuf;
+
+#[derive(Debug)]
+pub(crate) enum Output {
+ Stdout,
+ File(PathBuf),
+}
+
+impl Output {
+ pub(crate) fn ends_with(&self, suffix: &str) -> bool {
+ match self {
+ Output::Stdout => false,
+ Output::File(path) => path.to_string_lossy().ends_with(suffix),
+ }
+ }
+}
--- /dev/null
+use crate::syntax::Type;
+use proc_macro2::Ident;
+use std::fmt::{self, Display};
+
+#[derive(Copy, Clone, PartialEq)]
+pub 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 fn from(ident: &Ident) -> Option<Self> {
+ Self::from_str(ident.to_string().as_str())
+ }
+
+ pub fn from_str(s: &str) -> Option<Self> {
+ 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<str> 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<Atom> for Type {
+ fn eq(&self, atom: &Atom) -> bool {
+ match self {
+ Type::Ident(ident) => ident.rust == atom,
+ _ => false,
+ }
+ }
+}
+
+impl PartialEq<Atom> for &Ident {
+ fn eq(&self, atom: &Atom) -> bool {
+ *self == atom
+ }
+}
+
+impl PartialEq<Atom> for &Type {
+ fn eq(&self, atom: &Atom) -> bool {
+ *self == atom
+ }
+}
--- /dev/null
+use crate::syntax::namespace::Namespace;
+use crate::syntax::report::Errors;
+use crate::syntax::Atom::{self, *};
+use crate::syntax::{Derive, Doc, ForeignName};
+use proc_macro2::{Ident, TokenStream};
+use quote::ToTokens;
+use syn::parse::{Nothing, Parse, ParseStream, Parser as _};
+use syn::{parenthesized, token, Attribute, Error, LitStr, 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 struct Parser<'a> {
+ pub doc: Option<&'a mut Doc>,
+ pub derives: Option<&'a mut Vec<Derive>>,
+ pub repr: Option<&'a mut Option<Atom>>,
+ pub namespace: Option<&'a mut Namespace>,
+ pub cxx_name: Option<&'a mut Option<ForeignName>>,
+ pub rust_name: Option<&'a mut Option<Ident>>,
+ pub variants_from_header: Option<&'a mut Option<Attribute>>,
+
+ // Suppress clippy needless_update lint ("struct update has no effect, all
+ // the fields in the struct have already been specified") when preemptively
+ // writing `..Default::default()`.
+ pub(crate) _more: (),
+}
+
+pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
+ let mut passthrough_attrs = Vec::new();
+ for attr in attrs {
+ if attr.path.is_ident("doc") {
+ match parse_doc_attribute.parse2(attr.tokens.clone()) {
+ Ok(attr) => {
+ if let Some(doc) = &mut parser.doc {
+ match attr {
+ DocAttribute::Doc(lit) => doc.push(lit),
+ DocAttribute::Hidden => doc.hidden = true,
+ }
+ continue;
+ }
+ }
+ Err(err) => {
+ cx.push(err);
+ break;
+ }
+ }
+ } else if attr.path.is_ident("derive") {
+ match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
+ Ok(attr) => {
+ if let Some(derives) = &mut parser.derives {
+ derives.extend(attr);
+ continue;
+ }
+ }
+ Err(err) => {
+ cx.push(err);
+ break;
+ }
+ }
+ } else if attr.path.is_ident("repr") {
+ match attr.parse_args_with(parse_repr_attribute) {
+ Ok(attr) => {
+ if let Some(repr) = &mut parser.repr {
+ **repr = Some(attr);
+ continue;
+ }
+ }
+ Err(err) => {
+ cx.push(err);
+ break;
+ }
+ }
+ } else if attr.path.is_ident("namespace") {
+ match parse_namespace_attribute.parse2(attr.tokens.clone()) {
+ 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.parse2(attr.tokens.clone()) {
+ Ok(attr) => {
+ if let Some(cxx_name) = &mut parser.cxx_name {
+ **cxx_name = Some(attr);
+ continue;
+ }
+ }
+ Err(err) => {
+ cx.push(err);
+ break;
+ }
+ }
+ } else if attr.path.is_ident("rust_name") {
+ match parse_rust_name_attribute.parse2(attr.tokens.clone()) {
+ 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("variants_from_header") && cfg!(feature = "experimental") {
+ if let Err(err) = Nothing::parse.parse2(attr.tokens.clone()) {
+ cx.push(err);
+ }
+ if let Some(variants_from_header) = &mut parser.variants_from_header {
+ **variants_from_header = Some(attr);
+ continue;
+ }
+ } else if attr.path.is_ident("allow")
+ || attr.path.is_ident("warn")
+ || attr.path.is_ident("deny")
+ || attr.path.is_ident("forbid")
+ || attr.path.is_ident("deprecated")
+ || attr.path.is_ident("must_use")
+ {
+ // https://doc.rust-lang.org/reference/attributes/diagnostics.html
+ passthrough_attrs.push(attr);
+ continue;
+ } else if attr.path.is_ident("serde") {
+ passthrough_attrs.push(attr);
+ continue;
+ } else if attr.path.segments.len() > 1 {
+ let tool = &attr.path.segments.first().unwrap().ident;
+ if tool == "rustfmt" {
+ // Skip, rustfmt only needs to find it in the pre-expansion source file.
+ continue;
+ } else if tool == "clippy" {
+ passthrough_attrs.push(attr);
+ continue;
+ }
+ }
+ cx.error(attr, "unsupported attribute");
+ break;
+ }
+ OtherAttrs(passthrough_attrs)
+}
+
+enum DocAttribute {
+ Doc(LitStr),
+ Hidden,
+}
+
+mod kw {
+ syn::custom_keyword!(hidden);
+}
+
+fn parse_doc_attribute(input: ParseStream) -> Result<DocAttribute> {
+ let lookahead = input.lookahead1();
+ if lookahead.peek(Token![=]) {
+ input.parse::<Token![=]>()?;
+ let lit: LitStr = input.parse()?;
+ Ok(DocAttribute::Doc(lit))
+ } else if lookahead.peek(token::Paren) {
+ let content;
+ parenthesized!(content in input);
+ content.parse::<kw::hidden>()?;
+ Ok(DocAttribute::Hidden)
+ } else {
+ Err(lookahead.error())
+ }
+}
+
+fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
+ let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;
+
+ let mut derives = Vec::new();
+ for path in paths {
+ if let Some(ident) = path.get_ident() {
+ if let Some(derive) = Derive::from(ident) {
+ derives.push(derive);
+ continue;
+ }
+ }
+ cx.error(path, "unsupported derive");
+ }
+ Ok(derives)
+}
+
+fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
+ let begin = input.cursor();
+ let ident: Ident = input.parse()?;
+ if let Some(atom) = Atom::from(&ident) {
+ match atom {
+ U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
+ return Ok(atom);
+ }
+ _ => {}
+ }
+ }
+ Err(Error::new_spanned(
+ begin.token_stream(),
+ "unrecognized repr",
+ ))
+}
+
+fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
+ input.parse::<Token![=]>()?;
+ let namespace = input.parse::<Namespace>()?;
+ Ok(namespace)
+}
+
+fn parse_cxx_name_attribute(input: ParseStream) -> Result<ForeignName> {
+ input.parse::<Token![=]>()?;
+ if input.peek(LitStr) {
+ let lit: LitStr = input.parse()?;
+ ForeignName::parse(&lit.value(), lit.span())
+ } else {
+ let ident: Ident = input.parse()?;
+ ForeignName::parse(&ident.to_string(), ident.span())
+ }
+}
+
+fn parse_rust_name_attribute(input: ParseStream) -> Result<Ident> {
+ input.parse::<Token![=]>()?;
+ if input.peek(LitStr) {
+ let lit: LitStr = input.parse()?;
+ lit.parse()
+ } else {
+ input.parse()
+ }
+}
+
+pub struct OtherAttrs(Vec<Attribute>);
+
+impl OtherAttrs {
+ pub fn none() -> Self {
+ OtherAttrs(Vec::new())
+ }
+}
+
+impl ToTokens for OtherAttrs {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ for attr in &self.0 {
+ let Attribute {
+ pound_token,
+ style,
+ bracket_token,
+ path,
+ tokens: attr_tokens,
+ } = attr;
+ pound_token.to_tokens(tokens);
+ let _ = style; // ignore; render outer and inner attrs both as outer
+ bracket_token.surround(tokens, |tokens| {
+ path.to_tokens(tokens);
+ attr_tokens.to_tokens(tokens);
+ });
+ }
+ }
+}
--- /dev/null
+use crate::syntax::atom::Atom::{self, *};
+use crate::syntax::report::Errors;
+use crate::syntax::visit::{self, Visit};
+use crate::syntax::{
+ error, ident, trivial, Api, Array, Enum, ExternFn, ExternType, Impl, Lang, Lifetimes,
+ NamedType, Ptr, Receiver, Ref, Signature, SliceRef, Struct, Trait, Ty1, Type, TypeAlias, Types,
+};
+use proc_macro2::{Delimiter, Group, Ident, TokenStream};
+use quote::{quote, ToTokens};
+use std::fmt::Display;
+use syn::{GenericParam, Generics, Lifetime};
+
+pub(crate) struct Check<'a> {
+ apis: &'a [Api],
+ types: &'a Types<'a>,
+ errors: &'a mut Errors,
+ generator: Generator,
+}
+
+pub(crate) enum Generator {
+ // cxx-build crate, cxxbridge cli, cxx-gen.
+ #[allow(dead_code)]
+ Build,
+ // cxxbridge-macro. This is relevant in that the macro output is going to
+ // get fed straight to rustc, so for errors that rustc already contains
+ // logic to catch (probably with a better diagnostic than what the proc
+ // macro API is able to produce), we avoid duplicating them in our own
+ // diagnostics.
+ #[allow(dead_code)]
+ Macro,
+}
+
+pub(crate) fn typecheck(cx: &mut Errors, apis: &[Api], types: &Types, generator: Generator) {
+ do_typecheck(&mut Check {
+ apis,
+ types,
+ errors: cx,
+ generator,
+ });
+}
+
+fn do_typecheck(cx: &mut Check) {
+ ident::check_all(cx, cx.apis);
+
+ for ty in cx.types {
+ match ty {
+ Type::Ident(ident) => check_type_ident(cx, ident),
+ Type::RustBox(ptr) => check_type_box(cx, ptr),
+ Type::RustVec(ty) => check_type_rust_vec(cx, ty),
+ Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
+ Type::SharedPtr(ptr) => check_type_shared_ptr(cx, ptr),
+ Type::WeakPtr(ptr) => check_type_weak_ptr(cx, ptr),
+ Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr),
+ Type::Ref(ty) => check_type_ref(cx, ty),
+ Type::Ptr(ty) => check_type_ptr(cx, ty),
+ Type::Array(array) => check_type_array(cx, array),
+ Type::Fn(ty) => check_type_fn(cx, ty),
+ Type::SliceRef(ty) => check_type_slice_ref(cx, ty),
+ Type::Str(_) | Type::Void(_) => {}
+ }
+ }
+
+ for api in cx.apis {
+ match api {
+ Api::Include(_) => {}
+ Api::Struct(strct) => check_api_struct(cx, strct),
+ Api::Enum(enm) => check_api_enum(cx, enm),
+ Api::CxxType(ety) | Api::RustType(ety) => check_api_type(cx, ety),
+ Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
+ Api::TypeAlias(alias) => check_api_type_alias(cx, alias),
+ Api::Impl(imp) => check_api_impl(cx, imp),
+ }
+ }
+}
+
+impl Check<'_> {
+ pub(crate) fn error(&mut self, sp: impl ToTokens, msg: impl Display) {
+ self.errors.error(sp, msg);
+ }
+}
+
+fn check_type_ident(cx: &mut Check, name: &NamedType) {
+ let ident = &name.rust;
+ if Atom::from(ident).is_none()
+ && !cx.types.structs.contains_key(ident)
+ && !cx.types.enums.contains_key(ident)
+ && !cx.types.cxx.contains(ident)
+ && !cx.types.rust.contains(ident)
+ {
+ let msg = format!("unsupported type: {}", ident);
+ cx.error(ident, &msg);
+ }
+}
+
+fn check_type_box(cx: &mut Check, ptr: &Ty1) {
+ if let Type::Ident(ident) = &ptr.inner {
+ if cx.types.cxx.contains(&ident.rust)
+ && !cx.types.aliases.contains_key(&ident.rust)
+ && !cx.types.structs.contains_key(&ident.rust)
+ && !cx.types.enums.contains_key(&ident.rust)
+ {
+ cx.error(ptr, error::BOX_CXX_TYPE.msg);
+ }
+
+ if Atom::from(&ident.rust).is_none() {
+ return;
+ }
+ }
+
+ cx.error(ptr, "unsupported target type of Box");
+}
+
+fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) {
+ match &ty.inner {
+ Type::Ident(ident) => {
+ if cx.types.cxx.contains(&ident.rust)
+ && !cx.types.aliases.contains_key(&ident.rust)
+ && !cx.types.structs.contains_key(&ident.rust)
+ && !cx.types.enums.contains_key(&ident.rust)
+ {
+ cx.error(ty, "Rust Vec containing C++ type is not supported yet");
+ return;
+ }
+
+ match Atom::from(&ident.rust) {
+ None | Some(Char) | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize)
+ | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32)
+ | Some(F64) | Some(RustString) => return,
+ Some(Bool) => { /* todo */ }
+ Some(CxxString) => {}
+ }
+ }
+ Type::Str(_) => return,
+ _ => {}
+ }
+
+ cx.error(ty, "unsupported element type of Vec");
+}
+
+fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
+ if let Type::Ident(ident) = &ptr.inner {
+ if cx.types.rust.contains(&ident.rust) {
+ cx.error(ptr, "unique_ptr of a Rust type is not supported yet");
+ return;
+ }
+
+ match Atom::from(&ident.rust) {
+ None | Some(CxxString) => return,
+ _ => {}
+ }
+ } else if let Type::CxxVector(_) = &ptr.inner {
+ return;
+ }
+
+ cx.error(ptr, "unsupported unique_ptr target type");
+}
+
+fn check_type_shared_ptr(cx: &mut Check, ptr: &Ty1) {
+ if let Type::Ident(ident) = &ptr.inner {
+ if cx.types.rust.contains(&ident.rust) {
+ cx.error(ptr, "shared_ptr of a Rust type is not supported yet");
+ return;
+ }
+
+ match Atom::from(&ident.rust) {
+ None | Some(Bool) | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize)
+ | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32)
+ | Some(F64) | Some(CxxString) => return,
+ Some(Char) | Some(RustString) => {}
+ }
+ } else if let Type::CxxVector(_) = &ptr.inner {
+ cx.error(ptr, "std::shared_ptr<std::vector> 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) | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize)
+ | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32)
+ | Some(F64) | Some(CxxString) => return,
+ Some(Char) | Some(RustString) => {}
+ }
+ } else if let Type::CxxVector(_) = &ptr.inner {
+ cx.error(ptr, "std::weak_ptr<std::vector> 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) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8)
+ | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64)
+ | Some(CxxString) => return,
+ Some(Char) => { /* todo */ }
+ Some(Bool) | Some(RustString) => {}
+ }
+ }
+
+ cx.error(ptr, "unsupported vector element type");
+}
+
+fn check_type_ref(cx: &mut Check, ty: &Ref) {
+ if ty.mutable && !ty.pinned {
+ if let Some(requires_pin) = match &ty.inner {
+ Type::Ident(ident) if ident.rust == CxxString || is_opaque_cxx(cx, &ident.rust) => {
+ Some(ident.rust.to_string())
+ }
+ Type::CxxVector(_) => Some("CxxVector<...>".to_owned()),
+ _ => None,
+ } {
+ cx.error(
+ ty,
+ format!(
+ "mutable reference to C++ type requires a pin -- use Pin<&mut {}>",
+ requires_pin,
+ ),
+ );
+ }
+ }
+
+ match ty.inner {
+ Type::Fn(_) | Type::Void(_) => {}
+ Type::Ref(_) => {
+ cx.error(ty, "C++ does not allow references to references");
+ return;
+ }
+ _ => return,
+ }
+
+ cx.error(ty, "unsupported reference type");
+}
+
+fn check_type_ptr(cx: &mut Check, ty: &Ptr) {
+ match ty.inner {
+ Type::Fn(_) | Type::Void(_) => {}
+ Type::Ref(_) => {
+ cx.error(ty, "C++ does not allow pointer to reference as a type");
+ return;
+ }
+ _ => return,
+ }
+
+ cx.error(ty, "unsupported pointer type");
+}
+
+fn check_type_slice_ref(cx: &mut Check, ty: &SliceRef) {
+ let supported = !is_unsized(cx, &ty.inner)
+ || match &ty.inner {
+ Type::Ident(ident) => {
+ cx.types.rust.contains(&ident.rust) || cx.types.aliases.contains_key(&ident.rust)
+ }
+ _ => false,
+ };
+
+ if !supported {
+ let mutable = if ty.mutable { "mut " } else { "" };
+ let mut msg = format!("unsupported &{}[T] element type", mutable);
+ if let Type::Ident(ident) = &ty.inner {
+ if is_opaque_cxx(cx, &ident.rust) {
+ msg += ": opaque C++ type is not supported yet";
+ }
+ }
+ cx.error(ty, msg);
+ }
+}
+
+fn check_type_array(cx: &mut Check, ty: &Array) {
+ let supported = !is_unsized(cx, &ty.inner);
+
+ if !supported {
+ cx.error(ty, "unsupported array element type");
+ }
+}
+
+fn check_type_fn(cx: &mut Check, ty: &Signature) {
+ if ty.throws {
+ cx.error(ty, "function pointer returning Result is not supported yet");
+ }
+
+ for arg in &ty.args {
+ if let Type::Ptr(_) = arg.ty {
+ if ty.unsafety.is_none() {
+ cx.error(
+ arg,
+ "pointer argument requires that the function pointer be marked unsafe",
+ );
+ }
+ }
+ }
+}
+
+fn check_api_struct(cx: &mut Check, strct: &Struct) {
+ let name = &strct.name;
+ check_reserved_name(cx, &name.rust);
+ check_lifetimes(cx, &strct.generics);
+
+ if strct.fields.is_empty() {
+ let span = span_for_struct_error(strct);
+ cx.error(span, "structs without any fields are not supported");
+ }
+
+ if cx.types.cxx.contains(&name.rust) {
+ if let Some(ety) = cx.types.untrusted.get(&name.rust) {
+ let msg = "extern shared struct must be declared in an `unsafe extern` block";
+ cx.error(ety, msg);
+ }
+ }
+
+ for derive in &strct.derives {
+ if derive.what == Trait::ExternType {
+ let msg = format!("derive({}) on shared struct is not supported", derive);
+ cx.error(derive, msg);
+ }
+ }
+
+ for field in &strct.fields {
+ if let Type::Fn(_) = field.ty {
+ cx.error(
+ field,
+ "function pointers in a struct field are not implemented yet",
+ );
+ } else if is_unsized(cx, &field.ty) {
+ let desc = describe(cx, &field.ty);
+ let msg = format!("using {} by value is not supported", desc);
+ cx.error(field, msg);
+ }
+ }
+}
+
+fn check_api_enum(cx: &mut Check, enm: &Enum) {
+ check_reserved_name(cx, &enm.name.rust);
+ check_lifetimes(cx, &enm.generics);
+
+ if enm.variants.is_empty() && !enm.explicit_repr && !enm.variants_from_header {
+ let span = span_for_enum_error(enm);
+ cx.error(
+ span,
+ "explicit #[repr(...)] is required for enum without any variants",
+ );
+ }
+
+ for derive in &enm.derives {
+ if derive.what == Trait::Default || derive.what == Trait::ExternType {
+ let msg = format!("derive({}) on shared enum is not supported", derive);
+ cx.error(derive, msg);
+ }
+ }
+}
+
+fn check_api_type(cx: &mut Check, ety: &ExternType) {
+ check_reserved_name(cx, &ety.name.rust);
+ check_lifetimes(cx, &ety.generics);
+
+ for derive in &ety.derives {
+ if derive.what == Trait::ExternType && ety.lang == Lang::Rust {
+ continue;
+ }
+ let lang = match ety.lang {
+ Lang::Rust => "Rust",
+ Lang::Cxx => "C++",
+ };
+ let msg = format!(
+ "derive({}) on opaque {} type is not supported yet",
+ derive, lang,
+ );
+ cx.error(derive, msg);
+ }
+
+ if !ety.bounds.is_empty() {
+ let bounds = &ety.bounds;
+ let span = quote!(#(#bounds)*);
+ cx.error(span, "extern type bounds are not implemented yet");
+ }
+
+ if let Some(reasons) = cx.types.required_trivial.get(&ety.name.rust) {
+ let msg = format!(
+ "needs a cxx::ExternType impl in order to be used as {}",
+ trivial::as_what(&ety.name, reasons),
+ );
+ cx.error(ety, msg);
+ }
+}
+
+fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
+ match efn.lang {
+ Lang::Cxx => {
+ if !efn.generics.params.is_empty() && !efn.trusted {
+ let ref span = span_for_generics_error(efn);
+ cx.error(span, "extern C++ function with lifetimes must be declared in `unsafe extern \"C++\"` block");
+ }
+ }
+ Lang::Rust => {
+ if !efn.generics.params.is_empty() && efn.unsafety.is_none() {
+ let ref span = span_for_generics_error(efn);
+ let message = format!(
+ "must be `unsafe fn {}` in order to expose explicit lifetimes to C++",
+ efn.name.rust,
+ );
+ cx.error(span, message);
+ }
+ }
+ }
+
+ check_generics(cx, &efn.sig.generics);
+
+ if let Some(receiver) = &efn.receiver {
+ let ref span = span_for_receiver_error(receiver);
+
+ if receiver.ty.rust == "Self" {
+ let mutability = match receiver.mutable {
+ true => "mut ",
+ false => "",
+ };
+ let msg = format!(
+ "unnamed receiver type is only allowed if the surrounding extern block contains exactly one extern type; use `self: &{mutability}TheType`",
+ mutability = mutability,
+ );
+ cx.error(span, msg);
+ } else if cx.types.enums.contains_key(&receiver.ty.rust) {
+ cx.error(
+ span,
+ "unsupported receiver type; C++ does not allow member functions on enums",
+ );
+ } else if !cx.types.structs.contains_key(&receiver.ty.rust)
+ && !cx.types.cxx.contains(&receiver.ty.rust)
+ && !cx.types.rust.contains(&receiver.ty.rust)
+ {
+ cx.error(span, "unrecognized receiver type");
+ } else if receiver.mutable && !receiver.pinned && is_opaque_cxx(cx, &receiver.ty.rust) {
+ cx.error(
+ span,
+ format!(
+ "mutable reference to opaque C++ type requires a pin -- use `self: Pin<&mut {}>`",
+ receiver.ty.rust,
+ ),
+ );
+ }
+ }
+
+ for arg in &efn.args {
+ if let Type::Fn(_) = arg.ty {
+ if efn.lang == Lang::Rust {
+ cx.error(
+ arg,
+ "passing a function pointer from C++ to Rust is not implemented yet",
+ );
+ }
+ } else if let Type::Ptr(_) = arg.ty {
+ if efn.sig.unsafety.is_none() {
+ cx.error(
+ arg,
+ "pointer argument requires that the function be marked unsafe",
+ );
+ }
+ } else if is_unsized(cx, &arg.ty) {
+ let desc = describe(cx, &arg.ty);
+ let msg = format!("passing {} by value is not supported", desc);
+ cx.error(arg, msg);
+ }
+ }
+
+ if let Some(ty) = &efn.ret {
+ if let Type::Fn(_) = ty {
+ cx.error(ty, "returning a function pointer is not implemented yet");
+ } else if is_unsized(cx, ty) {
+ let desc = describe(cx, ty);
+ let msg = format!("returning {} by value is not supported", desc);
+ cx.error(ty, msg);
+ }
+ }
+
+ if efn.lang == Lang::Cxx {
+ check_mut_return_restriction(cx, efn);
+ }
+}
+
+fn check_api_type_alias(cx: &mut Check, alias: &TypeAlias) {
+ check_lifetimes(cx, &alias.generics);
+
+ for derive in &alias.derives {
+ let msg = format!("derive({}) on extern type alias is not supported", derive);
+ cx.error(derive, msg);
+ }
+}
+
+fn check_api_impl(cx: &mut Check, imp: &Impl) {
+ let ty = &imp.ty;
+
+ check_lifetimes(cx, &imp.impl_generics);
+
+ if let Some(negative) = imp.negative_token {
+ let span = quote!(#negative #ty);
+ cx.error(span, "negative impl is not supported yet");
+ return;
+ }
+
+ match ty {
+ Type::RustBox(ty)
+ | Type::RustVec(ty)
+ | Type::UniquePtr(ty)
+ | Type::SharedPtr(ty)
+ | Type::WeakPtr(ty)
+ | Type::CxxVector(ty) => {
+ if let Type::Ident(inner) = &ty.inner {
+ if Atom::from(&inner.rust).is_none() {
+ return;
+ }
+ }
+ }
+ _ => {}
+ }
+
+ cx.error(imp, "unsupported Self type of explicit impl");
+}
+
+fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
+ if efn.sig.unsafety.is_some() {
+ // Unrestricted as long as the function is made unsafe-to-call.
+ return;
+ }
+
+ match &efn.ret {
+ Some(Type::Ref(ty)) if ty.mutable => {}
+ Some(Type::SliceRef(slice)) if slice.mutable => {}
+ _ => return,
+ }
+
+ if let Some(receiver) = &efn.receiver {
+ if receiver.mutable {
+ return;
+ }
+ let resolve = match cx.types.try_resolve(&receiver.ty) {
+ Some(resolve) => resolve,
+ None => return,
+ };
+ if !resolve.generics.lifetimes.is_empty() {
+ return;
+ }
+ }
+
+ struct FindLifetimeMut<'a> {
+ cx: &'a Check<'a>,
+ found: bool,
+ }
+
+ impl<'t, 'a> Visit<'t> for FindLifetimeMut<'a> {
+ fn visit_type(&mut self, ty: &'t Type) {
+ self.found |= match ty {
+ Type::Ref(ty) => ty.mutable,
+ Type::SliceRef(slice) => slice.mutable,
+ Type::Ident(ident) if Atom::from(&ident.rust).is_none() => {
+ match self.cx.types.try_resolve(ident) {
+ Some(resolve) => !resolve.generics.lifetimes.is_empty(),
+ None => true,
+ }
+ }
+ _ => false,
+ };
+ visit::visit_type(self, ty);
+ }
+ }
+
+ let mut visitor = FindLifetimeMut { cx, found: false };
+
+ for arg in &efn.args {
+ visitor.visit_type(&arg.ty);
+ }
+
+ if visitor.found {
+ return;
+ }
+
+ cx.error(
+ efn,
+ "&mut return type is not allowed unless there is a &mut argument",
+ );
+}
+
+fn check_reserved_name(cx: &mut Check, ident: &Ident) {
+ if ident == "Box"
+ || ident == "UniquePtr"
+ || ident == "SharedPtr"
+ || ident == "WeakPtr"
+ || ident == "Vec"
+ || ident == "CxxVector"
+ || ident == "str"
+ || Atom::from(ident).is_some()
+ {
+ cx.error(ident, "reserved name");
+ }
+}
+
+fn check_reserved_lifetime(cx: &mut Check, lifetime: &Lifetime) {
+ if lifetime.ident == "static" {
+ match cx.generator {
+ Generator::Macro => { /* rustc already reports this */ }
+ Generator::Build => {
+ cx.error(lifetime, error::RESERVED_LIFETIME);
+ }
+ }
+ }
+}
+
+fn check_lifetimes(cx: &mut Check, generics: &Lifetimes) {
+ for lifetime in &generics.lifetimes {
+ check_reserved_lifetime(cx, lifetime);
+ }
+}
+
+fn check_generics(cx: &mut Check, generics: &Generics) {
+ for generic_param in &generics.params {
+ if let GenericParam::Lifetime(def) = generic_param {
+ check_reserved_lifetime(cx, &def.lifetime);
+ }
+ }
+}
+
+fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
+ match ty {
+ Type::Ident(ident) => {
+ let ident = &ident.rust;
+ ident == CxxString || is_opaque_cxx(cx, ident) || cx.types.rust.contains(ident)
+ }
+ Type::Array(array) => is_unsized(cx, &array.inner),
+ Type::CxxVector(_) | Type::Fn(_) | Type::Void(_) => true,
+ Type::RustBox(_)
+ | Type::RustVec(_)
+ | Type::UniquePtr(_)
+ | Type::SharedPtr(_)
+ | Type::WeakPtr(_)
+ | Type::Ref(_)
+ | Type::Ptr(_)
+ | Type::Str(_)
+ | Type::SliceRef(_) => false,
+ }
+}
+
+fn is_opaque_cxx(cx: &mut Check, ty: &Ident) -> bool {
+ cx.types.cxx.contains(ty)
+ && !cx.types.structs.contains_key(ty)
+ && !cx.types.enums.contains_key(ty)
+ && !(cx.types.aliases.contains_key(ty) && cx.types.required_trivial.contains_key(ty))
+}
+
+fn span_for_struct_error(strct: &Struct) -> TokenStream {
+ let struct_token = strct.struct_token;
+ let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
+ brace_token.set_span(strct.brace_token.span);
+ 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);
+ quote!(#enum_token #brace_token)
+}
+
+fn span_for_receiver_error(receiver: &Receiver) -> TokenStream {
+ let ampersand = receiver.ampersand;
+ let lifetime = &receiver.lifetime;
+ let mutability = receiver.mutability;
+ if receiver.shorthand {
+ let var = receiver.var;
+ quote!(#ampersand #lifetime #mutability #var)
+ } else {
+ let ty = &receiver.ty;
+ quote!(#ampersand #lifetime #mutability #ty)
+ }
+}
+
+fn span_for_generics_error(efn: &ExternFn) -> TokenStream {
+ let unsafety = efn.unsafety;
+ let fn_token = efn.fn_token;
+ let generics = &efn.generics;
+ quote!(#unsafety #fn_token #generics)
+}
+
+fn describe(cx: &mut Check, ty: &Type) -> String {
+ match ty {
+ Type::Ident(ident) => {
+ if cx.types.structs.contains_key(&ident.rust) {
+ "struct".to_owned()
+ } else if cx.types.enums.contains_key(&ident.rust) {
+ "enum".to_owned()
+ } else if cx.types.aliases.contains_key(&ident.rust) {
+ "C++ type".to_owned()
+ } else if cx.types.cxx.contains(&ident.rust) {
+ "opaque C++ type".to_owned()
+ } else if cx.types.rust.contains(&ident.rust) {
+ "opaque Rust type".to_owned()
+ } else if Atom::from(&ident.rust) == Some(CxxString) {
+ "C++ string".to_owned()
+ } else if Atom::from(&ident.rust) == Some(Char) {
+ "C char".to_owned()
+ } else {
+ ident.rust.to_string()
+ }
+ }
+ Type::RustBox(_) => "Box".to_owned(),
+ Type::RustVec(_) => "Vec".to_owned(),
+ Type::UniquePtr(_) => "unique_ptr".to_owned(),
+ Type::SharedPtr(_) => "shared_ptr".to_owned(),
+ Type::WeakPtr(_) => "weak_ptr".to_owned(),
+ Type::Ref(_) => "reference".to_owned(),
+ Type::Ptr(_) => "raw pointer".to_owned(),
+ Type::Str(_) => "&str".to_owned(),
+ Type::CxxVector(_) => "C++ vector".to_owned(),
+ Type::SliceRef(_) => "slice".to_owned(),
+ Type::Fn(_) => "function pointer".to_owned(),
+ Type::Void(_) => "()".to_owned(),
+ Type::Array(_) => "array".to_owned(),
+ }
+}
--- /dev/null
+use proc_macro2::{Ident, Span};
+use std::fmt::{self, Display};
+
+#[derive(Copy, Clone)]
+pub struct Derive {
+ pub what: Trait,
+ pub span: Span,
+}
+
+#[derive(Copy, Clone, PartialEq)]
+pub enum Trait {
+ Clone,
+ Copy,
+ Debug,
+ Default,
+ Eq,
+ ExternType,
+ Hash,
+ Ord,
+ PartialEq,
+ PartialOrd,
+ Serialize,
+ Deserialize,
+}
+
+impl Derive {
+ pub fn from(ident: &Ident) -> Option<Self> {
+ let what = match ident.to_string().as_str() {
+ "Clone" => Trait::Clone,
+ "Copy" => Trait::Copy,
+ "Debug" => Trait::Debug,
+ "Default" => Trait::Default,
+ "Eq" => Trait::Eq,
+ "ExternType" => Trait::ExternType,
+ "Hash" => Trait::Hash,
+ "Ord" => Trait::Ord,
+ "PartialEq" => Trait::PartialEq,
+ "PartialOrd" => Trait::PartialOrd,
+ "Serialize" => Trait::Serialize,
+ "Deserialize" => Trait::Deserialize,
+ _ => return None,
+ };
+ let span = ident.span();
+ Some(Derive { what, span })
+ }
+}
+
+impl PartialEq<Trait> for Derive {
+ fn eq(&self, other: &Trait) -> bool {
+ self.what == *other
+ }
+}
+
+impl AsRef<str> for Trait {
+ fn as_ref(&self) -> &str {
+ match self {
+ Trait::Clone => "Clone",
+ Trait::Copy => "Copy",
+ Trait::Debug => "Debug",
+ Trait::Default => "Default",
+ Trait::Eq => "Eq",
+ Trait::ExternType => "ExternType",
+ Trait::Hash => "Hash",
+ Trait::Ord => "Ord",
+ Trait::PartialEq => "PartialEq",
+ Trait::PartialOrd => "PartialOrd",
+ Trait::Serialize => "Serialize",
+ Trait::Deserialize => "Deserialize",
+ }
+ }
+}
+
+impl Display for Derive {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str(self.what.as_ref())
+ }
+}
+
+pub fn contains(derives: &[Derive], query: Trait) -> bool {
+ derives.iter().any(|derive| derive.what == query)
+}
--- /dev/null
+use 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 std::u64;
+use syn::{Error, Expr, Lit, Result, Token, UnOp};
+
+pub struct DiscriminantSet {
+ repr: Option<Atom>,
+ values: BTreeSet<Discriminant>,
+ previous: Option<Discriminant>,
+}
+
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub struct Discriminant {
+ sign: Sign,
+ magnitude: u64,
+}
+
+#[derive(Copy, Clone, Eq, PartialEq)]
+enum Sign {
+ Negative,
+ Positive,
+}
+
+impl DiscriminantSet {
+ pub fn new(repr: Option<Atom>) -> Self {
+ DiscriminantSet {
+ repr,
+ values: BTreeSet::new(),
+ previous: None,
+ }
+ }
+
+ pub fn insert(&mut self, expr: &Expr) -> Result<Discriminant> {
+ 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 fn insert_next(&mut self) -> Result<Discriminant> {
+ 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 fn inferred_repr(&self) -> Result<Atom> {
+ 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<Atom>)> {
+ match expr {
+ Expr::Lit(expr) => {
+ if let Lit::Int(lit) = &expr.lit {
+ let discriminant = lit.base10_parse::<Discriminant>()?;
+ 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<Discriminant> {
+ 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 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,
+ }
+ }
+
+ pub const fn checked_succ(self) -> Option<Self> {
+ match self.sign {
+ Sign::Negative => {
+ if self.magnitude == 1 {
+ Some(Discriminant::zero())
+ } else {
+ Some(Discriminant {
+ sign: Sign::Negative,
+ magnitude: self.magnitude - 1,
+ })
+ }
+ }
+ Sign::Positive => match self.magnitude.checked_add(1) {
+ Some(magnitude) => Some(Discriminant {
+ sign: Sign::Positive,
+ magnitude,
+ }),
+ None => None,
+ },
+ }
+ }
+}
+
+impl Display for Discriminant {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if self.sign == Sign::Negative {
+ f.write_str("-")?;
+ }
+ write!(f, "{}", self.magnitude)
+ }
+}
+
+impl ToTokens for Discriminant {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ if self.sign == Sign::Negative {
+ Token).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<Self> {
+ let sign = if s.starts_with('-') {
+ s = &s[1..];
+ Sign::Negative
+ } else {
+ Sign::Positive
+ };
+ match s.parse::<u64>() {
+ 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<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+fn parse_int_suffix(suffix: &str) -> Result<Option<Atom>> {
+ if suffix.is_empty() {
+ return Ok(None);
+ }
+ if let Some(atom) = Atom::from_str(suffix) {
+ match atom {
+ U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize => return Ok(Some(atom)),
+ _ => {}
+ }
+ }
+ let msg = format!("unrecognized integer suffix: `{}`", suffix);
+ Err(Error::new(Span::call_site(), msg))
+}
+
+#[derive(Copy, Clone)]
+struct Limits {
+ repr: Atom,
+ min: Discriminant,
+ max: Discriminant,
+}
+
+impl Limits {
+ fn of(repr: Atom) -> Option<Limits> {
+ 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(std::u8::MAX as u64),
+ },
+ Limits {
+ repr: I8,
+ min: Discriminant::neg(std::i8::MIN as i64),
+ max: Discriminant::pos(std::i8::MAX as u64),
+ },
+ Limits {
+ repr: U16,
+ min: Discriminant::zero(),
+ max: Discriminant::pos(std::u16::MAX as u64),
+ },
+ Limits {
+ repr: I16,
+ min: Discriminant::neg(std::i16::MIN as i64),
+ max: Discriminant::pos(std::i16::MAX as u64),
+ },
+ Limits {
+ repr: U32,
+ min: Discriminant::zero(),
+ max: Discriminant::pos(std::u32::MAX as u64),
+ },
+ Limits {
+ repr: I32,
+ min: Discriminant::neg(std::i32::MIN as i64),
+ max: Discriminant::pos(std::i32::MAX as u64),
+ },
+ Limits {
+ repr: U64,
+ min: Discriminant::zero(),
+ max: Discriminant::pos(std::u64::MAX),
+ },
+ Limits {
+ repr: I64,
+ min: Discriminant::neg(std::i64::MIN),
+ max: Discriminant::pos(std::i64::MAX as u64),
+ },
+];
--- /dev/null
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+use syn::LitStr;
+
+pub struct Doc {
+ pub(crate) hidden: bool,
+ fragments: Vec<LitStr>,
+}
+
+impl Doc {
+ pub fn new() -> Self {
+ Doc {
+ hidden: false,
+ fragments: Vec::new(),
+ }
+ }
+
+ pub fn push(&mut self, lit: LitStr) {
+ self.fragments.push(lit);
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.fragments.is_empty()
+ }
+
+ pub 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)] });
+ }
+ }
+}
--- /dev/null
+use std::fmt::{self, Display};
+
+#[derive(Copy, Clone)]
+pub struct Error {
+ pub msg: &'static str,
+ pub label: Option<&'static str>,
+ pub note: Option<&'static str>,
+}
+
+impl Display for Error {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ self.msg.fmt(formatter)
+ }
+}
+
+pub 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 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 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 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 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 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 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 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 static RESERVED_LIFETIME: Error = Error {
+ msg: "invalid lifetime parameter name: `'static`",
+ label: Some("'static is a reserved lifetime name"),
+ note: None,
+};
+
+pub 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 static UNSUPPORTED_TYPE: Error = Error {
+ msg: "unsupported type: ",
+ label: Some("unsupported type"),
+ note: None,
+};
+
+pub 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",
+ ),
+};
--- /dev/null
+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 struct Module {
+ pub namespace: Namespace,
+ pub attrs: Vec<Attribute>,
+ pub vis: Visibility,
+ pub unsafety: Option<Token![unsafe]>,
+ pub mod_token: Token![mod],
+ pub ident: Ident,
+ pub brace_token: token::Brace,
+ pub content: Vec<Item>,
+}
+
+pub enum Item {
+ Struct(ItemStruct),
+ Enum(ItemEnum),
+ ForeignMod(ItemForeignMod),
+ Use(ItemUse),
+ Impl(ItemImpl),
+ Other(RustItem),
+}
+
+pub struct ItemForeignMod {
+ pub attrs: Vec<Attribute>,
+ pub unsafety: Option<Token![unsafe]>,
+ pub abi: Abi,
+ pub brace_token: token::Brace,
+ pub items: Vec<ForeignItem>,
+}
+
+impl Parse for Module {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let namespace = Namespace::ROOT;
+ let mut attrs = input.call(Attribute::parse_outer)?;
+ let vis: Visibility = input.parse()?;
+ let unsafety: Option<Token![unsafe]> = input.parse()?;
+ let mod_token: Token![mod] = input.parse()?;
+ let ident: Ident = input.parse()?;
+
+ let semi: Option<Token![;]> = 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 {
+ namespace,
+ attrs,
+ vis,
+ unsafety,
+ mod_token,
+ ident,
+ brace_token,
+ content: items,
+ })
+ }
+}
+
+impl Parse for Item {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let attrs = input.call(Attribute::parse_outer)?;
+
+ let ahead = input.fork();
+ let unsafety = if ahead.parse::<Option<Token![unsafe]>>()?.is_some()
+ && ahead.parse::<Option<Token![extern]>>()?.is_some()
+ && ahead.parse::<Option<LitStr>>().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)),
+ }
+ }
+}
--- /dev/null
+use 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);
+ }
+ }
+ }
+}
--- /dev/null
+use 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 {
+ path,
+ kind,
+ begin_span: _,
+ end_span: _,
+ } = self;
+ let Include {
+ 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<H: Hasher>(&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<H: Hasher>(&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<H: Hasher>(&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<H: Hasher>(&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<H: Hasher>(&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<H: Hasher>(&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<H: Hasher>(&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 {
+ unsafety,
+ fn_token: _,
+ generics: _,
+ receiver,
+ args,
+ ret,
+ throws,
+ paren_token: _,
+ throws_tokens: _,
+ } = self;
+ let Signature {
+ unsafety: unsafety2,
+ fn_token: _,
+ generics: _,
+ receiver: receiver2,
+ args: args2,
+ ret: ret2,
+ throws: throws2,
+ paren_token: _,
+ throws_tokens: _,
+ } = other;
+ unsafety.is_some() == unsafety2.is_some()
+ && receiver == receiver2
+ && ret == ret2
+ && throws == throws2
+ && args.len() == args2.len()
+ && args.iter().zip(args2).all(|(arg, arg2)| {
+ let Var {
+ doc: _,
+ attrs: _,
+ visibility: _,
+ name: _,
+ colon_token: _,
+ ty,
+ } = arg;
+ let Var {
+ doc: _,
+ attrs: _,
+ visibility: _,
+ name: _,
+ colon_token: _,
+ ty: ty2,
+ } = arg2;
+ ty == ty2
+ })
+ }
+}
+
+impl Hash for Signature {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ let Signature {
+ unsafety,
+ fn_token: _,
+ generics: _,
+ receiver,
+ args,
+ ret,
+ throws,
+ paren_token: _,
+ throws_tokens: _,
+ } = self;
+ unsafety.is_some().hash(state);
+ receiver.hash(state);
+ for arg in args {
+ let Var {
+ 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<H: Hasher>(&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);
+ }
+}
--- /dev/null
+use self::ImproperCtype::*;
+use crate::syntax::atom::Atom::{self, *};
+use crate::syntax::{Type, Types};
+use proc_macro2::Ident;
+
+pub enum ImproperCtype<'a> {
+ Definite(bool),
+ Depends(&'a Ident),
+}
+
+impl<'a> Types<'a> {
+ // yes, no, maybe
+ pub fn determine_improper_ctype(&self, ty: &Type) -> ImproperCtype<'a> {
+ match ty {
+ Type::Ident(ident) => {
+ let ident = &ident.rust;
+ if let Some(atom) = Atom::from(ident) {
+ Definite(atom == RustString)
+ } else if let Some(strct) = self.structs.get(ident) {
+ Depends(&strct.name.rust) // iterate to fixed-point
+ } else {
+ Definite(self.rust.contains(ident) || self.aliases.contains_key(ident))
+ }
+ }
+ Type::RustBox(_)
+ | Type::RustVec(_)
+ | Type::Str(_)
+ | Type::Fn(_)
+ | Type::Void(_)
+ | Type::SliceRef(_) => Definite(true),
+ Type::UniquePtr(_) | Type::SharedPtr(_) | Type::WeakPtr(_) | Type::CxxVector(_) => {
+ Definite(false)
+ }
+ Type::Ref(ty) => self.determine_improper_ctype(&ty.inner),
+ Type::Ptr(ty) => self.determine_improper_ctype(&ty.inner),
+ Type::Array(ty) => self.determine_improper_ctype(&ty.inner),
+ }
+ }
+}
--- /dev/null
+use crate::syntax::{NamedType, Ty1, Type};
+use proc_macro2::{Ident, Span};
+use std::hash::{Hash, Hasher};
+use syn::Token;
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub enum ImplKey<'a> {
+ RustBox(NamedImplKey<'a>),
+ RustVec(NamedImplKey<'a>),
+ UniquePtr(NamedImplKey<'a>),
+ SharedPtr(NamedImplKey<'a>),
+ WeakPtr(NamedImplKey<'a>),
+ CxxVector(NamedImplKey<'a>),
+}
+
+#[derive(Copy, Clone)]
+pub struct NamedImplKey<'a> {
+ pub begin_span: Span,
+ pub rust: &'a Ident,
+ pub lt_token: Option<Token![<]>,
+ pub gt_token: Option<Token![>]>,
+ pub end_span: Span,
+}
+
+impl Type {
+ pub(crate) fn impl_key(&self) -> Option<ImplKey> {
+ if let Type::RustBox(ty) = self {
+ if let Type::Ident(ident) = &ty.inner {
+ return Some(ImplKey::RustBox(NamedImplKey::new(ty, ident)));
+ }
+ } else if let Type::RustVec(ty) = self {
+ if let Type::Ident(ident) = &ty.inner {
+ return Some(ImplKey::RustVec(NamedImplKey::new(ty, ident)));
+ }
+ } else if let Type::UniquePtr(ty) = self {
+ if let Type::Ident(ident) = &ty.inner {
+ return Some(ImplKey::UniquePtr(NamedImplKey::new(ty, ident)));
+ }
+ } else if let Type::SharedPtr(ty) = self {
+ if let Type::Ident(ident) = &ty.inner {
+ return Some(ImplKey::SharedPtr(NamedImplKey::new(ty, ident)));
+ }
+ } else if let Type::WeakPtr(ty) = self {
+ if let Type::Ident(ident) = &ty.inner {
+ return Some(ImplKey::WeakPtr(NamedImplKey::new(ty, ident)));
+ }
+ } else if let Type::CxxVector(ty) = self {
+ if let Type::Ident(ident) = &ty.inner {
+ return Some(ImplKey::CxxVector(NamedImplKey::new(ty, ident)));
+ }
+ }
+ None
+ }
+}
+
+impl<'a> PartialEq for NamedImplKey<'a> {
+ fn eq(&self, other: &Self) -> bool {
+ PartialEq::eq(self.rust, other.rust)
+ }
+}
+
+impl<'a> Eq for NamedImplKey<'a> {}
+
+impl<'a> Hash for NamedImplKey<'a> {
+ fn hash<H: Hasher>(&self, hasher: &mut H) {
+ self.rust.hash(hasher);
+ }
+}
+
+impl<'a> NamedImplKey<'a> {
+ fn new(outer: &Ty1, inner: &'a NamedType) -> Self {
+ NamedImplKey {
+ begin_span: outer.name.span(),
+ rust: &inner.rust,
+ lt_token: inner.generics.lt_token,
+ gt_token: inner.generics.gt_token,
+ end_span: outer.rangle.span,
+ }
+ }
+}
--- /dev/null
+// Mangled symbol arrangements:
+//
+// (a) One-off internal symbol.
+// pattern: {CXXBRIDGE} $ {NAME}
+// examples:
+// - cxxbridge1$exception
+// defining characteristics:
+// - 2 segments
+// - starts with cxxbridge
+//
+// (b) Behavior on a builtin binding without generic parameter.
+// pattern: {CXXBRIDGE} $ {TYPE} $ {NAME}
+// examples:
+// - cxxbridge1$string$len
+// defining characteristics:
+// - 3 segments
+// - starts with cxxbridge
+//
+// (c) Behavior on a builtin binding with generic parameter.
+// pattern: {CXXBRIDGE} $ {TYPE} $ {PARAM...} $ {NAME}
+// examples:
+// - cxxbridge1$box$org$rust$Struct$alloc
+// - cxxbridge1$unique_ptr$std$vector$u8$drop
+// defining characteristics:
+// - 4+ segments
+// - starts with cxxbridge
+//
+// (d) User-defined extern function.
+// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {NAME}
+// examples:
+// - cxxbridge1$new_client
+// - org$rust$cxxbridge1$new_client
+// defining characteristics:
+// - cxxbridge is second from end
+// FIXME: conflict with (a) if they collide with one of our one-off symbol names in the global namespace
+//
+// (e) User-defined extern member function.
+// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE} $ {NAME}
+// examples:
+// - org$cxxbridge1$Struct$get
+// defining characteristics:
+// - cxxbridge is third from end
+// FIXME: conflict with (b) if e.g. user binds a type in global namespace that collides with our builtin type names
+//
+// (f) Operator overload.
+// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE} $ operator $ {NAME}
+// examples:
+// - org$rust$cxxbridge1$Struct$operator$eq
+// defining characteristics:
+// - second segment from end is `operator` (not possible in type or namespace names)
+//
+// (g) Closure trampoline.
+// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE?} $ {NAME} $ {ARGUMENT} $ {DIRECTION}
+// examples:
+// - org$rust$cxxbridge1$Struct$invoke$f$0
+// defining characteristics:
+// - last symbol is `0` (C half) or `1` (Rust half) which are not legal identifiers on their own
+//
+//
+// Mangled preprocessor variable arrangements:
+//
+// (A) One-off internal variable.
+// pattern: {CXXBRIDGE} _ {NAME}
+// examples:
+// - CXXBRIDGE1_PANIC
+// - CXXBRIDGE1_RUST_STRING
+// defining characteristics:
+// - NAME does not begin with STRUCT or ENUM
+//
+// (B) Guard around user-defined type.
+// pattern: {CXXBRIDGE} _ {STRUCT or ENUM} _ {NAMESPACE...} $ {TYPE}
+// examples:
+// - CXXBRIDGE1_STRUCT_org$rust$Struct
+// - CXXBRIDGE1_ENUM_Enabled
+
+use crate::syntax::symbol::{self, Symbol};
+use crate::syntax::{ExternFn, Pair, Types};
+
+const CXXBRIDGE: &str = "cxxbridge1";
+
+macro_rules! join {
+ ($($segment:expr),+ $(,)?) => {
+ symbol::join(&[$(&$segment),+])
+ };
+}
+
+pub fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol {
+ match &efn.receiver {
+ Some(receiver) => {
+ let receiver_ident = types.resolve(&receiver.ty);
+ join!(
+ efn.name.namespace,
+ CXXBRIDGE,
+ receiver_ident.name.cxx,
+ efn.name.rust,
+ )
+ }
+ None => join!(efn.name.namespace, CXXBRIDGE, efn.name.rust),
+ }
+}
+
+pub fn operator(receiver: &Pair, operator: &'static str) -> Symbol {
+ join!(
+ receiver.namespace,
+ CXXBRIDGE,
+ receiver.cxx,
+ "operator",
+ operator,
+ )
+}
+
+// The C half of a function pointer trampoline.
+pub 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 fn r_trampoline(efn: &ExternFn, var: &Pair, types: &Types) -> Symbol {
+ join!(extern_fn(efn, types), var.rust, 1)
+}
--- /dev/null
+use std::borrow::Borrow;
+use std::hash::Hash;
+use std::ops::Index;
+use std::slice;
+
+pub use self::ordered::OrderedMap;
+pub use self::unordered::UnorderedMap;
+pub use std::collections::hash_map::Entry;
+
+mod ordered {
+ use super::{Entry, Iter, UnorderedMap};
+ use std::borrow::Borrow;
+ use std::hash::Hash;
+ use std::mem;
+
+ pub struct OrderedMap<K, V> {
+ map: UnorderedMap<K, usize>,
+ vec: Vec<(K, V)>,
+ }
+
+ impl<K, V> OrderedMap<K, V> {
+ pub fn new() -> Self {
+ OrderedMap {
+ map: UnorderedMap::new(),
+ vec: Vec::new(),
+ }
+ }
+
+ pub fn iter(&self) -> Iter<K, V> {
+ Iter(self.vec.iter())
+ }
+
+ pub fn keys(&self) -> impl Iterator<Item = &K> {
+ self.vec.iter().map(|(k, _v)| k)
+ }
+ }
+
+ impl<K, V> OrderedMap<K, V>
+ where
+ K: Copy + Hash + Eq,
+ {
+ pub fn insert(&mut self, key: K, value: V) -> Option<V> {
+ match self.map.entry(key) {
+ Entry::Occupied(entry) => {
+ let i = &mut self.vec[*entry.get()];
+ Some(mem::replace(&mut i.1, value))
+ }
+ Entry::Vacant(entry) => {
+ entry.insert(self.vec.len());
+ self.vec.push((key, value));
+ None
+ }
+ }
+ }
+
+ pub fn contains_key<Q>(&self, key: &Q) -> bool
+ where
+ K: Borrow<Q>,
+ Q: ?Sized + Hash + Eq,
+ {
+ self.map.contains_key(key)
+ }
+
+ pub fn get<Q>(&self, key: &Q) -> Option<&V>
+ where
+ K: Borrow<Q>,
+ Q: ?Sized + Hash + Eq,
+ {
+ let i = *self.map.get(key)?;
+ Some(&self.vec[i].1)
+ }
+ }
+
+ impl<'a, K, V> IntoIterator for &'a OrderedMap<K, V> {
+ type Item = (&'a K, &'a V);
+ type IntoIter = Iter<'a, K, V>;
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+ }
+}
+
+mod unordered {
+ use crate::syntax::set::UnorderedSet;
+ use std::borrow::Borrow;
+ use std::collections::hash_map::{Entry, HashMap};
+ use std::hash::Hash;
+
+ // Wrapper prohibits accidentally introducing iteration over the map, which
+ // could lead to nondeterministic generated code.
+ pub struct UnorderedMap<K, V>(HashMap<K, V>);
+
+ impl<K, V> UnorderedMap<K, V> {
+ pub fn new() -> Self {
+ UnorderedMap(HashMap::new())
+ }
+ }
+
+ impl<K, V> UnorderedMap<K, V>
+ where
+ K: Hash + Eq,
+ {
+ pub fn insert(&mut self, key: K, value: V) -> Option<V> {
+ self.0.insert(key, value)
+ }
+
+ pub fn contains_key<Q>(&self, key: &Q) -> bool
+ where
+ K: Borrow<Q>,
+ Q: ?Sized + Hash + Eq,
+ {
+ self.0.contains_key(key)
+ }
+
+ pub fn get<Q>(&self, key: &Q) -> Option<&V>
+ where
+ K: Borrow<Q>,
+ Q: ?Sized + Hash + Eq,
+ {
+ self.0.get(key)
+ }
+
+ pub fn entry(&mut self, key: K) -> Entry<K, V> {
+ self.0.entry(key)
+ }
+
+ pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
+ where
+ K: Borrow<Q>,
+ Q: ?Sized + Hash + Eq,
+ {
+ self.0.remove(key)
+ }
+
+ pub fn keys(&self) -> UnorderedSet<K>
+ where
+ K: Copy,
+ {
+ let mut set = UnorderedSet::new();
+ for key in self.0.keys() {
+ set.insert(*key);
+ }
+ set
+ }
+ }
+}
+
+pub struct Iter<'a, K, V>(slice::Iter<'a, (K, V)>);
+
+impl<'a, K, V> Iterator for Iter<'a, K, V> {
+ type Item = (&'a K, &'a V);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let (k, v) = self.0.next()?;
+ Some((k, v))
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.0.size_hint()
+ }
+}
+
+impl<K, V> Default for UnorderedMap<K, V> {
+ fn default() -> Self {
+ UnorderedMap::new()
+ }
+}
+
+impl<Q, K, V> Index<&Q> for UnorderedMap<K, V>
+where
+ K: Borrow<Q> + Hash + Eq,
+ Q: ?Sized + Hash + Eq,
+{
+ type Output = V;
+
+ fn index(&self, key: &Q) -> &V {
+ self.get(key).unwrap()
+ }
+}
--- /dev/null
+// Functionality that is shared between the cxxbridge macro and the cmd.
+
+pub mod atom;
+pub mod attrs;
+pub mod check;
+pub mod derive;
+mod discriminant;
+mod doc;
+pub mod error;
+pub mod file;
+pub mod ident;
+mod impls;
+mod improper;
+pub mod instantiate;
+pub mod mangle;
+pub mod map;
+mod names;
+pub mod namespace;
+mod parse;
+mod pod;
+pub mod qualified;
+pub mod report;
+pub mod resolve;
+pub mod set;
+pub mod symbol;
+mod tokens;
+mod toposort;
+pub mod trivial;
+pub mod types;
+mod visit;
+
+use self::attrs::OtherAttrs;
+use self::namespace::Namespace;
+use self::parse::kw;
+use self::symbol::Symbol;
+use proc_macro2::{Ident, Span};
+use syn::punctuated::Punctuated;
+use syn::token::{Brace, Bracket, Paren};
+use syn::{Attribute, Expr, Generics, Lifetime, LitInt, Path, Token, Type as RustType};
+
+pub use self::atom::Atom;
+pub use self::derive::{Derive, Trait};
+pub use self::discriminant::Discriminant;
+pub use self::doc::Doc;
+pub use self::names::ForeignName;
+pub use self::parse::parse_items;
+pub use self::types::Types;
+
+pub enum Api {
+ Include(Include),
+ Struct(Struct),
+ Enum(Enum),
+ CxxType(ExternType),
+ CxxFunction(ExternFn),
+ RustType(ExternType),
+ RustFunction(ExternFn),
+ TypeAlias(TypeAlias),
+ Impl(Impl),
+}
+
+pub struct Include {
+ pub path: String,
+ pub kind: IncludeKind,
+ pub begin_span: Span,
+ pub end_span: Span,
+}
+
+/// Whether to emit `#include "path"` or `#include <path>`.
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum IncludeKind {
+ /// `#include "quoted/path/to"`
+ Quoted,
+ /// `#include <bracketed/path/to>`
+ Bracketed,
+}
+
+pub struct ExternType {
+ pub lang: Lang,
+ pub doc: Doc,
+ pub derives: Vec<Derive>,
+ pub attrs: OtherAttrs,
+ pub visibility: Token![pub],
+ pub type_token: Token![type],
+ pub name: Pair,
+ pub generics: Lifetimes,
+ pub colon_token: Option<Token![:]>,
+ pub bounds: Vec<Derive>,
+ pub semi_token: Token![;],
+ pub trusted: bool,
+}
+
+pub struct Struct {
+ pub doc: Doc,
+ pub derives: Vec<Derive>,
+ pub attrs: OtherAttrs,
+ pub visibility: Token![pub],
+ pub struct_token: Token![struct],
+ pub name: Pair,
+ pub generics: Lifetimes,
+ pub brace_token: Brace,
+ pub fields: Vec<Var>,
+}
+
+pub struct Enum {
+ pub doc: Doc,
+ pub derives: Vec<Derive>,
+ pub attrs: OtherAttrs,
+ pub visibility: Token![pub],
+ pub enum_token: Token![enum],
+ pub name: Pair,
+ pub generics: Lifetimes,
+ pub brace_token: Brace,
+ pub variants: Vec<Variant>,
+ pub variants_from_header: bool,
+ pub variants_from_header_attr: Option<Attribute>,
+ pub repr: EnumRepr,
+ pub explicit_repr: bool,
+}
+
+pub enum EnumRepr {
+ Native { atom: Atom, repr_type: Type },
+ Foreign { rust_type: Path },
+}
+
+pub struct ExternFn {
+ pub lang: Lang,
+ pub doc: Doc,
+ pub attrs: OtherAttrs,
+ pub visibility: Token![pub],
+ pub name: Pair,
+ pub sig: Signature,
+ pub semi_token: Token![;],
+ pub trusted: bool,
+}
+
+pub struct TypeAlias {
+ pub doc: Doc,
+ pub derives: Vec<Derive>,
+ pub attrs: OtherAttrs,
+ pub visibility: Token![pub],
+ pub type_token: Token![type],
+ pub name: Pair,
+ pub generics: Lifetimes,
+ pub eq_token: Token![=],
+ pub ty: RustType,
+ pub semi_token: Token![;],
+}
+
+pub struct Impl {
+ pub impl_token: Token![impl],
+ pub impl_generics: Lifetimes,
+ pub negative: bool,
+ pub ty: Type,
+ pub ty_generics: Lifetimes,
+ pub brace_token: Brace,
+ pub negative_token: Option<Token![!]>,
+}
+
+#[derive(Clone, Default)]
+pub struct Lifetimes {
+ pub lt_token: Option<Token![<]>,
+ pub lifetimes: Punctuated<Lifetime, Token![,]>,
+ pub gt_token: Option<Token![>]>,
+}
+
+pub struct Signature {
+ pub unsafety: Option<Token![unsafe]>,
+ pub fn_token: Token![fn],
+ pub generics: Generics,
+ pub receiver: Option<Receiver>,
+ pub args: Punctuated<Var, Token![,]>,
+ pub ret: Option<Type>,
+ pub throws: bool,
+ pub paren_token: Paren,
+ pub throws_tokens: Option<(kw::Result, Token![<], Token![>])>,
+}
+
+pub struct Var {
+ pub doc: Doc,
+ pub attrs: OtherAttrs,
+ pub visibility: Token![pub],
+ pub name: Pair,
+ pub colon_token: Token![:],
+ pub ty: Type,
+}
+
+pub struct Receiver {
+ pub pinned: bool,
+ pub ampersand: Token![&],
+ pub lifetime: Option<Lifetime>,
+ pub mutable: bool,
+ pub var: Token![self],
+ pub ty: NamedType,
+ pub colon_token: Token![:],
+ pub shorthand: bool,
+ pub pin_tokens: Option<(kw::Pin, Token![<], Token![>])>,
+ pub mutability: Option<Token![mut]>,
+}
+
+pub struct Variant {
+ pub doc: Doc,
+ pub attrs: OtherAttrs,
+ pub name: Pair,
+ pub discriminant: Discriminant,
+ pub expr: Option<Expr>,
+}
+
+pub enum Type {
+ Ident(NamedType),
+ RustBox(Box<Ty1>),
+ RustVec(Box<Ty1>),
+ UniquePtr(Box<Ty1>),
+ SharedPtr(Box<Ty1>),
+ WeakPtr(Box<Ty1>),
+ Ref(Box<Ref>),
+ Ptr(Box<Ptr>),
+ Str(Box<Ref>),
+ CxxVector(Box<Ty1>),
+ Fn(Box<Signature>),
+ Void(Span),
+ SliceRef(Box<SliceRef>),
+ Array(Box<Array>),
+}
+
+pub struct Ty1 {
+ pub name: Ident,
+ pub langle: Token![<],
+ pub inner: Type,
+ pub rangle: Token![>],
+}
+
+pub struct Ref {
+ pub pinned: bool,
+ pub ampersand: Token![&],
+ pub lifetime: Option<Lifetime>,
+ pub mutable: bool,
+ pub inner: Type,
+ pub pin_tokens: Option<(kw::Pin, Token![<], Token![>])>,
+ pub mutability: Option<Token![mut]>,
+}
+
+pub struct Ptr {
+ pub star: Token![*],
+ pub mutable: bool,
+ pub inner: Type,
+ pub mutability: Option<Token![mut]>,
+ pub constness: Option<Token![const]>,
+}
+
+pub struct SliceRef {
+ pub ampersand: Token![&],
+ pub lifetime: Option<Lifetime>,
+ pub mutable: bool,
+ pub bracket: Bracket,
+ pub inner: Type,
+ pub mutability: Option<Token![mut]>,
+}
+
+pub struct Array {
+ pub bracket: Bracket,
+ pub inner: Type,
+ pub semi_token: Token![;],
+ pub len: usize,
+ pub len_token: LitInt,
+}
+
+#[derive(Copy, Clone, PartialEq)]
+pub enum Lang {
+ Cxx,
+ Rust,
+}
+
+// An association of a defined Rust name with a fully resolved, namespace
+// qualified C++ name.
+#[derive(Clone)]
+pub 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 struct NamedType {
+ pub rust: Ident,
+ pub generics: Lifetimes,
+}
--- /dev/null
+use 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::parse::{Error, Result};
+use syn::punctuated::Punctuated;
+
+#[derive(Clone)]
+pub struct ForeignName {
+ text: String,
+}
+
+impl Pair {
+ pub 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)
+ }
+
+ pub fn to_fully_qualified(&self) -> String {
+ let mut fully_qualified = String::new();
+ for segment in &self.namespace {
+ fully_qualified += "::";
+ fully_qualified += &segment.to_string();
+ }
+ fully_qualified += "::";
+ fully_qualified += &self.cxx.to_string();
+ fully_qualified
+ }
+}
+
+impl NamedType {
+ pub fn new(rust: Ident) -> Self {
+ let generics = Lifetimes {
+ lt_token: None,
+ lifetimes: Punctuated::new(),
+ gt_token: None,
+ };
+ NamedType { rust, generics }
+ }
+
+ pub fn span(&self) -> Span {
+ self.rust.span()
+ }
+}
+
+impl ForeignName {
+ pub fn parse(text: &str, span: Span) -> Result<Self> {
+ // TODO: support C++ names containing whitespace (`unsigned int`) or
+ // non-alphanumeric characters (`operator++`).
+ match syn::parse_str::<Ident>(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<str> for ForeignName {
+ fn eq(&self, rhs: &str) -> bool {
+ self.text == rhs
+ }
+}
--- /dev/null
+use crate::syntax::qualified::QualifiedName;
+use quote::IdentFragment;
+use std::fmt::{self, Display};
+use std::iter::FromIterator;
+use std::slice::Iter;
+use syn::parse::{Parse, ParseStream, Result};
+use syn::{Ident, Token};
+
+mod kw {
+ syn::custom_keyword!(namespace);
+}
+
+#[derive(Clone, Default)]
+pub struct Namespace {
+ segments: Vec<Ident>,
+}
+
+impl Namespace {
+ pub const ROOT: Self = Namespace {
+ segments: Vec::new(),
+ };
+
+ pub fn iter(&self) -> Iter<Ident> {
+ self.segments.iter()
+ }
+
+ pub fn parse_bridge_attr_namespace(input: ParseStream) -> Result<Namespace> {
+ if input.is_empty() {
+ return Ok(Namespace::ROOT);
+ }
+
+ input.parse::<kw::namespace>()?;
+ input.parse::<Token![=]>()?;
+ let namespace = input.parse::<Namespace>()?;
+ input.parse::<Option<Token![,]>>()?;
+ Ok(namespace)
+ }
+}
+
+impl Default for &Namespace {
+ fn default() -> Self {
+ const ROOT: &Namespace = &Namespace::ROOT;
+ ROOT
+ }
+}
+
+impl Parse for Namespace {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let segments = QualifiedName::parse_quoted_or_unquoted(input)?.segments;
+ Ok(Namespace { segments })
+ }
+}
+
+impl Display for Namespace {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ for segment in self {
+ write!(f, "{}$", segment)?;
+ }
+ Ok(())
+ }
+}
+
+impl IdentFragment for Namespace {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(self, f)
+ }
+}
+
+impl<'a> IntoIterator for &'a Namespace {
+ type Item = &'a Ident;
+ type IntoIter = Iter<'a, Ident>;
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+}
+
+impl<'a> FromIterator<&'a Ident> for Namespace {
+ fn from_iter<I>(idents: I) -> Self
+ where
+ I: IntoIterator<Item = &'a Ident>,
+ {
+ let segments = idents.into_iter().cloned().collect();
+ Namespace { segments }
+ }
+}
--- /dev/null
+use crate::syntax::attrs::OtherAttrs;
+use crate::syntax::discriminant::DiscriminantSet;
+use crate::syntax::file::{Item, ItemForeignMod};
+use crate::syntax::report::Errors;
+use crate::syntax::Atom::*;
+use crate::syntax::{
+ attrs, error, Api, Array, Derive, Doc, Enum, EnumRepr, ExternFn, ExternType, ForeignName, Impl,
+ Include, IncludeKind, Lang, Lifetimes, NamedType, Namespace, Pair, Ptr, Receiver, Ref,
+ Signature, SliceRef, Struct, Ty1, Type, TypeAlias, Var, Variant,
+};
+use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree};
+use quote::{format_ident, quote, quote_spanned};
+use std::mem;
+use syn::parse::{ParseStream, Parser};
+use syn::punctuated::Punctuated;
+use syn::{
+ Abi, Attribute, Error, Expr, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
+ GenericArgument, GenericParam, Generics, Ident, ItemEnum, ItemImpl, ItemStruct, Lit, LitStr,
+ Pat, PathArguments, Result, ReturnType, Signature as RustSignature, Token, TraitBound,
+ TraitBoundModifier, Type as RustType, TypeArray, TypeBareFn, TypeParamBound, TypePath, TypePtr,
+ TypeReference, Variant as RustVariant, Visibility,
+};
+
+pub mod kw {
+ syn::custom_keyword!(Pin);
+ syn::custom_keyword!(Result);
+}
+
+pub fn parse_items(
+ cx: &mut Errors,
+ items: Vec<Item>,
+ trusted: bool,
+ namespace: &Namespace,
+) -> Vec<Api> {
+ 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(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<Api> {
+ let mut doc = Doc::new();
+ let mut derives = Vec::new();
+ let mut namespace = namespace.clone();
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let attrs = attrs::parse(
+ cx,
+ mem::take(&mut item.attrs),
+ attrs::Parser {
+ doc: Some(&mut doc),
+ derives: Some(&mut derives),
+ namespace: Some(&mut namespace),
+ cxx_name: Some(&mut cxx_name),
+ rust_name: Some(&mut rust_name),
+ ..Default::default()
+ },
+ );
+
+ let named_fields = match item.fields {
+ Fields::Named(fields) => fields,
+ Fields::Unit => return Err(Error::new_spanned(item, "unit structs are not supported")),
+ Fields::Unnamed(_) => {
+ return Err(Error::new_spanned(item, "tuple structs are not supported"));
+ }
+ };
+
+ let mut lifetimes = Punctuated::new();
+ let mut has_unsupported_generic_param = false;
+ for pair in item.generics.params.into_pairs() {
+ let (param, punct) = pair.into_tuple();
+ match param {
+ GenericParam::Lifetime(param) => {
+ if !param.bounds.is_empty() && !has_unsupported_generic_param {
+ let msg = "lifetime parameter with bounds is not supported yet";
+ cx.error(¶m, msg);
+ has_unsupported_generic_param = true;
+ }
+ lifetimes.push_value(param.lifetime);
+ if let Some(punct) = punct {
+ lifetimes.push_punct(punct);
+ }
+ }
+ GenericParam::Type(param) => {
+ if !has_unsupported_generic_param {
+ let msg = "struct with generic type parameter is not supported yet";
+ cx.error(¶m, msg);
+ has_unsupported_generic_param = true;
+ }
+ }
+ GenericParam::Const(param) => {
+ if !has_unsupported_generic_param {
+ let msg = "struct with const generic parameter is not supported yet";
+ cx.error(¶m, msg);
+ has_unsupported_generic_param = true;
+ }
+ }
+ }
+ }
+
+ if let Some(where_clause) = &item.generics.where_clause {
+ cx.error(
+ where_clause,
+ "struct with where-clause is not supported yet",
+ );
+ }
+
+ let mut fields = Vec::new();
+ for field in named_fields.named {
+ let ident = field.ident.unwrap();
+ let mut doc = Doc::new();
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let attrs = attrs::parse(
+ cx,
+ field.attrs,
+ attrs::Parser {
+ 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 {
+ 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 {
+ doc,
+ derives,
+ attrs,
+ visibility,
+ struct_token,
+ name,
+ generics,
+ brace_token,
+ fields,
+ }))
+}
+
+fn parse_enum(cx: &mut Errors, item: ItemEnum, namespace: &Namespace) -> Api {
+ let mut doc = Doc::new();
+ let mut derives = Vec::new();
+ let mut repr = None;
+ let mut namespace = namespace.clone();
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let mut variants_from_header = None;
+ let attrs = attrs::parse(
+ cx,
+ item.attrs,
+ attrs::Parser {
+ doc: Some(&mut doc),
+ derives: Some(&mut derives),
+ repr: Some(&mut repr),
+ namespace: Some(&mut namespace),
+ cxx_name: Some(&mut cxx_name),
+ rust_name: Some(&mut rust_name),
+ variants_from_header: Some(&mut variants_from_header),
+ ..Default::default()
+ },
+ );
+
+ if !item.generics.params.is_empty() {
+ let vis = &item.vis;
+ let enum_token = item.enum_token;
+ let ident = &item.ident;
+ let generics = &item.generics;
+ let span = quote!(#vis #enum_token #ident #generics);
+ cx.error(span, "enum with generic parameters is not supported");
+ } else if let Some(where_clause) = &item.generics.where_clause {
+ cx.error(where_clause, "enum with where-clause is not supported");
+ }
+
+ let mut variants = Vec::new();
+ let mut discriminants = DiscriminantSet::new(repr);
+ for variant in item.variants {
+ match parse_variant(cx, variant, &mut discriminants) {
+ Ok(variant) => variants.push(variant),
+ Err(err) => cx.push(err),
+ }
+ }
+
+ let enum_token = item.enum_token;
+ let visibility = visibility_pub(&item.vis, enum_token.span);
+ let brace_token = item.brace_token;
+
+ let explicit_repr = repr.is_some();
+ let mut repr = U8;
+ match discriminants.inferred_repr() {
+ Ok(inferred) => repr = inferred,
+ Err(err) => {
+ let span = quote_spanned!(brace_token.span=> #enum_token {});
+ cx.error(span, err);
+ variants.clear();
+ }
+ }
+
+ let name = pair(namespace, &item.ident, cxx_name, rust_name);
+ let repr_ident = Ident::new(repr.as_ref(), Span::call_site());
+ let repr_type = Type::Ident(NamedType::new(repr_ident));
+ let repr = EnumRepr::Native {
+ atom: repr,
+ repr_type,
+ };
+ let generics = Lifetimes {
+ lt_token: None,
+ lifetimes: Punctuated::new(),
+ gt_token: None,
+ };
+ let variants_from_header_attr = variants_from_header;
+ let variants_from_header = variants_from_header_attr.is_some();
+
+ Api::Enum(Enum {
+ doc,
+ derives,
+ attrs,
+ visibility,
+ enum_token,
+ name,
+ generics,
+ brace_token,
+ variants,
+ variants_from_header,
+ variants_from_header_attr,
+ repr,
+ explicit_repr,
+ })
+}
+
+fn parse_variant(
+ cx: &mut Errors,
+ mut variant: RustVariant,
+ discriminants: &mut DiscriminantSet,
+) -> Result<Variant> {
+ let mut doc = Doc::new();
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let attrs = attrs::parse(
+ cx,
+ mem::take(&mut variant.attrs),
+ attrs::Parser {
+ doc: Some(&mut doc),
+ cxx_name: Some(&mut cxx_name),
+ rust_name: Some(&mut rust_name),
+ ..Default::default()
+ },
+ );
+
+ match variant.fields {
+ Fields::Unit => {}
+ _ => {
+ let msg = "enums with data are not supported yet";
+ return Err(Error::new_spanned(variant, msg));
+ }
+ }
+
+ let expr = variant.discriminant.as_ref().map(|(_, expr)| expr);
+ let try_discriminant = match &expr {
+ Some(lit) => discriminants.insert(lit),
+ None => discriminants.insert_next(),
+ };
+ let discriminant = match try_discriminant {
+ Ok(discriminant) => discriminant,
+ Err(err) => return Err(Error::new_spanned(variant, err)),
+ };
+
+ let name = pair(Namespace::ROOT, &variant.ident, cxx_name, rust_name);
+ let expr = variant.discriminant.map(|(_, expr)| expr);
+
+ Ok(Variant {
+ doc,
+ attrs,
+ name,
+ discriminant,
+ expr,
+ })
+}
+
+fn parse_foreign_mod(
+ cx: &mut Errors,
+ foreign_mod: ItemForeignMod,
+ out: &mut Vec<Api>,
+ trusted: bool,
+ namespace: &Namespace,
+) {
+ let lang = match parse_lang(&foreign_mod.abi) {
+ Ok(lang) => lang,
+ Err(err) => return cx.push(err),
+ };
+
+ match lang {
+ Lang::Rust => {
+ if foreign_mod.unsafety.is_some() {
+ let unsafety = foreign_mod.unsafety;
+ let abi = &foreign_mod.abi;
+ let span = quote!(#unsafety #abi);
+ cx.error(span, "extern \"Rust\" block does not need to be unsafe");
+ }
+ }
+ Lang::Cxx => {}
+ }
+
+ let trusted = trusted || foreign_mod.unsafety.is_some();
+
+ let mut namespace = namespace.clone();
+ attrs::parse(
+ cx,
+ foreign_mod.attrs,
+ attrs::Parser {
+ 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, &namespace);
+ items.push(ety);
+ }
+ ForeignItem::Fn(foreign) => {
+ match parse_extern_fn(cx, foreign, lang, trusted, &namespace) {
+ 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(include) => items.push(Api::Include(include)),
+ Err(err) => cx.push(err),
+ }
+ }
+ ForeignItem::Verbatim(tokens) => {
+ match parse_extern_verbatim(cx, tokens, lang, trusted, &namespace) {
+ Ok(api) => items.push(api),
+ Err(err) => cx.push(err),
+ }
+ }
+ _ => cx.error(foreign, "unsupported foreign item"),
+ }
+ }
+
+ if !trusted
+ && items.iter().any(|api| match api {
+ Api::CxxFunction(efn) => efn.unsafety.is_none(),
+ _ => false,
+ })
+ {
+ cx.error(
+ foreign_mod.abi,
+ "block must be declared `unsafe extern \"C++\"` if it contains any safe-to-call C++ functions",
+ );
+ }
+
+ let mut types = items.iter().filter_map(|item| match item {
+ Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.name),
+ Api::TypeAlias(alias) => Some(&alias.name),
+ _ => None,
+ });
+ if let (Some(single_type), None) = (types.next(), types.next()) {
+ let single_type = single_type.clone();
+ for item in &mut items {
+ if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item {
+ if let Some(receiver) = &mut efn.receiver {
+ if receiver.ty.rust == "Self" {
+ receiver.ty.rust = single_type.rust.clone();
+ }
+ }
+ }
+ }
+ }
+
+ out.extend(items);
+}
+
+fn parse_lang(abi: &Abi) -> Result<Lang> {
+ let name = match &abi.name {
+ Some(name) => name,
+ None => {
+ return Err(Error::new_spanned(
+ abi,
+ "ABI name is required, extern \"C++\" or extern \"Rust\"",
+ ));
+ }
+ };
+
+ match name.value().as_str() {
+ "C++" => Ok(Lang::Cxx),
+ "Rust" => Ok(Lang::Rust),
+ _ => Err(Error::new_spanned(
+ abi,
+ "unrecognized ABI, requires either \"C++\" or \"Rust\"",
+ )),
+ }
+}
+
+fn parse_extern_type(
+ cx: &mut Errors,
+ foreign_type: ForeignItemType,
+ lang: Lang,
+ trusted: bool,
+ namespace: &Namespace,
+) -> Api {
+ let mut doc = Doc::new();
+ let mut derives = Vec::new();
+ let mut namespace = namespace.clone();
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let attrs = attrs::parse(
+ cx,
+ foreign_type.attrs,
+ attrs::Parser {
+ 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 = Lifetimes {
+ lt_token: None,
+ lifetimes: Punctuated::new(),
+ gt_token: None,
+ };
+ let colon_token = None;
+ let bounds = Vec::new();
+ let semi_token = foreign_type.semi_token;
+
+ (match lang {
+ Lang::Cxx => Api::CxxType,
+ Lang::Rust => Api::RustType,
+ })(ExternType {
+ 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,
+ namespace: &Namespace,
+) -> Result<Api> {
+ let mut doc = Doc::new();
+ let mut namespace = namespace.clone();
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let attrs = attrs::parse(
+ cx,
+ mem::take(&mut foreign_fn.attrs),
+ attrs::Parser {
+ doc: Some(&mut doc),
+ namespace: Some(&mut namespace),
+ cxx_name: Some(&mut cxx_name),
+ rust_name: Some(&mut rust_name),
+ ..Default::default()
+ },
+ );
+
+ let generics = &foreign_fn.sig.generics;
+ if generics.where_clause.is_some()
+ || generics.params.iter().any(|param| match param {
+ GenericParam::Lifetime(lifetime) => !lifetime.bounds.is_empty(),
+ GenericParam::Type(_) | GenericParam::Const(_) => true,
+ })
+ {
+ return Err(Error::new_spanned(
+ foreign_fn,
+ "extern function with generic parameters is not supported yet",
+ ));
+ }
+
+ if let Some(variadic) = &foreign_fn.sig.variadic {
+ return Err(Error::new_spanned(
+ variadic,
+ "variadic function is not supported yet",
+ ));
+ }
+
+ if foreign_fn.sig.asyncness.is_some() {
+ 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,
+ ty: NamedType::new(Ident::new("Self", arg.self_token.span)),
+ shorthand: true,
+ pin_tokens: None,
+ mutability: arg.mutability,
+ });
+ continue;
+ }
+ return Err(Error::new_spanned(arg, "unsupported signature"));
+ }
+ 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)?;
+ if ident != "self" {
+ let doc = Doc::new();
+ let attrs = OtherAttrs::none();
+ let visibility = Token);
+ let name = pair(Namespace::default(), &ident, None, None);
+ let colon_token = arg.colon_token;
+ args.push_value(Var {
+ doc,
+ attrs,
+ visibility,
+ name,
+ colon_token,
+ ty,
+ });
+ if let Some(comma) = comma {
+ args.push_punct(*comma);
+ }
+ continue;
+ }
+ 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),
+ colon_token: arg.colon_token,
+ ty: ident,
+ shorthand: false,
+ pin_tokens: reference.pin_tokens,
+ mutability: reference.mutability,
+ });
+ continue;
+ }
+ }
+ return Err(Error::new_spanned(arg, "unsupported 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 unsafety = foreign_fn.sig.unsafety;
+ let fn_token = foreign_fn.sig.fn_token;
+ let inherited_span = unsafety.map_or(fn_token.span, |unsafety| unsafety.span);
+ let visibility = visibility_pub(&foreign_fn.vis, inherited_span);
+ let name = pair(namespace, &foreign_fn.sig.ident, cxx_name, rust_name);
+ let generics = generics.clone();
+ let paren_token = foreign_fn.sig.paren_token;
+ let semi_token = foreign_fn.semi_token;
+
+ Ok(match lang {
+ Lang::Cxx => Api::CxxFunction,
+ Lang::Rust => Api::RustFunction,
+ }(ExternFn {
+ lang,
+ doc,
+ attrs,
+ visibility,
+ name,
+ sig: Signature {
+ unsafety,
+ fn_token,
+ generics,
+ receiver,
+ args,
+ ret,
+ throws,
+ paren_token,
+ throws_tokens,
+ },
+ semi_token,
+ trusted,
+ }))
+}
+
+fn parse_extern_verbatim(
+ cx: &mut Errors,
+ tokens: TokenStream,
+ lang: Lang,
+ trusted: bool,
+ namespace: &Namespace,
+) -> Result<Api> {
+ |input: ParseStream| -> Result<Api> {
+ let attrs = input.call(Attribute::parse_outer)?;
+ let visibility: Visibility = input.parse()?;
+ if input.peek(Token![type]) {
+ parse_extern_verbatim_type(cx, attrs, visibility, input, lang, trusted, namespace)
+ } 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,
+ attrs: Vec<Attribute>,
+ visibility: Visibility,
+ input: ParseStream,
+ lang: Lang,
+ trusted: bool,
+ namespace: &Namespace,
+) -> Result<Api> {
+ let type_token: Token![type] = input.parse()?;
+ let ident: Ident = input.parse()?;
+ let generics: Generics = input.parse()?;
+ 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;
+ }
+ }
+ }
+ }
+ let lifetimes = Lifetimes {
+ lt_token: generics.lt_token,
+ lifetimes,
+ gt_token: generics.gt_token,
+ };
+ let lookahead = input.lookahead1();
+ if lookahead.peek(Token![=]) {
+ // type Alias = crate::path::to::Type;
+ parse_type_alias(
+ cx, attrs, visibility, type_token, ident, lifetimes, input, lang, namespace,
+ )
+ } else if lookahead.peek(Token![:]) || lookahead.peek(Token![;]) {
+ // type Opaque: Bound2 + Bound2;
+ parse_extern_type_bounded(
+ cx, attrs, visibility, type_token, ident, lifetimes, input, lang, trusted, namespace,
+ )
+ } else {
+ Err(lookahead.error())
+ }
+}
+
+fn parse_extern_verbatim_fn(input: ParseStream) -> Result<Api> {
+ input.parse::<RustSignature>()?;
+ input.parse::<Token![;]>()?;
+ unreachable!()
+}
+
+fn parse_type_alias(
+ cx: &mut Errors,
+ attrs: Vec<Attribute>,
+ visibility: Visibility,
+ type_token: Token![type],
+ ident: Ident,
+ generics: Lifetimes,
+ input: ParseStream,
+ lang: Lang,
+ namespace: &Namespace,
+) -> Result<Api> {
+ let eq_token: Token![=] = input.parse()?;
+ let ty: RustType = input.parse()?;
+ let semi_token: Token![;] = input.parse()?;
+
+ let mut doc = Doc::new();
+ let mut derives = Vec::new();
+ let mut namespace = namespace.clone();
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let attrs = attrs::parse(
+ cx,
+ attrs,
+ attrs::Parser {
+ 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 {
+ doc,
+ derives,
+ attrs,
+ visibility,
+ type_token,
+ name,
+ generics,
+ eq_token,
+ ty,
+ semi_token,
+ }))
+}
+
+fn parse_extern_type_bounded(
+ cx: &mut Errors,
+ attrs: Vec<Attribute>,
+ visibility: Visibility,
+ type_token: Token![type],
+ ident: Ident,
+ generics: Lifetimes,
+ input: ParseStream,
+ lang: Lang,
+ trusted: bool,
+ namespace: &Namespace,
+) -> Result<Api> {
+ let mut bounds = Vec::new();
+ let colon_token: Option<Token![:]> = 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 @ TypeParamBound::Trait(_) | bound @ TypeParamBound::Lifetime(_) => {
+ cx.error(bound, "unsupported trait");
+ }
+ }
+
+ let lookahead = input.lookahead1();
+ if lookahead.peek(Token![+]) {
+ input.parse::<Token![+]>()?;
+ } else if lookahead.peek(Token![;]) {
+ break;
+ } else {
+ return Err(lookahead.error());
+ }
+ }
+ }
+ let semi_token: Token![;] = input.parse()?;
+
+ let mut doc = Doc::new();
+ let mut derives = Vec::new();
+ let mut namespace = namespace.clone();
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let attrs = attrs::parse(
+ cx,
+ attrs,
+ attrs::Parser {
+ doc: Some(&mut doc),
+ derives: Some(&mut derives),
+ namespace: Some(&mut namespace),
+ cxx_name: Some(&mut cxx_name),
+ rust_name: Some(&mut rust_name),
+ ..Default::default()
+ },
+ );
+
+ let visibility = visibility_pub(&visibility, type_token.span);
+ let name = pair(namespace, &ident, cxx_name, rust_name);
+
+ Ok(match lang {
+ Lang::Cxx => Api::CxxType,
+ Lang::Rust => Api::RustType,
+ }(ExternType {
+ lang,
+ doc,
+ derives,
+ attrs,
+ visibility,
+ type_token,
+ name,
+ generics,
+ colon_token,
+ bounds,
+ semi_token,
+ trusted,
+ }))
+}
+
+fn parse_impl(imp: ItemImpl) -> Result<Api> {
+ let impl_token = imp.impl_token;
+
+ if !imp.items.is_empty() {
+ let mut span = Group::new(Delimiter::Brace, TokenStream::new());
+ span.set_span(imp.brace_token.span);
+ 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<T> {}`",
+ ));
+ }
+
+ 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::<TokenStream>();
+ if !ty.is_empty() {
+ negative_token = Some(Token));
+ self_ty = syn::parse2(ty)?;
+ }
+ }
+ }
+ }
+
+ let ty = parse_type(&self_ty)?;
+ let ty_generics = match &ty {
+ Type::RustBox(ty)
+ | Type::RustVec(ty)
+ | Type::UniquePtr(ty)
+ | Type::SharedPtr(ty)
+ | Type::WeakPtr(ty)
+ | Type::CxxVector(ty) => match &ty.inner {
+ Type::Ident(ident) => ident.generics.clone(),
+ _ => Lifetimes::default(),
+ },
+ Type::Ident(_)
+ | Type::Ref(_)
+ | Type::Ptr(_)
+ | Type::Str(_)
+ | Type::Fn(_)
+ | Type::Void(_)
+ | Type::SliceRef(_)
+ | Type::Array(_) => Lifetimes::default(),
+ };
+
+ let negative = negative_token.is_some();
+ let brace_token = imp.brace_token;
+
+ Ok(Api::Impl(Impl {
+ impl_token,
+ impl_generics,
+ negative,
+ ty,
+ ty_generics,
+ brace_token,
+ negative_token,
+ }))
+}
+
+fn parse_include(input: ParseStream) -> Result<Include> {
+ if input.peek(LitStr) {
+ let lit: LitStr = input.parse()?;
+ let span = lit.span();
+ return Ok(Include {
+ 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 {
+ path,
+ kind: IncludeKind::Bracketed,
+ begin_span: langle.span,
+ end_span: rangle.span,
+ });
+ }
+
+ Err(input.error("expected \"quoted/path/to\" or <bracketed/path/to>"))
+}
+
+fn parse_type(ty: &RustType) -> Result<Type> {
+ 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)),
+ _ => Err(Error::new_spanned(ty, "unsupported type")),
+ }
+}
+
+fn parse_type_reference(ty: &TypeReference) -> Result<Type> {
+ 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<Type> {
+ 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<Type> {
+ 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(_) => {}
+ }
+ }
+
+ Err(Error::new_spanned(ty, "unsupported type"))
+}
+
+fn parse_type_array(ty: &TypeArray) -> Result<Type> {
+ let inner = parse_type(&ty.elem)?;
+
+ let len_expr = if let Expr::Lit(lit) = &ty.len {
+ lit
+ } else {
+ let msg = "unsupported expression, array length must be an integer literal";
+ return Err(Error::new_spanned(&ty.len, msg));
+ };
+
+ let len_token = if let Lit::Int(int) = &len_expr.lit {
+ int.clone()
+ } else {
+ let msg = "array length must be an integer literal";
+ return Err(Error::new_spanned(len_expr, msg));
+ };
+
+ let len = len_token.base10_parse::<usize>()?;
+ 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,
+ })))
+}
+
+fn parse_type_fn(ty: &TypeBareFn) -> Result<Type> {
+ 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;
+ let ident = format_ident!("arg{}", i, span = fn_span);
+ let colon_token = Token;
+ (ident, colon_token)
+ }
+ };
+ let ty = parse_type(&arg.ty)?;
+ let doc = Doc::new();
+ let attrs = OtherAttrs::none();
+ let visibility = Token);
+ let name = pair(Namespace::default(), &ident, None, None);
+ Ok(Var {
+ doc,
+ attrs,
+ visibility,
+ name,
+ colon_token,
+ ty,
+ })
+ })
+ .collect::<Result<_>>()?;
+
+ let mut throws_tokens = None;
+ let ret = parse_return_type(&ty.output, &mut throws_tokens)?;
+ let throws = throws_tokens.is_some();
+
+ let unsafety = ty.unsafety;
+ let fn_token = ty.fn_token;
+ let generics = Generics::default();
+ let receiver = None;
+ let paren_token = ty.paren_token;
+
+ Ok(Type::Fn(Box::new(Signature {
+ unsafety,
+ fn_token,
+ generics,
+ receiver,
+ args,
+ ret,
+ throws,
+ paren_token,
+ throws_tokens,
+ })))
+}
+
+fn parse_return_type(
+ ty: &ReturnType,
+ throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>,
+) -> Result<Option<Type>> {
+ 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 => vis.pub_token.span,
+ Visibility::Crate(vis) => vis.crate_token.span,
+ Visibility::Restricted(vis) => vis.pub_token.span,
+ Visibility::Inherited => inherited,
+ })
+}
+
+fn pair(
+ namespace: Namespace,
+ default: &Ident,
+ cxx: Option<ForeignName>,
+ rust: Option<Ident>,
+) -> Pair {
+ Pair {
+ namespace,
+ cxx: cxx
+ .unwrap_or_else(|| ForeignName::parse(&default.to_string(), default.span()).unwrap()),
+ rust: rust.unwrap_or_else(|| default.clone()),
+ }
+}
--- /dev/null
+use crate::syntax::atom::Atom::{self, *};
+use crate::syntax::{derive, Trait, Type, Types};
+
+impl<'a> Types<'a> {
+ pub fn is_guaranteed_pod(&self, ty: &Type) -> bool {
+ match ty {
+ Type::Ident(ident) => {
+ let ident = &ident.rust;
+ if let Some(atom) = Atom::from(ident) {
+ match atom {
+ Bool | Char | U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64
+ | Isize | F32 | F64 => true,
+ CxxString | RustString => false,
+ }
+ } else if let Some(strct) = self.structs.get(ident) {
+ derive::contains(&strct.derives, Trait::Copy)
+ || strct
+ .fields
+ .iter()
+ .all(|field| self.is_guaranteed_pod(&field.ty))
+ } else {
+ self.enums.contains_key(ident)
+ }
+ }
+ Type::RustBox(_)
+ | Type::RustVec(_)
+ | Type::UniquePtr(_)
+ | Type::SharedPtr(_)
+ | Type::WeakPtr(_)
+ | Type::CxxVector(_)
+ | Type::Void(_) => false,
+ Type::Ref(_) | Type::Str(_) | Type::Fn(_) | Type::SliceRef(_) | Type::Ptr(_) => true,
+ Type::Array(array) => self.is_guaranteed_pod(&array.inner),
+ }
+ }
+}
--- /dev/null
+use syn::ext::IdentExt;
+use syn::parse::{ParseStream, Result};
+use syn::{Ident, LitStr, Token};
+
+pub struct QualifiedName {
+ pub segments: Vec<Ident>,
+}
+
+impl QualifiedName {
+ pub fn parse_unquoted(input: ParseStream) -> Result<Self> {
+ let mut segments = Vec::new();
+ let mut trailing_punct = true;
+ let leading_colons: Option<Token![::]> = input.parse()?;
+ while trailing_punct && input.peek(Ident::peek_any) {
+ let ident = Ident::parse_any(input)?;
+ segments.push(ident);
+ let colons: Option<Token![::]> = 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 })
+ }
+
+ pub fn parse_quoted_or_unquoted(input: ParseStream) -> Result<Self> {
+ if input.peek(LitStr) {
+ let lit: LitStr = input.parse()?;
+ if lit.value().is_empty() {
+ let segments = Vec::new();
+ Ok(QualifiedName { segments })
+ } else {
+ lit.parse_with(Self::parse_unquoted)
+ }
+ } else {
+ Self::parse_unquoted(input)
+ }
+ }
+}
--- /dev/null
+use quote::ToTokens;
+use std::fmt::Display;
+use syn::{Error, Result};
+
+pub struct Errors {
+ errors: Vec<Error>,
+}
+
+impl Errors {
+ pub fn new() -> Self {
+ Errors { errors: Vec::new() }
+ }
+
+ pub fn error(&mut self, sp: impl ToTokens, msg: impl Display) {
+ self.errors.push(Error::new_spanned(sp, msg));
+ }
+
+ pub fn push(&mut self, error: Error) {
+ self.errors.push(error);
+ }
+
+ pub fn propagate(&mut self) -> Result<()> {
+ let mut iter = self.errors.drain(..);
+ let mut all_errors = match iter.next() {
+ Some(err) => err,
+ None => return Ok(()),
+ };
+ for err in iter {
+ all_errors.combine(err);
+ }
+ Err(all_errors)
+ }
+}
--- /dev/null
+use crate::syntax::instantiate::NamedImplKey;
+use crate::syntax::{Lifetimes, NamedType, Pair, Types};
+use proc_macro2::Ident;
+
+#[derive(Copy, Clone)]
+pub struct Resolution<'a> {
+ pub name: &'a Pair,
+ pub generics: &'a Lifetimes,
+}
+
+impl<'a> Types<'a> {
+ pub 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 fn try_resolve(&self, ident: &impl UnresolvedName) -> Option<Resolution<'a>> {
+ let ident = ident.ident();
+ self.resolutions.get(ident).copied()
+ }
+}
+
+pub trait UnresolvedName {
+ fn ident(&self) -> &Ident;
+}
+
+impl UnresolvedName for Ident {
+ fn ident(&self) -> &Ident {
+ self
+ }
+}
+
+impl UnresolvedName for NamedType {
+ fn ident(&self) -> &Ident {
+ &self.rust
+ }
+}
+
+impl<'a> UnresolvedName for NamedImplKey<'a> {
+ fn ident(&self) -> &Ident {
+ self.rust
+ }
+}
--- /dev/null
+use std::fmt::{self, Debug};
+use std::slice;
+
+pub use self::ordered::OrderedSet;
+pub use self::unordered::UnorderedSet;
+
+mod ordered {
+ use super::{Iter, UnorderedSet};
+ use std::borrow::Borrow;
+ use std::hash::Hash;
+
+ pub struct OrderedSet<T> {
+ set: UnorderedSet<T>,
+ vec: Vec<T>,
+ }
+
+ impl<'a, T> OrderedSet<&'a T>
+ where
+ T: Hash + Eq,
+ {
+ pub fn new() -> Self {
+ OrderedSet {
+ set: UnorderedSet::new(),
+ vec: Vec::new(),
+ }
+ }
+
+ pub fn insert(&mut self, value: &'a T) -> bool {
+ let new = self.set.insert(value);
+ if new {
+ self.vec.push(value);
+ }
+ new
+ }
+
+ pub fn contains<Q>(&self, value: &Q) -> bool
+ where
+ &'a T: Borrow<Q>,
+ Q: ?Sized + Hash + Eq,
+ {
+ self.set.contains(value)
+ }
+
+ pub fn get<Q>(&self, value: &Q) -> Option<&'a T>
+ where
+ &'a T: Borrow<Q>,
+ Q: ?Sized + Hash + Eq,
+ {
+ self.set.get(value).copied()
+ }
+ }
+
+ impl<'a, T> OrderedSet<&'a T> {
+ pub fn is_empty(&self) -> bool {
+ self.vec.is_empty()
+ }
+
+ pub fn iter(&self) -> Iter<'_, 'a, T> {
+ Iter(self.vec.iter())
+ }
+ }
+
+ impl<'s, 'a, T> IntoIterator for &'s OrderedSet<&'a T> {
+ type Item = &'a T;
+ type IntoIter = Iter<'s, 'a, T>;
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+ }
+}
+
+mod unordered {
+ use std::borrow::Borrow;
+ use std::collections::HashSet;
+ use std::hash::Hash;
+
+ // Wrapper prohibits accidentally introducing iteration over the set, which
+ // could lead to nondeterministic generated code.
+ pub struct UnorderedSet<T>(HashSet<T>);
+
+ impl<T> UnorderedSet<T>
+ where
+ T: Hash + Eq,
+ {
+ pub fn new() -> Self {
+ UnorderedSet(HashSet::new())
+ }
+
+ pub fn insert(&mut self, value: T) -> bool {
+ self.0.insert(value)
+ }
+
+ pub fn contains<Q>(&self, value: &Q) -> bool
+ where
+ T: Borrow<Q>,
+ Q: ?Sized + Hash + Eq,
+ {
+ self.0.contains(value)
+ }
+
+ pub fn get<Q>(&self, value: &Q) -> Option<&T>
+ where
+ T: Borrow<Q>,
+ Q: ?Sized + Hash + Eq,
+ {
+ self.0.get(value)
+ }
+
+ pub fn retain(&mut self, f: impl FnMut(&T) -> bool) {
+ self.0.retain(f);
+ }
+ }
+}
+
+pub 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::Item> {
+ self.0.next().copied()
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.0.size_hint()
+ }
+}
+
+impl<'a, T> Debug for OrderedSet<&'a T>
+where
+ T: Debug,
+{
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.debug_set().entries(self).finish()
+ }
+}
--- /dev/null
+use crate::syntax::namespace::Namespace;
+use crate::syntax::{ForeignName, Pair};
+use proc_macro2::{Ident, TokenStream};
+use quote::ToTokens;
+use std::fmt::{self, Display, Write};
+
+// A mangled symbol consisting of segments separated by '$'.
+// For example: cxxbridge1$string$new
+pub 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 fn from_idents<'a>(it: impl Iterator<Item = &'a dyn Segment>) -> Self {
+ let mut symbol = Symbol(String::new());
+ for segment in it {
+ segment.write(&mut symbol);
+ }
+ assert!(!symbol.0.is_empty());
+ symbol
+ }
+
+ /// For example, for taking a symbol and then making a new symbol
+ /// for a vec of that symbol.
+ pub fn prefix_with(&self, prefix: &str) -> Symbol {
+ Symbol(format!("{}{}", prefix, self))
+ }
+}
+
+pub 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<T> Segment for &'_ T
+where
+ T: ?Sized + Segment + Display,
+{
+ fn write(&self, symbol: &mut Symbol) {
+ (**self).write(symbol);
+ }
+}
+
+pub 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
+}
--- /dev/null
+use crate::syntax::atom::Atom::*;
+use crate::syntax::{
+ Array, Atom, Derive, Enum, EnumRepr, ExternFn, ExternType, Impl, Lifetimes, NamedType, Ptr,
+ Receiver, 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=> ::std::os::raw::));
+ } else if ident.rust == CxxString {
+ let span = ident.rust.span();
+ tokens.extend(quote_spanned!(span=> ::cxx::));
+ }
+ 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 {
+ doc: _,
+ attrs: _,
+ visibility: _,
+ name,
+ colon_token: _,
+ ty,
+ } = self;
+ name.rust.to_tokens(tokens);
+ Token).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::));
+ }
+ "Vec" => {
+ tokens.extend(quote_spanned!(span=> ::std::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=> ::std::pin::Pin));
+ langle.to_tokens(tokens);
+ }
+ ampersand.to_tokens(tokens);
+ lifetime.to_tokens(tokens);
+ mutability.to_tokens(tokens);
+ inner.to_tokens(tokens);
+ if let Some((_pin, _langle, rangle)) = pin_tokens {
+ rangle.to_tokens(tokens);
+ }
+ }
+}
+
+impl ToTokens for Ptr {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let Ptr {
+ star,
+ mutable: _,
+ inner,
+ mutability,
+ constness,
+ } = self;
+ star.to_tokens(tokens);
+ mutability.to_tokens(tokens);
+ constness.to_tokens(tokens);
+ inner.to_tokens(tokens);
+ }
+}
+
+impl ToTokens for SliceRef {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let SliceRef {
+ ampersand,
+ lifetime,
+ mutable: _,
+ bracket,
+ inner,
+ mutability,
+ } = self;
+ ampersand.to_tokens(tokens);
+ lifetime.to_tokens(tokens);
+ mutability.to_tokens(tokens);
+ bracket.surround(tokens, |tokens| {
+ inner.to_tokens(tokens);
+ });
+ }
+}
+
+impl ToTokens for Array {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let Array {
+ bracket,
+ inner,
+ semi_token,
+ len: _,
+ len_token,
+ } = self;
+ bracket.surround(tokens, |tokens| {
+ inner.to_tokens(tokens);
+ semi_token.to_tokens(tokens);
+ len_token.to_tokens(tokens);
+ });
+ }
+}
+
+impl ToTokens for Atom {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ Ident::new(self.as_ref(), Span::call_site()).to_tokens(tokens);
+ }
+}
+
+impl ToTokens for Derive {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ Ident::new(self.what.as_ref(), self.span).to_tokens(tokens);
+ }
+}
+
+impl ToTokens for ExternType {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ // Notional token range for error reporting purposes.
+ self.type_token.to_tokens(tokens);
+ self.name.rust.to_tokens(tokens);
+ self.generics.to_tokens(tokens);
+ }
+}
+
+impl ToTokens for TypeAlias {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ // Notional token range for error reporting purposes.
+ self.type_token.to_tokens(tokens);
+ self.name.rust.to_tokens(tokens);
+ self.generics.to_tokens(tokens);
+ }
+}
+
+impl ToTokens for Struct {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ // Notional token range for error reporting purposes.
+ self.struct_token.to_tokens(tokens);
+ self.name.rust.to_tokens(tokens);
+ self.generics.to_tokens(tokens);
+ }
+}
+
+impl ToTokens for Enum {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ // Notional token range for error reporting purposes.
+ self.enum_token.to_tokens(tokens);
+ self.name.rust.to_tokens(tokens);
+ self.generics.to_tokens(tokens);
+ }
+}
+
+impl ToTokens for ExternFn {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ // Notional token range for error reporting purposes.
+ self.unsafety.to_tokens(tokens);
+ self.sig.fn_token.to_tokens(tokens);
+ self.semi_token.to_tokens(tokens);
+ }
+}
+
+impl ToTokens for Impl {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let Impl {
+ impl_token,
+ impl_generics,
+ negative: _,
+ ty,
+ ty_generics: _,
+ brace_token,
+ negative_token,
+ } = self;
+ impl_token.to_tokens(tokens);
+ impl_generics.to_tokens(tokens);
+ negative_token.to_tokens(tokens);
+ ty.to_tokens(tokens);
+ brace_token.surround(tokens, |_tokens| {});
+ }
+}
+
+impl ToTokens for Lifetimes {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let Lifetimes {
+ lt_token,
+ lifetimes,
+ gt_token,
+ } = self;
+ lt_token.to_tokens(tokens);
+ lifetimes.to_tokens(tokens);
+ gt_token.to_tokens(tokens);
+ }
+}
+
+impl ToTokens for Signature {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let Signature {
+ unsafety: _,
+ fn_token,
+ generics: _,
+ receiver: _,
+ args,
+ ret,
+ throws: _,
+ paren_token,
+ throws_tokens,
+ } = self;
+ fn_token.to_tokens(tokens);
+ paren_token.surround(tokens, |tokens| {
+ args.to_tokens(tokens);
+ });
+ if let Some(ret) = ret {
+ Token.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.to_tokens(tokens);
+ result.to_tokens(tokens);
+ langle.to_tokens(tokens);
+ token::Paren(langle.span).surround(tokens, |_| ());
+ rangle.to_tokens(tokens);
+ }
+ }
+}
+
+impl ToTokens for EnumRepr {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ match self {
+ EnumRepr::Native { atom, repr_type: _ } => atom.to_tokens(tokens),
+ EnumRepr::Foreign { rust_type } => rust_type.to_tokens(tokens),
+ }
+ }
+}
+
+impl ToTokens for NamedType {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let NamedType { rust, generics } = self;
+ rust.to_tokens(tokens);
+ generics.to_tokens(tokens);
+ }
+}
+
+pub struct ReceiverType<'a>(&'a Receiver);
+pub struct ReceiverTypeSelf<'a>(&'a Receiver);
+
+impl Receiver {
+ // &TheType
+ pub fn ty(&self) -> ReceiverType {
+ ReceiverType(self)
+ }
+
+ // &Self
+ pub 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=> ::std::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=> ::std::pin::Pin));
+ langle.to_tokens(tokens);
+ }
+ ampersand.to_tokens(tokens);
+ lifetime.to_tokens(tokens);
+ mutability.to_tokens(tokens);
+ Token).to_tokens(tokens);
+ if let Some((_pin, _langle, rangle)) = pin_tokens {
+ rangle.to_tokens(tokens);
+ }
+ }
+}
--- /dev/null
+use crate::syntax::map::{Entry, UnorderedMap as Map};
+use crate::syntax::report::Errors;
+use crate::syntax::{Api, Struct, Type, Types};
+
+enum Mark {
+ Visiting,
+ Visited,
+}
+
+pub 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
+}
--- /dev/null
+use crate::syntax::map::UnorderedMap;
+use crate::syntax::set::{OrderedSet as Set, UnorderedSet};
+use crate::syntax::{Api, Enum, ExternFn, NamedType, Pair, Struct, Type};
+use proc_macro2::Ident;
+use std::fmt::{self, Display};
+
+#[derive(Copy, Clone)]
+pub enum TrivialReason<'a> {
+ StructField(&'a Struct),
+ FunctionArgument(&'a ExternFn),
+ FunctionReturn(&'a ExternFn),
+ BoxTarget,
+ VecElement,
+ SliceElement { mutable: bool },
+ UnpinnedMut(&'a ExternFn),
+}
+
+pub fn required_trivial_reasons<'a>(
+ apis: &'a [Api],
+ all: &Set<&'a Type>,
+ structs: &UnorderedMap<&'a Ident, &'a Struct>,
+ enums: &UnorderedMap<&'a Ident, &'a Enum>,
+ cxx: &UnorderedSet<&'a Ident>,
+) -> UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>> {
+ let mut required_trivial = UnorderedMap::new();
+
+ let mut insist_extern_types_are_trivial = |ident: &'a NamedType, reason| {
+ if cxx.contains(&ident.rust)
+ && !structs.contains_key(&ident.rust)
+ && !enums.contains_key(&ident.rust)
+ {
+ required_trivial
+ .entry(&ident.rust)
+ .or_insert_with(Vec::new)
+ .push(reason);
+ }
+ };
+
+ for api in apis {
+ match api {
+ Api::Struct(strct) => {
+ for field in &strct.fields {
+ if let Type::Ident(ident) = &field.ty {
+ let reason = TrivialReason::StructField(strct);
+ insist_extern_types_are_trivial(ident, reason);
+ }
+ }
+ }
+ Api::CxxFunction(efn) | Api::RustFunction(efn) => {
+ if let Some(receiver) = &efn.receiver {
+ if receiver.mutable && !receiver.pinned {
+ let reason = TrivialReason::UnpinnedMut(efn);
+ insist_extern_types_are_trivial(&receiver.ty, reason);
+ }
+ }
+ for arg in &efn.args {
+ match &arg.ty {
+ Type::Ident(ident) => {
+ let reason = TrivialReason::FunctionArgument(efn);
+ insist_extern_types_are_trivial(ident, reason);
+ }
+ Type::Ref(ty) => {
+ if ty.mutable && !ty.pinned {
+ if let Type::Ident(ident) = &ty.inner {
+ let reason = TrivialReason::UnpinnedMut(efn);
+ insist_extern_types_are_trivial(ident, reason);
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ if let Some(ret) = &efn.ret {
+ match ret {
+ Type::Ident(ident) => {
+ let reason = TrivialReason::FunctionReturn(efn);
+ insist_extern_types_are_trivial(ident, reason);
+ }
+ Type::Ref(ty) => {
+ if ty.mutable && !ty.pinned {
+ if let Type::Ident(ident) = &ty.inner {
+ let reason = TrivialReason::UnpinnedMut(efn);
+ insist_extern_types_are_trivial(ident, reason);
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+
+ for ty in all {
+ match ty {
+ Type::RustBox(ty) => {
+ if let Type::Ident(ident) = &ty.inner {
+ let reason = TrivialReason::BoxTarget;
+ insist_extern_types_are_trivial(ident, reason);
+ }
+ }
+ Type::RustVec(ty) => {
+ if let Type::Ident(ident) = &ty.inner {
+ let reason = TrivialReason::VecElement;
+ insist_extern_types_are_trivial(ident, reason);
+ }
+ }
+ Type::SliceRef(ty) => {
+ if let Type::Ident(ident) = &ty.inner {
+ let reason = TrivialReason::SliceElement {
+ mutable: ty.mutable,
+ };
+ insist_extern_types_are_trivial(ident, reason);
+ }
+ }
+ _ => {}
+ }
+ }
+
+ required_trivial
+}
+
+// Context:
+// "type {type} should be trivially move constructible and trivially destructible in C++ to be used as {what} in Rust"
+// "needs a cxx::ExternType impl in order to be used as {what}"
+pub fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display + 'a {
+ struct Description<'a> {
+ name: &'a Pair,
+ reasons: &'a [TrivialReason<'a>],
+ }
+
+ impl<'a> Display for Description<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut field_of = Set::new();
+ let mut argument_of = Set::new();
+ let mut return_of = Set::new();
+ let mut box_target = false;
+ let mut vec_element = false;
+ let mut slice_shared_element = false;
+ let mut slice_mut_element = false;
+ let mut unpinned_mut = Set::new();
+
+ for reason in self.reasons {
+ match reason {
+ TrivialReason::StructField(strct) => {
+ field_of.insert(&strct.name.rust);
+ }
+ TrivialReason::FunctionArgument(efn) => {
+ argument_of.insert(&efn.name.rust);
+ }
+ TrivialReason::FunctionReturn(efn) => {
+ return_of.insert(&efn.name.rust);
+ }
+ TrivialReason::BoxTarget => box_target = true,
+ TrivialReason::VecElement => vec_element = true,
+ TrivialReason::SliceElement { mutable } => {
+ if *mutable {
+ slice_mut_element = true;
+ } else {
+ slice_shared_element = true;
+ }
+ }
+ TrivialReason::UnpinnedMut(efn) => {
+ unpinned_mut.insert(&efn.name.rust);
+ }
+ }
+ }
+
+ let mut clauses = Vec::new();
+ if !field_of.is_empty() {
+ clauses.push(Clause::Set {
+ article: "a",
+ desc: "field of",
+ set: &field_of,
+ });
+ }
+ if !argument_of.is_empty() {
+ clauses.push(Clause::Set {
+ article: "an",
+ desc: "argument of",
+ set: &argument_of,
+ });
+ }
+ if !return_of.is_empty() {
+ clauses.push(Clause::Set {
+ article: "a",
+ desc: "return value of",
+ set: &return_of,
+ });
+ }
+ if box_target {
+ clauses.push(Clause::Ty1 {
+ article: "type",
+ desc: "Box",
+ param: self.name,
+ });
+ }
+ if vec_element {
+ clauses.push(Clause::Ty1 {
+ article: "a",
+ desc: "vector element in Vec",
+ param: self.name,
+ });
+ }
+ if slice_shared_element || slice_mut_element {
+ clauses.push(Clause::Slice {
+ article: "a",
+ desc: "slice element in",
+ shared: slice_shared_element,
+ mutable: slice_mut_element,
+ param: self.name,
+ });
+ }
+ if !unpinned_mut.is_empty() {
+ clauses.push(Clause::Set {
+ article: "a",
+ desc: "non-pinned mutable reference in signature of",
+ set: &unpinned_mut,
+ });
+ }
+
+ for (i, clause) in clauses.iter().enumerate() {
+ if i == 0 {
+ write!(f, "{} ", clause.article())?;
+ } else if i + 1 < clauses.len() {
+ write!(f, ", ")?;
+ } else {
+ write!(f, " or ")?;
+ }
+ clause.fmt(f)?;
+ }
+
+ Ok(())
+ }
+ }
+
+ enum Clause<'a> {
+ Set {
+ article: &'a str,
+ desc: &'a str,
+ set: &'a Set<&'a Ident>,
+ },
+ Ty1 {
+ article: &'a str,
+ desc: &'a str,
+ param: &'a Pair,
+ },
+ Slice {
+ article: &'a str,
+ desc: &'a str,
+ shared: bool,
+ mutable: bool,
+ param: &'a Pair,
+ },
+ }
+
+ impl<'a> Clause<'a> {
+ fn article(&self) -> &'a str {
+ match self {
+ Clause::Set { article, .. }
+ | Clause::Ty1 { article, .. }
+ | Clause::Slice { article, .. } => article,
+ }
+ }
+
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Clause::Set {
+ article: _,
+ desc,
+ set,
+ } => {
+ write!(f, "{} ", desc)?;
+ for (i, ident) in set.iter().take(3).enumerate() {
+ if i > 0 {
+ write!(f, ", ")?;
+ }
+ write!(f, "`{}`", ident)?;
+ }
+ Ok(())
+ }
+ Clause::Ty1 {
+ article: _,
+ desc,
+ param,
+ } => write!(f, "{}<{}>", desc, param.rust),
+ Clause::Slice {
+ article: _,
+ desc,
+ shared,
+ mutable,
+ param,
+ } => {
+ write!(f, "{} ", desc)?;
+ if *shared {
+ write!(f, "&[{}]", param.rust)?;
+ }
+ if *shared && *mutable {
+ write!(f, " and ")?;
+ }
+ if *mutable {
+ write!(f, "&mut [{}]", param.rust)?;
+ }
+ Ok(())
+ }
+ }
+ }
+ }
+
+ Description { name, reasons }
+}
--- /dev/null
+use crate::syntax::improper::ImproperCtype;
+use crate::syntax::instantiate::ImplKey;
+use crate::syntax::map::{OrderedMap, UnorderedMap};
+use crate::syntax::report::Errors;
+use crate::syntax::resolve::Resolution;
+use crate::syntax::set::{OrderedSet, UnorderedSet};
+use crate::syntax::trivial::{self, TrivialReason};
+use crate::syntax::visit::{self, Visit};
+use crate::syntax::{
+ toposort, Api, Atom, Enum, EnumRepr, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias,
+};
+use proc_macro2::Ident;
+use quote::ToTokens;
+
+pub struct Types<'a> {
+ pub all: OrderedSet<&'a Type>,
+ pub structs: UnorderedMap<&'a Ident, &'a Struct>,
+ pub enums: UnorderedMap<&'a Ident, &'a Enum>,
+ pub cxx: UnorderedSet<&'a Ident>,
+ pub rust: UnorderedSet<&'a Ident>,
+ pub aliases: UnorderedMap<&'a Ident, &'a TypeAlias>,
+ pub untrusted: UnorderedMap<&'a Ident, &'a ExternType>,
+ pub required_trivial: UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>>,
+ pub impls: OrderedMap<ImplKey<'a>, Option<&'a Impl>>,
+ pub resolutions: UnorderedMap<&'a Ident, Resolution<'a>>,
+ pub struct_improper_ctypes: UnorderedSet<&'a Ident>,
+ pub toposorted_structs: Vec<&'a Struct>,
+}
+
+impl<'a> Types<'a> {
+ pub fn collect(cx: &mut Errors, apis: &'a [Api]) -> Self {
+ let mut all = OrderedSet::new();
+ let mut structs = UnorderedMap::new();
+ let mut enums = UnorderedMap::new();
+ let mut cxx = UnorderedSet::new();
+ let mut rust = UnorderedSet::new();
+ let mut aliases = UnorderedMap::new();
+ let mut untrusted = UnorderedMap::new();
+ let mut impls = OrderedMap::new();
+ let mut resolutions = UnorderedMap::new();
+ let struct_improper_ctypes = UnorderedSet::new();
+ let toposorted_structs = Vec::new();
+
+ fn visit<'a>(all: &mut OrderedSet<&'a Type>, ty: &'a Type) {
+ struct CollectTypes<'s, 'a>(&'s mut OrderedSet<&'a Type>);
+
+ impl<'s, 'a> Visit<'a> for CollectTypes<'s, 'a> {
+ fn visit_type(&mut self, ty: &'a Type) {
+ self.0.insert(ty);
+ visit::visit_type(self, ty);
+ }
+ }
+
+ CollectTypes(all).visit_type(ty);
+ }
+
+ let mut add_resolution = |name: &'a Pair, generics: &'a Lifetimes| {
+ resolutions.insert(&name.rust, Resolution { name, generics });
+ };
+
+ let mut type_names = UnorderedSet::new();
+ let mut function_names = UnorderedSet::new();
+ for api in apis {
+ // The same identifier is permitted to be declared as both a shared
+ // enum and extern C++ type, or shared struct and extern C++ type.
+ // That indicates to not emit the C++ enum/struct definition because
+ // it's defined by the included headers already.
+ //
+ // All other cases of duplicate identifiers are reported as an error.
+ match api {
+ Api::Include(_) => {}
+ Api::Struct(strct) => {
+ let ident = &strct.name.rust;
+ if !type_names.insert(ident)
+ && (!cxx.contains(ident)
+ || structs.contains_key(ident)
+ || enums.contains_key(ident))
+ {
+ // If already declared as a struct or enum, or if
+ // colliding with something other than an extern C++
+ // type, then error.
+ duplicate_name(cx, strct, ident);
+ }
+ structs.insert(&strct.name.rust, strct);
+ for field in &strct.fields {
+ visit(&mut all, &field.ty);
+ }
+ add_resolution(&strct.name, &strct.generics);
+ }
+ Api::Enum(enm) => {
+ match &enm.repr {
+ EnumRepr::Native { atom: _, repr_type } => {
+ all.insert(repr_type);
+ }
+ EnumRepr::Foreign { rust_type: _ } => {}
+ }
+ let ident = &enm.name.rust;
+ if !type_names.insert(ident)
+ && (!cxx.contains(ident)
+ || structs.contains_key(ident)
+ || enums.contains_key(ident))
+ {
+ // If already declared as a struct or enum, or if
+ // colliding with something other than an extern C++
+ // type, then error.
+ duplicate_name(cx, enm, ident);
+ }
+ enums.insert(ident, enm);
+ if enm.variants_from_header {
+ // #![variants_from_header] enums are implicitly extern
+ // C++ type.
+ cxx.insert(&enm.name.rust);
+ }
+ add_resolution(&enm.name, &enm.generics);
+ }
+ Api::CxxType(ety) => {
+ let ident = &ety.name.rust;
+ if !type_names.insert(ident)
+ && (cxx.contains(ident)
+ || !structs.contains_key(ident) && !enums.contains_key(ident))
+ {
+ // If already declared as an extern C++ type, or if
+ // colliding with something which is neither struct nor
+ // enum, then error.
+ duplicate_name(cx, ety, ident);
+ }
+ cxx.insert(ident);
+ if !ety.trusted {
+ untrusted.insert(ident, ety);
+ }
+ add_resolution(&ety.name, &ety.generics);
+ }
+ Api::RustType(ety) => {
+ let ident = &ety.name.rust;
+ if !type_names.insert(ident) {
+ duplicate_name(cx, ety, ident);
+ }
+ rust.insert(ident);
+ add_resolution(&ety.name, &ety.generics);
+ }
+ Api::CxxFunction(efn) | Api::RustFunction(efn) => {
+ // Note: duplication of the C++ name is fine because C++ has
+ // function overloading.
+ if !function_names.insert((&efn.receiver, &efn.name.rust)) {
+ duplicate_name(cx, efn, &efn.name.rust);
+ }
+ for arg in &efn.args {
+ visit(&mut all, &arg.ty);
+ }
+ if let Some(ret) = &efn.ret {
+ visit(&mut all, ret);
+ }
+ }
+ Api::TypeAlias(alias) => {
+ let ident = &alias.name.rust;
+ if !type_names.insert(ident) {
+ duplicate_name(cx, alias, ident);
+ }
+ cxx.insert(ident);
+ aliases.insert(ident, alias);
+ add_resolution(&alias.name, &alias.generics);
+ }
+ Api::Impl(imp) => {
+ visit(&mut all, &imp.ty);
+ if let Some(key) = imp.ty.impl_key() {
+ impls.insert(key, Some(imp));
+ }
+ }
+ }
+ }
+
+ for ty in &all {
+ let impl_key = match ty.impl_key() {
+ Some(impl_key) => impl_key,
+ None => continue,
+ };
+ let implicit_impl = match impl_key {
+ ImplKey::RustBox(ident)
+ | ImplKey::RustVec(ident)
+ | ImplKey::UniquePtr(ident)
+ | ImplKey::SharedPtr(ident)
+ | ImplKey::WeakPtr(ident)
+ | ImplKey::CxxVector(ident) => {
+ Atom::from(ident.rust).is_none() && !aliases.contains_key(ident.rust)
+ }
+ };
+ if implicit_impl && !impls.contains_key(&impl_key) {
+ impls.insert(impl_key, None);
+ }
+ }
+
+ // All these APIs may contain types passed by value. We need to ensure
+ // we check that this is permissible. We do this _after_ scanning all
+ // the APIs above, in case some function or struct references a type
+ // which is declared subsequently.
+ let required_trivial =
+ trivial::required_trivial_reasons(apis, &all, &structs, &enums, &cxx);
+
+ let mut types = Types {
+ all,
+ structs,
+ enums,
+ cxx,
+ rust,
+ aliases,
+ untrusted,
+ required_trivial,
+ impls,
+ resolutions,
+ struct_improper_ctypes,
+ toposorted_structs,
+ };
+
+ types.toposorted_structs = toposort::sort(cx, apis, &types);
+
+ let mut unresolved_structs = types.structs.keys();
+ let mut new_information = true;
+ while new_information {
+ new_information = false;
+ unresolved_structs.retain(|ident| {
+ let mut retain = false;
+ for var in &types.structs[ident].fields {
+ if match types.determine_improper_ctype(&var.ty) {
+ ImproperCtype::Depends(inner) => {
+ retain = true;
+ types.struct_improper_ctypes.contains(inner)
+ }
+ ImproperCtype::Definite(improper) => improper,
+ } {
+ types.struct_improper_ctypes.insert(ident);
+ new_information = true;
+ return false;
+ }
+ }
+ // If all fields definite false, remove from unresolved_structs.
+ retain
+ });
+ }
+
+ types
+ }
+
+ pub fn needs_indirect_abi(&self, ty: &Type) -> bool {
+ match ty {
+ Type::RustBox(_) | Type::UniquePtr(_) => false,
+ Type::Array(_) => true,
+ _ => !self.is_guaranteed_pod(ty),
+ }
+ }
+
+ // Types that trigger rustc's default #[warn(improper_ctypes)] lint, even if
+ // they may be otherwise unproblematic to mention in an extern signature.
+ // For example in a signature like `extern "C" fn(*const String)`, rustc
+ // refuses to believe that C could know how to supply us with a pointer to a
+ // Rust String, even though C could easily have obtained that pointer
+ // legitimately from a Rust call.
+ pub fn is_considered_improper_ctype(&self, ty: &Type) -> bool {
+ match self.determine_improper_ctype(ty) {
+ ImproperCtype::Definite(improper) => improper,
+ ImproperCtype::Depends(ident) => self.struct_improper_ctypes.contains(ident),
+ }
+ }
+
+ // Types which we need to assume could possibly exist by value on the Rust
+ // side.
+ pub fn is_maybe_trivial(&self, ty: &Ident) -> bool {
+ self.structs.contains_key(ty)
+ || self.enums.contains_key(ty)
+ || self.aliases.contains_key(ty)
+ }
+}
+
+impl<'t, 'a> IntoIterator for &'t Types<'a> {
+ type Item = &'a Type;
+ type IntoIter = crate::syntax::set::Iter<'t, 'a, Type>;
+ fn into_iter(self) -> Self::IntoIter {
+ self.all.into_iter()
+ }
+}
+
+fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) {
+ let msg = format!("the name `{}` is defined multiple times", ident);
+ cx.error(sp, msg);
+}
--- /dev/null
+use crate::syntax::Type;
+
+pub trait Visit<'a> {
+ fn visit_type(&mut self, ty: &'a Type) {
+ visit_type(self, ty);
+ }
+}
+
+pub 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);
+ }
+ }
+ }
+}
--- /dev/null
+const EXPECTED: &str = "\
+cxxbridge $VERSION
+David Tolnay <dtolnay@gmail.com>
+https://github.com/dtolnay/cxx
+
+USAGE:
+ cxxbridge <input>.rs Emit .cc file for bridge to stdout
+ cxxbridge <input>.rs --header Emit .h file for bridge to stdout
+ cxxbridge --header Emit \"rust/cxx.h\" header to stdout
+
+ARGS:
+ <input>
+ Input Rust source file containing #[cxx::bridge].
+
+OPTIONS:
+ --cxx-impl-annotations <annotation>
+ Optional annotation for implementations of C++ function wrappers
+ that may be exposed to Rust. You may for example need to provide
+ __declspec(dllexport) or __attribute__((visibility(\"default\")))
+ if Rust code from one shared object or executable depends on
+ these C++ functions in another.
+
+ -h, --help
+ Print help information.
+
+ --header
+ Emit header with declarations only. Optional if using `-o` with
+ a path ending in `.h`.
+
+ -i, --include <include>
+ Any additional headers to #include. The cxxbridge tool does not
+ parse or even require the given paths to exist; they simply go
+ into the generated C++ code as #include lines.
+
+ -o, --output <output>
+ Path of file to write as output. Output goes to stdout if -o is
+ not specified.
+
+ -V, --version
+ Print version information.
+";
+
+#[test]
+fn test_help() {
+ let mut app = super::app();
+ let mut out = Vec::new();
+ app.write_long_help(&mut out).unwrap();
+ let help = String::from_utf8(out).unwrap();
+ let version = option_env!("CARGO_PKG_VERSION").unwrap_or_default();
+ let expected = EXPECTED.replace("$VERSION", version);
+ assert_eq!(help, expected);
+}
+
+#[test]
+fn test_cli() {
+ let app = super::app();
+ app.debug_assert();
+}