// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::{ builder_modifiers::{ make_clang_arg_adder, make_clang_optional_arg_adder, make_cpp17_adder, EnableAutodiscover, SetSuppressSystemHeaders, }, code_checkers::{ make_error_finder, make_rust_code_finder, make_string_finder, CppMatcher, NoSystemHeadersChecker, }, }; use autocxx_integration_tests::{ directives_from_lists, do_run_test, do_run_test_manual, run_generate_all_test, run_test, run_test_ex, run_test_expect_fail, run_test_expect_fail_ex, BuilderModifier, TestError, }; use indoc::indoc; use itertools::Itertools; use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::{parse_quote, Token}; use test_log::test; #[test] fn test_return_void() { let cxx = indoc! {" void do_nothing() { } "}; let hdr = indoc! {" void do_nothing(); "}; let rs = quote! { ffi::do_nothing(); }; run_test(cxx, hdr, rs, &["do_nothing"], &[]); } #[test] fn test_two_funcs() { let cxx = indoc! {" void do_nothing1() { } void do_nothing2() { } "}; let hdr = indoc! {" void do_nothing1(); void do_nothing2(); "}; let rs = quote! { ffi::do_nothing1(); ffi::do_nothing2(); }; run_test(cxx, hdr, rs, &["do_nothing1", "do_nothing2"], &[]); } #[test] fn test_two_funcs_with_definition() { // Test to ensure C++ header isn't included twice let cxx = indoc! {" void do_nothing1() { } void do_nothing2() { } "}; let hdr = indoc! {" struct Bob { int a; }; void do_nothing1(); void do_nothing2(); "}; let rs = quote! { ffi::do_nothing1(); ffi::do_nothing2(); }; run_test(cxx, hdr, rs, &["do_nothing1", "do_nothing2"], &[]); } #[test] fn test_return_i32() { let cxx = indoc! {" uint32_t give_int() { return 5; } "}; let hdr = indoc! {" #include uint32_t give_int(); "}; let rs = quote! { assert_eq!(ffi::give_int(), 5); }; run_test(cxx, hdr, rs, &["give_int"], &[]); } #[test] fn test_take_i32() { let cxx = indoc! {" uint32_t take_int(uint32_t a) { return a + 3; } "}; let hdr = indoc! {" #include uint32_t take_int(uint32_t a); "}; let rs = quote! { assert_eq!(ffi::take_int(3), 6); }; run_test(cxx, hdr, rs, &["take_int"], &[]); } #[test] fn test_nested_module() { let cxx = indoc! {" void do_nothing() { } "}; let hdr = indoc! {" void do_nothing(); "}; let hexathorpe = Token![#](Span::call_site()); let unexpanded_rust = quote! { mod a { use autocxx::prelude::*; include_cpp!( #hexathorpe include "input.h" generate!("do_nothing") safety!(unsafe) ); pub use ffi::*; } fn main() { a::do_nothing(); } }; do_run_test_manual(cxx, hdr, unexpanded_rust, None, None).unwrap(); } #[test] #[ignore] // https://github.com/google/autocxx/issues/681 #[cfg(target_pointer_width = "64")] fn test_return_big_ints() { let cxx = indoc! {" "}; let hdr = indoc! {" #include inline uint32_t give_u32() { return 5; } inline uint64_t give_u64() { return 5; } inline int32_t give_i32() { return 5; } inline int64_t give_i64() { return 5; } inline __int128 give_i128() { return 5; } "}; let rs = quote! { assert_eq!(ffi::give_u32(), 5); assert_eq!(ffi::give_u64(), 5); assert_eq!(ffi::give_i32(), 5); assert_eq!(ffi::give_i64(), 5); assert_eq!(ffi::give_i128(), 5); }; run_test( cxx, hdr, rs, &["give_u32", "give_u64", "give_i32", "give_i64", "give_i128"], &[], ); } #[test] #[ignore] // because cxx doesn't support unique_ptrs to primitives. fn test_give_up_int() { let cxx = indoc! {" std::unique_ptr give_up() { return std::make_unique(12); } "}; let hdr = indoc! {" #include #include std::unique_ptr give_up(); "}; let rs = quote! { assert_eq!(ffi::give_up().as_ref().unwrap(), 12); }; run_test(cxx, hdr, rs, &["give_up"], &[]); } #[test] #[ignore] // because we don't yet implement UniquePtr etc. for autocxx::c_int and friends fn test_give_up_ctype() { let cxx = indoc! {" std::unique_ptr give_up() { return std::make_unique(12); } "}; let hdr = indoc! {" #include std::unique_ptr give_up(); "}; let rs = quote! { assert_eq!(ffi::give_up().as_ref().unwrap(), autocxx::c_int(12)); }; run_test(cxx, hdr, rs, &["give_up"], &[]); } #[test] fn test_give_string_up() { let cxx = indoc! {" std::unique_ptr give_str_up() { return std::make_unique(\"Bob\"); } "}; let hdr = indoc! {" #include #include std::unique_ptr give_str_up(); "}; let rs = quote! { assert_eq!(ffi::give_str_up().as_ref().unwrap().to_str().unwrap(), "Bob"); }; run_test(cxx, hdr, rs, &["give_str_up"], &[]); } #[test] fn test_give_string_plain() { let cxx = indoc! {" std::string give_str() { return std::string(\"Bob\"); } "}; let hdr = indoc! {" #include std::string give_str(); "}; let rs = quote! { assert_eq!(ffi::give_str().as_ref().unwrap(), "Bob"); }; run_test(cxx, hdr, rs, &["give_str"], &[]); } #[test] fn test_cycle_string_up() { let cxx = indoc! {" std::unique_ptr give_str_up() { return std::make_unique(\"Bob\"); } uint32_t take_str_up(std::unique_ptr a) { return a->length(); } "}; let hdr = indoc! {" #include #include #include std::unique_ptr give_str_up(); uint32_t take_str_up(std::unique_ptr a); "}; let rs = quote! { let s = ffi::give_str_up(); assert_eq!(ffi::take_str_up(s), 3); }; run_test(cxx, hdr, rs, &["give_str_up", "take_str_up"], &[]); } #[test] fn test_cycle_string() { let cxx = indoc! {" std::string give_str() { return std::string(\"Bob\"); } uint32_t take_str(std::string a) { return a.length(); } "}; let hdr = indoc! {" #include #include std::string give_str(); uint32_t take_str(std::string a); "}; let rs = quote! { let s = ffi::give_str(); assert_eq!(ffi::take_str(s), 3); }; let generate = &["give_str", "take_str"]; run_test(cxx, hdr, rs, generate, &[]); } #[test] fn test_cycle_string_by_ref() { let cxx = indoc! {" std::unique_ptr give_str() { return std::make_unique(\"Bob\"); } uint32_t take_str(const std::string& a) { return a.length(); } "}; let hdr = indoc! {" #include #include #include std::unique_ptr give_str(); uint32_t take_str(const std::string& a); "}; let rs = quote! { let s = ffi::give_str(); assert_eq!(ffi::take_str(s.as_ref().unwrap()), 3); }; let generate = &["give_str", "take_str"]; run_test(cxx, hdr, rs, generate, &[]); } #[test] fn test_cycle_string_by_mut_ref() { let cxx = indoc! {" std::unique_ptr give_str() { return std::make_unique(\"Bob\"); } uint32_t take_str(std::string& a) { return a.length(); } "}; let hdr = indoc! {" #include #include #include std::unique_ptr give_str(); uint32_t take_str(std::string& a); "}; let rs = quote! { let mut s = ffi::give_str(); assert_eq!(ffi::take_str(s.as_mut().unwrap()), 3); }; let generate = &["give_str", "take_str"]; run_test(cxx, hdr, rs, generate, &[]); } #[test] fn test_give_pod_by_value() { let cxx = indoc! {" Bob give_bob() { Bob a; a.a = 3; a.b = 4; return a; } "}; let hdr = indoc! {" #include struct Bob { uint32_t a; uint32_t b; }; Bob give_bob(); "}; let rs = quote! { assert_eq!(ffi::give_bob().b, 4); }; run_test(cxx, hdr, rs, &["give_bob"], &["Bob"]); } #[test] fn test_give_pod_class_by_value() { let cxx = indoc! {" Bob give_bob() { Bob a; a.a = 3; a.b = 4; return a; } "}; let hdr = indoc! {" #include class Bob { public: uint32_t a; uint32_t b; }; Bob give_bob(); "}; let rs = quote! { assert_eq!(ffi::give_bob().b, 4); }; run_test(cxx, hdr, rs, &["give_bob"], &["Bob"]); } #[test] fn test_give_pod_by_up() { let cxx = indoc! {" std::unique_ptr give_bob() { auto a = std::make_unique(); a->a = 3; a->b = 4; return a; } "}; let hdr = indoc! {" #include #include struct Bob { uint32_t a; uint32_t b; }; std::unique_ptr give_bob(); "}; let rs = quote! { assert_eq!(ffi::give_bob().as_ref().unwrap().b, 4); }; run_test(cxx, hdr, rs, &["give_bob"], &["Bob"]); } #[test] fn test_take_pod_by_value() { let cxx = indoc! {" uint32_t take_bob(Bob a) { return a.a; } "}; let hdr = indoc! {" #include struct Bob { uint32_t a; uint32_t b; }; uint32_t take_bob(Bob a); "}; let rs = quote! { let a = ffi::Bob { a: 12, b: 13 }; assert_eq!(ffi::take_bob(a), 12); }; run_test(cxx, hdr, rs, &["take_bob"], &["Bob"]); } #[test] fn test_negative_take_as_pod_with_destructor() { let cxx = indoc! {" uint32_t take_bob(Bob a) { return a.a; } "}; let hdr = indoc! {" #include struct Bob { uint32_t a; uint32_t b; inline ~Bob() {} }; uint32_t take_bob(Bob a); "}; let rs = quote! { let a = ffi::Bob { a: 12, b: 13 }; assert_eq!(ffi::take_bob(a), 12); }; run_test_expect_fail(cxx, hdr, rs, &["take_bob"], &["Bob"]); } #[test] fn test_negative_take_as_pod_with_move_constructor() { let cxx = indoc! {" uint32_t take_bob(Bob a) { return a.a; } "}; let hdr = indoc! {" #include #include struct Bob { uint32_t a; uint32_t b; inline Bob(Bob&& other_bob) {} }; uint32_t take_bob(Bob a); "}; let rs = quote! { let a = ffi::Bob { a: 12, b: 13 }; assert_eq!(ffi::take_bob(a), 12); }; run_test_expect_fail(cxx, hdr, rs, &["take_bob"], &["Bob"]); } #[ignore] // https://github.com/google/autocxx/issues/1252 #[test] fn test_take_as_pod_with_is_relocatable() { let cxx = indoc! {" uint32_t take_bob(Bob a) { return a.a; } "}; let hdr = indoc! {" #include #include struct Bob { uint32_t a; uint32_t b; inline Bob() {} inline ~Bob() {} inline Bob(Bob&& other_bob) { a = other_bob.a; b = other_bob.b; } using IsRelocatable = std::true_type; }; uint32_t take_bob(Bob a); "}; let rs = quote! { let a = ffi::Bob { a: 12, b: 13 }; assert_eq!(ffi::take_bob(a), 12); }; run_test(cxx, hdr, rs, &["take_bob"], &["Bob"]); } #[test] fn test_take_pod_by_ref() { let cxx = indoc! {" uint32_t take_bob(const Bob& a) { return a.a; } "}; let hdr = indoc! {" #include struct Bob { uint32_t a; uint32_t b; }; uint32_t take_bob(const Bob& a); "}; let rs = quote! { let a = ffi::Bob { a: 12, b: 13 }; assert_eq!(ffi::take_bob(&a), 12); }; run_test(cxx, hdr, rs, &["take_bob"], &["Bob"]); } #[test] fn test_take_pod_by_ref_and_ptr() { let cxx = indoc! {" uint32_t take_bob_ref(const Bob& a) { return a.a; } uint32_t take_bob_ptr(const Bob* a) { return a->a; } "}; let hdr = indoc! {" #include struct Bob { uint32_t a; uint32_t b; }; uint32_t take_bob_ref(const Bob& a); uint32_t take_bob_ptr(const Bob* a); "}; let rs = quote! { let a = ffi::Bob { a: 12, b: 13 }; assert_eq!(ffi::take_bob_ref(&a), 12); }; run_test(cxx, hdr, rs, &["take_bob_ref", "take_bob_ptr"], &["Bob"]); } #[test] fn test_return_pod_by_ref_and_ptr() { let hdr = indoc! {" #include struct B { uint32_t a; }; struct A { B b; }; inline const B& return_b_ref(const A& a) { return a.b; } inline const B* return_b_ptr(const A& a) { return &a.b; } "}; let rs = quote! { let a = ffi::A { b: ffi::B { a: 3 } }; assert_eq!(ffi::return_b_ref(&a).a, 3); let b_ptr = ffi::return_b_ptr(&a); assert_eq!(unsafe { b_ptr.as_ref() }.unwrap().a, 3); }; run_test("", hdr, rs, &["return_b_ref", "return_b_ptr"], &["A", "B"]); } #[test] fn test_take_pod_by_mut_ref() { let cxx = indoc! {" uint32_t take_bob(Bob& a) { a.b = 14; return a.a; } "}; let hdr = indoc! {" #include struct Bob { uint32_t a; uint32_t b; }; uint32_t take_bob(Bob& a); "}; let rs = quote! { let mut a = Box::pin(ffi::Bob { a: 12, b: 13 }); assert_eq!(ffi::take_bob(a.as_mut()), 12); assert_eq!(a.b, 14); }; run_test(cxx, hdr, rs, &["take_bob"], &["Bob"]); } #[test] fn test_take_nested_pod_by_value() { let cxx = indoc! {" uint32_t take_bob(Bob a) { return a.a; } "}; let hdr = indoc! {" #include struct Phil { uint32_t d; }; struct Bob { uint32_t a; uint32_t b; Phil c; }; uint32_t take_bob(Bob a); "}; let rs = quote! { let a = ffi::Bob { a: 12, b: 13, c: ffi::Phil { d: 4 } }; assert_eq!(ffi::take_bob(a), 12); }; // Should be no need to allowlist Phil below run_test(cxx, hdr, rs, &["take_bob"], &["Bob"]); } #[test] fn test_take_nonpod_by_value() { let cxx = indoc! {" Bob::Bob(uint32_t a0, uint32_t b0) : a(a0), b(b0) {} uint32_t take_bob(Bob a) { return a.a; } "}; let hdr = indoc! {" #include #include struct Bob { Bob(uint32_t a, uint32_t b); uint32_t a; uint32_t b; std::string reason_why_this_is_nonpod; }; uint32_t take_bob(Bob a); "}; let rs = quote! { let a = ffi::Bob::new(12, 13).within_unique_ptr(); assert_eq!(ffi::take_bob(a), 12); }; run_test(cxx, hdr, rs, &["take_bob", "Bob"], &[]); } #[test] fn test_take_nonpod_by_ref() { let cxx = indoc! {" uint32_t take_bob(const Bob& a) { return a.a; } std::unique_ptr make_bob(uint32_t a) { auto b = std::make_unique(); b->a = a; return b; } "}; let hdr = indoc! {" #include #include struct Bob { uint32_t a; }; std::unique_ptr make_bob(uint32_t a); uint32_t take_bob(const Bob& a); "}; let rs = quote! { let a = ffi::make_bob(12); assert_eq!(ffi::take_bob(&a), 12); }; run_test(cxx, hdr, rs, &["take_bob", "Bob", "make_bob"], &[]); } #[test] fn test_take_nonpod_by_up() { let cxx = indoc! {" uint32_t take_bob(std::unique_ptr a) { return a->a; } std::unique_ptr make_bob(uint32_t a) { auto b = std::make_unique(); b->a = a; return b; } "}; let hdr = indoc! {" #include #include struct Bob { uint32_t a; }; struct NOP { inline void take_bob(); }; std::unique_ptr make_bob(uint32_t a); uint32_t take_bob(std::unique_ptr a); "}; let rs = quote! { let a = ffi::make_bob(12); assert_eq!(ffi::take_bob(a), 12); }; run_test(cxx, hdr, rs, &["take_bob", "Bob", "make_bob", "NOP"], &[]); } #[test] fn test_take_nonpod_by_ptr_simple() { let cxx = indoc! {" uint32_t take_bob(const Bob* a) { return a->a; } std::unique_ptr make_bob(uint32_t a) { auto b = std::make_unique(); b->a = a; return b; } "}; let hdr = indoc! {" #include #include struct Bob { uint32_t a; }; std::unique_ptr make_bob(uint32_t a); uint32_t take_bob(const Bob* a); "}; let rs = quote! { let a = ffi::make_bob(12); let a_ptr = a.into_raw(); assert_eq!(unsafe { ffi::take_bob(a_ptr) }, 12); unsafe { cxx::UniquePtr::from_raw(a_ptr) }; // so we drop }; run_test(cxx, hdr, rs, &["take_bob", "Bob", "make_bob"], &[]); } #[test] fn test_take_nonpod_by_ptr_in_method() { let hdr = indoc! {" #include #include struct Bob { uint32_t a; }; #include class A { public: A() {}; uint32_t take_bob(const Bob* a) const { return a->a; } std::unique_ptr make_bob(uint32_t a) const { auto b = std::make_unique(); b->a = a; return b; } uint16_t a; }; "}; let rs = quote! { let a = ffi::A::new().within_unique_ptr(); let b = a.as_ref().unwrap().make_bob(12); let b_ptr = b.into_raw(); assert_eq!(unsafe { a.as_ref().unwrap().take_bob(b_ptr) }, 12); unsafe { cxx::UniquePtr::from_raw(b_ptr) }; // so we drop }; run_test("", hdr, rs, &["A", "Bob"], &[]); } #[test] fn test_take_nonpod_by_ptr_in_wrapped_method() { let hdr = indoc! {" #include #include struct C { C() {} uint32_t a; }; struct Bob { uint32_t a; }; class A { public: A() {}; uint32_t take_bob(const Bob* a, C) const { return a->a; } std::unique_ptr make_bob(uint32_t a) const { auto b = std::make_unique(); b->a = a; return b; } uint16_t a; }; "}; let rs = quote! { let a = ffi::A::new().within_unique_ptr(); let c = ffi::C::new().within_unique_ptr(); let b = a.as_ref().unwrap().make_bob(12); let b_ptr = b.into_raw(); assert_eq!(unsafe { a.as_ref().unwrap().take_bob(b_ptr, c) }, 12); unsafe { cxx::UniquePtr::from_raw(b_ptr) }; // so we drop }; run_test("", hdr, rs, &["A", "Bob", "C"], &[]); } fn run_char_test(builder_modifier: Option) { let hdr = indoc! {" #include #include struct C { C() { test = \"hi\"; } uint32_t a; const char* test; }; class A { public: A() {}; uint32_t take_char(const char* a, C) const { return a[0]; } const char* make_char(C extra) const { return extra.test; } uint16_t a; }; "}; let rs = quote! { let a = ffi::A::new().within_unique_ptr(); let c1 = ffi::C::new().within_unique_ptr(); let c2 = ffi::C::new().within_unique_ptr(); let ch = a.as_ref().unwrap().make_char(c1); assert_eq!(unsafe { ch.as_ref()}.unwrap(), &104i8); assert_eq!(unsafe { a.as_ref().unwrap().take_char(ch, c2) }, 104); }; run_test_ex( "", hdr, rs, directives_from_lists(&["A", "C"], &[], None), builder_modifier, None, None, ); } #[test] fn test_take_char_by_ptr_in_wrapped_method() { run_char_test(None) } #[test] fn test_take_char_by_ptr_in_wrapped_method_with_unsigned_chars() { run_char_test(make_clang_arg_adder(&["-funsigned-char"])) } #[test] fn test_take_nonpod_by_mut_ref() { let cxx = indoc! {" uint32_t take_bob(Bob& a) { return a.a; } std::unique_ptr make_bob(uint32_t a) { auto b = std::make_unique(); b->a = a; return b; } "}; let hdr = indoc! {" #include #include struct Bob { uint32_t a; }; std::unique_ptr make_bob(uint32_t a); uint32_t take_bob(Bob& a); "}; let rs = quote! { let mut a = ffi::make_bob(12); assert_eq!(ffi::take_bob(a.pin_mut()), 12); }; // TODO confirm that the object really was mutated by C++ in this // and similar tests. run_test(cxx, hdr, rs, &["take_bob", "Bob", "make_bob"], &[]); } #[test] fn test_return_nonpod_by_value() { let cxx = indoc! {" Bob::Bob(uint32_t a0, uint32_t b0) : a(a0), b(b0) {} Bob give_bob(uint32_t a) { Bob c(a, 44); return c; } uint32_t take_bob(std::unique_ptr a) { return a->a; } "}; let hdr = indoc! {" #include #include struct Bob { Bob(uint32_t a, uint32_t b); uint32_t a; uint32_t b; }; Bob give_bob(uint32_t a); uint32_t take_bob(std::unique_ptr a); "}; let rs = quote! { let a = ffi::give_bob(13).within_unique_ptr(); assert_eq!(ffi::take_bob(a), 13); }; run_test(cxx, hdr, rs, &["take_bob", "give_bob", "Bob"], &[]); } #[test] fn test_get_str_by_up() { let cxx = indoc! {" std::unique_ptr get_str() { return std::make_unique(\"hello\"); } "}; let hdr = indoc! {" #include #include std::unique_ptr get_str(); "}; let rs = quote! { assert_eq!(ffi::get_str().as_ref().unwrap(), "hello"); }; run_test(cxx, hdr, rs, &["get_str"], &[]); } #[test] fn test_get_str_by_value() { let cxx = indoc! {" std::string get_str() { return \"hello\"; } "}; let hdr = indoc! {" #include std::string get_str(); "}; let rs = quote! { assert_eq!(ffi::get_str().as_ref().unwrap(), "hello"); }; run_test(cxx, hdr, rs, &["get_str"], &[]); } #[test] fn test_cycle_nonpod_with_str_by_ref() { let cxx = indoc! {" uint32_t take_bob(const Bob& a) { return a.a; } std::unique_ptr make_bob() { auto a = std::make_unique(); a->a = 32; a->b = \"hello\"; return a; } "}; let hdr = indoc! {" #include #include #include struct Bob { uint32_t a; std::string b; }; uint32_t take_bob(const Bob& a); std::unique_ptr make_bob(); "}; let rs = quote! { let a = ffi::make_bob(); assert_eq!(ffi::take_bob(a.as_ref().unwrap()), 32); }; run_test(cxx, hdr, rs, &["take_bob", "Bob", "make_bob"], &[]); } #[test] fn test_make_up() { let cxx = indoc! {" Bob::Bob() : a(3) { } uint32_t take_bob(const Bob& a) { return a.a; } "}; let hdr = indoc! {" #include class Bob { public: Bob(); uint32_t a; }; uint32_t take_bob(const Bob& a); "}; let rs = quote! { let a = ffi::Bob::new().within_unique_ptr(); // TODO test with all sorts of arguments. assert_eq!(ffi::take_bob(a.as_ref().unwrap()), 3); }; run_test(cxx, hdr, rs, &["Bob", "take_bob"], &[]); } #[test] fn test_make_up_with_args() { let cxx = indoc! {" Bob::Bob(uint32_t a0, uint32_t b0) : a(a0), b(b0) {} uint32_t take_bob(const Bob& a) { return a.a; } "}; let hdr = indoc! {" #include struct Bob { Bob(uint32_t a, uint32_t b); uint32_t a; uint32_t b; }; uint32_t take_bob(const Bob& a); "}; let rs = quote! { let a = ffi::Bob::new(12, 13).within_unique_ptr(); assert_eq!(ffi::take_bob(a.as_ref().unwrap()), 12); }; run_test(cxx, hdr, rs, &["take_bob", "Bob"], &[]); } #[test] #[ignore] // because we don't support unique_ptrs to primitives fn test_make_up_int() { let cxx = indoc! {" Bob::Bob(uint32_t a) : b(a) { } "}; let hdr = indoc! {" #include class Bob { public: Bob(uint32_t a); uint32_t b; }; "}; let rs = quote! { let a = ffi::Bob::new(3).within_unique_ptr(); assert_eq!(a.as_ref().unwrap().b, 3); }; run_test(cxx, hdr, rs, &["Bob"], &[]); } #[test] fn test_enum_with_funcs() { let cxx = indoc! {" Bob give_bob() { return Bob::BOB_VALUE_2; } "}; let hdr = indoc! {" #include enum Bob { BOB_VALUE_1, BOB_VALUE_2, }; Bob give_bob(); "}; let rs = quote! { let a = ffi::Bob::BOB_VALUE_2; let b = ffi::give_bob(); assert!(a == b); }; run_test(cxx, hdr, rs, &["Bob", "give_bob"], &[]); } #[test] fn test_re_export() { let cxx = indoc! {" Bob give_bob() { return Bob::BOB_VALUE_2; } "}; let hdr = indoc! {" #include enum Bob { BOB_VALUE_1, BOB_VALUE_2, }; Bob give_bob(); "}; let rs = quote! { let a = ffi::Bob::BOB_VALUE_2; let b = ffi::give_bob(); assert!(a == b); }; run_test_ex( cxx, hdr, rs, directives_from_lists(&["Bob", "give_bob"], &[], None), None, None, Some(quote! { pub use ffi::Bob; }), ); } #[test] fn test_enum_no_funcs() { let cxx = indoc! {" "}; let hdr = indoc! {" enum Bob { BOB_VALUE_1, BOB_VALUE_2, }; "}; let rs = quote! { let a = ffi::Bob::BOB_VALUE_1; let b = ffi::Bob::BOB_VALUE_2; assert!(a != b); }; run_test(cxx, hdr, rs, &["Bob"], &[]); } #[test] fn test_enum_with_funcs_as_pod() { let cxx = indoc! {" Bob give_bob() { return Bob::BOB_VALUE_2; } "}; let hdr = indoc! {" #include enum Bob { BOB_VALUE_1, BOB_VALUE_2, }; Bob give_bob(); "}; let rs = quote! { let a = ffi::Bob::BOB_VALUE_2; let b = ffi::give_bob(); assert!(a == b); }; run_test(cxx, hdr, rs, &["give_bob"], &["Bob"]); } #[test] // works, but causes compile warnings fn test_take_pod_class_by_value() { let cxx = indoc! {" uint32_t take_bob(Bob a) { return a.a; } "}; let hdr = indoc! {" #include class Bob { public: uint32_t a; uint32_t b; }; uint32_t take_bob(Bob a); "}; let rs = quote! { let a = ffi::Bob { a: 12, b: 13 }; assert_eq!(ffi::take_bob(a), 12); }; run_test(cxx, hdr, rs, &["take_bob"], &["Bob"]); } #[test] fn test_pod_method() { let cxx = indoc! {" uint32_t Bob::get_bob() const { return a; } "}; let hdr = indoc! {" #include struct Bob { public: uint32_t a; uint32_t b; uint32_t get_bob() const; }; "}; let rs = quote! { let a = ffi::Bob { a: 12, b: 13 }; assert_eq!(a.get_bob(), 12); }; run_test(cxx, hdr, rs, &[], &["Bob"]); } #[test] #[ignore] // https://github.com/google/autocxx/issues/723 fn test_constructors_for_specialized_types() { // bindgen sometimes makes such opaque types as type Bob = u32[2]; let hdr = indoc! {" #include template class A { uint32_t foo() { return 12; }; private: T a[2]; }; typedef A B; typedef B C; "}; let rs = quote! { let a = ffi::C::new().within_unique_ptr(); assert_eq!(a.foo(), 12); }; run_test("", hdr, rs, &["C"], &[]); } #[test] fn test_pod_mut_method() { let cxx = indoc! {" uint32_t Bob::get_bob() { return a; } "}; let hdr = indoc! {" #include struct Bob { public: uint32_t a; uint32_t b; uint32_t get_bob(); }; "}; let rs = quote! { let mut a = Box::pin(ffi::Bob { a: 12, b: 13 }); assert_eq!(a.as_mut().get_bob(), 12); }; run_test(cxx, hdr, rs, &[], &["Bob"]); } #[test] fn test_define_int() { let cxx = indoc! {" "}; let hdr = indoc! {" #define BOB 3 "}; let rs = quote! { assert_eq!(ffi::BOB, 3); }; run_test(cxx, hdr, rs, &["BOB"], &[]); } #[test] fn test_define_str() { let cxx = indoc! {" "}; let hdr = indoc! {" #define BOB \"foo\" "}; let rs = quote! { assert_eq!(core::str::from_utf8(ffi::BOB).unwrap().trim_end_matches(char::from(0)), "foo"); }; run_test(cxx, hdr, rs, &["BOB"], &[]); } #[test] fn test_i32_const() { let cxx = indoc! {" "}; let hdr = indoc! {" #include const uint32_t BOB = 3; "}; let rs = quote! { assert_eq!(ffi::BOB, 3); }; run_test(cxx, hdr, rs, &["BOB"], &[]); } #[test] fn test_negative_rs_nonsense() { // Really just testing the test infrastructure. let cxx = indoc! {" "}; let hdr = indoc! {" #include const uint32_t BOB = 3; "}; let rs = quote! { foo bar }; run_test_expect_fail(cxx, hdr, rs, &["BOB"], &[]); } #[test] fn test_negative_cpp_nonsense() { // Really just testing the test infrastructure. let cxx = indoc! {" "}; let hdr = indoc! {" #include const uint32_t BOB = CAT; "}; let rs = quote! { assert_eq!(ffi::BOB, 3); }; run_test_expect_fail(cxx, hdr, rs, &["BOB"], &[]); } #[test] fn test_negative_make_nonpod() { let cxx = indoc! {" uint32_t take_bob(const Bob& a) { return a.a; } std::unique_ptr make_bob(uint32_t a) { auto b = std::make_unique(); b->a = a; return b; } "}; let hdr = indoc! {" #include #include struct Bob { uint32_t a; }; std::unique_ptr make_bob(uint32_t a); uint32_t take_bob(const Bob& a); "}; let rs = quote! { ffi::Bob {}; }; let rs2 = quote! { ffi::Bob { a: 12 }; }; let rs3 = quote! { ffi::Bob { do_not_attempt_to_allocate_nonpod_types: [] }; }; run_test_expect_fail(cxx, hdr, rs, &["take_bob", "Bob", "make_bob"], &[]); run_test_expect_fail(cxx, hdr, rs2, &["take_bob", "Bob", "make_bob"], &[]); run_test_expect_fail(cxx, hdr, rs3, &["take_bob", "Bob", "make_bob"], &[]); } #[test] fn test_method_pass_pod_by_value() { let cxx = indoc! {" uint32_t Bob::get_bob(Anna) const { return a; } "}; let hdr = indoc! {" #include struct Anna { uint32_t a; }; struct Bob { public: uint32_t a; uint32_t b; uint32_t get_bob(Anna a) const; }; "}; let rs = quote! { let a = ffi::Anna { a: 14 }; let b = ffi::Bob { a: 12, b: 13 }; assert_eq!(b.get_bob(a), 12); }; run_test(cxx, hdr, rs, &[], &["Bob", "Anna"]); } fn perform_asan_doom_test(into_raw: TokenStream, box_type: TokenStream) { if std::env::var_os("AUTOCXX_ASAN").is_none() { return; } // Testing that we get an asan fail when it's enabled. // Really just testing our CI is working to spot ASAN mistakes. let hdr = indoc! {" #include struct A { int a; }; inline size_t how_big_is_a() { return sizeof(A); } "}; let rs = quote! { let a = #box_type::emplace(ffi::A::new()); unsafe { let a_raw = #into_raw; // Intentional memory unsafety. Don't @ me. let a_offset_into_doom = a_raw.offset(ffi::how_big_is_a().try_into().unwrap()); a_offset_into_doom.write_bytes(0x69, 1); #box_type::from_raw(a_raw); // to delete. If we haven't yet crashed. } }; run_test_expect_fail("", hdr, rs, &["A", "how_big_is_a"], &[]); } #[test] fn test_asan_working_as_expected_for_cpp_allocations() { perform_asan_doom_test(quote! { a.into_raw() }, quote! { UniquePtr }) } #[test] fn test_asan_working_as_expected_for_rust_allocations() { perform_asan_doom_test( quote! { Box::into_raw(core::pin::Pin::into_inner_unchecked(a)) }, quote! { Box }, ) } #[test] fn test_inline_method() { let hdr = indoc! {" #include struct Anna { uint32_t a; }; struct Bob { public: uint32_t a; uint32_t b; uint32_t get_bob(Anna) const { return a; } }; "}; let rs = quote! { let a = ffi::Anna { a: 14 }; let b = ffi::Bob { a: 12, b: 13 }; assert_eq!(b.get_bob(a), 12); }; run_test("", hdr, rs, &[], &["Bob", "Anna"]); } #[test] fn test_method_pass_pod_by_reference() { let cxx = indoc! {" uint32_t Bob::get_bob(const Anna&) const { return a; } "}; let hdr = indoc! {" #include struct Anna { uint32_t a; }; struct Bob { public: uint32_t a; uint32_t b; uint32_t get_bob(const Anna& a) const; }; "}; let rs = quote! { let a = ffi::Anna { a: 14 }; let b = ffi::Bob { a: 12, b: 13 }; assert_eq!(b.get_bob(&a), 12); }; run_test(cxx, hdr, rs, &[], &["Bob", "Anna"]); } #[test] fn test_method_pass_pod_by_mut_reference() { let cxx = indoc! {" uint32_t Bob::get_bob(Anna&) const { return a; } "}; let hdr = indoc! {" #include struct Anna { uint32_t a; }; struct Bob { public: uint32_t a; uint32_t b; uint32_t get_bob(Anna& a) const; }; "}; let rs = quote! { let mut a = Box::pin(ffi::Anna { a: 14 }); let b = ffi::Bob { a: 12, b: 13 }; assert_eq!(b.get_bob(a.as_mut()), 12); }; run_test(cxx, hdr, rs, &[], &["Bob", "Anna"]); } #[test] fn test_method_pass_pod_by_up() { let cxx = indoc! {" uint32_t Bob::get_bob(std::unique_ptr) const { return a; } "}; let hdr = indoc! {" #include #include struct Anna { uint32_t a; }; struct Bob { public: uint32_t a; uint32_t b; uint32_t get_bob(std::unique_ptr z) const; }; "}; let rs = quote! { let a = ffi::Anna { a: 14 }; let b = ffi::Bob { a: 12, b: 13 }; assert_eq!(b.get_bob(cxx::UniquePtr::new(a)), 12); }; run_test(cxx, hdr, rs, &[], &["Bob", "Anna"]); } #[test] fn test_method_pass_nonpod_by_value() { let cxx = indoc! {" uint32_t Bob::get_bob(Anna) const { return a; } Anna give_anna() { Anna a; a.a = 10; return a; } "}; let hdr = indoc! {" #include #include struct Anna { uint32_t a; std::string b; }; Anna give_anna(); struct Bob { public: uint32_t a; uint32_t b; uint32_t get_bob(Anna a) const; }; "}; let rs = quote! { let a = ffi::give_anna().within_box(); let b = ffi::Bob { a: 12, b: 13 }; assert_eq!(b.get_bob(a), 12); }; run_test(cxx, hdr, rs, &["Anna", "give_anna"], &["Bob"]); } #[test] fn test_pass_two_nonpod_by_value() { let cxx = indoc! {" void take_a(A, A) { } "}; let hdr = indoc! {" #include struct A { std::string b; }; void take_a(A, A); "}; let rs = quote! { let a = ffi::A::new().within_unique_ptr(); let a2 = ffi::A::new().within_unique_ptr(); ffi::take_a(a, a2); }; run_test(cxx, hdr, rs, &["A", "take_a"], &[]); } #[test] fn test_issue_931() { let cxx = ""; let hdr = indoc! {" namespace a { struct __cow_string { __cow_string(); }; class b { public: __cow_string c; }; class j { public: b d; }; template class e; } // namespace a template struct f {}; namespace llvm { template class g { union { f h; }; }; class MemoryBuffer { public: g> i; }; } // namespace llvm "}; let rs = quote! {}; run_test(cxx, hdr, rs, &["llvm::MemoryBuffer"], &[]); } #[test] fn test_issue_936() { let cxx = ""; let hdr = indoc! {" struct a; class B { public: B(a &, bool); }; "}; let rs = quote! {}; run_test(cxx, hdr, rs, &["B"], &[]); } #[test] fn test_method_pass_nonpod_by_value_with_up() { // Checks that existing UniquePtr params are not wrecked // by the conversion we do here. let cxx = indoc! {" uint32_t Bob::get_bob(Anna, std::unique_ptr) const { return a; } Anna give_anna() { Anna a; a.a = 10; return a; } "}; let hdr = indoc! {" #include #include #include struct Anna { uint32_t a; std::string b; }; Anna give_anna(); struct Bob { public: uint32_t a; uint32_t b; uint32_t get_bob(Anna a, std::unique_ptr) const; }; "}; let rs = quote! { let a = ffi::give_anna().within_unique_ptr(); let a2 = ffi::give_anna().within_unique_ptr(); let b = ffi::Bob { a: 12, b: 13 }; assert_eq!(b.get_bob(a, a2), 12); }; run_test(cxx, hdr, rs, &["Anna", "give_anna"], &["Bob"]); } #[test] fn test_issue_940() { let cxx = ""; let hdr = indoc! {" template class b; template struct c; struct identity; template class f { using g = e; g h; }; template , class l = b> using j = f; class n; class RenderFrameHost { public: virtual void o(const j &); virtual ~RenderFrameHost() {} }; "}; let rs = quote! {}; run_test_ex( cxx, hdr, rs, directives_from_lists(&["RenderFrameHost"], &[], None), make_cpp17_adder(), None, None, ); } #[test] fn test_method_pass_nonpod_by_reference() { let cxx = indoc! {" uint32_t Bob::get_bob(const Anna&) const { return a; } Anna give_anna() { Anna a; a.a = 10; return a; } "}; let hdr = indoc! {" #include #include struct Anna { uint32_t a; std::string b; }; Anna give_anna(); struct Bob { public: uint32_t a; uint32_t b; uint32_t get_bob(const Anna& a) const; }; "}; let rs = quote! { let a = ffi::give_anna().within_box(); let b = ffi::Bob { a: 12, b: 13 }; assert_eq!(b.get_bob(a.as_ref().get_ref()), 12); }; run_test(cxx, hdr, rs, &["Anna", "give_anna"], &["Bob"]); } #[test] fn test_method_pass_nonpod_by_mut_reference() { let cxx = indoc! {" uint32_t Bob::get_bob(Anna&) const { return a; } Anna give_anna() { Anna a; a.a = 10; return a; } "}; let hdr = indoc! {" #include #include struct Anna { uint32_t a; std::string b; }; Anna give_anna(); struct Bob { public: uint32_t a; uint32_t b; uint32_t get_bob(Anna& a) const; }; "}; let rs = quote! { let mut a = ffi::give_anna().within_unique_ptr(); let b = ffi::Bob { a: 12, b: 13 }; assert_eq!(b.get_bob(a.as_mut().unwrap()), 12); }; run_test(cxx, hdr, rs, &["Anna", "give_anna"], &["Bob"]); } #[test] fn test_method_pass_nonpod_by_up() { let cxx = indoc! {" uint32_t Bob::get_bob(std::unique_ptr) const { return a; } Anna give_anna() { Anna a; a.a = 10; return a; } "}; let hdr = indoc! {" #include #include #include struct Anna { uint32_t a; std::string b; }; Anna give_anna(); struct Bob { public: uint32_t a; uint32_t b; uint32_t get_bob(std::unique_ptr z) const; }; "}; let rs = quote! { let a = ffi::give_anna().within_unique_ptr(); let b = ffi::Bob { a: 12, b: 13 }; assert_eq!(b.get_bob(a), 12); }; run_test(cxx, hdr, rs, &["give_anna"], &["Bob"]); } #[test] fn test_method_return_nonpod_by_value() { let cxx = indoc! {" Anna Bob::get_anna() const { Anna a; a.a = 12; return a; } "}; let hdr = indoc! {" #include #include struct Anna { uint32_t a; std::string b; }; struct Bob { public: uint32_t a; uint32_t b; Anna get_anna() const; }; "}; let rs = quote! { let b = ffi::Bob { a: 12, b: 13 }; let a = b.get_anna().within_unique_ptr(); assert!(!a.is_null()); }; run_test(cxx, hdr, rs, &["Anna"], &["Bob"]); } #[test] fn test_pass_string_by_value() { let cxx = indoc! {" uint32_t measure_string(std::string z) { return z.length(); } std::unique_ptr get_msg() { return std::make_unique(\"hello\"); } "}; let hdr = indoc! {" #include #include #include uint32_t measure_string(std::string a); std::unique_ptr get_msg(); "}; let rs = quote! { let a = ffi::get_msg(); let c = ffi::measure_string(a); assert_eq!(c, 5); }; run_test(cxx, hdr, rs, &["measure_string", "get_msg"], &[]); } #[test] fn test_return_string_by_value() { let cxx = indoc! {" std::string get_msg() { return \"hello\"; } "}; let hdr = indoc! {" #include std::string get_msg(); "}; let rs = quote! { let a = ffi::get_msg(); assert!(a.as_ref().unwrap() == "hello"); }; run_test(cxx, hdr, rs, &["get_msg"], &[]); } #[test] #[cfg_attr(skip_windows_gnu_failing_tests, ignore)] fn test_method_pass_string_by_value() { let cxx = indoc! {" uint32_t Bob::measure_string(std::string z) const { return z.length(); } std::unique_ptr get_msg() { return std::make_unique(\"hello\"); } "}; let hdr = indoc! {" #include #include #include struct Bob { public: uint32_t a; uint32_t b; uint32_t measure_string(std::string a) const; }; std::unique_ptr get_msg(); "}; let rs = quote! { let a = ffi::get_msg(); let b = ffi::Bob { a: 12, b: 13 }; let c = b.measure_string(a); assert_eq!(c, 5); }; run_test(cxx, hdr, rs, &["Bob", "get_msg"], &["Bob"]); } #[test] fn test_method_return_string_by_value() { let cxx = indoc! {" std::string Bob::get_msg() const { return \"hello\"; } "}; let hdr = indoc! {" #include #include struct Bob { public: uint32_t a; uint32_t b; std::string get_msg() const; }; "}; let rs = quote! { let b = ffi::Bob { a: 12, b: 13 }; let a = b.get_msg(); assert!(a.as_ref().unwrap() == "hello"); }; run_test(cxx, hdr, rs, &[], &["Bob"]); } #[test] fn test_pass_rust_string_by_ref() { let cxx = indoc! {" uint32_t measure_string(const rust::String& z) { return std::string(z).length(); } "}; let hdr = indoc! {" #include #include uint32_t measure_string(const rust::String& z); "}; let rs = quote! { let c = ffi::measure_string(&"hello".to_string()); assert_eq!(c, 5); }; run_test(cxx, hdr, rs, &["measure_string"], &[]); } #[test] fn test_pass_rust_string_by_value() { let cxx = indoc! {" uint32_t measure_string(rust::String z) { return std::string(z).length(); } "}; let hdr = indoc! {" #include #include uint32_t measure_string(rust::String z); "}; let rs = quote! { let c = ffi::measure_string("hello".into()); assert_eq!(c, 5); }; run_test(cxx, hdr, rs, &["measure_string"], &[]); } #[test] fn test_pass_rust_str() { // passing by value is the only legal option let cxx = indoc! {" uint32_t measure_string(rust::Str z) { return std::string(z).length(); } "}; let hdr = indoc! {" #include #include uint32_t measure_string(rust::Str z); "}; let rs = quote! { let c = ffi::measure_string("hello"); assert_eq!(c, 5); }; run_test(cxx, hdr, rs, &["measure_string"], &[]); } #[test] fn test_multiple_classes_with_methods() { let hdr = indoc! {" #include struct TrivialStruct { uint32_t val = 0; uint32_t get() const; uint32_t inc(); }; TrivialStruct make_trivial_struct(); class TrivialClass { public: uint32_t get() const; uint32_t inc(); private: uint32_t val_ = 1; }; TrivialClass make_trivial_class(); struct OpaqueStruct { // ~OpaqueStruct(); uint32_t val = 2; uint32_t get() const; uint32_t inc(); }; OpaqueStruct make_opaque_struct(); class OpaqueClass { public: // ~OpaqueClass(); uint32_t get() const; uint32_t inc(); private: uint32_t val_ = 3; }; OpaqueClass make_opaque_class(); "}; let cxx = indoc! {" TrivialStruct make_trivial_struct() { return {}; } TrivialClass make_trivial_class() { return {}; } OpaqueStruct make_opaque_struct() { return {}; } OpaqueClass make_opaque_class() { return {}; } uint32_t TrivialStruct::get() const { return val;} uint32_t TrivialClass::get() const { return val_; } uint32_t OpaqueStruct::get() const { return val;} uint32_t OpaqueClass::get() const { return val_; } uint32_t TrivialStruct::inc() { return ++val; } uint32_t TrivialClass::inc() { return ++val_; } uint32_t OpaqueStruct::inc() { return ++val; } uint32_t OpaqueClass::inc() { return ++val_; } "}; let rs = quote! { use ffi::*; let mut ts = Box::pin(make_trivial_struct()); assert_eq!(ts.get(), 0); assert_eq!(ts.as_mut().inc(), 1); assert_eq!(ts.as_mut().inc(), 2); let mut tc = Box::pin(make_trivial_class()); assert_eq!(tc.get(), 1); assert_eq!(tc.as_mut().inc(), 2); assert_eq!(tc.as_mut().inc(), 3); let mut os= make_opaque_struct().within_unique_ptr(); assert_eq!(os.get(), 2); assert_eq!(os.pin_mut().inc(), 3); assert_eq!(os.pin_mut().inc(), 4); let mut oc = make_opaque_class().within_unique_ptr(); assert_eq!(oc.get(), 3); assert_eq!(oc.pin_mut().inc(), 4); assert_eq!(oc.pin_mut().inc(), 5); }; run_test( cxx, hdr, rs, &[ "make_trivial_struct", "make_trivial_class", "make_opaque_struct", "make_opaque_class", "OpaqueStruct", "OpaqueClass", ], &["TrivialStruct", "TrivialClass"], ); } #[test] fn test_ns_return_struct() { let cxx = indoc! {" A::B::Bob give_bob() { A::B::Bob a; a.a = 3; a.b = 4; return a; } "}; let hdr = indoc! {" #include namespace A { namespace B { struct Bob { uint32_t a; uint32_t b; }; } } A::B::Bob give_bob(); "}; let rs = quote! { assert_eq!(ffi::give_bob().b, 4); }; run_test(cxx, hdr, rs, &["give_bob"], &["A::B::Bob"]); } #[test] fn test_ns_take_struct() { let cxx = indoc! {" uint32_t take_bob(A::B::Bob a) { return a.a; } "}; let hdr = indoc! {" #include namespace A { namespace B { struct Bob { uint32_t a; uint32_t b; }; } } uint32_t take_bob(A::B::Bob a); "}; let rs = quote! { let a = ffi::A::B::Bob { a: 12, b: 13 }; assert_eq!(ffi::take_bob(a), 12); }; run_test(cxx, hdr, rs, &["take_bob"], &["A::B::Bob"]); } #[test] fn test_ns_func() { let cxx = indoc! {" using namespace C; A::B::Bob C::give_bob() { A::B::Bob a; a.a = 3; a.b = 4; return a; } "}; let hdr = indoc! {" #include namespace A { namespace B { struct Bob { uint32_t a; uint32_t b; }; } } namespace C { ::A::B::Bob give_bob(); } "}; let rs = quote! { assert_eq!(ffi::C::give_bob().b, 4); }; run_test(cxx, hdr, rs, &["C::give_bob"], &["A::B::Bob"]); } #[test] fn test_overload_constructors() { let cxx = indoc! {" Bob::Bob() {} Bob::Bob(uint32_t _a) :a(_a) {} "}; let hdr = indoc! {" #include #include struct Bob { Bob(); Bob(uint32_t a); uint32_t a; uint32_t b; }; "}; let rs = quote! { ffi::Bob::new().within_unique_ptr(); ffi::Bob::new1(32).within_unique_ptr(); }; run_test(cxx, hdr, rs, &["Bob"], &[]); } #[test] fn test_overload_functions() { let cxx = indoc! {" void daft(uint32_t) {} void daft(uint8_t) {} void daft(std::string) {} void daft(Fred) {} void daft(Norma) {} "}; let hdr = indoc! {" #include #include struct Fred { uint32_t a; }; struct Norma { Norma() {} uint32_t a; }; void daft(uint32_t); void daft(uint8_t); void daft(std::string); void daft(Fred); void daft(Norma); "}; let rs = quote! { use ffi::ToCppString; ffi::daft(32); ffi::daft1(8); ffi::daft2("hello".into_cpp()); let b = ffi::Fred { a: 3 }; ffi::daft3(b); let c = ffi::Norma::new().within_unique_ptr(); ffi::daft4(c); }; run_test( cxx, hdr, rs, &["Norma", "daft", "daft1", "daft2", "daft3", "daft4"], &["Fred"], ); } #[test] #[ignore] // At present, bindgen generates two separate 'daft1' // functions here, and there's not much we can do about that. fn test_overload_numeric_functions() { // Because bindgen deals with conflicting overloaded functions by // appending a numeric suffix, let's see if we can cope. let cxx = indoc! {" void daft1(uint32_t) {} void daft2(uint8_t) {} void daft(std::string) {} void daft(Fred) {} void daft(Norma) {} "}; let hdr = indoc! {" #include #include struct Fred { uint32_t a; }; struct Norma { uint32_t a; }; void daft1(uint32_t a); void daft2(uint8_t a); void daft(std::string a); void daft(Fred a); void daft(Norma a); "}; let rs = quote! { use ffi::ToCppString; ffi::daft(32); ffi::daft1(8); ffi::daft2("hello".into_cpp()); let b = ffi::Fred { a: 3 }; ffi::daft3(b); let c = ffi::Norma::new().within_unique_ptr(); ffi::daft4(c); }; run_test( cxx, hdr, rs, &["Norma", "daft", "daft1", "daft2", "daft3", "daft4"], &["Fred"], ); } #[test] fn test_overload_methods() { let cxx = indoc! {" void Bob::daft(uint32_t) const {} void Bob::daft(uint8_t) const {} void Bob::daft(std::string) const {} void Bob::daft(Fred) const {} void Bob::daft(Norma) const {} "}; let hdr = indoc! {" #include #include struct Fred { uint32_t a; }; struct Norma { Norma() {} uint32_t a; }; struct Bob { uint32_t a; void daft(uint32_t) const; void daft(uint8_t) const; void daft(std::string) const; void daft(Fred) const; void daft(Norma) const; }; "}; let rs = quote! { use ffi::ToCppString; let a = ffi::Bob { a: 12 }; a.daft(32); a.daft1(8); a.daft2("hello".into_cpp()); let b = ffi::Fred { a: 3 }; a.daft3(b); let c = ffi::Norma::new().within_unique_ptr(); a.daft4(c); }; run_test(cxx, hdr, rs, &["Norma"], &["Fred", "Bob"]); } #[test] fn test_ns_constructor() { let cxx = indoc! {" A::Bob::Bob() {} "}; let hdr = indoc! {" #include #include namespace A { struct Bob { Bob(); uint32_t a; uint32_t b; }; } "}; let rs = quote! { ffi::A::Bob::new().within_unique_ptr(); }; run_test(cxx, hdr, rs, &["A::Bob"], &[]); } #[test] fn test_ns_up_direct() { let cxx = indoc! {" std::unique_ptr A::get_bob() { A::Bob b; b.a = 2; b.b = 3; return std::make_unique(b); } uint32_t give_bob(A::Bob bob) { return bob.a; } "}; let hdr = indoc! {" #include #include namespace A { struct Bob { uint32_t a; uint32_t b; }; std::unique_ptr get_bob(); } uint32_t give_bob(A::Bob bob); "}; let rs = quote! { assert_eq!(ffi::give_bob(ffi::A::get_bob()), 2); }; run_test(cxx, hdr, rs, &["give_bob", "A::get_bob"], &[]); } #[test] fn test_ns_up_wrappers() { let cxx = indoc! {" A::Bob get_bob() { A::Bob b; b.a = 2; b.b = 3; return b; } uint32_t give_bob(A::Bob bob) { return bob.a; } "}; let hdr = indoc! {" #include namespace A { struct Bob { uint32_t a; uint32_t b; }; } A::Bob get_bob(); uint32_t give_bob(A::Bob bob); "}; let rs = quote! { assert_eq!(ffi::give_bob(as_new(ffi::get_bob())), 2); }; run_test(cxx, hdr, rs, &["give_bob", "get_bob"], &[]); } #[test] fn test_ns_up_wrappers_in_up() { let cxx = indoc! {" A::Bob A::get_bob() { A::Bob b; b.a = 2; b.b = 3; return b; } uint32_t give_bob(A::Bob bob) { return bob.a; } "}; let hdr = indoc! {" #include namespace A { struct Bob { uint32_t a; uint32_t b; }; Bob get_bob(); } uint32_t give_bob(A::Bob bob); "}; let rs = quote! { assert_eq!(ffi::give_bob(as_new(ffi::A::get_bob())), 2); }; run_test(cxx, hdr, rs, &["give_bob", "A::get_bob"], &[]); } #[test] fn test_return_reference() { let cxx = indoc! {" const Bob& give_bob(const Bob& input_bob) { return input_bob; } "}; let hdr = indoc! {" #include struct Bob { uint32_t a; uint32_t b; }; const Bob& give_bob(const Bob& input_bob); "}; let rs = quote! { let b = ffi::Bob { a: 3, b: 4 }; assert_eq!(ffi::give_bob(&b).b, 4); }; run_test(cxx, hdr, rs, &["give_bob"], &["Bob"]); } #[test] fn test_return_reference_non_pod() { let cxx = indoc! {" const Bob& give_bob(const Bob& input_bob) { return input_bob; } "}; let hdr = indoc! {" #include struct Bob { uint32_t a; uint32_t b; }; namespace A { void give_bob(); // force wrapper generation } const Bob& give_bob(const Bob& input_bob); "}; let rs = quote! {}; run_test(cxx, hdr, rs, &["give_bob", "Bob", "A::give_bob"], &[]); } #[test] fn test_return_reference_non_pod_string() { let cxx = indoc! {" const std::string& give_bob(const Bob& input_bob) { return input_bob.a; } "}; let hdr = indoc! {" #include struct Bob { std::string a; }; // namespace A { // void give_bob(); // force wrapper generation // } const std::string& give_bob(const Bob& input_bob); "}; let rs = quote! {}; run_test(cxx, hdr, rs, &["give_bob", "Bob"], &[]); } #[test] fn test_member_return_reference() { let hdr = indoc! {" #include class A { public: virtual const std::string& get_str() { return a; } virtual ~A() {} std::string a; }; "}; let rs = quote! { let mut b = ffi::A::new().within_unique_ptr(); b.pin_mut().get_str(); }; run_test("", hdr, rs, &["A"], &[]); } #[test] fn test_destructor() { let hdr = indoc! {" struct WithDtor { ~WithDtor(); }; WithDtor make_with_dtor(); "}; let cxx = indoc! {" WithDtor::~WithDtor() {} WithDtor make_with_dtor() { return {}; } "}; let rs = quote! { use ffi::*; let with_dtor: cxx::UniquePtr = make_with_dtor().within_unique_ptr(); drop(with_dtor); }; run_test(cxx, hdr, rs, &["WithDtor", "make_with_dtor"], &[]); } #[test] fn test_nested_with_destructor() { // Regression test, naming the destructor in the generated C++ is a bit tricky. let hdr = indoc! {" struct A { struct B { B() = default; ~B() = default; }; }; "}; let rs = quote! { ffi::A_B::new().within_unique_ptr(); }; run_test("", hdr, rs, &["A", "A_B"], &[]); } // Even without a `safety!`, we still need to generate a safe `fn drop`. #[test] fn test_destructor_no_safety() { let hdr = indoc! {" struct WithDtor { ~WithDtor(); }; "}; let cxx = indoc! {" WithDtor::~WithDtor() {} "}; let hexathorpe = Token![#](Span::call_site()); let unexpanded_rust = quote! { use autocxx::prelude::*; include_cpp!( #hexathorpe include "input.h" generate!("WithDtor") ); fn main() {} }; do_run_test_manual(cxx, hdr, unexpanded_rust, None, None).unwrap(); } #[test] fn test_static_func() { let hdr = indoc! {" #include struct WithStaticMethod { static uint32_t call(); }; "}; let cxx = indoc! {" uint32_t WithStaticMethod::call() { return 42; } "}; let rs = quote! { assert_eq!(ffi::WithStaticMethod::call(), 42); }; run_test(cxx, hdr, rs, &["WithStaticMethod"], &[]); } #[test] fn test_static_func_wrapper() { let hdr = indoc! {" #include #include struct A { std::string a; static A CreateA(std::string a, std::string) { A c; c.a = a; return c; } }; "}; let rs = quote! { use ffi::ToCppString; ffi::A::CreateA("a".into_cpp(), "b".into_cpp()); }; run_test("", hdr, rs, &["A"], &[]); } #[test] fn test_give_pod_typedef_by_value() { let cxx = indoc! {" Horace give_bob() { Horace a; a.a = 3; a.b = 4; return a; } "}; let hdr = indoc! {" #include struct Bob { uint32_t a; uint32_t b; }; using Horace = Bob; Horace give_bob(); "}; let rs = quote! { assert_eq!(ffi::give_bob().b, 4); }; run_test(cxx, hdr, rs, &["give_bob"], &["Bob"]); } #[ignore] // because we need to put some aliases in the output ffi mod. #[test] fn test_use_pod_typedef() { let cxx = indoc! {" "}; let hdr = indoc! {" #include struct Bob { uint32_t a; uint32_t b; }; using Horace = Bob; "}; let rs = quote! { let h = Horace { a: 3, b: 4 }; assert_eq!(h.b, 4); }; run_test(cxx, hdr, rs, &[], &["Bob"]); } #[test] fn test_typedef_to_ns() { let hdr = indoc! {" #include namespace A { template struct C { T* t; }; typedef C B; } "}; let rs = quote! {}; run_test("", hdr, rs, &["A::B"], &[]); } #[ignore] // we don't yet allow typedefs to be listed in allow_pod #[test] fn test_use_pod_typedef_with_allowpod() { let cxx = indoc! {" "}; let hdr = indoc! {" #include struct Bob { uint32_t a; uint32_t b; }; using Horace = Bob; "}; let rs = quote! { let h = Horace { a: 3, b: 4 }; assert_eq!(h.b, 4); }; run_test(cxx, hdr, rs, &[], &["Horace"]); } #[test] fn test_give_nonpod_typedef_by_value() { let cxx = indoc! {" Horace give_bob() { Horace a; a.a = 3; a.b = 4; return a; } "}; let hdr = indoc! {" #include struct Bob { uint32_t a; uint32_t b; }; using Horace = Bob; Horace give_bob(); inline uint32_t take_horace(const Horace& horace) { return horace.b; } "}; let rs = quote! { assert_eq!(ffi::take_horace(&moveit!(ffi::give_bob())), 4); }; run_test(cxx, hdr, rs, &["give_bob", "take_horace"], &[]); } #[test] fn test_conflicting_static_functions() { let cxx = indoc! {" Bob Bob::create() { Bob a; return a; } Fred Fred::create() { Fred b; return b; } "}; let hdr = indoc! {" #include struct Bob { Bob() : a(0) {} uint32_t a; static Bob create(); }; struct Fred { Fred() : b(0) {} uint32_t b; static Fred create(); }; "}; let rs = quote! { ffi::Bob::create(); ffi::Fred::create(); }; run_test(cxx, hdr, rs, &[], &["Bob", "Fred"]); } #[test] fn test_conflicting_ns_up_functions() { let cxx = indoc! {" uint32_t A::create(C) { return 3; } uint32_t B::create(C) { return 4; } "}; let hdr = indoc! {" #include struct C { C() {} uint32_t a; }; namespace A { uint32_t create(C c); }; namespace B { uint32_t create(C c); }; "}; let rs = quote! { let c = ffi::C::new().within_unique_ptr(); let c2 = ffi::C::new().within_unique_ptr(); assert_eq!(ffi::A::create(c), 3); assert_eq!(ffi::B::create(c2), 4); }; run_test(cxx, hdr, rs, &["A::create", "B::create", "C"], &[]); } #[test] fn test_conflicting_methods() { let cxx = indoc! {" uint32_t Bob::get() const { return a; } uint32_t Fred::get() const { return b; } "}; let hdr = indoc! {" #include struct Bob { uint32_t a; uint32_t get() const; }; struct Fred { uint32_t b; uint32_t get() const; }; "}; let rs = quote! { let a = ffi::Bob { a: 10 }; let b = ffi::Fred { b: 20 }; assert_eq!(a.get(), 10); assert_eq!(b.get(), 20); }; run_test(cxx, hdr, rs, &[], &["Bob", "Fred"]); } #[test] // There's a bindgen bug here. bindgen generates // functions called 'get' and 'get1' but then generates impl // blocks which call 'get' and 'get'. By luck, we currently // should not be broken by this, but at some point we should take // the time to create a minimal bindgen test case and submit it // as a bindgen bug. fn test_conflicting_up_wrapper_methods_not_in_ns() { // Ensures the two names 'get' do not conflict in the flat // cxx::bridge mod namespace. let cxx = indoc! {" Bob::Bob() : a(\"hello\") {} Fred::Fred() : b(\"goodbye\") {} std::string Bob::get() const { return a; } std::string Fred::get() const { return b; } "}; let hdr = indoc! {" #include #include struct Bob { Bob(); std::string a; std::string get() const; }; struct Fred { Fred(); std::string b; std::string get() const; }; "}; let rs = quote! { let a = ffi::Bob::new().within_unique_ptr(); let b = ffi::Fred::new().within_unique_ptr(); assert_eq!(a.get().as_ref().unwrap().to_str().unwrap(), "hello"); assert_eq!(b.get().as_ref().unwrap().to_str().unwrap(), "goodbye"); }; run_test(cxx, hdr, rs, &["Bob", "Fred"], &[]); } #[test] fn test_conflicting_methods_in_ns() { let cxx = indoc! {" uint32_t A::Bob::get() const { return a; } uint32_t B::Fred::get() const { return b; } "}; let hdr = indoc! {" #include namespace A { struct Bob { uint32_t a; uint32_t get() const; }; } namespace B { struct Fred { uint32_t b; uint32_t get() const; }; } "}; let rs = quote! { let a = ffi::A::Bob { a: 10 }; let b = ffi::B::Fred { b: 20 }; assert_eq!(a.get(), 10); assert_eq!(b.get(), 20); }; run_test(cxx, hdr, rs, &[], &["A::Bob", "B::Fred"]); } #[test] fn test_conflicting_up_wrapper_methods_in_ns() { let cxx = indoc! {" A::Bob::Bob() : a(\"hello\") {} B::Fred::Fred() : b(\"goodbye\") {} std::string A::Bob::get() const { return a; } std::string B::Fred::get() const { return b; } "}; let hdr = indoc! {" #include #include namespace A { struct Bob { Bob(); std::string a; std::string get() const; }; } namespace B { struct Fred { Fred(); std::string b; std::string get() const; }; } "}; let rs = quote! { let a = ffi::A::Bob::new().within_unique_ptr(); let b = ffi::B::Fred::new().within_unique_ptr(); assert_eq!(a.get().as_ref().unwrap().to_str().unwrap(), "hello"); assert_eq!(b.get().as_ref().unwrap().to_str().unwrap(), "goodbye"); }; run_test(cxx, hdr, rs, &["A::Bob", "B::Fred"], &[]); } #[test] fn test_ns_struct_pod_request() { let hdr = indoc! {" #include namespace A { struct Bob { uint32_t a; }; } "}; let rs = quote! { ffi::A::Bob { a: 12 }; }; run_test("", hdr, rs, &[], &["A::Bob"]); } #[test] fn test_conflicting_ns_funcs() { let cxx = indoc! {" uint32_t A::get() { return 10; } uint32_t B::get() { return 20; } "}; let hdr = indoc! {" #include namespace A { uint32_t get(); } namespace B { uint32_t get(); } "}; let rs = quote! { assert_eq!(ffi::A::get(), 10); assert_eq!(ffi::B::get(), 20); }; run_test(cxx, hdr, rs, &["A::get", "B::get"], &[]); } #[ignore] // because currently we feed a flat namespace to cxx // This would be relatively easy to enable now that we have the facility // to add aliases to the 'use' statements we generate, plus // bridge_name_tracker to pick a unique name. TODO. #[test] fn test_conflicting_ns_structs() { let hdr = indoc! {" #include namespace A { struct Bob { uint32_t a; }; } namespace B { struct Bob { uint32_t a; }; } "}; let rs = quote! { ffi::A::Bob { a: 12 }; ffi::B::Bob { b: 12 }; }; run_test("", hdr, rs, &[], &["A::Bob", "B::Bob"]); } #[test] fn test_make_string() { let hdr = indoc! {" #include struct Bob { uint32_t a; }; "}; let rs = quote! { use ffi::ToCppString; let a = "hello".into_cpp(); assert_eq!(a.to_str().unwrap(), "hello"); }; run_test("", hdr, rs, &["Bob"], &[]); } #[test] fn test_string_make_unique() { let hdr = indoc! {" #include inline void take_string(const std::string*) {}; "}; let rs = quote! { let s = ffi::make_string(""); unsafe { ffi::take_string(s.as_ref().unwrap()) }; }; run_test("", hdr, rs, &["take_string"], &[]); } #[test] fn test_string_constant() { let hdr = indoc! {" #include const char* STRING = \"Foo\"; "}; let rs = quote! { let a = core::str::from_utf8(ffi::STRING).unwrap().trim_end_matches(char::from(0)); assert_eq!(a, "Foo"); }; run_test("", hdr, rs, &["STRING"], &[]); } #[test] fn test_string_let_cxx_string() { let hdr = indoc! {" #include inline void take_string(const std::string&) {}; "}; let rs = quote! { autocxx::cxx::let_cxx_string!(s = "hello"); ffi::take_string(&s); }; run_test("", hdr, rs, &["take_string"], &[]); } #[test] fn test_pod_constant_harmless_inside_type() { // Check that the presence of this constant doesn't break anything. let hdr = indoc! {" #include struct Bob { uint32_t a; }; struct Anna { uint32_t a; const Bob BOB = Bob { 10 }; }; "}; let rs = quote! {}; run_test("", hdr, rs, &[], &["Anna"]); } #[test] #[ignore] // https://github.com/google/autocxx/issues/93 fn test_pod_constant() { let hdr = indoc! {" #include struct Bob { uint32_t a; }; const Bob BOB = Bob { 10 }; "}; let rs = quote! { let a = &ffi::BOB; assert_eq!(a.a, 10); }; run_test("", hdr, rs, &["BOB"], &["Bob"]); } #[test] fn test_pod_static_harmless_inside_type() { // Check that the presence of this constant doesn't break anything. // Remove this test when the following one is enabled. let hdr = indoc! {" #include struct Bob { uint32_t a; }; struct Anna { uint32_t a; static Bob BOB; }; Bob Anna::BOB = Bob { 10 }; "}; let rs = quote! {}; run_test("", hdr, rs, &[], &["Anna"]); } #[test] #[ignore] // https://github.com/google/autocxx/issues/93 fn test_pod_static() { let hdr = indoc! {" #include struct Bob { uint32_t a; }; static Bob BOB = Bob { 10 }; "}; let rs = quote! { let a = &ffi::BOB; assert_eq!(a.a, 10); }; run_test("", hdr, rs, &["BOB"], &["Bob"]); } #[test] #[ignore] // this probably requires code generation on the C++ // side. It's not at all clear how best to handle this. fn test_non_pod_constant() { let hdr = indoc! {" #include #include struct Bob { std::string a; std::string get() { return a }; }; const Bob BOB = Bob { \"hello\" }; "}; let rs = quote! { let a = ffi::BOB; // following line assumes that 'a' is a &Bob // but who knows how we'll really do this. assert_eq!(a.get().as_ref().unwrap().to_str().unwrap(), "hello"); }; run_test("", hdr, rs, &["BOB"], &[]); } #[test] fn test_templated_typedef() { let hdr = indoc! {" #include #include template class BasicStringPiece { public: const STRING_TYPE* ptr_; size_t length_; }; typedef BasicStringPiece StringPiece; struct Origin { Origin() {} StringPiece host; }; "}; let rs = quote! { ffi::Origin::new().within_unique_ptr(); }; run_test("", hdr, rs, &["Origin"], &[]); } #[test] fn test_struct_templated_typedef() { let hdr = indoc! {" #include #include struct Concrete { uint8_t a; }; template class BasicStringPiece { public: const STRING_TYPE* ptr_; size_t length_; }; typedef BasicStringPiece StringPiece; struct Origin { Origin() {} StringPiece host; }; "}; let rs = quote! { ffi::Origin::new().within_unique_ptr(); }; run_test("", hdr, rs, &["Origin"], &[]); } #[test] fn test_enum_typedef() { let hdr = indoc! {" enum ConstraintSolverParameters_TrailCompression : int { ConstraintSolverParameters_TrailCompression_NO_COMPRESSION = 0, ConstraintSolverParameters_TrailCompression_COMPRESS_WITH_ZLIB = 1 }; typedef ConstraintSolverParameters_TrailCompression TrailCompression; "}; let rs = quote! { let _ = ffi::TrailCompression::ConstraintSolverParameters_TrailCompression_NO_COMPRESSION; }; run_test("", hdr, rs, &["TrailCompression"], &[]); } #[test] #[ignore] // https://github.com/google/autocxx/issues/264 fn test_conflicting_usings() { let hdr = indoc! {" #include #include typedef size_t diff; struct A { using diff = diff; diff a; }; struct B { using diff = diff; diff a; }; "}; let rs = quote! {}; run_test("", hdr, rs, &[], &["A", "B"]); } #[test] fn test_conflicting_usings_with_self_declaration1() { let hdr = indoc! {" #include #include struct common_params { using difference_type = ptrdiff_t; }; template class btree_node { public: using difference_type = typename Params::difference_type; Params params; }; template class btree_container { public: using difference_type = typename Tree::difference_type; void clear() {} Tree b; uint32_t a; }; typedef btree_container> my_tree; "}; let rs = quote! {}; run_test("", hdr, rs, &["my_tree"], &[]); } #[test] #[ignore] // https://github.com/google/autocxx/issues/106 fn test_string_templated_typedef() { let hdr = indoc! {" #include #include template class BasicStringPiece { public: const STRING_TYPE* ptr_; size_t length_; }; typedef BasicStringPiece StringPiece; struct Origin { Origin() {} StringPiece host; }; "}; let rs = quote! { ffi::Origin::new().within_unique_ptr(); }; run_test("", hdr, rs, &["Origin"], &[]); } #[test] fn test_associated_type_problem() { // Regression test for a potential bindgen bug let hdr = indoc! {" namespace a { template class b {}; } // namespace a class bl { public: a::b bm; }; struct B { int a; }; "}; let rs = quote! {}; run_test("", hdr, rs, &["B"], &[]); } #[test] fn test_two_type_constructors() { // https://github.com/google/autocxx/issues/877 let hdr = indoc! {" struct A { int a; }; struct B { int B; }; "}; let rs = quote! {}; run_test("", hdr, rs, &["A", "B"], &[]); } #[ignore] // https://github.com/rust-lang/rust-bindgen/issues/1924 #[test] fn test_associated_type_templated_typedef_in_struct() { let hdr = indoc! {" #include #include template class BasicStringPiece { public: typedef size_t size_type; typedef typename STRING_TYPE::value_type value_type; const value_type* ptr_; size_type length_; }; typedef BasicStringPiece StringPiece; struct Origin { // void SetHost(StringPiece host); StringPiece host; }; "}; let rs = quote! { ffi::Origin::new().within_unique_ptr(); }; run_test("", hdr, rs, &["Origin"], &[]); } #[test] fn test_associated_type_templated_typedef() { let hdr = indoc! {" #include #include template class BasicStringPiece { public: typedef size_t size_type; typedef typename STRING_TYPE::value_type value_type; const value_type* ptr_; size_type length_; }; typedef BasicStringPiece StringPiece; struct Container { Container() {} const StringPiece& get_string_piece() const { return sp; } StringPiece sp; }; inline void take_string_piece(const StringPiece&) {} "}; let rs = quote! { let sp = ffi::Container::new().within_box(); ffi::take_string_piece(sp.get_string_piece()); }; run_test("", hdr, rs, &["take_string_piece", "Container"], &[]); } #[test] fn test_associated_type_templated_typedef_by_value_regular() { let hdr = indoc! {" #include #include template class BasicStringPiece { public: BasicStringPiece() : ptr_(nullptr), length_(0) {} typedef size_t size_type; typedef typename STRING_TYPE::value_type value_type; const value_type* ptr_; size_type length_; }; typedef BasicStringPiece StringPiece; inline StringPiece give_string_piece() { StringPiece s; return s; } inline void take_string_piece(StringPiece) {} "}; let rs = quote! { let sp = ffi::give_string_piece(); ffi::take_string_piece(sp); }; run_test_ex( "", hdr, rs, quote! { generate!("take_string_piece") generate!("give_string_piece") instantiable!("StringPiece") }, None, None, None, ); } #[test] fn test_associated_type_templated_typedef_by_value_forward_declaration() { let hdr = indoc! {" #include #include template class BasicStringPiece; typedef BasicStringPiece StringPiece; struct Container { StringPiece give_string_piece() const; void take_string_piece(StringPiece string_piece) const; const StringPiece& get_string_piece() const; uint32_t b; }; inline void take_string_piece_by_ref(const StringPiece&) {} "}; let cpp = indoc! {" template class BasicStringPiece { public: BasicStringPiece() : ptr_(nullptr), length_(0) {} typedef size_t size_type; typedef typename STRING_TYPE::value_type value_type; const value_type* ptr_; size_type length_; }; StringPiece Container::give_string_piece() const { StringPiece s; return s; } void Container::take_string_piece(StringPiece) const {} StringPiece a; const StringPiece& Container::get_string_piece() const { return a; } "}; // As this template is forward declared we shouldn't be able to pass it by // value, but we still want to be able to use it by reference. let rs = quote! { let cont = ffi::Container::new().within_box(); ffi::take_string_piece_by_ref(cont.as_ref().get_string_piece()); }; run_test( cpp, hdr, rs, &["take_string_piece_by_ref", "Container"], &[], ); } #[test] fn test_remove_cv_t_pathological() { let hdr = indoc! {" template struct remove_cv { using type = _Ty; template