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

1
vendor/cxx-build/.cargo-checksum.json vendored Normal file

File diff suppressed because one or more lines are too long

80
vendor/cxx-build/Cargo.toml vendored Normal file
View file

@ -0,0 +1,80 @@
# 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 = "cxx-build"
version = "1.0.85"
authors = ["David Tolnay <dtolnay@gmail.com>"]
exclude = ["build.rs"]
description = "C++ code generator for integrating `cxx` crate into a Cargo build."
homepage = "https://cxx.rs"
keywords = [
"ffi",
"build-dependencies",
]
categories = [
"development-tools::build-utils",
"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]
doc-scrape-examples = false
[dependencies.cc]
version = "1.0.49"
[dependencies.codespan-reporting]
version = "0.11.1"
[dependencies.once_cell]
version = "1.9"
[dependencies.proc-macro2]
version = "1.0.39"
features = ["span-locations"]
default-features = false
[dependencies.quote]
version = "1.0"
default-features = false
[dependencies.scratch]
version = "1.0"
[dependencies.syn]
version = "1.0.95"
features = [
"parsing",
"printing",
"clone-impls",
"full",
]
default-features = false
[dev-dependencies.cxx]
version = "1.0"
[dev-dependencies.cxx-gen]
version = "0.7"
[dev-dependencies.pkg-config]
version = "0.3"
[features]
experimental-async-fn = []
parallel = ["cc/parallel"]

201
vendor/cxx-build/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/cxx-build/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.

157
vendor/cxx-build/src/cargo.rs vendored Normal file
View file

@ -0,0 +1,157 @@
use crate::gen::{CfgEvaluator, CfgResult};
use once_cell::sync::OnceCell;
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::collections::{BTreeMap as Map, BTreeSet as Set};
use std::env;
static ENV: OnceCell<CargoEnv> = OnceCell::new();
struct CargoEnv {
features: Set<Name>,
cfgs: Map<Name, String>,
}
pub(super) struct CargoEnvCfgEvaluator;
impl CfgEvaluator for CargoEnvCfgEvaluator {
fn eval(&self, name: &str, query_value: Option<&str>) -> CfgResult {
let env = ENV.get_or_init(CargoEnv::load);
if name == "feature" {
return if let Some(query_value) = query_value {
CfgResult::from(env.features.contains(Lookup::new(query_value)))
} else {
let msg = "expected `feature = \"...\"`".to_owned();
CfgResult::Undetermined { msg }
};
}
if name == "test" && query_value.is_none() {
let msg = "cfg(test) is not supported because Cargo runs your build script only once across the lib and test build of the same crate".to_owned();
return CfgResult::Undetermined { msg };
}
if let Some(cargo_value) = env.cfgs.get(Lookup::new(name)) {
return if let Some(query_value) = query_value {
CfgResult::from(cargo_value.split(',').any(|value| value == query_value))
} else {
CfgResult::True
};
}
if name == "debug_assertions" && query_value.is_none() {
return CfgResult::from(cfg!(debug_assertions));
}
CfgResult::False
}
}
impl CargoEnv {
fn load() -> Self {
const CARGO_FEATURE_PREFIX: &str = "CARGO_FEATURE_";
const CARGO_CFG_PREFIX: &str = "CARGO_CFG_";
let mut features = Set::new();
let mut cfgs = Map::new();
for (k, v) in env::vars_os() {
let k = match k.to_str() {
Some(k) => k,
None => continue,
};
let v = match v.into_string() {
Ok(v) => v,
Err(_) => continue,
};
if let Some(feature_name) = k.strip_prefix(CARGO_FEATURE_PREFIX) {
let feature_name = Name(feature_name.to_owned());
features.insert(feature_name);
} else if let Some(cfg_name) = k.strip_prefix(CARGO_CFG_PREFIX) {
let cfg_name = Name(cfg_name.to_owned());
cfgs.insert(cfg_name, v);
}
}
CargoEnv { features, cfgs }
}
}
struct Name(String);
impl Ord for Name {
fn cmp(&self, rhs: &Self) -> Ordering {
Lookup::new(&self.0).cmp(Lookup::new(&rhs.0))
}
}
impl PartialOrd for Name {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
Some(self.cmp(rhs))
}
}
impl Eq for Name {}
impl PartialEq for Name {
fn eq(&self, rhs: &Self) -> bool {
Lookup::new(&self.0).eq(Lookup::new(&rhs.0))
}
}
#[repr(transparent)]
struct Lookup(str);
impl Lookup {
fn new(name: &str) -> &Self {
unsafe { &*(name as *const str as *const Self) }
}
}
impl Borrow<Lookup> for Name {
fn borrow(&self) -> &Lookup {
Lookup::new(&self.0)
}
}
impl Ord for Lookup {
fn cmp(&self, rhs: &Self) -> Ordering {
self.0
.bytes()
.map(CaseAgnosticByte)
.cmp(rhs.0.bytes().map(CaseAgnosticByte))
}
}
impl PartialOrd for Lookup {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
Some(self.cmp(rhs))
}
}
impl Eq for Lookup {}
impl PartialEq for Lookup {
fn eq(&self, rhs: &Self) -> bool {
self.0
.bytes()
.map(CaseAgnosticByte)
.eq(rhs.0.bytes().map(CaseAgnosticByte))
}
}
struct CaseAgnosticByte(u8);
impl Ord for CaseAgnosticByte {
fn cmp(&self, rhs: &Self) -> Ordering {
self.0.to_ascii_lowercase().cmp(&rhs.0.to_ascii_lowercase())
}
}
impl PartialOrd for CaseAgnosticByte {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
Some(self.cmp(rhs))
}
}
impl Eq for CaseAgnosticByte {}
impl PartialEq for CaseAgnosticByte {
fn eq(&self, rhs: &Self) -> bool {
self.cmp(rhs) == Ordering::Equal
}
}

495
vendor/cxx-build/src/cfg.rs vendored Normal file
View file

@ -0,0 +1,495 @@
use std::fmt::{self, Debug};
use std::marker::PhantomData;
use std::path::Path;
/// Build configuration. See [CFG].
pub struct Cfg<'a> {
/// See [`CFG.include_prefix`][CFG#cfginclude_prefix].
pub include_prefix: &'a str,
/// See [`CFG.exported_header_dirs`][CFG#cfgexported_header_dirs].
pub exported_header_dirs: Vec<&'a Path>,
/// See [`CFG.exported_header_prefixes`][CFG#cfgexported_header_prefixes].
pub exported_header_prefixes: Vec<&'a str>,
/// See [`CFG.exported_header_links`][CFG#cfgexported_header_links].
pub exported_header_links: Vec<&'a str>,
/// See [`CFG.doxygen`][CFG#cfgdoxygen].
pub doxygen: bool,
marker: PhantomData<*const ()>, // !Send + !Sync
}
/// Global configuration of the current build.
///
/// <br>
///
/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;opacity:.73"><strong>&amp;str</strong></div>
///
/// ## **`CFG.include_prefix`**
///
/// The prefix at which C++ code from your crate as well as directly dependent
/// crates can access the code generated during this build.
///
/// By default, the `include_prefix` is equal to the name of the current crate.
/// That means if your crate is called `demo` and has Rust source files in a
/// *src/* directory and maybe some handwritten C++ header files in an
/// *include/* directory, then the current crate as well as downstream crates
/// might include them as follows:
///
/// ```
/// # const _: &str = stringify! {
/// // include one of the handwritten headers:
/// #include "demo/include/wow.h"
///
/// // include a header generated from Rust cxx::bridge:
/// #include "demo/src/lib.rs.h"
/// # };
/// ```
///
/// By modifying `CFG.include_prefix` we can substitute a prefix that is
/// different from the crate name if desired. Here we'll change it to
/// `"path/to"` which will make import paths take the form
/// `"path/to/include/wow.h"` and `"path/to/src/lib.rs.h"`.
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
///
/// fn main() {
/// CFG.include_prefix = "path/to";
///
/// cxx_build::bridge("src/lib.rs")
/// .file("src/demo.cc") // probably contains `#include "path/to/src/lib.rs.h"`
/// /* ... */
/// .compile("demo");
/// }
/// ```
///
/// Note that cross-crate imports are only made available between **direct
/// dependencies**. Another crate must directly depend on your crate in order to
/// #include its headers; a transitive dependency is not sufficient.
/// Additionally, headers from a direct dependency are only importable if the
/// dependency's Cargo.toml manifest contains a `links` key. If not, its headers
/// will not be importable from outside of the same crate.
///
/// <br>
///
/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;opacity:.73"><strong>Vec&lt;&amp;Path&gt;</strong></div>
///
/// ## **`CFG.exported_header_dirs`**
///
/// A vector of absolute paths. The current crate, directly dependent crates,
/// and further crates to which this crate's headers are exported (see below)
/// will be able to `#include` headers from these directories.
///
/// Adding a directory to `exported_header_dirs` is similar to adding it to the
/// current build via the `cc` crate's [`Build::include`][cc::Build::include],
/// but *also* makes the directory available to downstream crates that want to
/// `#include` one of the headers from your crate. If the dir were added only
/// using `Build::include`, the downstream crate including your header would
/// need to manually add the same directory to their own build as well.
///
/// When using `exported_header_dirs`, your crate must also set a `links` key
/// for itself in Cargo.toml. See [*the `links` manifest key*][links]. The
/// reason is that Cargo imposes no ordering on the execution of build scripts
/// without a `links` key, which means the downstream crate's build script might
/// execute before yours decides what to put into `exported_header_dirs`.
///
/// [links]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key
///
/// ### Example
///
/// One of your crate's headers wants to include a system library, such as
/// `#include "Python.h"`.
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
/// use std::path::PathBuf;
///
/// fn main() {
/// let python3 = pkg_config::probe_library("python3").unwrap();
/// let python_include_paths = python3.include_paths.iter().map(PathBuf::as_path);
/// CFG.exported_header_dirs.extend(python_include_paths);
///
/// cxx_build::bridge("src/bridge.rs").compile("demo");
/// }
/// ```
///
/// ### Example
///
/// Your crate wants to rearrange the headers that it exports vs how they're
/// laid out locally inside the crate's source directory.
///
/// Suppose the crate as published contains a file at `./include/myheader.h` but
/// wants it available to downstream crates as `#include "foo/v1/public.h"`.
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
/// use std::path::Path;
/// use std::{env, fs};
///
/// fn main() {
/// let out_dir = env::var_os("OUT_DIR").unwrap();
/// let headers = Path::new(&out_dir).join("headers");
/// CFG.exported_header_dirs.push(&headers);
///
/// // We contain `include/myheader.h` locally, but
/// // downstream will use `#include "foo/v1/public.h"`
/// let foo = headers.join("foo").join("v1");
/// fs::create_dir_all(&foo).unwrap();
/// fs::copy("include/myheader.h", foo.join("public.h")).unwrap();
///
/// cxx_build::bridge("src/bridge.rs").compile("demo");
/// }
/// ```
///
/// <p style="margin:0"><br><br></p>
///
/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;opacity:.73"><strong>Vec&lt;&amp;str&gt;</strong></div>
///
/// ## **`CFG.exported_header_prefixes`**
///
/// Vector of strings. These each refer to the `include_prefix` of one of your
/// direct dependencies, or a prefix thereof. They describe which of your
/// dependencies participate in your crate's C++ public API, as opposed to
/// private use by your crate's implementation.
///
/// As a general rule, if one of your headers `#include`s something from one of
/// your dependencies, you need to put that dependency's `include_prefix` into
/// `CFG.exported_header_prefixes` (*or* their `links` key into
/// `CFG.exported_header_links`; see below). On the other hand if only your C++
/// implementation files and *not* your headers are importing from the
/// dependency, you do not export that dependency.
///
/// The significance of exported headers is that if downstream code (crate 𝒜)
/// contains an `#include` of a header from your crate () and your header
/// contains an `#include` of something from your dependency (𝒞), the exported
/// dependency 𝒞 becomes available during the downstream crate 𝒜's build.
/// Otherwise the downstream crate 𝒜 doesn't know about 𝒞 and wouldn't be able
/// to find what header your header is referring to, and would fail to build.
///
/// When using `exported_header_prefixes`, your crate must also set a `links`
/// key for itself in Cargo.toml.
///
/// ### Example
///
/// Suppose you have a crate with 5 direct dependencies and the `include_prefix`
/// for each one are:
///
/// - "crate0"
/// - "group/api/crate1"
/// - "group/api/crate2"
/// - "group/api/contrib/crate3"
/// - "detail/crate4"
///
/// Your header involves types from the first four so we re-export those as part
/// of your public API, while crate4 is only used internally by your cc file not
/// your header, so we do not export:
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
///
/// fn main() {
/// CFG.exported_header_prefixes = vec!["crate0", "group/api"];
///
/// cxx_build::bridge("src/bridge.rs")
/// .file("src/impl.cc")
/// .compile("demo");
/// }
/// ```
///
/// <p style="margin:0"><br><br></p>
///
/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;opacity:.73"><strong>Vec&lt;&amp;str&gt;</strong></div>
///
/// ## **`CFG.exported_header_links`**
///
/// Vector of strings. These each refer to the `links` attribute ([*the `links`
/// manifest key*][links]) of one of your crate's direct dependencies.
///
/// This achieves an equivalent result to `CFG.exported_header_prefixes` by
/// re-exporting a dependency as part of your crate's public API, except with
/// finer grained control for cases when multiple crates might be sharing the
/// same `include_prefix` and you'd like to export some but not others. Links
/// attributes are guaranteed to be unique identifiers by Cargo.
///
/// When using `exported_header_links`, your crate must also set a `links` key
/// for itself in Cargo.toml.
///
/// ### Example
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
///
/// fn main() {
/// CFG.exported_header_links.push("git2");
///
/// cxx_build::bridge("src/bridge.rs").compile("demo");
/// }
/// ```
///
/// <p style="margin:0"><br><br></p>
///
/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;opacity:.73"><strong>bool</strong></div>
///
/// ## **`CFG.doxygen`**
///
/// Boolean. Whether to propagate Rust documentation from inside the cxx::bridge
/// module as Doxygen-style comments in the generated C++ header.
///
/// Documentation on the following are supported:
///
/// - shared structs, and fields of shared structs
/// - shared enums, and their variants
/// - extern "Rust" opaque types
/// - extern "Rust" functions, including methods/member functions
///
/// ### Example
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
///
/// fn main() {
/// CFG.doxygen = true;
///
/// cxx_build::bridge("src/bridge.rs").compile("demo");
/// }
/// ```
///
/// ```rust
/// // src/bridge.rs
///
/// #[cxx::bridge]
/// mod ffi {
/// /// documentation of MyStruct
/// pub struct MyStruct {
/// /// documentation of the struct field
/// lol: String,
/// }
///
/// extern "Rust" {
/// /// documentation of MyType
/// type MyType;
///
/// /// function documentation
/// fn asdf() -> bool;
/// }
/// }
/// #
/// # pub struct MyType;
/// # fn asdf() -> bool { true }
/// # fn main() {}
/// ```
///
/// With `CFG.doxygen` enabled, the generated C++ header through which
/// downstream C++ code will be able to access these shared structs and extern
/// "Rust" signatures will have the Rust documentation comments propagated as
/// Doxygen-style comments:
///
/// ```cpp
/// /// documentation of MyStruct
/// struct MyStruct final {
/// /// documentation of the struct field
/// ::rust::String lol;
/// …
/// };
/// ```
///
/// Otherwise by default (without `CFG.doxygen`) they'll just be `//` comments.
#[cfg(doc)]
pub static mut CFG: Cfg = Cfg {
include_prefix: "",
exported_header_dirs: Vec::new(),
exported_header_prefixes: Vec::new(),
exported_header_links: Vec::new(),
doxygen: false,
marker: PhantomData,
};
impl<'a> Debug for Cfg<'a> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let Self {
include_prefix,
exported_header_dirs,
exported_header_prefixes,
exported_header_links,
doxygen,
marker: _,
} = self;
formatter
.debug_struct("Cfg")
.field("include_prefix", include_prefix)
.field("exported_header_dirs", exported_header_dirs)
.field("exported_header_prefixes", exported_header_prefixes)
.field("exported_header_links", exported_header_links)
.field("doxygen", doxygen)
.finish()
}
}
#[cfg(not(doc))]
pub use self::r#impl::Cfg::CFG;
#[cfg(not(doc))]
mod r#impl {
use crate::intern::{intern, InternedString};
use crate::syntax::map::UnorderedMap as Map;
use crate::vec::{self, InternedVec as _};
use once_cell::sync::Lazy;
use std::cell::RefCell;
use std::fmt::{self, Debug};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::sync::{PoisonError, RwLock};
struct CurrentCfg {
include_prefix: InternedString,
exported_header_dirs: Vec<InternedString>,
exported_header_prefixes: Vec<InternedString>,
exported_header_links: Vec<InternedString>,
doxygen: bool,
}
impl CurrentCfg {
fn default() -> Self {
let include_prefix = crate::env_os("CARGO_PKG_NAME")
.map(|pkg| intern(&pkg.to_string_lossy()))
.unwrap_or_default();
let exported_header_dirs = Vec::new();
let exported_header_prefixes = Vec::new();
let exported_header_links = Vec::new();
let doxygen = false;
CurrentCfg {
include_prefix,
exported_header_dirs,
exported_header_prefixes,
exported_header_links,
doxygen,
}
}
}
static CURRENT: Lazy<RwLock<CurrentCfg>> = Lazy::new(|| RwLock::new(CurrentCfg::default()));
thread_local! {
// FIXME: If https://github.com/rust-lang/rust/issues/77425 is resolved,
// we can delete this thread local side table and instead make each CFG
// instance directly own the associated super::Cfg.
//
// #[allow(const_item_mutation)]
// pub const CFG: Cfg = Cfg {
// cfg: AtomicPtr::new(ptr::null_mut()),
// };
// pub struct Cfg {
// cfg: AtomicPtr<super::Cfg>,
// }
//
static CONST_DEREFS: RefCell<Map<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
}
#[derive(Eq, PartialEq, Hash)]
struct Handle(*const Cfg<'static>);
impl<'a> Cfg<'a> {
fn current() -> super::Cfg<'a> {
let current = CURRENT.read().unwrap_or_else(PoisonError::into_inner);
let include_prefix = current.include_prefix.str();
let exported_header_dirs = current.exported_header_dirs.vec();
let exported_header_prefixes = current.exported_header_prefixes.vec();
let exported_header_links = current.exported_header_links.vec();
let doxygen = current.doxygen;
super::Cfg {
include_prefix,
exported_header_dirs,
exported_header_prefixes,
exported_header_links,
doxygen,
marker: PhantomData,
}
}
const fn handle(self: &Cfg<'a>) -> Handle {
Handle(<*const Cfg>::cast(self))
}
}
// Since super::Cfg is !Send and !Sync, all Cfg are thread local and will
// drop on the same thread where they were created.
pub enum Cfg<'a> {
Mut(super::Cfg<'a>),
CFG,
}
impl<'a> Debug for Cfg<'a> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
if let Cfg::Mut(cfg) = self {
Debug::fmt(cfg, formatter)
} else {
Debug::fmt(&Cfg::current(), formatter)
}
}
}
impl<'a> Deref for Cfg<'a> {
type Target = super::Cfg<'a>;
fn deref(&self) -> &Self::Target {
if let Cfg::Mut(cfg) = self {
cfg
} else {
let cfg = CONST_DEREFS.with(|derefs| -> *mut super::Cfg {
&mut **derefs
.borrow_mut()
.entry(self.handle())
.or_insert_with(|| Box::new(Cfg::current()))
});
unsafe { &mut *cfg }
}
}
}
impl<'a> DerefMut for Cfg<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
if let Cfg::CFG = self {
CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
*self = Cfg::Mut(Cfg::current());
}
match self {
Cfg::Mut(cfg) => cfg,
Cfg::CFG => unreachable!(),
}
}
}
impl<'a> Drop for Cfg<'a> {
fn drop(&mut self) {
if let Cfg::Mut(cfg) = self {
let super::Cfg {
include_prefix,
exported_header_dirs,
exported_header_prefixes,
exported_header_links,
doxygen,
marker: _,
} = cfg;
let mut current = CURRENT.write().unwrap_or_else(PoisonError::into_inner);
current.include_prefix = intern(include_prefix);
current.exported_header_dirs = vec::intern(exported_header_dirs);
current.exported_header_prefixes = vec::intern(exported_header_prefixes);
current.exported_header_links = vec::intern(exported_header_links);
current.doxygen = *doxygen;
} else {
CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
}
}
}
}

107
vendor/cxx-build/src/deps.rs vendored Normal file
View file

@ -0,0 +1,107 @@
use std::collections::BTreeMap;
use std::env;
use std::ffi::OsString;
use std::path::PathBuf;
#[derive(Default)]
pub struct Crate {
pub include_prefix: Option<PathBuf>,
pub links: Option<OsString>,
pub header_dirs: Vec<HeaderDir>,
}
pub struct HeaderDir {
pub exported: bool,
pub path: PathBuf,
}
impl Crate {
pub fn print_to_cargo(&self) {
if let Some(include_prefix) = &self.include_prefix {
println!(
"cargo:CXXBRIDGE_PREFIX={}",
include_prefix.to_string_lossy(),
);
}
if let Some(links) = &self.links {
println!("cargo:CXXBRIDGE_LINKS={}", links.to_string_lossy());
}
for (i, header_dir) in self.header_dirs.iter().enumerate() {
if header_dir.exported {
println!(
"cargo:CXXBRIDGE_DIR{}={}",
i,
header_dir.path.to_string_lossy(),
);
}
}
}
}
pub fn direct_dependencies() -> Vec<Crate> {
let mut crates: BTreeMap<String, Crate> = BTreeMap::new();
let mut exported_header_dirs: BTreeMap<String, Vec<(usize, PathBuf)>> = BTreeMap::new();
// Only variables set from a build script of direct dependencies are
// observable. That's exactly what we want! Your crate needs to declare a
// direct dependency on the other crate in order to be able to #include its
// headers.
//
// Also, they're only observable if the dependency's manifest contains a
// `links` key. This is important because Cargo imposes no ordering on the
// execution of build scripts without a `links` key. When exposing a
// generated header for the current crate to #include, we need to be sure
// the dependency's build script has already executed and emitted that
// generated header.
//
// References:
// - https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key
// - https://doc.rust-lang.org/cargo/reference/build-script-examples.html#using-another-sys-crate
for (k, v) in env::vars_os() {
let mut k = k.to_string_lossy().into_owned();
if !k.starts_with("DEP_") {
continue;
}
if k.ends_with("_CXXBRIDGE_PREFIX") {
k.truncate(k.len() - "_CXXBRIDGE_PREFIX".len());
crates.entry(k).or_default().include_prefix = Some(PathBuf::from(v));
continue;
}
if k.ends_with("_CXXBRIDGE_LINKS") {
k.truncate(k.len() - "_CXXBRIDGE_LINKS".len());
crates.entry(k).or_default().links = Some(v);
continue;
}
let without_counter = k.trim_end_matches(|ch: char| ch.is_ascii_digit());
let counter_len = k.len() - without_counter.len();
if counter_len == 0 || !without_counter.ends_with("_CXXBRIDGE_DIR") {
continue;
}
let sort_key = k[k.len() - counter_len..]
.parse::<usize>()
.unwrap_or(usize::MAX);
k.truncate(k.len() - counter_len - "_CXXBRIDGE_DIR".len());
exported_header_dirs
.entry(k)
.or_default()
.push((sort_key, PathBuf::from(v)));
}
for (k, mut dirs) in exported_header_dirs {
dirs.sort_by_key(|(sort_key, _dir)| *sort_key);
crates
.entry(k)
.or_default()
.header_dirs
.extend(dirs.into_iter().map(|(_sort_key, dir)| HeaderDir {
exported: true,
path: dir,
}));
}
crates.into_iter().map(|entry| entry.1).collect()
}

98
vendor/cxx-build/src/error.rs vendored Normal file
View file

@ -0,0 +1,98 @@
use crate::cfg::CFG;
use crate::gen::fs;
use std::error::Error as StdError;
use std::ffi::OsString;
use std::fmt::{self, Display};
use std::path::Path;
pub(super) type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug)]
pub(super) enum Error {
NoEnv(OsString),
Fs(fs::Error),
ExportedDirNotAbsolute(&'static Path),
ExportedEmptyPrefix,
ExportedDirsWithoutLinks,
ExportedPrefixesWithoutLinks,
ExportedLinksWithoutLinks,
UnusedExportedPrefix(&'static str),
UnusedExportedLinks(&'static str),
}
macro_rules! expr {
($expr:expr) => {{
let _ = $expr; // ensure it doesn't fall out of sync with CFG definition
stringify!($expr)
}};
}
const LINKS_DOCUMENTATION: &str =
"https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key";
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::NoEnv(var) => {
write!(f, "missing {} environment variable", var.to_string_lossy())
}
Error::Fs(err) => err.fmt(f),
Error::ExportedDirNotAbsolute(path) => write!(
f,
"element of {} must be absolute path, but was: {:?}",
expr!(CFG.exported_header_dirs),
path,
),
Error::ExportedEmptyPrefix => write!(
f,
"element of {} must not be empty string",
expr!(CFG.exported_header_prefixes),
),
Error::ExportedDirsWithoutLinks => write!(
f,
"if {} is nonempty then `links` needs to be set in Cargo.toml; see {}",
expr!(CFG.exported_header_dirs),
LINKS_DOCUMENTATION,
),
Error::ExportedPrefixesWithoutLinks => write!(
f,
"if {} is nonempty then `links` needs to be set in Cargo.toml; see {}",
expr!(CFG.exported_header_prefixes),
LINKS_DOCUMENTATION,
),
Error::ExportedLinksWithoutLinks => write!(
f,
"if {} is nonempty then `links` needs to be set in Cargo.toml; see {}",
expr!(CFG.exported_header_links),
LINKS_DOCUMENTATION,
),
Error::UnusedExportedPrefix(unused) => write!(
f,
"unused element in {}: {:?} does not match the include prefix of any direct dependency",
expr!(CFG.exported_header_prefixes),
unused,
),
Error::UnusedExportedLinks(unused) => write!(
f,
"unused element in {}: {:?} does not match the `links` attribute any direct dependency",
expr!(CFG.exported_header_links),
unused,
),
}
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
Error::Fs(err) => err.source(),
_ => None,
}
}
}
impl From<fs::Error> for Error {
fn from(err: fs::Error) -> Self {
Error::Fs(err)
}
}

45
vendor/cxx-build/src/gen/block.rs vendored Normal file
View file

@ -0,0 +1,45 @@
use proc_macro2::Ident;
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Block<'a> {
AnonymousNamespace,
Namespace(&'static str),
UserDefinedNamespace(&'a Ident),
InlineNamespace(&'static str),
ExternC,
}
impl<'a> Block<'a> {
pub fn write_begin(self, out: &mut String) {
if let Block::InlineNamespace(_) = self {
out.push_str("inline ");
}
self.write_common(out);
out.push_str(" {\n");
}
pub fn write_end(self, out: &mut String) {
out.push_str("} // ");
self.write_common(out);
out.push('\n');
}
fn write_common(self, out: &mut String) {
match self {
Block::AnonymousNamespace => out.push_str("namespace"),
Block::Namespace(name) => {
out.push_str("namespace ");
out.push_str(name);
}
Block::UserDefinedNamespace(name) => {
out.push_str("namespace ");
out.push_str(&name.to_string());
}
Block::InlineNamespace(name) => {
out.push_str("namespace ");
out.push_str(name);
}
Block::ExternC => out.push_str("extern \"C\""),
}
}
}

422
vendor/cxx-build/src/gen/builtin.rs vendored Normal file
View file

@ -0,0 +1,422 @@
use crate::gen::block::Block;
use crate::gen::ifndef;
use crate::gen::out::{Content, OutFile};
#[derive(Default, PartialEq)]
pub struct Builtins<'a> {
pub panic: bool,
pub rust_string: bool,
pub rust_str: bool,
pub rust_slice: bool,
pub rust_box: bool,
pub rust_vec: bool,
pub rust_fn: bool,
pub rust_isize: bool,
pub opaque: bool,
pub layout: bool,
pub unsafe_bitcopy: bool,
pub unsafe_bitcopy_t: bool,
pub rust_error: bool,
pub manually_drop: bool,
pub maybe_uninit: bool,
pub trycatch: bool,
pub ptr_len: bool,
pub repr_fat: bool,
pub rust_str_new_unchecked: bool,
pub rust_str_repr: bool,
pub rust_slice_new: bool,
pub rust_slice_repr: bool,
pub relocatable: bool,
pub relocatable_or_array: bool,
pub friend_impl: bool,
pub is_complete: bool,
pub destroy: bool,
pub deleter_if: bool,
pub content: Content<'a>,
}
impl<'a> Builtins<'a> {
pub fn new() -> Self {
Builtins::default()
}
}
pub(super) fn write(out: &mut OutFile) {
if out.builtin == Default::default() {
return;
}
let include = &mut out.include;
let builtin = &mut out.builtin;
let out = &mut builtin.content;
if builtin.rust_string {
include.array = true;
include.cstdint = true;
include.string = true;
}
if builtin.rust_str {
include.array = true;
include.cstdint = true;
include.string = true;
builtin.friend_impl = true;
}
if builtin.rust_vec {
include.algorithm = true;
include.array = true;
include.cassert = true;
include.cstddef = true;
include.cstdint = true;
include.initializer_list = true;
include.iterator = true;
include.new = true;
include.stdexcept = true;
include.type_traits = true;
include.utility = true;
builtin.panic = true;
builtin.rust_slice = true;
builtin.unsafe_bitcopy_t = true;
}
if builtin.rust_slice {
include.array = true;
include.cassert = true;
include.cstddef = true;
include.cstdint = true;
include.iterator = true;
include.stdexcept = true;
include.type_traits = true;
builtin.friend_impl = true;
builtin.layout = true;
builtin.panic = true;
}
if builtin.rust_box {
include.new = true;
include.type_traits = true;
include.utility = true;
}
if builtin.rust_fn {
include.utility = true;
}
if builtin.rust_error {
include.exception = true;
builtin.friend_impl = true;
}
if builtin.rust_isize {
include.basetsd = true;
include.sys_types = true;
}
if builtin.relocatable_or_array {
include.cstddef = true;
builtin.relocatable = true;
}
if builtin.relocatable {
include.type_traits = true;
}
if builtin.layout {
include.type_traits = true;
include.cstddef = true;
builtin.is_complete = true;
}
if builtin.is_complete {
include.cstddef = true;
include.type_traits = true;
}
if builtin.unsafe_bitcopy {
builtin.unsafe_bitcopy_t = true;
}
if builtin.trycatch {
builtin.ptr_len = true;
}
out.begin_block(Block::Namespace("rust"));
out.begin_block(Block::InlineNamespace("cxxbridge1"));
let cxx_header = include.has_cxx_header();
if !cxx_header {
writeln!(out, "// #include \"rust/cxx.h\"");
ifndef::write(out, builtin.panic, "CXXBRIDGE1_PANIC");
if builtin.rust_string {
out.next_section();
writeln!(out, "struct unsafe_bitcopy_t;");
}
if builtin.friend_impl {
out.begin_block(Block::AnonymousNamespace);
writeln!(out, "template <typename T>");
writeln!(out, "class impl;");
out.end_block(Block::AnonymousNamespace);
}
out.next_section();
if builtin.rust_str && !builtin.rust_string {
writeln!(out, "class String;");
}
if builtin.layout && !builtin.opaque {
writeln!(out, "class Opaque;");
}
if builtin.rust_slice {
out.next_section();
writeln!(out, "template <typename T>");
writeln!(out, "::std::size_t size_of();");
writeln!(out, "template <typename T>");
writeln!(out, "::std::size_t align_of();");
}
ifndef::write(out, builtin.rust_string, "CXXBRIDGE1_RUST_STRING");
ifndef::write(out, builtin.rust_str, "CXXBRIDGE1_RUST_STR");
ifndef::write(out, builtin.rust_slice, "CXXBRIDGE1_RUST_SLICE");
ifndef::write(out, builtin.rust_box, "CXXBRIDGE1_RUST_BOX");
ifndef::write(out, builtin.unsafe_bitcopy_t, "CXXBRIDGE1_RUST_BITCOPY_T");
ifndef::write(out, builtin.unsafe_bitcopy, "CXXBRIDGE1_RUST_BITCOPY");
ifndef::write(out, builtin.rust_vec, "CXXBRIDGE1_RUST_VEC");
ifndef::write(out, builtin.rust_fn, "CXXBRIDGE1_RUST_FN");
ifndef::write(out, builtin.rust_error, "CXXBRIDGE1_RUST_ERROR");
ifndef::write(out, builtin.rust_isize, "CXXBRIDGE1_RUST_ISIZE");
ifndef::write(out, builtin.opaque, "CXXBRIDGE1_RUST_OPAQUE");
ifndef::write(out, builtin.is_complete, "CXXBRIDGE1_IS_COMPLETE");
ifndef::write(out, builtin.layout, "CXXBRIDGE1_LAYOUT");
ifndef::write(out, builtin.relocatable, "CXXBRIDGE1_RELOCATABLE");
}
if builtin.rust_str_new_unchecked {
out.next_section();
writeln!(out, "class Str::uninit {{}};");
writeln!(out, "inline Str::Str(uninit) noexcept {{}}");
}
if builtin.rust_slice_new {
out.next_section();
writeln!(out, "template <typename T>");
writeln!(out, "class Slice<T>::uninit {{}};");
writeln!(out, "template <typename T>");
writeln!(out, "inline Slice<T>::Slice(uninit) noexcept {{}}");
}
out.begin_block(Block::Namespace("repr"));
if builtin.repr_fat {
include.array = true;
include.cstdint = true;
out.next_section();
writeln!(out, "using Fat = ::std::array<::std::uintptr_t, 2>;");
}
if builtin.ptr_len {
include.cstddef = true;
out.next_section();
writeln!(out, "struct PtrLen final {{");
writeln!(out, " void *ptr;");
writeln!(out, " ::std::size_t len;");
writeln!(out, "}};");
}
out.end_block(Block::Namespace("repr"));
out.begin_block(Block::Namespace("detail"));
if builtin.maybe_uninit {
include.cstddef = true;
include.new = true;
out.next_section();
writeln!(out, "template <typename T, typename = void *>");
writeln!(out, "struct operator_new {{");
writeln!(
out,
" void *operator()(::std::size_t sz) {{ return ::operator new(sz); }}",
);
writeln!(out, "}};");
out.next_section();
writeln!(out, "template <typename T>");
writeln!(
out,
"struct operator_new<T, decltype(T::operator new(sizeof(T)))> {{",
);
writeln!(
out,
" void *operator()(::std::size_t sz) {{ return T::operator new(sz); }}",
);
writeln!(out, "}};");
}
if builtin.trycatch {
include.string = true;
out.next_section();
writeln!(out, "class Fail final {{");
writeln!(out, " ::rust::repr::PtrLen &throw$;");
writeln!(out, "public:");
writeln!(
out,
" Fail(::rust::repr::PtrLen &throw$) noexcept : throw$(throw$) {{}}",
);
writeln!(out, " void operator()(char const *) noexcept;");
writeln!(out, " void operator()(std::string const &) noexcept;");
writeln!(out, "}};");
}
out.end_block(Block::Namespace("detail"));
if builtin.manually_drop {
out.next_section();
include.utility = true;
writeln!(out, "template <typename T>");
writeln!(out, "union ManuallyDrop {{");
writeln!(out, " T value;");
writeln!(
out,
" ManuallyDrop(T &&value) : value(::std::move(value)) {{}}",
);
writeln!(out, " ~ManuallyDrop() {{}}");
writeln!(out, "}};");
}
if builtin.maybe_uninit {
include.cstddef = true;
out.next_section();
writeln!(out, "template <typename T>");
writeln!(out, "union MaybeUninit {{");
writeln!(out, " T value;");
writeln!(
out,
" void *operator new(::std::size_t sz) {{ return detail::operator_new<T>{{}}(sz); }}",
);
writeln!(out, " MaybeUninit() {{}}");
writeln!(out, " ~MaybeUninit() {{}}");
writeln!(out, "}};");
}
out.begin_block(Block::AnonymousNamespace);
if builtin.rust_str_new_unchecked || builtin.rust_str_repr {
out.next_section();
writeln!(out, "template <>");
writeln!(out, "class impl<Str> final {{");
writeln!(out, "public:");
if builtin.rust_str_new_unchecked {
writeln!(
out,
" static Str new_unchecked(repr::Fat repr) noexcept {{",
);
writeln!(out, " Str str = Str::uninit{{}};");
writeln!(out, " str.repr = repr;");
writeln!(out, " return str;");
writeln!(out, " }}");
}
if builtin.rust_str_repr {
writeln!(out, " static repr::Fat repr(Str str) noexcept {{");
writeln!(out, " return str.repr;");
writeln!(out, " }}");
}
writeln!(out, "}};");
}
if builtin.rust_slice_new || builtin.rust_slice_repr {
out.next_section();
writeln!(out, "template <typename T>");
writeln!(out, "class impl<Slice<T>> final {{");
writeln!(out, "public:");
if builtin.rust_slice_new {
writeln!(out, " static Slice<T> slice(repr::Fat repr) noexcept {{");
writeln!(out, " Slice<T> slice = typename Slice<T>::uninit{{}};");
writeln!(out, " slice.repr = repr;");
writeln!(out, " return slice;");
writeln!(out, " }}");
}
if builtin.rust_slice_repr {
writeln!(out, " static repr::Fat repr(Slice<T> slice) noexcept {{");
writeln!(out, " return slice.repr;");
writeln!(out, " }}");
}
writeln!(out, "}};");
}
if builtin.rust_error {
out.next_section();
writeln!(out, "template <>");
writeln!(out, "class impl<Error> final {{");
writeln!(out, "public:");
writeln!(out, " static Error error(repr::PtrLen repr) noexcept {{");
writeln!(out, " Error error;");
writeln!(out, " error.msg = static_cast<char const *>(repr.ptr);");
writeln!(out, " error.len = repr.len;");
writeln!(out, " return error;");
writeln!(out, " }}");
writeln!(out, "}};");
}
if builtin.destroy {
out.next_section();
writeln!(out, "template <typename T>");
writeln!(out, "void destroy(T *ptr) {{");
writeln!(out, " ptr->~T();");
writeln!(out, "}}");
}
if builtin.deleter_if {
out.next_section();
writeln!(out, "template <bool> struct deleter_if {{");
writeln!(out, " template <typename T> void operator()(T *) {{}}");
writeln!(out, "}};");
out.next_section();
writeln!(out, "template <> struct deleter_if<true> {{");
writeln!(
out,
" template <typename T> void operator()(T *ptr) {{ ptr->~T(); }}",
);
writeln!(out, "}};");
}
if builtin.relocatable_or_array {
out.next_section();
writeln!(out, "template <typename T>");
writeln!(out, "struct IsRelocatableOrArray : IsRelocatable<T> {{}};");
writeln!(out, "template <typename T, ::std::size_t N>");
writeln!(
out,
"struct IsRelocatableOrArray<T[N]> : IsRelocatableOrArray<T> {{}};",
);
}
out.end_block(Block::AnonymousNamespace);
out.end_block(Block::InlineNamespace("cxxbridge1"));
if builtin.trycatch {
out.begin_block(Block::Namespace("behavior"));
include.exception = true;
include.type_traits = true;
include.utility = true;
writeln!(out, "class missing {{}};");
writeln!(out, "missing trycatch(...);");
writeln!(out);
writeln!(out, "template <typename Try, typename Fail>");
writeln!(out, "static typename ::std::enable_if<");
writeln!(
out,
" ::std::is_same<decltype(trycatch(::std::declval<Try>(), ::std::declval<Fail>())),",
);
writeln!(out, " missing>::value>::type");
writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{");
writeln!(out, " func();");
writeln!(out, "}} catch (::std::exception const &e) {{");
writeln!(out, " fail(e.what());");
writeln!(out, "}}");
out.end_block(Block::Namespace("behavior"));
}
out.end_block(Block::Namespace("rust"));
}

133
vendor/cxx-build/src/gen/cfg.rs vendored Normal file
View file

@ -0,0 +1,133 @@
use crate::gen::{CfgEvaluator, CfgResult};
use crate::syntax::cfg::CfgExpr;
use crate::syntax::report::Errors;
use crate::syntax::Api;
use quote::quote;
use std::collections::BTreeSet as Set;
use syn::Error;
pub(super) struct UnsupportedCfgEvaluator;
impl CfgEvaluator for UnsupportedCfgEvaluator {
fn eval(&self, name: &str, value: Option<&str>) -> CfgResult {
let _ = name;
let _ = value;
let msg = "cfg attribute is not supported".to_owned();
CfgResult::Undetermined { msg }
}
}
pub(super) fn strip(
cx: &mut Errors,
cfg_errors: &mut Set<String>,
cfg_evaluator: &dyn CfgEvaluator,
apis: &mut Vec<Api>,
) {
apis.retain(|api| eval(cx, cfg_errors, cfg_evaluator, api.cfg()));
for api in apis {
match api {
Api::Struct(strct) => strct
.fields
.retain(|field| eval(cx, cfg_errors, cfg_evaluator, &field.cfg)),
Api::Enum(enm) => enm
.variants
.retain(|variant| eval(cx, cfg_errors, cfg_evaluator, &variant.cfg)),
_ => {}
}
}
}
pub(super) fn eval(
cx: &mut Errors,
cfg_errors: &mut Set<String>,
cfg_evaluator: &dyn CfgEvaluator,
expr: &CfgExpr,
) -> bool {
match try_eval(cfg_evaluator, expr) {
Ok(value) => value,
Err(errors) => {
for error in errors {
if cfg_errors.insert(error.to_string()) {
cx.push(error);
}
}
false
}
}
}
fn try_eval(cfg_evaluator: &dyn CfgEvaluator, expr: &CfgExpr) -> Result<bool, Vec<Error>> {
match expr {
CfgExpr::Unconditional => Ok(true),
CfgExpr::Eq(ident, string) => {
let key = ident.to_string();
let value = string.as_ref().map(|string| string.value());
match cfg_evaluator.eval(&key, value.as_deref()) {
CfgResult::True => Ok(true),
CfgResult::False => Ok(false),
CfgResult::Undetermined { msg } => {
let span = quote!(#ident #string);
Err(vec![Error::new_spanned(span, msg)])
}
}
}
CfgExpr::All(list) => {
let mut all_errors = Vec::new();
for subexpr in list {
match try_eval(cfg_evaluator, subexpr) {
Ok(true) => {}
Ok(false) => return Ok(false),
Err(errors) => all_errors.extend(errors),
}
}
if all_errors.is_empty() {
Ok(true)
} else {
Err(all_errors)
}
}
CfgExpr::Any(list) => {
let mut all_errors = Vec::new();
for subexpr in list {
match try_eval(cfg_evaluator, subexpr) {
Ok(true) => return Ok(true),
Ok(false) => {}
Err(errors) => all_errors.extend(errors),
}
}
if all_errors.is_empty() {
Ok(false)
} else {
Err(all_errors)
}
}
CfgExpr::Not(subexpr) => match try_eval(cfg_evaluator, subexpr) {
Ok(value) => Ok(!value),
Err(errors) => Err(errors),
},
}
}
impl Api {
fn cfg(&self) -> &CfgExpr {
match self {
Api::Include(include) => &include.cfg,
Api::Struct(strct) => &strct.cfg,
Api::Enum(enm) => &enm.cfg,
Api::CxxType(ety) | Api::RustType(ety) => &ety.cfg,
Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.cfg,
Api::TypeAlias(alias) => &alias.cfg,
Api::Impl(imp) => &imp.cfg,
}
}
}
impl From<bool> for CfgResult {
fn from(value: bool) -> Self {
if value {
CfgResult::True
} else {
CfgResult::False
}
}
}

27
vendor/cxx-build/src/gen/check.rs vendored Normal file
View file

@ -0,0 +1,27 @@
use crate::gen::Opt;
use crate::syntax::report::Errors;
use crate::syntax::{error, Api};
use quote::{quote, quote_spanned};
use std::path::{Component, Path};
pub(super) use crate::syntax::check::{typecheck, Generator};
pub(super) fn precheck(cx: &mut Errors, apis: &[Api], opt: &Opt) {
if !opt.allow_dot_includes {
check_dot_includes(cx, apis);
}
}
fn check_dot_includes(cx: &mut Errors, apis: &[Api]) {
for api in apis {
if let Api::Include(include) = api {
let first_component = Path::new(&include.path).components().next();
if let Some(Component::CurDir) | Some(Component::ParentDir) = first_component {
let begin = quote_spanned!(include.begin_span=> .);
let end = quote_spanned!(include.end_span=> .);
let span = quote!(#begin #end);
cx.error(span, error::DOT_INCLUDE.msg);
}
}
}
}

175
vendor/cxx-build/src/gen/error.rs vendored Normal file
View file

@ -0,0 +1,175 @@
use crate::gen::fs;
use crate::syntax;
use codespan_reporting::diagnostic::{Diagnostic, Label};
use codespan_reporting::files::SimpleFiles;
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor};
use codespan_reporting::term::{self, Config};
use std::borrow::Cow;
use std::error::Error as StdError;
use std::fmt::{self, Display};
use std::io::{self, Write};
use std::ops::Range;
use std::path::{Path, PathBuf};
use std::process;
use std::str::Utf8Error;
pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug)]
pub(crate) enum Error {
NoBridgeMod,
Fs(fs::Error),
Utf8(PathBuf, Utf8Error),
Syn(syn::Error),
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"),
Error::Fs(err) => err.fmt(f),
Error::Utf8(path, _) => write!(f, "Failed to read file `{}`", path.display()),
Error::Syn(err) => err.fmt(f),
}
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
Error::Fs(err) => err.source(),
Error::Utf8(_, err) => Some(err),
Error::Syn(err) => err.source(),
_ => None,
}
}
}
impl From<fs::Error> for Error {
fn from(err: fs::Error) -> Self {
Error::Fs(err)
}
}
impl From<syn::Error> for Error {
fn from(err: syn::Error) -> Self {
Error::Syn(err)
}
}
pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! {
match error {
Error::Syn(syn_error) => {
let syn_error = sort_syn_errors(syn_error);
let writer = StandardStream::stderr(ColorChoice::Auto);
let ref mut stderr = writer.lock();
for error in syn_error {
let _ = writeln!(stderr);
display_syn_error(stderr, path, source, error);
}
}
Error::NoBridgeMod => {
let _ = writeln!(
io::stderr(),
"cxxbridge: no #[cxx::bridge] module found in {}",
path.display(),
);
}
_ => {
let _ = writeln!(io::stderr(), "cxxbridge: {}", report(error));
}
}
process::exit(1);
}
pub(crate) fn report(error: impl StdError) -> impl Display {
struct Report<E>(E);
impl<E: StdError> Display for Report<E> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "{}", self.0)?;
let mut error: &dyn StdError = &self.0;
while let Some(cause) = error.source() {
write!(formatter, "\n\nCaused by:\n {}", cause)?;
error = cause;
}
Ok(())
}
}
Report(error)
}
fn sort_syn_errors(error: syn::Error) -> Vec<syn::Error> {
let mut errors: Vec<_> = error.into_iter().collect();
errors.sort_by_key(|e| {
let start = e.span().start();
(start.line, start.column)
});
errors
}
fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, error: syn::Error) {
let span = error.span();
let start = span.start();
let end = span.end();
let mut start_offset = 0;
for _ in 1..start.line {
start_offset += source[start_offset..].find('\n').unwrap() + 1;
}
let start_column = source[start_offset..]
.chars()
.take(start.column)
.map(char::len_utf8)
.sum::<usize>();
start_offset += start_column;
let mut end_offset = start_offset;
if start.line == end.line {
end_offset -= start_column;
} else {
for _ in 0..end.line - start.line {
end_offset += source[end_offset..].find('\n').unwrap() + 1;
}
}
end_offset += source[end_offset..]
.chars()
.take(end.column)
.map(char::len_utf8)
.sum::<usize>();
let mut path = path.to_string_lossy();
if path == "-" {
path = Cow::Borrowed(if cfg!(unix) { "/dev/stdin" } else { "stdin" });
}
let mut files = SimpleFiles::new();
let file = files.add(path, source);
let diagnostic = diagnose(file, start_offset..end_offset, error);
let config = Config::default();
let _ = term::emit(stderr, &config, &files, &diagnostic);
}
fn diagnose(file: usize, range: Range<usize>, error: syn::Error) -> Diagnostic<usize> {
let message = error.to_string();
let info = syntax::error::ERRORS
.iter()
.find(|e| message.contains(e.msg));
let mut diagnostic = Diagnostic::error().with_message(&message);
let mut label = Label::primary(file, range);
if let Some(info) = info {
label.message = info.label.map_or(message, str::to_owned);
diagnostic.labels.push(label);
diagnostic.notes.extend(info.note.map(str::to_owned));
} else {
label.message = message;
diagnostic.labels.push(label);
}
diagnostic.code = Some("cxxbridge".to_owned());
diagnostic
}

72
vendor/cxx-build/src/gen/file.rs vendored Normal file
View file

@ -0,0 +1,72 @@
use crate::syntax::file::Module;
use crate::syntax::namespace::Namespace;
use syn::parse::discouraged::Speculative;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::{braced, Attribute, Ident, Item, Token, Visibility};
pub struct File {
pub modules: Vec<Module>,
}
impl Parse for File {
fn parse(input: ParseStream) -> Result<Self> {
let mut modules = Vec::new();
input.call(Attribute::parse_inner)?;
parse(input, &mut modules)?;
Ok(File { modules })
}
}
fn parse(input: ParseStream, modules: &mut Vec<Module>) -> Result<()> {
while !input.is_empty() {
let mut cxx_bridge = false;
let mut namespace = Namespace::ROOT;
let mut attrs = input.call(Attribute::parse_outer)?;
for attr in &attrs {
let path = &attr.path.segments;
if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" {
cxx_bridge = true;
namespace = parse_args(attr)?;
break;
}
}
let ahead = input.fork();
ahead.parse::<Visibility>()?;
ahead.parse::<Option<Token![unsafe]>>()?;
if !ahead.peek(Token![mod]) {
let item: Item = input.parse()?;
if cxx_bridge {
return Err(Error::new_spanned(item, "expected a module"));
}
continue;
}
if cxx_bridge {
let mut module: Module = input.parse()?;
module.namespace = namespace;
attrs.extend(module.attrs);
module.attrs = attrs;
modules.push(module);
} else {
input.advance_to(&ahead);
input.parse::<Token![mod]>()?;
input.parse::<Ident>()?;
let semi: Option<Token![;]> = input.parse()?;
if semi.is_none() {
let content;
braced!(content in input);
parse(&content, modules)?;
}
}
}
Ok(())
}
fn parse_args(attr: &Attribute) -> Result<Namespace> {
if attr.tokens.is_empty() {
Ok(Namespace::ROOT)
} else {
attr.parse_args_with(Namespace::parse_bridge_attr_namespace)
}
}

172
vendor/cxx-build/src/gen/fs.rs vendored Normal file
View file

@ -0,0 +1,172 @@
#![allow(dead_code)]
use std::error::Error as StdError;
use std::fmt::{self, Display};
use std::io::{self, Read};
use std::path::{Path, PathBuf};
pub(crate) type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub(crate) struct Error {
source: Option<io::Error>,
message: String,
}
impl Error {
pub fn kind(&self) -> io::ErrorKind {
match &self.source {
Some(io_error) => io_error.kind(),
None => io::ErrorKind::Other,
}
}
}
impl Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(&self.message)
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
let source = self.source.as_ref()?;
Some(source)
}
}
macro_rules! err {
($io_error:expr, $fmt:expr $(, $path:expr)* $(,)?) => {
Err(Error {
source: Option::from($io_error),
message: format!($fmt $(, $path.display())*),
})
}
}
pub(crate) fn copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<u64> {
let from = from.as_ref();
let to = to.as_ref();
match std::fs::copy(from, to) {
Ok(n) => Ok(n),
Err(e) => err!(e, "Failed to copy `{}` -> `{}`", from, to),
}
}
pub(crate) fn create_dir_all(path: impl AsRef<Path>) -> Result<()> {
let path = path.as_ref();
match std::fs::create_dir_all(path) {
Ok(()) => Ok(()),
Err(e) => err!(e, "Failed to create directory `{}`", path),
}
}
pub(crate) fn current_dir() -> Result<PathBuf> {
match std::env::current_dir() {
Ok(dir) => Ok(dir),
Err(e) => err!(e, "Failed to determine current directory"),
}
}
pub(crate) fn exists(path: impl AsRef<Path>) -> bool {
let path = path.as_ref();
// If path is a symlink, this returns true, regardless of whether the
// symlink points to a path that exists.
std::fs::symlink_metadata(path).is_ok()
}
pub(crate) fn read(path: impl AsRef<Path>) -> Result<Vec<u8>> {
let path = path.as_ref();
match std::fs::read(path) {
Ok(string) => Ok(string),
Err(e) => err!(e, "Failed to read file `{}`", path),
}
}
pub(crate) fn read_stdin() -> Result<Vec<u8>> {
let mut bytes = Vec::new();
match io::stdin().read_to_end(&mut bytes) {
Ok(_len) => Ok(bytes),
Err(e) => err!(e, "Failed to read input from stdin"),
}
}
pub(crate) fn remove_file(path: impl AsRef<Path>) -> Result<()> {
let path = path.as_ref();
match std::fs::remove_file(path) {
Ok(()) => Ok(()),
Err(e) => err!(e, "Failed to remove file `{}`", path),
}
}
pub(crate) fn remove_dir(path: impl AsRef<Path>) -> Result<()> {
let path = path.as_ref();
match std::fs::remove_dir(path) {
Ok(()) => Ok(()),
Err(e) => err!(e, "Failed to remove directory `{}`", path),
}
}
fn symlink<'a>(
original: &'a Path,
link: &'a Path,
fun: fn(&'a Path, &'a Path) -> io::Result<()>,
) -> Result<()> {
match fun(original, link) {
Ok(()) => Ok(()),
Err(e) => err!(
e,
"Failed to create symlink `{}` pointing to `{}`",
link,
original,
),
}
}
pub(crate) fn symlink_fail(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
err!(
None,
"Failed to create symlink `{}` pointing to `{}`",
link.as_ref(),
original.as_ref(),
)
}
#[cfg(unix)]
#[allow(unused_imports)]
pub(crate) use self::symlink_file as symlink_dir;
#[cfg(not(any(unix, windows)))]
#[allow(unused_imports)]
pub(crate) use self::symlink_fail as symlink_dir;
#[cfg(unix)]
pub(crate) fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
symlink(original.as_ref(), link.as_ref(), std::os::unix::fs::symlink)
}
#[cfg(windows)]
pub(crate) fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
symlink(
original.as_ref(),
link.as_ref(),
std::os::windows::fs::symlink_file,
)
}
#[cfg(windows)]
pub(crate) fn symlink_dir(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
symlink(
original.as_ref(),
link.as_ref(),
std::os::windows::fs::symlink_dir,
)
}
pub(crate) fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> Result<()> {
let path = path.as_ref();
match std::fs::write(path, contents) {
Ok(()) => Ok(()),
Err(e) => err!(e, "Failed to write file `{}`", path),
}
}

46
vendor/cxx-build/src/gen/ifndef.rs vendored Normal file
View file

@ -0,0 +1,46 @@
use crate::gen::include::HEADER;
use crate::gen::out::Content;
pub(super) fn write(out: &mut Content, needed: bool, guard: &str) {
let ifndef = format!("#ifndef {}", guard);
let define = format!("#define {}", guard);
let endif = format!("#endif // {}", guard);
let mut offset = 0;
loop {
let begin = find_line(offset, &ifndef);
let end = find_line(offset, &endif);
if let (Some(begin), Some(end)) = (begin, end) {
if !needed {
return;
}
out.next_section();
if offset == 0 {
writeln!(out, "{}", ifndef);
writeln!(out, "{}", define);
}
for line in HEADER[begin + ifndef.len()..end].trim().lines() {
if line != define && !line.trim_start().starts_with("//") {
writeln!(out, "{}", line);
}
}
offset = end + endif.len();
} else if offset == 0 {
panic!("not found in cxx.h header: {}", guard)
} else {
writeln!(out, "{}", endif);
return;
}
}
}
fn find_line(mut offset: usize, line: &str) -> Option<usize> {
loop {
offset += HEADER[offset..].find(line)?;
let rest = &HEADER[offset + line.len()..];
if rest.starts_with('\n') || rest.starts_with('\r') {
return Some(offset);
}
offset += line.len();
}
}

204
vendor/cxx-build/src/gen/include.rs vendored Normal file
View file

@ -0,0 +1,204 @@
use crate::gen::out::{Content, OutFile};
use crate::syntax::{self, IncludeKind};
use std::ops::{Deref, DerefMut};
/// The complete contents of the "rust/cxx.h" header.
pub static HEADER: &str = include_str!("include/cxx.h");
/// A header to #include.
///
/// The cxxbridge tool does not parse or even require the given paths to exist;
/// they simply go into the generated C++ code as #include lines.
#[derive(Clone, PartialEq, Debug)]
pub struct Include {
/// The header's path, not including the enclosing quotation marks or angle
/// brackets.
pub path: String,
/// Whether to emit `#include "path"` or `#include <path>`.
pub kind: IncludeKind,
}
#[derive(Default, PartialEq)]
pub struct Includes<'a> {
pub custom: Vec<Include>,
pub algorithm: bool,
pub array: bool,
pub cassert: bool,
pub cstddef: bool,
pub cstdint: bool,
pub cstring: bool,
pub exception: bool,
pub functional: bool,
pub initializer_list: bool,
pub iterator: bool,
pub memory: bool,
pub new: bool,
pub stdexcept: bool,
pub string: bool,
pub type_traits: bool,
pub utility: bool,
pub vector: bool,
pub basetsd: bool,
pub sys_types: bool,
pub content: Content<'a>,
}
impl<'a> Includes<'a> {
pub fn new() -> Self {
Includes::default()
}
pub fn insert(&mut self, include: impl Into<Include>) {
self.custom.push(include.into());
}
pub fn has_cxx_header(&self) -> bool {
self.custom
.iter()
.any(|header| header.path == "rust/cxx.h" || header.path == "rust\\cxx.h")
}
}
pub(super) fn write(out: &mut OutFile) {
let header = out.header;
let include = &mut out.include;
let cxx_header = include.has_cxx_header();
let out = &mut include.content;
if header {
writeln!(out, "#pragma once");
}
for include in &include.custom {
match include.kind {
IncludeKind::Quoted => {
writeln!(out, "#include \"{}\"", include.path.escape_default());
}
IncludeKind::Bracketed => {
writeln!(out, "#include <{}>", include.path);
}
}
}
let Includes {
custom: _,
algorithm,
array,
cassert,
cstddef,
cstdint,
cstring,
exception,
functional,
initializer_list,
iterator,
memory,
new,
stdexcept,
string,
type_traits,
utility,
vector,
basetsd,
sys_types,
content: _,
} = *include;
if algorithm && !cxx_header {
writeln!(out, "#include <algorithm>");
}
if array && !cxx_header {
writeln!(out, "#include <array>");
}
if cassert && !cxx_header {
writeln!(out, "#include <cassert>");
}
if cstddef && !cxx_header {
writeln!(out, "#include <cstddef>");
}
if cstdint && !cxx_header {
writeln!(out, "#include <cstdint>");
}
if cstring {
writeln!(out, "#include <cstring>");
}
if exception && !cxx_header {
writeln!(out, "#include <exception>");
}
if functional {
writeln!(out, "#include <functional>");
}
if initializer_list && !cxx_header {
writeln!(out, "#include <initializer_list>");
}
if iterator && !cxx_header {
writeln!(out, "#include <iterator>");
}
if memory {
writeln!(out, "#include <memory>");
}
if new && !cxx_header {
writeln!(out, "#include <new>");
}
if stdexcept && !cxx_header {
writeln!(out, "#include <stdexcept>");
}
if string && !cxx_header {
writeln!(out, "#include <string>");
}
if type_traits && !cxx_header {
writeln!(out, "#include <type_traits>");
}
if utility && !cxx_header {
writeln!(out, "#include <utility>");
}
if vector && !cxx_header {
writeln!(out, "#include <vector>");
}
if basetsd && !cxx_header {
writeln!(out, "#if defined(_WIN32)");
writeln!(out, "#include <basetsd.h>");
}
if sys_types && !cxx_header {
if basetsd {
writeln!(out, "#else");
} else {
writeln!(out, "#if not defined(_WIN32)");
}
}
if sys_types && !cxx_header {
writeln!(out, "#include <sys/types.h>");
}
if (basetsd || sys_types) && !cxx_header {
writeln!(out, "#endif");
}
}
impl<'i, 'a> Extend<&'i Include> for Includes<'a> {
fn extend<I: IntoIterator<Item = &'i Include>>(&mut self, iter: I) {
self.custom.extend(iter.into_iter().cloned());
}
}
impl<'i> From<&'i syntax::Include> for Include {
fn from(include: &syntax::Include) -> Self {
Include {
path: include.path.clone(),
kind: include.kind,
}
}
}
impl<'a> Deref for Includes<'a> {
type Target = Content<'a>;
fn deref(&self) -> &Self::Target {
&self.content
}
}
impl<'a> DerefMut for Includes<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.content
}
}

1111
vendor/cxx-build/src/gen/include/cxx.h vendored Normal file

File diff suppressed because it is too large Load diff

188
vendor/cxx-build/src/gen/mod.rs vendored Normal file
View file

@ -0,0 +1,188 @@
// Functionality that is shared between the cxx_build::bridge entry point and
// the cxxbridge CLI command.
mod block;
mod builtin;
mod cfg;
mod check;
pub(super) mod error;
mod file;
pub(super) mod fs;
mod ifndef;
pub(super) mod include;
mod names;
mod namespace;
mod nested;
pub(super) mod out;
mod write;
use self::cfg::UnsupportedCfgEvaluator;
use self::error::{format_err, Result};
use self::file::File;
use self::include::Include;
use crate::syntax::cfg::CfgExpr;
use crate::syntax::report::Errors;
use crate::syntax::{self, attrs, Types};
use std::collections::BTreeSet as Set;
use std::path::Path;
pub(super) use self::error::Error;
/// Options for C++ code generation.
///
/// We expect options to be added over time, so this is a non-exhaustive struct.
/// To instantiate one you need to crate a default value and mutate those fields
/// that you want to modify.
///
/// ```
/// # use cxx_gen::Opt;
/// #
/// let impl_annotations = r#"__attribute__((visibility("default")))"#.to_owned();
///
/// let mut opt = Opt::default();
/// opt.cxx_impl_annotations = Some(impl_annotations);
/// ```
#[non_exhaustive]
pub struct Opt {
/// Any additional headers to #include. The cxxbridge tool does not parse or
/// even require the given paths to exist; they simply go into the generated
/// C++ code as #include lines.
pub include: Vec<Include>,
/// Optional annotation for implementations of C++ function wrappers that
/// may be exposed to Rust. You may for example need to provide
/// `__declspec(dllexport)` or `__attribute__((visibility("default")))` if
/// Rust code from one shared object or executable depends on these C++
/// functions in another.
pub cxx_impl_annotations: Option<String>,
pub(super) gen_header: bool,
pub(super) gen_implementation: bool,
pub(super) allow_dot_includes: bool,
pub(super) cfg_evaluator: Box<dyn CfgEvaluator>,
pub(super) doxygen: bool,
}
pub(super) trait CfgEvaluator {
fn eval(&self, name: &str, value: Option<&str>) -> CfgResult;
}
pub(super) enum CfgResult {
True,
False,
Undetermined { msg: String },
}
/// Results of code generation.
#[derive(Default)]
pub struct GeneratedCode {
/// The bytes of a C++ header file.
pub header: Vec<u8>,
/// The bytes of a C++ implementation file (e.g. .cc, cpp etc.)
pub implementation: Vec<u8>,
}
impl Default for Opt {
fn default() -> Self {
Opt {
include: Vec::new(),
cxx_impl_annotations: None,
gen_header: true,
gen_implementation: true,
allow_dot_includes: true,
cfg_evaluator: Box::new(UnsupportedCfgEvaluator),
doxygen: false,
}
}
}
pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode {
let source = match read_to_string(path) {
Ok(source) => source,
Err(err) => format_err(path, "", err),
};
match generate_from_string(&source, opt) {
Ok(out) => out,
Err(err) => format_err(path, &source, err),
}
}
fn read_to_string(path: &Path) -> Result<String> {
let bytes = if path == Path::new("-") {
fs::read_stdin()
} else {
fs::read(path)
}?;
match String::from_utf8(bytes) {
Ok(string) => Ok(string),
Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())),
}
}
fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
let mut source = source;
if source.starts_with("#!") && !source.starts_with("#![") {
let shebang_end = source.find('\n').unwrap_or(source.len());
source = &source[shebang_end..];
}
proc_macro2::fallback::force();
let syntax: File = syn::parse_str(source)?;
generate(syntax, opt)
}
pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
if syntax.modules.is_empty() {
return Err(Error::NoBridgeMod);
}
let ref mut apis = Vec::new();
let ref mut errors = Errors::new();
let ref mut cfg_errors = Set::new();
for bridge in syntax.modules {
let mut cfg = CfgExpr::Unconditional;
attrs::parse(
errors,
bridge.attrs,
attrs::Parser {
cfg: Some(&mut cfg),
ignore_unrecognized: true,
..Default::default()
},
);
if cfg::eval(errors, cfg_errors, opt.cfg_evaluator.as_ref(), &cfg) {
let ref namespace = bridge.namespace;
let trusted = bridge.unsafety.is_some();
apis.extend(syntax::parse_items(
errors,
bridge.content,
trusted,
namespace,
));
}
}
cfg::strip(errors, cfg_errors, opt.cfg_evaluator.as_ref(), apis);
errors.propagate()?;
let ref types = Types::collect(errors, apis);
check::precheck(errors, apis, opt);
errors.propagate()?;
let generator = check::Generator::Build;
check::typecheck(errors, apis, types, generator);
errors.propagate()?;
// Some callers may wish to generate both header and implementation from the
// same token stream to avoid parsing twice. Others only need to generate
// one or the other.
let (mut header, mut implementation) = Default::default();
if opt.gen_header {
header = write::gen(apis, types, opt, true);
}
if opt.gen_implementation {
implementation = write::gen(apis, types, opt, false);
}
Ok(GeneratedCode {
header,
implementation,
})
}

14
vendor/cxx-build/src/gen/names.rs vendored Normal file
View file

@ -0,0 +1,14 @@
use crate::syntax::Pair;
impl Pair {
pub fn to_fully_qualified(&self) -> String {
let mut fully_qualified = String::new();
for segment in &self.namespace {
fully_qualified += "::";
fully_qualified += &segment.to_string();
}
fully_qualified += "::";
fully_qualified += &self.cxx.to_string();
fully_qualified
}
}

14
vendor/cxx-build/src/gen/namespace.rs vendored Normal file
View file

@ -0,0 +1,14 @@
use crate::syntax::namespace::Namespace;
use crate::syntax::Api;
impl Api {
pub fn namespace(&self) -> &Namespace {
match self {
Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.name.namespace,
Api::CxxType(ety) | Api::RustType(ety) => &ety.name.namespace,
Api::Enum(enm) => &enm.name.namespace,
Api::Struct(strct) => &strct.name.namespace,
Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => Default::default(),
}
}
}

155
vendor/cxx-build/src/gen/nested.rs vendored Normal file
View file

@ -0,0 +1,155 @@
use crate::syntax::map::UnorderedMap as Map;
use crate::syntax::Api;
use proc_macro2::Ident;
pub struct NamespaceEntries<'a> {
direct: Vec<&'a Api>,
nested: Vec<(&'a Ident, NamespaceEntries<'a>)>,
}
impl<'a> NamespaceEntries<'a> {
pub fn new(apis: Vec<&'a Api>) -> Self {
sort_by_inner_namespace(apis, 0)
}
pub fn direct_content(&self) -> &[&'a Api] {
&self.direct
}
pub fn nested_content(&self) -> impl Iterator<Item = (&'a Ident, &NamespaceEntries<'a>)> {
self.nested.iter().map(|(k, entries)| (*k, entries))
}
}
fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries {
let mut direct = Vec::new();
let mut nested_namespaces = Vec::new();
let mut index_of_namespace = Map::new();
for api in &apis {
if let Some(first_ns_elem) = api.namespace().iter().nth(depth) {
match index_of_namespace.get(first_ns_elem) {
None => {
index_of_namespace.insert(first_ns_elem, nested_namespaces.len());
nested_namespaces.push((first_ns_elem, vec![*api]));
}
Some(&index) => nested_namespaces[index].1.push(*api),
}
continue;
}
direct.push(*api);
}
let nested = nested_namespaces
.into_iter()
.map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1)))
.collect();
NamespaceEntries { direct, nested }
}
#[cfg(test)]
mod tests {
use super::NamespaceEntries;
use crate::syntax::attrs::OtherAttrs;
use crate::syntax::cfg::CfgExpr;
use crate::syntax::namespace::Namespace;
use crate::syntax::{Api, Doc, ExternType, ForeignName, Lang, Lifetimes, Pair};
use proc_macro2::{Ident, Span};
use std::iter::FromIterator;
use syn::punctuated::Punctuated;
use syn::Token;
#[test]
fn test_ns_entries_sort() {
let apis = &[
make_api(None, "C"),
make_api(None, "A"),
make_api(Some("G"), "E"),
make_api(Some("D"), "F"),
make_api(Some("G"), "H"),
make_api(Some("D::K"), "L"),
make_api(Some("D::K"), "M"),
make_api(None, "B"),
make_api(Some("D"), "I"),
make_api(Some("D"), "J"),
];
let root = NamespaceEntries::new(Vec::from_iter(apis));
// ::
let root_direct = root.direct_content();
assert_eq!(root_direct.len(), 3);
assert_ident(root_direct[0], "C");
assert_ident(root_direct[1], "A");
assert_ident(root_direct[2], "B");
let mut root_nested = root.nested_content();
let (id, g) = root_nested.next().unwrap();
assert_eq!(id, "G");
let (id, d) = root_nested.next().unwrap();
assert_eq!(id, "D");
assert!(root_nested.next().is_none());
// ::G
let g_direct = g.direct_content();
assert_eq!(g_direct.len(), 2);
assert_ident(g_direct[0], "E");
assert_ident(g_direct[1], "H");
let mut g_nested = g.nested_content();
assert!(g_nested.next().is_none());
// ::D
let d_direct = d.direct_content();
assert_eq!(d_direct.len(), 3);
assert_ident(d_direct[0], "F");
assert_ident(d_direct[1], "I");
assert_ident(d_direct[2], "J");
let mut d_nested = d.nested_content();
let (id, k) = d_nested.next().unwrap();
assert_eq!(id, "K");
// ::D::K
let k_direct = k.direct_content();
assert_eq!(k_direct.len(), 2);
assert_ident(k_direct[0], "L");
assert_ident(k_direct[1], "M");
}
fn assert_ident(api: &Api, expected: &str) {
if let Api::CxxType(cxx_type) = api {
assert_eq!(cxx_type.name.cxx.to_string(), expected);
} else {
unreachable!()
}
}
fn make_api(ns: Option<&str>, ident: &str) -> Api {
let ns = ns.map_or(Namespace::ROOT, |ns| syn::parse_str(ns).unwrap());
Api::CxxType(ExternType {
cfg: CfgExpr::Unconditional,
lang: Lang::Rust,
doc: Doc::new(),
derives: Vec::new(),
attrs: OtherAttrs::none(),
visibility: Token![pub](Span::call_site()),
type_token: Token![type](Span::call_site()),
name: Pair {
namespace: ns,
cxx: ForeignName::parse(ident, Span::call_site()).unwrap(),
rust: Ident::new(ident, Span::call_site()),
},
generics: Lifetimes {
lt_token: None,
lifetimes: Punctuated::new(),
gt_token: None,
},
colon_token: None,
bounds: Vec::new(),
semi_token: Token![;](Span::call_site()),
trusted: false,
})
}
}

210
vendor/cxx-build/src/gen/out.rs vendored Normal file
View file

@ -0,0 +1,210 @@
use crate::gen::block::Block;
use crate::gen::builtin::Builtins;
use crate::gen::include::Includes;
use crate::gen::Opt;
use crate::syntax::namespace::Namespace;
use crate::syntax::Types;
use std::cell::RefCell;
use std::fmt::{self, Arguments, Write};
pub(crate) struct OutFile<'a> {
pub header: bool,
pub opt: &'a Opt,
pub types: &'a Types<'a>,
pub include: Includes<'a>,
pub builtin: Builtins<'a>,
content: RefCell<Content<'a>>,
}
#[derive(Default)]
pub struct Content<'a> {
bytes: String,
namespace: &'a Namespace,
blocks: Vec<BlockBoundary<'a>>,
section_pending: bool,
blocks_pending: usize,
}
#[derive(Copy, Clone, PartialEq, Debug)]
enum BlockBoundary<'a> {
Begin(Block<'a>),
End(Block<'a>),
}
impl<'a> OutFile<'a> {
pub fn new(header: bool, opt: &'a Opt, types: &'a Types) -> Self {
OutFile {
header,
opt,
types,
include: Includes::new(),
builtin: Builtins::new(),
content: RefCell::new(Content::new()),
}
}
// Write a blank line if the preceding section had any contents.
pub fn next_section(&mut self) {
self.content.get_mut().next_section();
}
pub fn begin_block(&mut self, block: Block<'a>) {
self.content.get_mut().begin_block(block);
}
pub fn end_block(&mut self, block: Block<'a>) {
self.content.get_mut().end_block(block);
}
pub fn set_namespace(&mut self, namespace: &'a Namespace) {
self.content.get_mut().set_namespace(namespace);
}
pub fn write_fmt(&self, args: Arguments) {
let content = &mut *self.content.borrow_mut();
Write::write_fmt(content, args).unwrap();
}
pub fn content(&mut self) -> Vec<u8> {
self.flush();
let include = &self.include.content.bytes;
let builtin = &self.builtin.content.bytes;
let content = &self.content.get_mut().bytes;
let len = include.len() + builtin.len() + content.len() + 2;
let mut out = String::with_capacity(len);
out.push_str(include);
if !out.is_empty() && !builtin.is_empty() {
out.push('\n');
}
out.push_str(builtin);
if !out.is_empty() && !content.is_empty() {
out.push('\n');
}
out.push_str(content);
if out.is_empty() {
out.push_str("// empty\n");
}
out.into_bytes()
}
fn flush(&mut self) {
self.include.content.flush();
self.builtin.content.flush();
self.content.get_mut().flush();
}
}
impl<'a> Write for Content<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write(s);
Ok(())
}
}
impl<'a> PartialEq for Content<'a> {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl<'a> Content<'a> {
fn new() -> Self {
Content::default()
}
pub fn next_section(&mut self) {
self.section_pending = true;
}
pub fn begin_block(&mut self, block: Block<'a>) {
self.push_block_boundary(BlockBoundary::Begin(block));
}
pub fn end_block(&mut self, block: Block<'a>) {
self.push_block_boundary(BlockBoundary::End(block));
}
pub fn set_namespace(&mut self, namespace: &'a Namespace) {
for name in self.namespace.iter().rev() {
self.end_block(Block::UserDefinedNamespace(name));
}
for name in namespace {
self.begin_block(Block::UserDefinedNamespace(name));
}
self.namespace = namespace;
}
pub fn write_fmt(&mut self, args: Arguments) {
Write::write_fmt(self, args).unwrap();
}
fn write(&mut self, b: &str) {
if !b.is_empty() {
if self.blocks_pending > 0 {
self.flush_blocks();
}
if self.section_pending && !self.bytes.is_empty() {
self.bytes.push('\n');
}
self.bytes.push_str(b);
self.section_pending = false;
self.blocks_pending = 0;
}
}
fn push_block_boundary(&mut self, boundary: BlockBoundary<'a>) {
if self.blocks_pending > 0 && boundary == self.blocks.last().unwrap().rev() {
self.blocks.pop();
self.blocks_pending -= 1;
} else {
self.blocks.push(boundary);
self.blocks_pending += 1;
}
}
fn flush(&mut self) {
self.set_namespace(Default::default());
if self.blocks_pending > 0 {
self.flush_blocks();
}
}
fn flush_blocks(&mut self) {
self.section_pending = !self.bytes.is_empty();
let mut read = self.blocks.len() - self.blocks_pending;
let mut write = read;
while read < self.blocks.len() {
match self.blocks[read] {
BlockBoundary::Begin(begin_block) => {
if self.section_pending {
self.bytes.push('\n');
self.section_pending = false;
}
Block::write_begin(begin_block, &mut self.bytes);
self.blocks[write] = BlockBoundary::Begin(begin_block);
write += 1;
}
BlockBoundary::End(end_block) => {
write = write.checked_sub(1).unwrap();
let begin_block = self.blocks[write];
assert_eq!(begin_block, BlockBoundary::Begin(end_block));
Block::write_end(end_block, &mut self.bytes);
self.section_pending = true;
}
}
read += 1;
}
self.blocks.truncate(write);
}
}
impl<'a> BlockBoundary<'a> {
fn rev(self) -> BlockBoundary<'a> {
match self {
BlockBoundary::Begin(block) => BlockBoundary::End(block),
BlockBoundary::End(block) => BlockBoundary::Begin(block),
}
}
}

1932
vendor/cxx-build/src/gen/write.rs vendored Normal file

File diff suppressed because it is too large Load diff

30
vendor/cxx-build/src/intern.rs vendored Normal file
View file

@ -0,0 +1,30 @@
use crate::syntax::set::UnorderedSet as Set;
use once_cell::sync::OnceCell;
use std::sync::{Mutex, PoisonError};
#[derive(Copy, Clone, Default)]
pub struct InternedString(&'static str);
impl InternedString {
pub fn str(self) -> &'static str {
self.0
}
}
pub fn intern(s: &str) -> InternedString {
static INTERN: OnceCell<Mutex<Set<&'static str>>> = OnceCell::new();
let mut set = INTERN
.get_or_init(|| Mutex::new(Set::new()))
.lock()
.unwrap_or_else(PoisonError::into_inner);
InternedString(match set.get(s) {
Some(interned) => *interned,
None => {
let interned = Box::leak(Box::from(s));
set.insert(interned);
interned
}
})
}

477
vendor/cxx-build/src/lib.rs vendored Normal file
View file

@ -0,0 +1,477 @@
//! The CXX code generator for constructing and compiling C++ code.
//!
//! This is intended to be used from Cargo build scripts to execute CXX's
//! C++ code generator, set up any additional compiler flags depending on
//! the use case, and make the C++ compiler invocation.
//!
//! <br>
//!
//! # Example
//!
//! Example of a canonical Cargo build script that builds a CXX bridge:
//!
//! ```no_run
//! // build.rs
//!
//! fn main() {
//! cxx_build::bridge("src/main.rs")
//! .file("src/demo.cc")
//! .flag_if_supported("-std=c++11")
//! .compile("cxxbridge-demo");
//!
//! println!("cargo:rerun-if-changed=src/main.rs");
//! println!("cargo:rerun-if-changed=src/demo.cc");
//! println!("cargo:rerun-if-changed=include/demo.h");
//! }
//! ```
//!
//! A runnable working setup with this build script is shown in the *demo*
//! directory of [https://github.com/dtolnay/cxx].
//!
//! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx
//!
//! <br>
//!
//! # Alternatives
//!
//! For use in non-Cargo builds like Bazel or Buck, CXX provides an
//! alternate way of invoking the C++ code generator as a standalone command
//! line tool. The tool is packaged as the `cxxbridge-cmd` crate.
//!
//! ```bash
//! $ cargo install cxxbridge-cmd # or build it from the repo
//!
//! $ cxxbridge src/main.rs --header > path/to/mybridge.h
//! $ cxxbridge src/main.rs > path/to/mybridge.cc
//! ```
#![allow(
clippy::cast_sign_loss,
clippy::default_trait_access,
clippy::derive_partial_eq_without_eq,
clippy::doc_markdown,
clippy::drop_copy,
clippy::enum_glob_use,
clippy::explicit_auto_deref,
clippy::if_same_then_else,
clippy::inherent_to_string,
clippy::items_after_statements,
clippy::let_underscore_drop,
clippy::match_bool,
clippy::match_on_vec_items,
clippy::match_same_arms,
clippy::module_name_repetitions,
clippy::needless_doctest_main,
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::significant_drop_in_scrutinee,
clippy::similar_names,
clippy::single_match_else,
clippy::struct_excessive_bools,
clippy::too_many_arguments,
clippy::too_many_lines,
clippy::toplevel_ref_arg,
clippy::upper_case_acronyms,
// clippy bug: https://github.com/rust-lang/rust-clippy/issues/6983
clippy::wrong_self_convention
)]
mod cargo;
mod cfg;
mod deps;
mod error;
mod gen;
mod intern;
mod out;
mod paths;
mod syntax;
mod target;
mod vec;
use crate::cargo::CargoEnvCfgEvaluator;
use crate::deps::{Crate, HeaderDir};
use crate::error::{Error, Result};
use crate::gen::error::report;
use crate::gen::Opt;
use crate::paths::PathExt;
use crate::syntax::map::{Entry, UnorderedMap};
use crate::target::TargetDir;
use cc::Build;
use std::collections::BTreeSet;
use std::env;
use std::ffi::{OsStr, OsString};
use std::io::{self, Write};
use std::iter;
use std::path::{Path, PathBuf};
use std::process;
pub use crate::cfg::{Cfg, CFG};
/// This returns a [`cc::Build`] on which you should continue to set up any
/// additional source files or compiler flags, and lastly call its [`compile`]
/// method to execute the C++ build.
///
/// [`compile`]: https://docs.rs/cc/1.0.49/cc/struct.Build.html#method.compile
#[must_use]
pub fn bridge(rust_source_file: impl AsRef<Path>) -> Build {
bridges(iter::once(rust_source_file))
}
/// `cxx_build::bridge` but for when more than one file contains a
/// #\[cxx::bridge\] module.
///
/// ```no_run
/// let source_files = vec!["src/main.rs", "src/path/to/other.rs"];
/// cxx_build::bridges(source_files)
/// .file("src/demo.cc")
/// .flag_if_supported("-std=c++11")
/// .compile("cxxbridge-demo");
/// ```
#[must_use]
pub fn bridges(rust_source_files: impl IntoIterator<Item = impl AsRef<Path>>) -> Build {
let ref mut rust_source_files = rust_source_files.into_iter();
build(rust_source_files).unwrap_or_else(|err| {
let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {}\n\n", report(err));
process::exit(1);
})
}
struct Project {
include_prefix: PathBuf,
manifest_dir: PathBuf,
// The `links = "..."` value from Cargo.toml.
links_attribute: Option<OsString>,
// Output directory as received from Cargo.
out_dir: PathBuf,
// Directory into which to symlink all generated code.
//
// This is *not* used for an #include path, only as a debugging convenience.
// Normally available at target/cxxbridge/ if we are able to know where the
// target dir is, otherwise under a common scratch dir.
//
// The reason this isn't the #include dir is that we do not want builds to
// have access to headers from arbitrary other parts of the dependency
// graph. Using a global directory for all builds would be both a race
// condition depending on what order Cargo randomly executes the build
// scripts, as well as semantically undesirable for builds not to have to
// declare their real dependencies.
shared_dir: PathBuf,
}
impl Project {
fn init() -> Result<Self> {
let include_prefix = Path::new(CFG.include_prefix);
assert!(include_prefix.is_relative());
let include_prefix = include_prefix.components().collect();
let links_attribute = env::var_os("CARGO_MANIFEST_LINKS");
let manifest_dir = paths::manifest_dir()?;
let out_dir = paths::out_dir()?;
let shared_dir = match target::find_target_dir(&out_dir) {
TargetDir::Path(target_dir) => target_dir.join("cxxbridge"),
TargetDir::Unknown => scratch::path("cxxbridge"),
};
Ok(Project {
include_prefix,
manifest_dir,
links_attribute,
out_dir,
shared_dir,
})
}
}
// We lay out the OUT_DIR as follows. Everything is namespaced under a cxxbridge
// subdirectory to avoid stomping on other things that the caller's build script
// might be doing inside OUT_DIR.
//
// $OUT_DIR/
// cxxbridge/
// crate/
// $CARGO_PKG_NAME -> $CARGO_MANIFEST_DIR
// include/
// rust/
// cxx.h
// $CARGO_PKG_NAME/
// .../
// lib.rs.h
// sources/
// $CARGO_PKG_NAME/
// .../
// lib.rs.cc
//
// The crate/ and include/ directories are placed on the #include path for the
// current build as well as for downstream builds that have a direct dependency
// on the current crate.
fn build(rust_source_files: &mut dyn Iterator<Item = impl AsRef<Path>>) -> Result<Build> {
let ref prj = Project::init()?;
validate_cfg(prj)?;
let this_crate = make_this_crate(prj)?;
let mut build = Build::new();
build.cpp(true);
build.cpp_link_stdlib(None); // linked via link-cplusplus crate
for path in rust_source_files {
generate_bridge(prj, &mut build, path.as_ref())?;
}
this_crate.print_to_cargo();
eprintln!("\nCXX include path:");
for header_dir in this_crate.header_dirs {
build.include(&header_dir.path);
if header_dir.exported {
eprintln!(" {}", header_dir.path.display());
} else {
eprintln!(" {} (private)", header_dir.path.display());
}
}
Ok(build)
}
fn validate_cfg(prj: &Project) -> Result<()> {
for exported_dir in &CFG.exported_header_dirs {
if !exported_dir.is_absolute() {
return Err(Error::ExportedDirNotAbsolute(exported_dir));
}
}
for prefix in &CFG.exported_header_prefixes {
if prefix.is_empty() {
return Err(Error::ExportedEmptyPrefix);
}
}
if prj.links_attribute.is_none() {
if !CFG.exported_header_dirs.is_empty() {
return Err(Error::ExportedDirsWithoutLinks);
}
if !CFG.exported_header_prefixes.is_empty() {
return Err(Error::ExportedPrefixesWithoutLinks);
}
if !CFG.exported_header_links.is_empty() {
return Err(Error::ExportedLinksWithoutLinks);
}
}
Ok(())
}
fn make_this_crate(prj: &Project) -> Result<Crate> {
let crate_dir = make_crate_dir(prj);
let include_dir = make_include_dir(prj)?;
let mut this_crate = Crate {
include_prefix: Some(prj.include_prefix.clone()),
links: prj.links_attribute.clone(),
header_dirs: Vec::new(),
};
// The generated code directory (include_dir) is placed in front of
// crate_dir on the include line so that `#include "path/to/file.rs"` from
// C++ "magically" works and refers to the API generated from that Rust
// source file.
this_crate.header_dirs.push(HeaderDir {
exported: true,
path: include_dir,
});
this_crate.header_dirs.push(HeaderDir {
exported: true,
path: crate_dir,
});
for exported_dir in &CFG.exported_header_dirs {
this_crate.header_dirs.push(HeaderDir {
exported: true,
path: PathBuf::from(exported_dir),
});
}
let mut header_dirs_index = UnorderedMap::new();
let mut used_header_links = BTreeSet::new();
let mut used_header_prefixes = BTreeSet::new();
for krate in deps::direct_dependencies() {
let mut is_link_exported = || match &krate.links {
None => false,
Some(links_attribute) => CFG.exported_header_links.iter().any(|&exported| {
let matches = links_attribute == exported;
if matches {
used_header_links.insert(exported);
}
matches
}),
};
let mut is_prefix_exported = || match &krate.include_prefix {
None => false,
Some(include_prefix) => CFG.exported_header_prefixes.iter().any(|&exported| {
let matches = include_prefix.starts_with(exported);
if matches {
used_header_prefixes.insert(exported);
}
matches
}),
};
let exported = is_link_exported() || is_prefix_exported();
for dir in krate.header_dirs {
// Deduplicate dirs reachable via multiple transitive dependencies.
match header_dirs_index.entry(dir.path.clone()) {
Entry::Vacant(entry) => {
entry.insert(this_crate.header_dirs.len());
this_crate.header_dirs.push(HeaderDir {
exported,
path: dir.path,
});
}
Entry::Occupied(entry) => {
let index = *entry.get();
this_crate.header_dirs[index].exported |= exported;
}
}
}
}
if let Some(unused) = CFG
.exported_header_links
.iter()
.find(|&exported| !used_header_links.contains(exported))
{
return Err(Error::UnusedExportedLinks(unused));
}
if let Some(unused) = CFG
.exported_header_prefixes
.iter()
.find(|&exported| !used_header_prefixes.contains(exported))
{
return Err(Error::UnusedExportedPrefix(unused));
}
Ok(this_crate)
}
fn make_crate_dir(prj: &Project) -> PathBuf {
if prj.include_prefix.as_os_str().is_empty() {
return prj.manifest_dir.clone();
}
let crate_dir = prj.out_dir.join("cxxbridge").join("crate");
let ref link = crate_dir.join(&prj.include_prefix);
let ref manifest_dir = prj.manifest_dir;
if out::symlink_dir(manifest_dir, link).is_err() && cfg!(not(unix)) {
let cachedir_tag = "\
Signature: 8a477f597d28d172789f06886806bc55\n\
# This file is a cache directory tag created by cxx.\n\
# For information about cache directory tags see https://bford.info/cachedir/\n";
let _ = out::write(crate_dir.join("CACHEDIR.TAG"), cachedir_tag.as_bytes());
let max_depth = 6;
best_effort_copy_headers(manifest_dir, link, max_depth);
}
crate_dir
}
fn make_include_dir(prj: &Project) -> Result<PathBuf> {
let include_dir = prj.out_dir.join("cxxbridge").join("include");
let cxx_h = include_dir.join("rust").join("cxx.h");
let ref shared_cxx_h = prj.shared_dir.join("rust").join("cxx.h");
if let Some(ref original) = env::var_os("DEP_CXXBRIDGE1_HEADER") {
out::symlink_file(original, cxx_h)?;
out::symlink_file(original, shared_cxx_h)?;
} else {
out::write(shared_cxx_h, gen::include::HEADER.as_bytes())?;
out::symlink_file(shared_cxx_h, cxx_h)?;
}
Ok(include_dir)
}
fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> Result<()> {
let opt = Opt {
allow_dot_includes: false,
cfg_evaluator: Box::new(CargoEnvCfgEvaluator),
doxygen: CFG.doxygen,
..Opt::default()
};
let generated = gen::generate_from_path(rust_source_file, &opt);
let ref rel_path = paths::local_relative_path(rust_source_file);
let cxxbridge = prj.out_dir.join("cxxbridge");
let include_dir = cxxbridge.join("include").join(&prj.include_prefix);
let sources_dir = cxxbridge.join("sources").join(&prj.include_prefix);
let ref rel_path_h = rel_path.with_appended_extension(".h");
let ref header_path = include_dir.join(rel_path_h);
out::write(header_path, &generated.header)?;
let ref link_path = include_dir.join(rel_path);
let _ = out::symlink_file(header_path, link_path);
let ref rel_path_cc = rel_path.with_appended_extension(".cc");
let ref implementation_path = sources_dir.join(rel_path_cc);
out::write(implementation_path, &generated.implementation)?;
build.file(implementation_path);
let shared_h = prj.shared_dir.join(&prj.include_prefix).join(rel_path_h);
let shared_cc = prj.shared_dir.join(&prj.include_prefix).join(rel_path_cc);
let _ = out::symlink_file(header_path, shared_h);
let _ = out::symlink_file(implementation_path, shared_cc);
Ok(())
}
fn best_effort_copy_headers(src: &Path, dst: &Path, max_depth: usize) {
// Not using crate::gen::fs because we aren't reporting the errors.
use std::fs;
let mut dst_created = false;
let mut entries = match fs::read_dir(src) {
Ok(entries) => entries,
Err(_) => return,
};
while let Some(Ok(entry)) = entries.next() {
let file_name = entry.file_name();
if file_name.to_string_lossy().starts_with('.') {
continue;
}
match entry.file_type() {
Ok(file_type) if file_type.is_dir() && max_depth > 0 => {
let src = entry.path();
if src.join("Cargo.toml").exists() || src.join("CACHEDIR.TAG").exists() {
continue;
}
let dst = dst.join(file_name);
best_effort_copy_headers(&src, &dst, max_depth - 1);
}
Ok(file_type) if file_type.is_file() => {
let src = entry.path();
match src.extension().and_then(OsStr::to_str) {
Some("h") | Some("hh") | Some("hpp") => {}
_ => continue,
}
if !dst_created && fs::create_dir_all(dst).is_err() {
return;
}
dst_created = true;
let dst = dst.join(file_name);
let _ = fs::remove_file(&dst);
let _ = fs::copy(src, dst);
}
_ => {}
}
}
}
fn env_os(key: impl AsRef<OsStr>) -> Result<OsString> {
let key = key.as_ref();
env::var_os(key).ok_or_else(|| Error::NoEnv(key.to_owned()))
}

119
vendor/cxx-build/src/out.rs vendored Normal file
View file

@ -0,0 +1,119 @@
use crate::error::{Error, Result};
use crate::gen::fs;
use crate::paths;
use std::io;
use std::path::Path;
pub(crate) fn write(path: impl AsRef<Path>, content: &[u8]) -> Result<()> {
let path = path.as_ref();
let mut create_dir_error = None;
if fs::exists(path) {
if let Ok(existing) = fs::read(path) {
if existing == content {
// Avoid bumping modified time with unchanged contents.
return Ok(());
}
}
best_effort_remove(path);
} else {
let parent = path.parent().unwrap();
create_dir_error = fs::create_dir_all(parent).err();
}
match fs::write(path, content) {
// As long as write succeeded, ignore any create_dir_all error.
Ok(()) => Ok(()),
// If create_dir_all and write both failed, prefer the first error.
Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))),
}
}
pub(crate) fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
let original = original.as_ref();
let link = link.as_ref();
let mut create_dir_error = None;
if fs::exists(link) {
best_effort_remove(link);
} else {
let parent = link.parent().unwrap();
create_dir_error = fs::create_dir_all(parent).err();
}
match paths::symlink_or_copy(original, link) {
// As long as symlink_or_copy succeeded, ignore any create_dir_all error.
Ok(()) => Ok(()),
Err(err) => {
if err.kind() == io::ErrorKind::AlreadyExists {
// This is fine, a different simultaneous build script already
// created the same link or copy. The cxx_build target directory
// is laid out such that the same path never refers to two
// different targets during the same multi-crate build, so if
// some other build script already created the same path then we
// know it refers to the identical target that the current build
// script was trying to create.
Ok(())
} else {
// If create_dir_all and symlink_or_copy both failed, prefer the
// first error.
Err(Error::Fs(create_dir_error.unwrap_or(err)))
}
}
}
}
pub(crate) fn symlink_dir(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
let original = original.as_ref();
let link = link.as_ref();
let mut create_dir_error = None;
if fs::exists(link) {
best_effort_remove(link);
} else {
let parent = link.parent().unwrap();
create_dir_error = fs::create_dir_all(parent).err();
}
match fs::symlink_dir(original, link) {
// As long as symlink_dir succeeded, ignore any create_dir_all error.
Ok(()) => Ok(()),
// If create_dir_all and symlink_dir both failed, prefer the first error.
Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))),
}
}
fn best_effort_remove(path: &Path) {
use std::fs;
if cfg!(windows) {
// On Windows, the correct choice of remove_file vs remove_dir needs to
// be used according to what the symlink *points to*. Trying to use
// remove_file to remove a symlink which points to a directory fails
// with "Access is denied".
if let Ok(metadata) = fs::metadata(path) {
if metadata.is_dir() {
let _ = fs::remove_dir_all(path);
} else {
let _ = fs::remove_file(path);
}
} else if fs::symlink_metadata(path).is_ok() {
// The symlink might exist but be dangling, in which case there is
// no standard way to determine what "kind" of symlink it is. Try
// deleting both ways.
if fs::remove_dir_all(path).is_err() {
let _ = fs::remove_file(path);
}
}
} else {
// On non-Windows, we check metadata not following symlinks. All
// symlinks are removed using remove_file.
if let Ok(metadata) = fs::symlink_metadata(path) {
if metadata.is_dir() {
let _ = fs::remove_dir_all(path);
} else {
let _ = fs::remove_file(path);
}
}
}
}

67
vendor/cxx-build/src/paths.rs vendored Normal file
View file

@ -0,0 +1,67 @@
use crate::error::Result;
use crate::gen::fs;
use std::ffi::OsStr;
use std::path::{Component, Path, PathBuf};
pub(crate) fn manifest_dir() -> Result<PathBuf> {
crate::env_os("CARGO_MANIFEST_DIR").map(PathBuf::from)
}
pub(crate) fn out_dir() -> Result<PathBuf> {
crate::env_os("OUT_DIR").map(PathBuf::from)
}
// Given a path provided by the user, determines where generated files related
// to that path should go in our out dir. In particular we don't want to
// accidentally write generated code upward of our out dir, even if the user
// passed a path containing lots of `..` or an absolute path.
pub(crate) fn local_relative_path(path: &Path) -> PathBuf {
let mut rel_path = PathBuf::new();
for component in path.components() {
match component {
Component::Prefix(_) | Component::RootDir | Component::CurDir => {}
Component::ParentDir => drop(rel_path.pop()), // noop if empty
Component::Normal(name) => rel_path.push(name),
}
}
rel_path
}
pub(crate) trait PathExt {
fn with_appended_extension(&self, suffix: impl AsRef<OsStr>) -> PathBuf;
}
impl PathExt for Path {
fn with_appended_extension(&self, suffix: impl AsRef<OsStr>) -> PathBuf {
let mut file_name = self.file_name().unwrap().to_owned();
file_name.push(suffix);
self.with_file_name(file_name)
}
}
#[cfg(unix)]
pub(crate) use self::fs::symlink_file as symlink_or_copy;
#[cfg(windows)]
pub(crate) fn symlink_or_copy(
original: impl AsRef<Path>,
link: impl AsRef<Path>,
) -> fs::Result<()> {
// Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they
// require Developer Mode. If it fails, fall back to copying the file.
let original = original.as_ref();
let link = link.as_ref();
if fs::symlink_file(original, link).is_err() {
fs::copy(original, link)?;
}
Ok(())
}
#[cfg(not(any(unix, windows)))]
pub(crate) fn symlink_or_copy(
original: impl AsRef<Path>,
copy: impl AsRef<Path>,
) -> fs::Result<()> {
fs::copy(original, copy)?;
Ok(())
}

103
vendor/cxx-build/src/syntax/atom.rs vendored Normal file
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
}
}

302
vendor/cxx-build/src/syntax/attrs.rs vendored Normal file
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);
});
}
}
}

77
vendor/cxx-build/src/syntax/cfg.rs vendored Normal file
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)
}

733
vendor/cxx-build/src/syntax/check.rs vendored Normal file
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(),
}
}

81
vendor/cxx-build/src/syntax/derive.rs vendored Normal file
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),
},
];

46
vendor/cxx-build/src/syntax/doc.rs vendored Normal file
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)] });
}
}
}

98
vendor/cxx-build/src/syntax/error.rs vendored Normal file
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",
),
};

127
vendor/cxx-build/src/syntax/file.rs vendored Normal file
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)),
}
}
}

57
vendor/cxx-build/src/syntax/ident.rs vendored Normal file
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);
}
}
}
}

450
vendor/cxx-build/src/syntax/impls.rs vendored Normal file
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);
}
}

39
vendor/cxx-build/src/syntax/improper.rs vendored Normal file
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,
}
}
}

120
vendor/cxx-build/src/syntax/mangle.rs vendored Normal file
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/cxx-build/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/cxx-build/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,
}

64
vendor/cxx-build/src/syntax/names.rs vendored Normal file
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/cxx-build/src/syntax/parse.rs vendored Normal file

File diff suppressed because it is too large Load diff

36
vendor/cxx-build/src/syntax/pod.rs vendored Normal file
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 })
}

33
vendor/cxx-build/src/syntax/report.rs vendored Normal file
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)
}
}

46
vendor/cxx-build/src/syntax/resolve.rs vendored Normal file
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/cxx-build/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()
}
}

110
vendor/cxx-build/src/syntax/symbol.rs vendored Normal file
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
}

308
vendor/cxx-build/src/syntax/tokens.rs vendored Normal file
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);
}
}

51
vendor/cxx-build/src/syntax/toposort.rs vendored Normal file
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
}

312
vendor/cxx-build/src/syntax/trivial.rs vendored Normal file
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 }
}

285
vendor/cxx-build/src/syntax/types.rs vendored Normal file
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);
}

34
vendor/cxx-build/src/syntax/visit.rs vendored Normal file
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);
}
}
}
}

49
vendor/cxx-build/src/target.rs vendored Normal file
View file

@ -0,0 +1,49 @@
use std::env;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
pub(crate) enum TargetDir {
Path(PathBuf),
Unknown,
}
pub(crate) fn find_target_dir(out_dir: &Path) -> TargetDir {
if let Some(target_dir) = env::var_os("CARGO_TARGET_DIR") {
let target_dir = PathBuf::from(target_dir);
if target_dir.is_absolute() {
return TargetDir::Path(target_dir);
} else {
return TargetDir::Unknown;
};
}
// fs::canonicalize on Windows produces UNC paths which cl.exe is unable to
// handle in includes.
// https://github.com/rust-lang/rust/issues/42869
// https://github.com/alexcrichton/cc-rs/issues/169
let mut also_try_canonical = cfg!(not(windows));
let mut dir = out_dir.to_owned();
loop {
if dir.join(".rustc_info.json").exists()
|| dir.join("CACHEDIR.TAG").exists()
|| dir.file_name() == Some(OsStr::new("target"))
&& dir
.parent()
.map_or(false, |parent| parent.join("Cargo.toml").exists())
{
return TargetDir::Path(dir);
}
if dir.pop() {
continue;
}
if also_try_canonical {
if let Ok(canonical_dir) = out_dir.canonicalize() {
dir = canonical_dir;
also_try_canonical = false;
continue;
}
}
return TargetDir::Unknown;
}
}

50
vendor/cxx-build/src/vec.rs vendored Normal file
View file

@ -0,0 +1,50 @@
use crate::intern::{self, InternedString};
use std::path::Path;
pub trait InternedVec<T>
where
T: ?Sized,
{
fn vec(&self) -> Vec<&'static T>;
}
impl<T> InternedVec<T> for Vec<InternedString>
where
T: ?Sized + Element,
{
fn vec(&self) -> Vec<&'static T> {
self.iter().copied().map(Element::unintern).collect()
}
}
pub fn intern<T>(elements: &[&T]) -> Vec<InternedString>
where
T: ?Sized + Element,
{
elements.iter().copied().map(Element::intern).collect()
}
pub trait Element {
fn intern(&self) -> InternedString;
fn unintern(_: InternedString) -> &'static Self;
}
impl Element for str {
fn intern(&self) -> InternedString {
intern::intern(self)
}
fn unintern(interned: InternedString) -> &'static Self {
interned.str()
}
}
impl Element for Path {
fn intern(&self) -> InternedString {
intern::intern(&self.to_string_lossy())
}
fn unintern(interned: InternedString) -> &'static Self {
Path::new(interned.str())
}
}