Vendor dependencies

Let's see how I like this workflow.
This commit is contained in:
John Doty 2022-12-19 08:27:18 -08:00
parent 34d1830413
commit 9c435dc440
7500 changed files with 1665121 additions and 99 deletions

View file

@ -0,0 +1 @@
{"files":{"Cargo.toml":"0be1acfa0e5ade7db052e363cb204a4a9293897ce2b9cec6bf2636e616c43c7b","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","src/clang.rs":"c80e15e0423d405d4cf0e436db08f804dfc47b185b534e270ad3029687d3872f","src/derive.rs":"38ccd4642486d1ae94695d8a364d0df5e8c88218923712da298cd29889251b65","src/expand.rs":"c257cfbbc805113dac07ef464bfd188d796477912125ea31299dc24e6df699c6","src/generics.rs":"128aeaac4de300361dc97f0fd89c803cc55a2cc5a4c3ddf54515eb5bf4f5696b","src/lib.rs":"ce1f594783033e2ddd92970bccaa77f038bcd12d396d094113b37403730ddbef","src/load.rs":"4c0ff4ae075a06824d1411dff9297539727396148ca5d77a0a18a855bc1f9b97","src/syntax/atom.rs":"a289630b603f615f79156627a8498d795b9b388d241a318b0e2be530e879cd37","src/syntax/attrs.rs":"7a6725b7e458980f0e18c9828f03d42dc186d9fec7ca2273dcf409ab47263f75","src/syntax/cfg.rs":"a329854326108bc97509e213d363ef516baed7420696ad85ba6a25590295d11b","src/syntax/check.rs":"a4368b03ac9d1d36b3d4605ac350eb96910e9da5d44eb177df299f96e2b4b13f","src/syntax/derive.rs":"004f4aea489955c1b5a72f6fb2106a1d12df874420d2e0d103a202821c5f75db","src/syntax/discriminant.rs":"1b73eadd935d86f11cf1bf6d06d9de77167042e1ce3e574fe231d0897a623045","src/syntax/doc.rs":"a8aca64ee87a220da603ef9f78c1556ecc9b1991032d1d9fc49349b00ed15e51","src/syntax/error.rs":"04f1628b32bc1152a332a7e0c4d72b23acddd313c41069e47aae98a19e13a4dd","src/syntax/file.rs":"43a17146a5b8571d6c7aebdf606ceaedcd4fafd3c296b98c8a697071cf35701c","src/syntax/ident.rs":"a7804ff658cccebedc8e95b714c5738672c671e0c4c741625b750d7542ca258d","src/syntax/impls.rs":"9bab5656bec90cafc6ff488d6ea5c8aeced32737b0a244be6f9873207039d190","src/syntax/improper.rs":"280735758913534c71a811759aaf3a5b514f6ed3634c65c5d3b43e6e5bdfe2f6","src/syntax/instantiate.rs":"55fb1aeccdbd7f449410962eb454f9f5ce9f4b8d1d4724e77772e3c4c95b2d71","src/syntax/mangle.rs":"51db2c2528759d224b26e49619d3f0f188531c1d0944df0775607f4dcdef645b","src/syntax/map.rs":"fc4dfa0270063d221c682c94bdeb56359cdc3241f605e28a103f87ad59813265","src/syntax/mod.rs":"9a6338892cdd67b5fdb7944a6a92f61b7ccf79274e629da8ed3af99623ed31de","src/syntax/names.rs":"6938e960790331cd356ebb347057de3a319a03cc2ee382a975b22747324585e1","src/syntax/namespace.rs":"67408e0ab90979165e19de7a5fd557b2df2b97d127f37879ef1812c97f98e15b","src/syntax/parse.rs":"4d776a33d35715de27a36ab5cd235f7076a75627f4631894b607f491d360bd3f","src/syntax/pod.rs":"82c0171f90e289615184cf98f86bf814855f81c9b7ecd20d5b54ece4c57b8599","src/syntax/qualified.rs":"86e49af8d3ae46489fa37ff08358e001cf5345503f6c03becec36b88984d678d","src/syntax/report.rs":"e6c35148802a1581ad1204d34f49a40f9dca9f0b863bd3d9b56389a63fba1969","src/syntax/resolve.rs":"6a25e3cdaf9176cf72de6ced354a2d941d11ab36fe7d2afca51ec707034a1819","src/syntax/set.rs":"6440cd155cd2c5cd36becb76456f74ab14f83f108b0e12555860c7b778e8a0d2","src/syntax/symbol.rs":"045e685ca33192dd73bb995a2e4cda0a4b8f37ae90674d142a1994bba43baf03","src/syntax/tokens.rs":"ce7ca8a560ec79264cc1bb49d9b00cf6e059bc0c7463dfed7607478c596a34bc","src/syntax/toposort.rs":"1ff97c49743369808bd866d176759bd7939b2b4afc31ffea65929d1e2ccc611d","src/syntax/trivial.rs":"3e47542b526ae74ccab254040499ff6c5cdd11e309ee3d7e8857b92f6be2e5ee","src/syntax/types.rs":"89686498d1d14ffe843795ad54933363f6019d1063c74b519dcf3890bd24272c","src/syntax/visit.rs":"35ff95ed4e13a463531694d7c78199ad83249d7ba3dd8984e591b26f39b1eb3b","src/tokens.rs":"c9bac9556260005bf5dc9faddbf04fb512e1665276dfd3846335638aa3337c53","src/type_id.rs":"5517bc0da5d9dc469c126bd3e47e383f0a29dc28c7fae30e8243789b65eabde9"},"package":"3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6"}

78
vendor/cxxbridge-macro/Cargo.toml vendored Normal file
View file

@ -0,0 +1,78 @@
# 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.48"
name = "cxxbridge-macro"
version = "1.0.85"
authors = ["David Tolnay <dtolnay@gmail.com>"]
exclude = [
"build.rs",
"README.md",
]
description = "Implementation detail of the `cxx` crate."
homepage = "https://cxx.rs"
readme = "README.md"
keywords = ["ffi"]
categories = ["development-tools::ffi"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/dtolnay/cxx"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[lib]
proc-macro = true
[dependencies.clang-ast]
version = "0.1"
optional = true
[dependencies.flate2]
version = "1.0"
optional = true
[dependencies.memmap]
version = "0.7"
optional = true
[dependencies.proc-macro2]
version = "1.0.39"
[dependencies.quote]
version = "1.0.4"
[dependencies.serde]
version = "1.0"
features = ["derive"]
optional = true
[dependencies.serde_json]
version = "1.0"
optional = true
[dependencies.syn]
version = "1.0.95"
features = ["full"]
[dev-dependencies.cxx]
version = "1.0"
[features]
experimental-async-fn = []
experimental-enum-variants-from-header = [
"clang-ast",
"flate2",
"memmap",
"serde",
"serde_json",
]

201
vendor/cxxbridge-macro/LICENSE-APACHE vendored Normal file
View file

@ -0,0 +1,201 @@
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.

23
vendor/cxxbridge-macro/LICENSE-MIT vendored Normal file
View file

@ -0,0 +1,23 @@
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.

51
vendor/cxxbridge-macro/src/clang.rs vendored Normal file
View file

@ -0,0 +1,51 @@
use serde::{Deserialize, Serialize};
pub type Node = clang_ast::Node<Clang>;
#[derive(Deserialize, Serialize)]
pub enum Clang {
NamespaceDecl(NamespaceDecl),
EnumDecl(EnumDecl),
EnumConstantDecl(EnumConstantDecl),
ImplicitCastExpr,
ConstantExpr(ConstantExpr),
Unknown,
}
#[derive(Deserialize, Serialize)]
pub struct NamespaceDecl {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<Box<str>>,
}
#[derive(Deserialize, Serialize)]
pub struct EnumDecl {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<Box<str>>,
#[serde(
rename = "fixedUnderlyingType",
skip_serializing_if = "Option::is_none"
)]
pub fixed_underlying_type: Option<Type>,
}
#[derive(Deserialize, Serialize)]
pub struct EnumConstantDecl {
pub name: Box<str>,
}
#[derive(Deserialize, Serialize)]
pub struct ConstantExpr {
pub value: Box<str>,
}
#[derive(Deserialize, Serialize)]
pub struct Type {
#[serde(rename = "qualType")]
pub qual_type: Box<str>,
#[serde(rename = "desugaredQualType", skip_serializing_if = "Option::is_none")]
pub desugared_qual_type: Option<Box<str>>,
}
#[cfg(all(test, target_pointer_width = "64"))]
const _: [(); core::mem::size_of::<Node>()] = [(); 88];

286
vendor/cxxbridge-macro/src/derive.rs vendored Normal file
View file

@ -0,0 +1,286 @@
use crate::syntax::{derive, Enum, Struct, Trait};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
pub use crate::syntax::derive::*;
pub fn expand_struct(strct: &Struct, actual_derives: &mut Option<TokenStream>) -> TokenStream {
let mut expanded = TokenStream::new();
let mut traits = Vec::new();
for derive in &strct.derives {
let span = derive.span;
match derive.what {
Trait::Copy => expanded.extend(struct_copy(strct, span)),
Trait::Clone => expanded.extend(struct_clone(strct, span)),
Trait::Debug => expanded.extend(struct_debug(strct, span)),
Trait::Default => expanded.extend(struct_default(strct, span)),
Trait::Eq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq)),
Trait::ExternType => unreachable!(),
Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
Trait::Ord => expanded.extend(struct_ord(strct, span)),
Trait::PartialEq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq)),
Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
}
}
if traits.is_empty() {
*actual_derives = None;
} else {
*actual_derives = Some(quote!(#[derive(#(#traits),*)]));
}
expanded
}
pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
let mut expanded = TokenStream::new();
let mut traits = Vec::new();
let mut has_copy = false;
let mut has_clone = false;
let mut has_eq = false;
let mut has_partial_eq = false;
for derive in &enm.derives {
let span = derive.span;
match derive.what {
Trait::Copy => {
expanded.extend(enum_copy(enm, span));
has_copy = true;
}
Trait::Clone => {
expanded.extend(enum_clone(enm, span));
has_clone = true;
}
Trait::Debug => expanded.extend(enum_debug(enm, span)),
Trait::Default => unreachable!(),
Trait::Eq => {
traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq));
has_eq = true;
}
Trait::ExternType => unreachable!(),
Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
Trait::Ord => expanded.extend(enum_ord(enm, span)),
Trait::PartialEq => {
traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq));
has_partial_eq = true;
}
Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)),
Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
}
}
let span = enm.name.rust.span();
if !has_copy {
expanded.extend(enum_copy(enm, span));
}
if !has_clone {
expanded.extend(enum_clone(enm, span));
}
if !has_eq {
// Required to be derived in order for the enum's "variants" to be
// usable in patterns.
traits.push(quote!(::cxx::core::cmp::Eq));
}
if !has_partial_eq {
traits.push(quote!(::cxx::core::cmp::PartialEq));
}
*actual_derives = Some(quote!(#[derive(#(#traits),*)]));
expanded
}
fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
let ident = &strct.name.rust;
let generics = &strct.generics;
quote_spanned! {span=>
impl #generics ::cxx::core::marker::Copy for #ident #generics {}
}
}
fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
let ident = &strct.name.rust;
let generics = &strct.generics;
let body = if derive::contains(&strct.derives, Trait::Copy) {
quote!(*self)
} else {
let fields = strct.fields.iter().map(|field| &field.name.rust);
let values = strct.fields.iter().map(|field| {
let ident = &field.name.rust;
let ty = field.ty.to_token_stream();
let span = ty.into_iter().last().unwrap().span();
quote_spanned!(span=> &self.#ident)
});
quote_spanned!(span=> #ident {
#(#fields: ::cxx::core::clone::Clone::clone(#values),)*
})
};
quote_spanned! {span=>
impl #generics ::cxx::core::clone::Clone for #ident #generics {
fn clone(&self) -> Self {
#body
}
}
}
}
fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
let ident = &strct.name.rust;
let generics = &strct.generics;
let struct_name = ident.to_string();
let fields = strct.fields.iter().map(|field| &field.name.rust);
let field_names = fields.clone().map(Ident::to_string);
quote_spanned! {span=>
impl #generics ::cxx::core::fmt::Debug for #ident #generics {
fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
formatter.debug_struct(#struct_name)
#(.field(#field_names, &self.#fields))*
.finish()
}
}
}
}
fn struct_default(strct: &Struct, span: Span) -> TokenStream {
let ident = &strct.name.rust;
let generics = &strct.generics;
let fields = strct.fields.iter().map(|field| &field.name.rust);
quote_spanned! {span=>
#[allow(clippy::derivable_impls)] // different spans than the derived impl
impl #generics ::cxx::core::default::Default for #ident #generics {
fn default() -> Self {
#ident {
#(
#fields: ::cxx::core::default::Default::default(),
)*
}
}
}
}
}
fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
let ident = &strct.name.rust;
let generics = &strct.generics;
let fields = strct.fields.iter().map(|field| &field.name.rust);
quote_spanned! {span=>
impl #generics ::cxx::core::cmp::Ord for #ident #generics {
fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
#(
match ::cxx::core::cmp::Ord::cmp(&self.#fields, &other.#fields) {
::cxx::core::cmp::Ordering::Equal => {}
ordering => return ordering,
}
)*
::cxx::core::cmp::Ordering::Equal
}
}
}
}
fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
let ident = &strct.name.rust;
let generics = &strct.generics;
let body = if derive::contains(&strct.derives, Trait::Ord) {
quote! {
::cxx::core::option::Option::Some(::cxx::core::cmp::Ord::cmp(self, other))
}
} else {
let fields = strct.fields.iter().map(|field| &field.name.rust);
quote! {
#(
match ::cxx::core::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal) => {}
ordering => return ordering,
}
)*
::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal)
}
};
quote_spanned! {span=>
impl #generics ::cxx::core::cmp::PartialOrd for #ident #generics {
fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
#body
}
}
}
}
fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
let ident = &enm.name.rust;
quote_spanned! {span=>
impl ::cxx::core::marker::Copy for #ident {}
}
}
fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
let ident = &enm.name.rust;
quote_spanned! {span=>
impl ::cxx::core::clone::Clone for #ident {
fn clone(&self) -> Self {
*self
}
}
}
}
fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
let ident = &enm.name.rust;
let variants = enm.variants.iter().map(|variant| {
let variant = &variant.name.rust;
let name = variant.to_string();
quote_spanned! {span=>
#ident::#variant => formatter.write_str(#name),
}
});
let fallback = format!("{}({{}})", ident);
quote_spanned! {span=>
impl ::cxx::core::fmt::Debug for #ident {
fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
match *self {
#(#variants)*
_ => ::cxx::core::write!(formatter, #fallback, self.repr),
}
}
}
}
}
fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
let ident = &enm.name.rust;
quote_spanned! {span=>
impl ::cxx::core::cmp::Ord for #ident {
fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
::cxx::core::cmp::Ord::cmp(&self.repr, &other.repr)
}
}
}
}
fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
let ident = &enm.name.rust;
quote_spanned! {span=>
impl ::cxx::core::cmp::PartialOrd for #ident {
fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
::cxx::core::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr)
}
}
}
}

1819
vendor/cxxbridge-macro/src/expand.rs vendored Normal file

File diff suppressed because it is too large Load diff

91
vendor/cxxbridge-macro/src/generics.rs vendored Normal file
View file

@ -0,0 +1,91 @@
use crate::syntax::instantiate::NamedImplKey;
use crate::syntax::resolve::Resolution;
use crate::syntax::{Impl, Lifetimes};
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{Lifetime, Token};
pub struct ImplGenerics<'a> {
explicit_impl: Option<&'a Impl>,
resolve: Resolution<'a>,
}
pub struct TyGenerics<'a> {
key: NamedImplKey<'a>,
explicit_impl: Option<&'a Impl>,
resolve: Resolution<'a>,
}
pub fn split_for_impl<'a>(
key: NamedImplKey<'a>,
explicit_impl: Option<&'a Impl>,
resolve: Resolution<'a>,
) -> (ImplGenerics<'a>, TyGenerics<'a>) {
let impl_generics = ImplGenerics {
explicit_impl,
resolve,
};
let ty_generics = TyGenerics {
key,
explicit_impl,
resolve,
};
(impl_generics, ty_generics)
}
impl<'a> ToTokens for ImplGenerics<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
if let Some(imp) = self.explicit_impl {
imp.impl_generics.to_tokens(tokens);
} else {
self.resolve.generics.to_tokens(tokens);
}
}
}
impl<'a> ToTokens for TyGenerics<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
if let Some(imp) = self.explicit_impl {
imp.ty_generics.to_tokens(tokens);
} else if !self.resolve.generics.lifetimes.is_empty() {
let span = self.key.rust.span();
self.key
.lt_token
.unwrap_or_else(|| Token![<](span))
.to_tokens(tokens);
self.resolve.generics.lifetimes.to_tokens(tokens);
self.key
.gt_token
.unwrap_or_else(|| Token![>](span))
.to_tokens(tokens);
}
}
}
pub struct UnderscoreLifetimes<'a> {
generics: &'a Lifetimes,
}
impl Lifetimes {
pub fn to_underscore_lifetimes(&self) -> UnderscoreLifetimes {
UnderscoreLifetimes { generics: self }
}
}
impl<'a> ToTokens for UnderscoreLifetimes<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Lifetimes {
lt_token,
lifetimes,
gt_token,
} = self.generics;
lt_token.to_tokens(tokens);
for pair in lifetimes.pairs() {
let (lifetime, punct) = pair.into_tuple();
let lifetime = Lifetime::new("'_", lifetime.span());
lifetime.to_tokens(tokens);
punct.to_tokens(tokens);
}
gt_token.to_tokens(tokens);
}
}

105
vendor/cxxbridge-macro/src/lib.rs vendored Normal file
View file

@ -0,0 +1,105 @@
#![allow(
clippy::cast_sign_loss,
clippy::default_trait_access,
clippy::derive_partial_eq_without_eq,
clippy::doc_markdown,
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_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,
clippy::single_match_else,
clippy::too_many_arguments,
clippy::too_many_lines,
clippy::toplevel_ref_arg,
clippy::useless_let_if_seq,
// clippy bug: https://github.com/rust-lang/rust-clippy/issues/6983
clippy::wrong_self_convention
)]
extern crate proc_macro;
mod derive;
mod expand;
mod generics;
mod syntax;
mod tokens;
mod type_id;
#[cfg(feature = "experimental-enum-variants-from-header")]
mod clang;
#[cfg(feature = "experimental-enum-variants-from-header")]
mod load;
use crate::syntax::file::Module;
use crate::syntax::namespace::Namespace;
use crate::syntax::qualified::QualifiedName;
use crate::type_id::Crate;
use proc_macro::TokenStream;
use syn::parse::{Parse, ParseStream, Parser, Result};
use syn::parse_macro_input;
/// `#[cxx::bridge] mod ffi { ... }`
///
/// Refer to the crate-level documentation for the explanation of how this macro
/// is intended to be used.
///
/// The only additional thing to note here is namespace support &mdash; if the
/// types and functions on the `extern "C++"` side of our bridge are in a
/// namespace, specify that namespace as an argument of the cxx::bridge
/// attribute macro.
///
/// ```
/// #[cxx::bridge(namespace = "mycompany::rust")]
/// # mod ffi {}
/// ```
///
/// The types and functions from the `extern "Rust"` side of the bridge will be
/// placed into that same namespace in the generated C++ code.
#[proc_macro_attribute]
pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream {
let _ = syntax::error::ERRORS;
let namespace = match Namespace::parse_bridge_attr_namespace.parse(args) {
Ok(namespace) => namespace,
Err(err) => return err.to_compile_error().into(),
};
let mut ffi = parse_macro_input!(input as Module);
ffi.namespace = namespace;
expand::bridge(ffi)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
#[doc(hidden)]
#[proc_macro]
pub fn type_id(input: TokenStream) -> TokenStream {
struct TypeId {
krate: Crate,
path: QualifiedName,
}
impl Parse for TypeId {
fn parse(input: ParseStream) -> Result<Self> {
let krate = input.parse().map(Crate::DollarCrate)?;
let path = QualifiedName::parse_quoted_or_unquoted(input)?;
Ok(TypeId { krate, path })
}
}
let arg = parse_macro_input!(input as TypeId);
type_id::expand(arg.krate, arg.path).into()
}

317
vendor/cxxbridge-macro/src/load.rs vendored Normal file
View file

@ -0,0 +1,317 @@
use crate::clang::{Clang, Node};
use crate::syntax::attrs::OtherAttrs;
use crate::syntax::cfg::CfgExpr;
use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::{Api, Discriminant, Doc, Enum, EnumRepr, ForeignName, Pair, Variant};
use flate2::write::GzDecoder;
use memmap::Mmap;
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use std::env;
use std::fmt::{self, Display};
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::str::FromStr;
use syn::{parse_quote, Path};
const CXX_CLANG_AST: &str = "CXX_CLANG_AST";
pub fn load(cx: &mut Errors, apis: &mut [Api]) {
let ref mut variants_from_header = Vec::new();
for api in apis {
if let Api::Enum(enm) = api {
if enm.variants_from_header {
if enm.variants.is_empty() {
variants_from_header.push(enm);
} else {
let span = span_for_enum_error(enm);
cx.error(
span,
"enum with #![variants_from_header] must be written with no explicit variants",
);
}
}
}
}
let span = match variants_from_header.get(0) {
None => return,
Some(enm) => enm.variants_from_header_attr.clone().unwrap(),
};
let ast_dump_path = match env::var_os(CXX_CLANG_AST) {
Some(ast_dump_path) => PathBuf::from(ast_dump_path),
None => {
let msg = format!(
"environment variable ${} has not been provided",
CXX_CLANG_AST,
);
return cx.error(span, msg);
}
};
let memmap = File::open(&ast_dump_path).and_then(|file| unsafe { Mmap::map(&file) });
let mut gunzipped;
let ast_dump_bytes = match match memmap {
Ok(ref memmap) => {
let is_gzipped = memmap.get(..2) == Some(b"\x1f\x8b");
if is_gzipped {
gunzipped = Vec::new();
let decode_result = GzDecoder::new(&mut gunzipped).write_all(memmap);
decode_result.map(|_| gunzipped.as_slice())
} else {
Ok(memmap as &[u8])
}
}
Err(error) => Err(error),
} {
Ok(bytes) => bytes,
Err(error) => {
let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
return cx.error(span, msg);
}
};
let ref root: Node = match serde_json::from_slice(ast_dump_bytes) {
Ok(root) => root,
Err(error) => {
let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
return cx.error(span, msg);
}
};
let ref mut namespace = Vec::new();
traverse(cx, root, namespace, variants_from_header, None);
for enm in variants_from_header {
if enm.variants.is_empty() {
let span = &enm.variants_from_header_attr;
let name = CxxName(&enm.name);
let msg = format!("failed to find any C++ definition of enum {}", name);
cx.error(span, msg);
}
}
}
fn traverse<'a>(
cx: &mut Errors,
node: &'a Node,
namespace: &mut Vec<&'a str>,
variants_from_header: &mut [&mut Enum],
mut idx: Option<usize>,
) {
match &node.kind {
Clang::NamespaceDecl(decl) => {
let name = match &decl.name {
Some(name) => name,
// Can ignore enums inside an anonymous namespace.
None => return,
};
namespace.push(name);
idx = None;
}
Clang::EnumDecl(decl) => {
let name = match &decl.name {
Some(name) => name,
None => return,
};
idx = None;
for (i, enm) in variants_from_header.iter_mut().enumerate() {
if enm.name.cxx == **name && enm.name.namespace.iter().eq(&*namespace) {
if !enm.variants.is_empty() {
let span = &enm.variants_from_header_attr;
let qual_name = CxxName(&enm.name);
let msg = format!("found multiple C++ definitions of enum {}", qual_name);
cx.error(span, msg);
return;
}
let fixed_underlying_type = match &decl.fixed_underlying_type {
Some(fixed_underlying_type) => fixed_underlying_type,
None => {
let span = &enm.variants_from_header_attr;
let name = &enm.name.cxx;
let qual_name = CxxName(&enm.name);
let msg = format!(
"implicit implementation-defined repr for enum {} is not supported yet; consider changing its C++ definition to `enum {}: int {{...}}",
qual_name, name,
);
cx.error(span, msg);
return;
}
};
let repr = translate_qual_type(
cx,
enm,
fixed_underlying_type
.desugared_qual_type
.as_ref()
.unwrap_or(&fixed_underlying_type.qual_type),
);
enm.repr = EnumRepr::Foreign { rust_type: repr };
idx = Some(i);
break;
}
}
if idx.is_none() {
return;
}
}
Clang::EnumConstantDecl(decl) => {
if let Some(idx) = idx {
let enm = &mut *variants_from_header[idx];
let span = enm
.variants_from_header_attr
.as_ref()
.unwrap()
.path
.get_ident()
.unwrap()
.span();
let cxx_name = match ForeignName::parse(&decl.name, span) {
Ok(foreign_name) => foreign_name,
Err(_) => {
let span = &enm.variants_from_header_attr;
let msg = format!("unsupported C++ variant name: {}", decl.name);
return cx.error(span, msg);
}
};
let rust_name: Ident = match syn::parse_str(&decl.name) {
Ok(ident) => ident,
Err(_) => format_ident!("__Variant{}", enm.variants.len()),
};
let discriminant = match discriminant_value(&node.inner) {
ParsedDiscriminant::Constant(discriminant) => discriminant,
ParsedDiscriminant::Successor => match enm.variants.last() {
None => Discriminant::zero(),
Some(last) => match last.discriminant.checked_succ() {
Some(discriminant) => discriminant,
None => {
let span = &enm.variants_from_header_attr;
let msg = format!(
"overflow processing discriminant value for variant: {}",
decl.name,
);
return cx.error(span, msg);
}
},
},
ParsedDiscriminant::Fail => {
let span = &enm.variants_from_header_attr;
let msg = format!(
"failed to obtain discriminant value for variant: {}",
decl.name,
);
cx.error(span, msg);
Discriminant::zero()
}
};
enm.variants.push(Variant {
cfg: CfgExpr::Unconditional,
doc: Doc::new(),
attrs: OtherAttrs::none(),
name: Pair {
namespace: Namespace::ROOT,
cxx: cxx_name,
rust: rust_name,
},
discriminant,
expr: None,
});
}
}
_ => {}
}
for inner in &node.inner {
traverse(cx, inner, namespace, variants_from_header, idx);
}
if let Clang::NamespaceDecl(_) = &node.kind {
let _ = namespace.pop().unwrap();
}
}
fn translate_qual_type(cx: &mut Errors, enm: &Enum, qual_type: &str) -> Path {
let rust_std_name = match qual_type {
"char" => "c_char",
"int" => "c_int",
"long" => "c_long",
"long long" => "c_longlong",
"signed char" => "c_schar",
"short" => "c_short",
"unsigned char" => "c_uchar",
"unsigned int" => "c_uint",
"unsigned long" => "c_ulong",
"unsigned long long" => "c_ulonglong",
"unsigned short" => "c_ushort",
unsupported => {
let span = &enm.variants_from_header_attr;
let qual_name = CxxName(&enm.name);
let msg = format!(
"unsupported underlying type for {}: {}",
qual_name, unsupported,
);
cx.error(span, msg);
"c_int"
}
};
let span = enm
.variants_from_header_attr
.as_ref()
.unwrap()
.path
.get_ident()
.unwrap()
.span();
let ident = Ident::new(rust_std_name, span);
let path = quote_spanned!(span=> ::cxx::core::ffi::#ident);
parse_quote!(#path)
}
enum ParsedDiscriminant {
Constant(Discriminant),
Successor,
Fail,
}
fn discriminant_value(mut clang: &[Node]) -> ParsedDiscriminant {
if clang.is_empty() {
// No discriminant expression provided; use successor of previous
// descriminant.
return ParsedDiscriminant::Successor;
}
loop {
if clang.len() != 1 {
return ParsedDiscriminant::Fail;
}
let node = &clang[0];
match &node.kind {
Clang::ImplicitCastExpr => clang = &node.inner,
Clang::ConstantExpr(expr) => match Discriminant::from_str(&expr.value) {
Ok(discriminant) => return ParsedDiscriminant::Constant(discriminant),
Err(_) => return ParsedDiscriminant::Fail,
},
_ => return ParsedDiscriminant::Fail,
}
}
}
fn span_for_enum_error(enm: &Enum) -> TokenStream {
let enum_token = enm.enum_token;
let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
brace_token.set_span(enm.brace_token.span);
quote!(#enum_token #brace_token)
}
struct CxxName<'a>(&'a Pair);
impl<'a> Display for CxxName<'a> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
for namespace in &self.0.namespace {
write!(formatter, "{}::", namespace)?;
}
write!(formatter, "{}", self.0.cxx)
}
}

View file

@ -0,0 +1,103 @@
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
}
}

View file

@ -0,0 +1,302 @@
use crate::syntax::cfg::CfgExpr;
use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::Atom::{self, *};
use crate::syntax::{cfg, Derive, Doc, ForeignName};
use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
use syn::parse::{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 cfg: Option<&'a mut CfgExpr>,
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>>,
pub ignore_unrecognized: bool,
// Suppress clippy needless_update lint ("struct update has no effect, all
// the fields in the struct have already been specified") when preemptively
// writing `..Default::default()`.
pub(crate) _more: (),
}
pub 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("cfg") {
match cfg::parse_attribute.parse2(attr.tokens.clone()) {
Ok(cfg_expr) => {
if let Some(cfg) = &mut parser.cfg {
cfg.merge(cfg_expr);
passthrough_attrs.push(attr);
continue;
}
}
Err(err) => {
cx.push(err);
break;
}
}
} else if attr.path.is_ident("variants_from_header")
&& cfg!(feature = "experimental-enum-variants-from-header")
{
if let Err(err) = 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;
}
}
if !parser.ignore_unrecognized {
cx.error(attr, "unsupported attribute");
break;
}
}
OtherAttrs(passthrough_attrs)
}
enum DocAttribute {
Doc(LitStr),
Hidden,
}
mod kw {
syn::custom_keyword!(hidden);
}
fn parse_doc_attribute(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()
}
}
#[derive(Clone)]
pub struct OtherAttrs(Vec<Attribute>);
impl OtherAttrs {
pub fn none() -> Self {
OtherAttrs(Vec::new())
}
pub fn extend(&mut self, other: Self) {
self.0.extend(other.0);
}
}
impl ToTokens for OtherAttrs {
fn to_tokens(&self, tokens: &mut TokenStream) {
for attr in &self.0 {
let Attribute {
pound_token,
style,
bracket_token,
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);
});
}
}
}

View file

@ -0,0 +1,77 @@
use proc_macro2::Ident;
use std::mem;
use syn::parse::{Error, ParseStream, Result};
use syn::{parenthesized, token, LitStr, Token};
#[derive(Clone)]
pub enum CfgExpr {
Unconditional,
Eq(Ident, Option<LitStr>),
All(Vec<CfgExpr>),
Any(Vec<CfgExpr>),
Not(Box<CfgExpr>),
}
impl CfgExpr {
pub fn merge(&mut self, expr: CfgExpr) {
if let CfgExpr::Unconditional = self {
*self = expr;
} else if let CfgExpr::All(list) = self {
list.push(expr);
} else {
let prev = mem::replace(self, CfgExpr::Unconditional);
*self = CfgExpr::All(vec![prev, expr]);
}
}
}
pub fn parse_attribute(input: ParseStream) -> Result<CfgExpr> {
let content;
parenthesized!(content in input);
let cfg_expr = content.call(parse_single)?;
content.parse::<Option<Token![,]>>()?;
Ok(cfg_expr)
}
fn parse_single(input: ParseStream) -> Result<CfgExpr> {
let ident: Ident = input.parse()?;
let lookahead = input.lookahead1();
if input.peek(token::Paren) {
let content;
parenthesized!(content in input);
if ident == "all" {
let list = content.call(parse_multiple)?;
Ok(CfgExpr::All(list))
} else if ident == "any" {
let list = content.call(parse_multiple)?;
Ok(CfgExpr::Any(list))
} else if ident == "not" {
let expr = content.call(parse_single)?;
content.parse::<Option<Token![,]>>()?;
Ok(CfgExpr::Not(Box::new(expr)))
} else {
Err(Error::new(ident.span(), "unrecognized cfg expression"))
}
} else if lookahead.peek(Token![=]) {
input.parse::<Token![=]>()?;
let string: LitStr = input.parse()?;
Ok(CfgExpr::Eq(ident, Some(string)))
} else if lookahead.peek(Token![,]) || input.is_empty() {
Ok(CfgExpr::Eq(ident, None))
} else {
Err(lookahead.error())
}
}
fn parse_multiple(input: ParseStream) -> Result<Vec<CfgExpr>> {
let mut vec = Vec::new();
while !input.is_empty() {
let expr = input.call(parse_single)?;
vec.push(expr);
if input.is_empty() {
break;
}
input.parse::<Token![,]>()?;
}
Ok(vec)
}

View file

@ -0,0 +1,733 @@
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(Bool) | 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(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(),
}
}

View file

@ -0,0 +1,81 @@
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)
}

View file

@ -0,0 +1,336 @@
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,
}
}
#[cfg(feature = "experimental-enum-variants-from-header")]
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![-](Span::call_site()).to_tokens(tokens);
}
Literal::u64_unsuffixed(self.magnitude).to_tokens(tokens);
}
}
impl FromStr for Discriminant {
type Err = Error;
fn from_str(mut s: &str) -> Result<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),
},
];

View file

@ -0,0 +1,46 @@
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);
}
#[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro
pub fn is_empty(&self) -> bool {
self.fragments.is_empty()
}
#[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro
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)] });
}
}
}

View file

@ -0,0 +1,98 @@
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",
),
};

View file

@ -0,0 +1,127 @@
use crate::syntax::cfg::CfgExpr;
use crate::syntax::namespace::Namespace;
use quote::quote;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::{
braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemImpl,
ItemStruct, ItemUse, LitStr, Token, Visibility,
};
pub struct Module {
pub cfg: CfgExpr,
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 cfg = CfgExpr::Unconditional;
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 {
cfg,
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)),
}
}
}

View file

@ -0,0 +1,57 @@
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);
}
}
}
}

View file

@ -0,0 +1,450 @@
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 {
cfg: _,
path,
kind,
begin_span: _,
end_span: _,
} = self;
let Include {
cfg: _,
path: path2,
kind: kind2,
begin_span: _,
end_span: _,
} = other;
path == path2 && kind == kind2
}
}
impl Deref for ExternFn {
type Target = Signature;
fn deref(&self) -> &Self::Target {
&self.sig
}
}
impl DerefMut for ExternFn {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.sig
}
}
impl Hash for Type {
fn hash<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 {
asyncness,
unsafety,
fn_token: _,
generics: _,
receiver,
args,
ret,
throws,
paren_token: _,
throws_tokens: _,
} = self;
let Signature {
asyncness: asyncness2,
unsafety: unsafety2,
fn_token: _,
generics: _,
receiver: receiver2,
args: args2,
ret: ret2,
throws: throws2,
paren_token: _,
throws_tokens: _,
} = other;
asyncness.is_some() == asyncness2.is_some()
&& unsafety.is_some() == unsafety2.is_some()
&& receiver == receiver2
&& ret == ret2
&& throws == throws2
&& args.len() == args2.len()
&& args.iter().zip(args2).all(|(arg, arg2)| {
let Var {
cfg: _,
doc: _,
attrs: _,
visibility: _,
name: _,
colon_token: _,
ty,
} = arg;
let Var {
cfg: _,
doc: _,
attrs: _,
visibility: _,
name: _,
colon_token: _,
ty: ty2,
} = arg2;
ty == ty2
})
}
}
impl Hash for Signature {
fn hash<H: Hasher>(&self, state: &mut H) {
let Signature {
asyncness,
unsafety,
fn_token: _,
generics: _,
receiver,
args,
ret,
throws,
paren_token: _,
throws_tokens: _,
} = self;
asyncness.is_some().hash(state);
unsafety.is_some().hash(state);
receiver.hash(state);
for arg in args {
let Var {
cfg: _,
doc: _,
attrs: _,
visibility: _,
name: _,
colon_token: _,
ty,
} = arg;
ty.hash(state);
}
ret.hash(state);
throws.hash(state);
}
}
impl Eq for Receiver {}
impl PartialEq for Receiver {
fn eq(&self, other: &Self) -> bool {
let Receiver {
pinned,
ampersand: _,
lifetime,
mutable,
var: _,
colon_token: _,
ty,
shorthand: _,
pin_tokens: _,
mutability: _,
} = self;
let Receiver {
pinned: pinned2,
ampersand: _,
lifetime: lifetime2,
mutable: mutable2,
var: _,
colon_token: _,
ty: ty2,
shorthand: _,
pin_tokens: _,
mutability: _,
} = other;
pinned == pinned2 && lifetime == lifetime2 && mutable == mutable2 && ty == ty2
}
}
impl Hash for Receiver {
fn hash<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);
}
}

View file

@ -0,0 +1,39 @@
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),
}
}
}

View file

@ -0,0 +1,80 @@
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,
}
}
}

View file

@ -0,0 +1,120 @@
// 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)
}

180
vendor/cxxbridge-macro/src/syntax/map.rs vendored Normal file
View file

@ -0,0 +1,180 @@
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)
}
#[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro
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()
}
}

306
vendor/cxxbridge-macro/src/syntax/mod.rs vendored Normal file
View file

@ -0,0 +1,306 @@
// Functionality that is shared between the cxxbridge macro and the cmd.
pub mod atom;
pub mod attrs;
pub mod cfg;
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::cfg::CfgExpr;
use self::namespace::Namespace;
use self::parse::kw;
use self::symbol::Symbol;
use proc_macro2::{Ident, Span};
use syn::punctuated::Punctuated;
use syn::token::{Brace, Bracket, Paren};
use syn::{Attribute, Expr, Generics, Lifetime, LitInt, Token, Type as RustType};
pub 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 cfg: CfgExpr,
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 cfg: CfgExpr,
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 cfg: CfgExpr,
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 cfg: CfgExpr,
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,
},
#[cfg(feature = "experimental-enum-variants-from-header")]
Foreign {
rust_type: syn::Path,
},
}
pub struct ExternFn {
pub cfg: CfgExpr,
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 cfg: CfgExpr,
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 cfg: CfgExpr,
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 asyncness: Option<Token![async]>,
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 cfg: CfgExpr,
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 cfg: CfgExpr,
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,
}

View file

@ -0,0 +1,64 @@
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)
}
}
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
}
}

View file

@ -0,0 +1,85 @@
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 }
}
}

1492
vendor/cxxbridge-macro/src/syntax/parse.rs vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,36 @@
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),
}
}
}

View file

@ -0,0 +1,59 @@
use syn::ext::IdentExt;
use syn::parse::{Error, 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 allow_raw = true;
parse_unquoted(input, allow_raw)
}
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(|input: ParseStream| {
let allow_raw = false;
parse_unquoted(input, allow_raw)
})
}
} else {
Self::parse_unquoted(input)
}
}
}
fn parse_unquoted(input: ParseStream, allow_raw: bool) -> Result<QualifiedName> {
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 mut ident = Ident::parse_any(input)?;
if let Some(unraw) = ident.to_string().strip_prefix("r#") {
if !allow_raw {
let msg = format!(
"raw identifier `{}` is not allowed in a quoted namespace; use `{}`, or remove quotes",
ident, unraw,
);
return Err(Error::new(ident.span(), msg));
}
ident = Ident::new(unraw, ident.span());
}
segments.push(ident);
let colons: Option<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 })
}

View file

@ -0,0 +1,33 @@
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)
}
}

View file

@ -0,0 +1,46 @@
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
}
}

136
vendor/cxxbridge-macro/src/syntax/set.rs vendored Normal file
View file

@ -0,0 +1,136 @@
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()
}
}

View file

@ -0,0 +1,110 @@
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
}
}
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
}

View file

@ -0,0 +1,308 @@
use crate::syntax::atom::Atom::*;
use crate::syntax::{
Array, Atom, Derive, Enum, EnumRepr, ExternFn, ExternType, Impl, Lifetimes, NamedType, Ptr,
Ref, Signature, SliceRef, Struct, Ty1, Type, TypeAlias, Var,
};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote_spanned, ToTokens};
use syn::{token, Token};
impl ToTokens for Type {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Type::Ident(ident) => {
if ident.rust == Char {
let span = ident.rust.span();
tokens.extend(quote_spanned!(span=> ::cxx::private::));
} else if ident.rust == CxxString {
let span = ident.rust.span();
tokens.extend(quote_spanned!(span=> ::cxx::));
} else if ident.rust == RustString {
let span = ident.rust.span();
tokens.extend(quote_spanned!(span=> ::cxx::alloc::string::));
}
ident.to_tokens(tokens);
}
Type::RustBox(ty)
| Type::UniquePtr(ty)
| Type::SharedPtr(ty)
| Type::WeakPtr(ty)
| Type::CxxVector(ty)
| Type::RustVec(ty) => ty.to_tokens(tokens),
Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens),
Type::Ptr(p) => p.to_tokens(tokens),
Type::Array(a) => a.to_tokens(tokens),
Type::Fn(f) => f.to_tokens(tokens),
Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())),
Type::SliceRef(r) => r.to_tokens(tokens),
}
}
}
impl ToTokens for Var {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Var {
cfg: _,
doc: _,
attrs: _,
visibility: _,
name,
colon_token: _,
ty,
} = self;
name.rust.to_tokens(tokens);
Token![:](name.rust.span()).to_tokens(tokens);
ty.to_tokens(tokens);
}
}
impl ToTokens for Ty1 {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Ty1 {
name,
langle,
inner,
rangle,
} = self;
let span = name.span();
match name.to_string().as_str() {
"UniquePtr" | "SharedPtr" | "WeakPtr" | "CxxVector" => {
tokens.extend(quote_spanned!(span=> ::cxx::));
}
"Box" => {
tokens.extend(quote_spanned!(span=> ::cxx::alloc::boxed::));
}
"Vec" => {
tokens.extend(quote_spanned!(span=> ::cxx::alloc::vec::));
}
_ => {}
}
name.to_tokens(tokens);
langle.to_tokens(tokens);
inner.to_tokens(tokens);
rangle.to_tokens(tokens);
}
}
impl ToTokens for Ref {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Ref {
pinned: _,
ampersand,
lifetime,
mutable: _,
inner,
pin_tokens,
mutability,
} = self;
if let Some((pin, langle, _rangle)) = pin_tokens {
tokens.extend(quote_spanned!(pin.span=> ::cxx::core::pin::Pin));
langle.to_tokens(tokens);
}
ampersand.to_tokens(tokens);
lifetime.to_tokens(tokens);
mutability.to_tokens(tokens);
inner.to_tokens(tokens);
if let Some((_pin, _langle, rangle)) = pin_tokens {
rangle.to_tokens(tokens);
}
}
}
impl ToTokens for Ptr {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Ptr {
star,
mutable: _,
inner,
mutability,
constness,
} = self;
star.to_tokens(tokens);
mutability.to_tokens(tokens);
constness.to_tokens(tokens);
inner.to_tokens(tokens);
}
}
impl ToTokens for SliceRef {
fn to_tokens(&self, tokens: &mut TokenStream) {
let SliceRef {
ampersand,
lifetime,
mutable: _,
bracket,
inner,
mutability,
} = self;
ampersand.to_tokens(tokens);
lifetime.to_tokens(tokens);
mutability.to_tokens(tokens);
bracket.surround(tokens, |tokens| {
inner.to_tokens(tokens);
});
}
}
impl ToTokens for Array {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Array {
bracket,
inner,
semi_token,
len: _,
len_token,
} = self;
bracket.surround(tokens, |tokens| {
inner.to_tokens(tokens);
semi_token.to_tokens(tokens);
len_token.to_tokens(tokens);
});
}
}
impl ToTokens for Atom {
fn to_tokens(&self, tokens: &mut TokenStream) {
Ident::new(self.as_ref(), Span::call_site()).to_tokens(tokens);
}
}
impl ToTokens for Derive {
fn to_tokens(&self, tokens: &mut TokenStream) {
Ident::new(self.what.as_ref(), self.span).to_tokens(tokens);
}
}
impl ToTokens for ExternType {
fn to_tokens(&self, tokens: &mut TokenStream) {
// Notional token range for error reporting purposes.
self.type_token.to_tokens(tokens);
self.name.rust.to_tokens(tokens);
self.generics.to_tokens(tokens);
}
}
impl ToTokens for TypeAlias {
fn to_tokens(&self, tokens: &mut TokenStream) {
// Notional token range for error reporting purposes.
self.type_token.to_tokens(tokens);
self.name.rust.to_tokens(tokens);
self.generics.to_tokens(tokens);
}
}
impl ToTokens for Struct {
fn to_tokens(&self, tokens: &mut TokenStream) {
// Notional token range for error reporting purposes.
self.struct_token.to_tokens(tokens);
self.name.rust.to_tokens(tokens);
self.generics.to_tokens(tokens);
}
}
impl ToTokens for Enum {
fn to_tokens(&self, tokens: &mut TokenStream) {
// Notional token range for error reporting purposes.
self.enum_token.to_tokens(tokens);
self.name.rust.to_tokens(tokens);
self.generics.to_tokens(tokens);
}
}
impl ToTokens for ExternFn {
fn to_tokens(&self, tokens: &mut TokenStream) {
// Notional token range for error reporting purposes.
self.unsafety.to_tokens(tokens);
self.sig.fn_token.to_tokens(tokens);
self.semi_token.to_tokens(tokens);
}
}
impl ToTokens for Impl {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Impl {
cfg: _,
impl_token,
impl_generics,
negative: _,
ty,
ty_generics: _,
brace_token,
negative_token,
} = self;
impl_token.to_tokens(tokens);
impl_generics.to_tokens(tokens);
negative_token.to_tokens(tokens);
ty.to_tokens(tokens);
brace_token.surround(tokens, |_tokens| {});
}
}
impl ToTokens for Lifetimes {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Lifetimes {
lt_token,
lifetimes,
gt_token,
} = self;
lt_token.to_tokens(tokens);
lifetimes.to_tokens(tokens);
gt_token.to_tokens(tokens);
}
}
impl ToTokens for Signature {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Signature {
asyncness: _,
unsafety: _,
fn_token,
generics: _,
receiver: _,
args,
ret,
throws: _,
paren_token,
throws_tokens,
} = self;
fn_token.to_tokens(tokens);
paren_token.surround(tokens, |tokens| {
args.to_tokens(tokens);
});
if let Some(ret) = ret {
Token![->](paren_token.span).to_tokens(tokens);
if let Some((result, langle, rangle)) = throws_tokens {
result.to_tokens(tokens);
langle.to_tokens(tokens);
ret.to_tokens(tokens);
rangle.to_tokens(tokens);
} else {
ret.to_tokens(tokens);
}
} else if let Some((result, langle, rangle)) = throws_tokens {
Token![->](paren_token.span).to_tokens(tokens);
result.to_tokens(tokens);
langle.to_tokens(tokens);
token::Paren(langle.span).surround(tokens, |_| ());
rangle.to_tokens(tokens);
}
}
}
impl ToTokens for EnumRepr {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
EnumRepr::Native { atom, repr_type: _ } => atom.to_tokens(tokens),
#[cfg(feature = "experimental-enum-variants-from-header")]
EnumRepr::Foreign { rust_type } => rust_type.to_tokens(tokens),
}
}
}
impl ToTokens for NamedType {
fn to_tokens(&self, tokens: &mut TokenStream) {
let NamedType { rust, generics } = self;
rust.to_tokens(tokens);
generics.to_tokens(tokens);
}
}

View file

@ -0,0 +1,51 @@
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
}

View file

@ -0,0 +1,312 @@
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 }
}

View file

@ -0,0 +1,285 @@
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);
}
#[cfg(feature = "experimental-enum-variants-from-header")]
EnumRepr::Foreign { rust_type: _ } => {}
}
let ident = &enm.name.rust;
if !type_names.insert(ident)
&& (!cxx.contains(ident)
|| structs.contains_key(ident)
|| enums.contains_key(ident))
{
// If already declared as a struct or enum, or if
// colliding with something other than an extern C++
// type, then error.
duplicate_name(cx, enm, ident);
}
enums.insert(ident, enm);
if enm.variants_from_header {
// #![variants_from_header] enums are implicitly extern
// C++ type.
cxx.insert(&enm.name.rust);
}
add_resolution(&enm.name, &enm.generics);
}
Api::CxxType(ety) => {
let ident = &ety.name.rust;
if !type_names.insert(ident)
&& (cxx.contains(ident)
|| !structs.contains_key(ident) && !enums.contains_key(ident))
{
// If already declared as an extern C++ type, or if
// colliding with something which is neither struct nor
// enum, then error.
duplicate_name(cx, ety, ident);
}
cxx.insert(ident);
if !ety.trusted {
untrusted.insert(ident, ety);
}
add_resolution(&ety.name, &ety.generics);
}
Api::RustType(ety) => {
let ident = &ety.name.rust;
if !type_names.insert(ident) {
duplicate_name(cx, ety, ident);
}
rust.insert(ident);
add_resolution(&ety.name, &ety.generics);
}
Api::CxxFunction(efn) | Api::RustFunction(efn) => {
// Note: duplication of the C++ name is fine because C++ has
// function overloading.
if !function_names.insert((&efn.receiver, &efn.name.rust)) {
duplicate_name(cx, efn, &efn.name.rust);
}
for arg in &efn.args {
visit(&mut all, &arg.ty);
}
if let Some(ret) = &efn.ret {
visit(&mut all, ret);
}
}
Api::TypeAlias(alias) => {
let ident = &alias.name.rust;
if !type_names.insert(ident) {
duplicate_name(cx, alias, ident);
}
cxx.insert(ident);
aliases.insert(ident, alias);
add_resolution(&alias.name, &alias.generics);
}
Api::Impl(imp) => {
visit(&mut all, &imp.ty);
if let Some(key) = imp.ty.impl_key() {
impls.insert(key, Some(imp));
}
}
}
}
for ty in &all {
let 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);
}

View file

@ -0,0 +1,34 @@
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);
}
}
}
}

75
vendor/cxxbridge-macro/src/tokens.rs vendored Normal file
View file

@ -0,0 +1,75 @@
use crate::syntax::Receiver;
use proc_macro2::TokenStream;
use quote::{quote_spanned, ToTokens};
use syn::Token;
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=> ::cxx::core::pin::Pin));
langle.to_tokens(tokens);
}
ampersand.to_tokens(tokens);
lifetime.to_tokens(tokens);
mutability.to_tokens(tokens);
ty.to_tokens(tokens);
if let Some((_pin, _langle, rangle)) = pin_tokens {
rangle.to_tokens(tokens);
}
}
}
impl ToTokens for ReceiverTypeSelf<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Receiver {
pinned: _,
ampersand,
lifetime,
mutable: _,
var: _,
colon_token: _,
ty,
shorthand: _,
pin_tokens,
mutability,
} = &self.0;
if let Some((pin, langle, _rangle)) = pin_tokens {
tokens.extend(quote_spanned!(pin.span=> ::cxx::core::pin::Pin));
langle.to_tokens(tokens);
}
ampersand.to_tokens(tokens);
lifetime.to_tokens(tokens);
mutability.to_tokens(tokens);
Token![Self](ty.rust.span()).to_tokens(tokens);
if let Some((_pin, _langle, rangle)) = pin_tokens {
rangle.to_tokens(tokens);
}
}
}

44
vendor/cxxbridge-macro/src/type_id.rs vendored Normal file
View file

@ -0,0 +1,44 @@
use crate::syntax::qualified::QualifiedName;
use proc_macro2::{TokenStream, TokenTree};
use quote::{format_ident, quote, ToTokens};
use syn::ext::IdentExt;
pub enum Crate {
Cxx,
DollarCrate(TokenTree),
}
impl ToTokens for Crate {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Crate::Cxx => tokens.extend(quote!(::cxx)),
Crate::DollarCrate(krate) => krate.to_tokens(tokens),
}
}
}
// "folly::File" => `(f, o, l, l, y, (), F, i, l, e)`
pub fn expand(krate: Crate, arg: QualifiedName) -> TokenStream {
let mut ids = Vec::new();
for word in arg.segments {
if !ids.is_empty() {
ids.push(quote!(()));
}
for ch in word.unraw().to_string().chars() {
ids.push(match ch {
'A'..='Z' | 'a'..='z' => {
let t = format_ident!("{}", ch);
quote!(#krate::#t)
}
'0'..='9' | '_' => {
let t = format_ident!("_{}", ch);
quote!(#krate::#t)
}
_ => quote!([(); #ch as _]),
});
}
}
quote! { (#(#ids,)*) }
}