use super::*;
fn build(fns: &[&str]) -> String {
let loc = SourceLocation::default();
let mut items: Vec<(syn::Item, SourceLocation)> = vec![
(
syn::Item::Struct(syn::parse_quote!(
pub struct Thing {
pub v: u64,
}
)),
loc.clone(),
),
(
syn::Item::Struct(syn::parse_quote!(
pub struct Other {
pub v: u64,
}
)),
loc.clone(),
),
(syn::Item::Struct(error_struct()), loc.clone()),
];
let mut idents: Vec<syn::Ident> = Vec::new();
for src in fns {
let f: syn::ItemFn = syn::parse_str(src).expect("test fn");
idents.push(f.sig.ident.clone());
items.push((syn::Item::Fn(f), loc.clone()));
}
let registry =
crate::test_util::reg_from_items(declare_referenced(items)).expect("index items");
let mut cbindgen = CbindgenBuilder::new()
.source_module(syn::parse_quote!(myflat))
.free_memory_function("my_free")
.mangle_type_name(|base| format!("{base}_t"))
.mangle_destructor(|base| format!("{base}_drop"))
.mangle_function(|n| n.to_string())
.opaque_ptr(syn::parse_quote!(Thing))
.opaque_ptr(syn::parse_quote!(Other))
.data_struct(syn::parse_quote!(Error))
.error();
for id in idents {
cbindgen = cbindgen.function(syn::parse_quote!(#id)).panic();
}
write(cbindgen, registry, "cbindgen_aliasing")
}
#[test]
fn preflight_is_emitted_exactly_for_the_predicate() {
for (src, want) in [
(
"pub fn f(a: Thing, b: Thing) -> Result<u64, Error> { unimplemented!() }",
true,
),
(
"pub fn f(a: Thing, b: &Thing) -> Result<u64, Error> { unimplemented!() }",
true,
),
(
"pub fn f(a: &mut Thing, b: &Thing) -> Result<u64, Error> { unimplemented!() }",
true,
),
(
"pub fn f(a: &mut Thing, b: &mut Thing) -> Result<u64, Error> { unimplemented!() }",
true,
),
(
"pub fn f(a: &Thing, b: &Thing) -> Result<u64, Error> { unimplemented!() }",
false,
),
(
"pub fn f(a: Thing, b: u64) -> Result<u64, Error> { unimplemented!() }",
false,
),
(
"pub fn f(a: Thing, b: Other) -> Result<u64, Error> { unimplemented!() }",
false,
),
] {
let src_gen = build(&[src]);
assert_eq!(
src_gen.contains("aliasing arguments"),
want,
"preflight emission wrong for `{src}`:\n{src_gen}"
);
}
}
#[test]
fn option_and_bare_share_a_resource_domain() {
let src =
build(&["pub fn f(a: Thing, b: Option<Thing>) -> Result<u64, Error> { unimplemented!() }"]);
assert!(src.contains("aliasing arguments"), "{src}");
let compact: String = src.split_whitespace().collect();
assert!(
compact.contains("if!(aas*const()).is_null()&&(aas*const())==(bas*const())"),
"{src}"
);
}
#[test]
fn preflight_precedes_every_conversion() {
let src = build(&["pub fn f(a: Thing, b: Thing) -> Result<u64, Error> { unimplemented!() }"]);
let alias_at = src.find("aliasing arguments").expect("preflight emitted");
let first_conv = src
.find("__cbg_in_Thing(a)")
.expect("input conversion emitted");
assert!(
alias_at < first_conv,
"preflight must precede the first conversion:\n{src}"
);
}
#[test]
fn every_qualifying_pair_is_checked() {
let src = build(&[
"pub fn f(a: Thing, b: &Thing, c: &Thing) -> Result<u64, Error> { unimplemented!() }",
]);
let compact: String = src.split_whitespace().collect();
assert!(compact.contains("(aas*const())==(bas*const())"), "{src}");
assert!(compact.contains("(aas*const())==(cas*const())"), "{src}");
assert!(!compact.contains("(bas*const())==(cas*const())"), "{src}");
}