use crate::expand;
use crate::syntax::file::Module;
use proc_macro2::TokenStream;
use quote::quote;
use syn::File;
fn bridge(cxx_bridge: TokenStream) -> String {
let module = syn::parse2::<Module>(cxx_bridge).unwrap();
let tokens = expand::bridge(module).unwrap();
let file = match syn::parse2::<File>(tokens.clone()) {
Ok(file) => file,
Err(err) => {
eprintln!("The code below is syntactically invalid: {err}:");
eprintln!("{tokens}");
panic!("`expand::bridge` should generate syntactically valid code");
}
};
let pretty = prettyplease::unparse(&file);
eprintln!("{0:/<80}\n{pretty}{0:/<80}", "");
pretty
}
#[test]
fn test_unique_ptr_with_elided_lifetime_implicit_impl() {
let rs = bridge(quote! {
mod ffi {
unsafe extern "C++" {
type Borrowed<'a>;
fn borrowed(arg: &i32) -> UniquePtr<Borrowed>;
}
}
});
assert!(rs.contains("pub fn borrowed(arg: &i32) -> ::cxx::UniquePtr<Borrowed>"));
assert!(rs.contains("unsafe impl<'a> ::cxx::ExternType for Borrowed<'a> {"));
assert!(rs.contains("unsafe impl<'a> ::cxx::memory::UniquePtrTarget for Borrowed<'a> {"));
assert!(!rs.contains("unsafe impl ::cxx::ExternType for Borrowed {"));
assert!(!rs.contains("unsafe impl ::cxx::memory::UniquePtrTarget for Borrowed {"));
assert!(!rs.contains("unsafe impl ::cxx::ExternType for Borrowed<'_> {"));
assert!(!rs.contains("unsafe impl ::cxx::memory::UniquePtrTarget for Borrowed<'_> {"));
}
#[test]
fn test_unique_ptr_lifetimes_from_explicit_impl() {
let rs = bridge(quote! {
mod ffi {
unsafe extern "C++" {
type Borrowed<'a>;
}
impl<'b> UniquePtr<Borrowed<'c>> {}
}
});
assert!(rs.contains("unsafe impl<'a> ::cxx::ExternType for Borrowed<'a>"));
assert!(rs.contains("unsafe impl<'b> ::cxx::memory::UniquePtrTarget for Borrowed<'c>"));
}
#[test]
fn test_vec_string() {
let rs = bridge(quote! {
mod ffi {
extern "Rust" {
fn foo() -> Vec<String>;
}
}
});
assert!(rs.contains("__return: *mut ::cxx::private::RustVec<::cxx::alloc::string::String>"));
assert!(rs.contains("fn __foo() -> ::cxx::alloc::vec::Vec<::cxx::alloc::string::String>"));
let rs = bridge(quote! {
mod ffi {
extern "Rust" {
fn foo(v: &Vec<String>);
}
}
});
assert!(rs.contains("v: &::cxx::private::RustVec<::cxx::alloc::string::String>"));
assert!(rs.contains("fn __foo(v: &::cxx::alloc::vec::Vec<::cxx::alloc::string::String>)"));
}
#[test]
fn test_mangling_covers_cpp_namespace_of_vec_elements() {
let rs = bridge(quote! {
mod ffi {
#[namespace = "test_namespace"]
struct Context { x: i32 }
impl Vec<Context> {}
}
});
assert!(rs.contains("export_name = \"cxxbridge1$rust_vec$test_namespace$Context$set_len\""));
}
#[test]
fn test_struct_with_lifetime() {
let rs = bridge(quote! {
mod ffi {
struct StructWithLifetime<'a> {
s: &'a str,
}
extern "Rust" {
fn f(_: UniquePtr<StructWithLifetime<>>);
}
}
});
assert!(rs.contains("impl<'a> ::cxx::memory::UniquePtrTarget for StructWithLifetime<'a> {"));
assert!(rs.contains("pub struct StructWithLifetime<'a> {"));
assert!(rs.contains("cast::<StructWithLifetime<'a>>()"));
assert!(rs.contains("fn __f(arg0: ::cxx::UniquePtr<StructWithLifetime>) {"));
assert!(rs.contains("impl<'a> self::Drop for super::StructWithLifetime<'a>"));
}
#[test]
fn test_original_lifetimes_used_in_impls() {
let rs = bridge(quote! {
mod ffi {
struct Context<'sess> {
session: &'sess str,
}
struct Server<'srv> {
ctx: UniquePtr<Context<'srv>>,
}
struct Client<'clt> {
ctx: UniquePtr<Context<'clt>>,
}
}
});
assert!(rs.contains("impl<'sess> ::cxx::memory::UniquePtrTarget for Context<'sess> {"));
}
#[test]
fn test_vec_of_box() {
let rs = bridge(quote! {
mod ffi {
extern "Rust" {
type R;
fn foo() -> Vec<Box<R>>;
}
}
});
assert!(rs.contains("unsafe impl ::cxx::private::ImplBox for R {}"));
assert!(rs.contains("export_name = \"cxxbridge1$box$R$drop\""));
assert!(rs.contains("unsafe impl ::cxx::private::ImplVec for ::cxx::alloc::boxed::Box<R> {}"));
assert!(rs.contains("export_name = \"cxxbridge1$rust_vec$box$R$set_len\""));
assert!(rs.contains("__return: *mut ::cxx::private::RustVec<::cxx::alloc::boxed::Box<R>>"));
assert!(rs.contains("fn __foo() -> ::cxx::alloc::vec::Vec<::cxx::alloc::boxed::Box<R>>"));
}