nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: dtolnay/async-trait
Pull request #201 (2022-06-02 20:27:22)

Add 'async_trait lifetime to impl Trait arguments
Fixes #177.


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index ac1063d..236dff3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,7 +17,7 @@ proc-macro = true
 [dependencies]
 proc-macro2 = "1.0"
 quote = "1.0"
-syn = { version = "1.0.84", features = ["full", "visit-mut"] }
+syn = { version = "1.0.96", features = ["full", "visit-mut"] }
 
 [dev-dependencies]
 futures = "0.3"
diff --git a/src/expand.rs b/src/expand.rs
index ed0818e..fbbac5d 100644
--- a/src/expand.rs
+++ b/src/expand.rs
@@ -1,4 +1,4 @@
-use crate::lifetime::CollectLifetimes;
+use crate::lifetime::{AddLifetimeToImplTrait, CollectLifetimes};
 use crate::parse::Item;
 use crate::receiver::{has_self_in_block, has_self_in_sig, mut_pat, ReplaceSelf};
 use proc_macro2::TokenStream;
@@ -283,6 +283,7 @@ fn transform_sig(
                     let m = mut_pat(&mut arg.pat);
                     arg.pat = parse_quote!(#m #positional);
                 }
+                AddLifetimeToImplTrait.visit_type_mut(&mut arg.ty);
             }
         }
     }
diff --git a/src/lifetime.rs b/src/lifetime.rs
index ff25d32..6713069 100644
--- a/src/lifetime.rs
+++ b/src/lifetime.rs
@@ -1,6 +1,8 @@
 use proc_macro2::Span;
 use syn::visit_mut::{self, VisitMut};
-use syn::{GenericArgument, Lifetime, Receiver, TypeReference};
+use syn::{
+    parse_quote_spanned, Expr, GenericArgument, Lifetime, Receiver, TypeImplTrait, TypeReference,
+};
 
 pub struct CollectLifetimes {
     pub elided: Vec<Lifetime>,
@@ -62,3 +64,22 @@ impl VisitMut for CollectLifetimes {
         visit_mut::visit_generic_argument_mut(self, gen);
     }
 }
+
+pub struct AddLifetimeToImplTrait;
+
+impl VisitMut for AddLifetimeToImplTrait {
+    fn visit_type_impl_trait_mut(&mut self, ty: &mut TypeImplTrait) {
+        let span = ty.impl_token.span;
+        let lifetime = parse_quote_spanned!(span=> 'async_trait);
+        ty.bounds.insert(0, lifetime);
+        if let Some(punct) = ty.bounds.pairs_mut().next().unwrap().punct_mut() {
+            punct.span = span;
+        }
+    }
+
+    fn visit_expr_mut(&mut self, _e: &mut Expr) {
+        // Do not recurse into impl Traits inside of an array length expression.
+        //
+        //    fn outer(arg: [u8; { fn inner(_: impl Trait) {}; 0 }]);
+    }
+}


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index ea6dd02..8a5baab 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -1374,6 +1374,23 @@ pub mod issue169 {
     pub fn test(_t: &dyn Trait) {}
 }
 
+// https://github.com/dtolnay/async-trait/issues/177
+pub mod issue177 {
+    use async_trait::async_trait;
+
+    #[async_trait]
+    pub trait Trait {
+        async fn foo(&self, _callback: impl FnMut(&str) + Send) {}
+    }
+
+    pub struct Struct;
+
+    #[async_trait]
+    impl Trait for Struct {
+        async fn foo(&self, _callback: impl FnMut(&str) + Send) {}
+    }
+}
+
 // https://github.com/dtolnay/async-trait/issues/183
 pub mod issue183 {
     #![deny(clippy::shadow_same)]
Repository: dtolnay/async-trait
Pull request #206 (2022-06-02 22:28:37)

Parenthesize other cases that require parens around impl Trait
Cases similar to #204.


Patch:
diff --git a/src/lifetime.rs b/src/lifetime.rs
index 7ceda4f..8e4ec38 100644
--- a/src/lifetime.rs
+++ b/src/lifetime.rs
@@ -2,8 +2,8 @@ use proc_macro2::{Span, TokenStream};
 use std::mem;
 use syn::visit_mut::{self, VisitMut};
 use syn::{
-    parse_quote_spanned, token, Expr, GenericArgument, Lifetime, Receiver, Type, TypeImplTrait,
-    TypeParen, TypeReference,
+    parse_quote_spanned, token, Expr, GenericArgument, Lifetime, Receiver, ReturnType, Type,
+    TypeBareFn, TypeImplTrait, TypeParen, TypePtr, TypeReference,
 };
 
 pub struct CollectLifetimes {
@@ -81,19 +81,35 @@ impl VisitMut for AddLifetimeToImplTrait {
     }
 
     fn visit_type_reference_mut(&mut self, ty: &mut TypeReference) {
-        if let Type::ImplTrait(_) = *ty.elem {
-            let elem = mem::replace(&mut *ty.elem, Type::Verbatim(TokenStream::new()));
-            *ty.elem = Type::Paren(TypeParen {
-                paren_token: token::Paren(ty.and_token.span),
-                elem: Box::new(elem),
-            });
-        }
+        parenthesize_impl_trait(&mut ty.elem, ty.and_token.span);
         visit_mut::visit_type_reference_mut(self, ty);
     }
 
+    fn visit_type_ptr_mut(&mut self, ty: &mut TypePtr) {
+        parenthesize_impl_trait(&mut ty.elem, ty.star_token.span);
+        visit_mut::visit_type_ptr_mut(self, ty);
+    }
+
+    fn visit_type_bare_fn_mut(&mut self, ty: &mut TypeBareFn) {
+        if let ReturnType::Type(arrow, return_type) = &mut ty.output {
+            parenthesize_impl_trait(return_type, arrow.spans[0]);
+        }
+        visit_mut::visit_type_bare_fn_mut(self, ty);
+    }
+
     fn visit_expr_mut(&mut self, _e: &mut Expr) {
         // Do not recurse into impl Traits inside of an array length expression.
         //
         //    fn outer(arg: [u8; { fn inner(_: impl Trait) {}; 0 }]);
     }
 }
+
+fn parenthesize_impl_trait(elem: &mut Type, paren_span: Span) {
+    if let Type::ImplTrait(_) = *elem {
+        let placeholder = Type::Verbatim(TokenStream::new());
+        *elem = Type::Paren(TypeParen {
+            paren_token: token::Paren(paren_span),
+            elem: Box::new(mem::replace(elem, placeholder)),
+        });
+    }
+}


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index 6859639..d3aab37 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -1447,5 +1447,6 @@ pub mod issue204 {
     #[async_trait]
     pub trait Trait {
         async fn f(arg: &impl Trait);
+        async fn g(arg: *const impl Trait);
     }
 }
Repository: dtolnay/cxx
Pull request #1155 (2023-01-20 02:56:36)

Improve formatting of single quote in Debug of CxxString


Patch:
diff --git a/src/lossy.rs b/src/lossy.rs
index d68a2e7b..8ccf0f93 100644
--- a/src/lossy.rs
+++ b/src/lossy.rs
@@ -37,7 +37,7 @@ pub fn debug(mut bytes: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
         let mut written = 0;
         for (i, ch) in valid.char_indices() {
             let esc = ch.escape_debug();
-            if esc.len() != 1 {
+            if esc.len() != 1 && ch != '\'' {
                 f.write_str(&valid[written..i])?;
                 for ch in esc {
                     f.write_char(ch)?;


Test patch:
diff --git a/tests/cxx_string.rs b/tests/cxx_string.rs
index be693cd2..67444fa5 100644
--- a/tests/cxx_string.rs
+++ b/tests/cxx_string.rs
@@ -13,3 +13,10 @@ fn test_async_cxx_string() {
     fn assert_send(_: impl Send) {}
     assert_send(f());
 }
+
+#[test]
+fn test_debug() {
+    let_cxx_string!(s = "x\"y\'z");
+
+    assert_eq!(format!("{:?}", s), r#""x\"y'z""#);
+}
Repository: dtolnay/cxx
Pull request #1202 (2023-03-23 22:14:10)

Implement fmt::Write for CxxString
This allows formatted writes (e.g. with the `write!()` macro) directly on a `CxxString`. This can eliminate a needless allocation from formatting to a `String` then converting to a `CxxString`.

While testing this, I ran into #1201. Nothing else stood out as unusual.

parasyte: Great observation! I can add an impl for io::Writer on `CxxVec` to this PR. That should address the semver hazards. Are there any other concerns that come to mind?
parasyte: @dtolnay I have addressed the concerns with breaking changes by implementing both `fmt::Write` and `io::Write`. I decided not to do anything with `CxxVector` because I don't need it and it's more work to test.

Patch:
diff --git a/src/cxx_string.rs b/src/cxx_string.rs
index 9ecbcc64..c32648dd 100644
--- a/src/cxx_string.rs
+++ b/src/cxx_string.rs
@@ -5,7 +5,7 @@ use alloc::borrow::Cow;
 #[cfg(feature = "alloc")]
 use alloc::string::String;
 use core::cmp::Ordering;
-use core::fmt::{self, Debug, Display};
+use core::fmt::{self, Debug, Display, Write};
 use core::hash::{Hash, Hasher};
 use core::marker::{PhantomData, PhantomPinned};
 use core::mem::MaybeUninit;
@@ -257,6 +257,27 @@ impl Hash for CxxString {
     }
 }
 
+impl Write for Pin<&mut CxxString> {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        self.as_mut().push_str(s);
+
+        Ok(())
+    }
+}
+
+#[cfg(feature = "std")]
+impl std::io::Write for Pin<&mut CxxString> {
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        self.as_mut().push_bytes(buf);
+
+        Ok(buf.len())
+    }
+
+    fn flush(&mut self) -> std::io::Result<()> {
+        Ok(())
+    }
+}
+
 #[doc(hidden)]
 #[repr(C)]
 pub struct StackString {


Test patch:
diff --git a/tests/cxx_string.rs b/tests/cxx_string.rs
index 67444fa5..038fc72c 100644
--- a/tests/cxx_string.rs
+++ b/tests/cxx_string.rs
@@ -1,4 +1,5 @@
 use cxx::{let_cxx_string, CxxString};
+use std::fmt::Write as _;
 
 #[test]
 fn test_async_cxx_string() {
@@ -20,3 +21,24 @@ fn test_debug() {
 
     assert_eq!(format!("{:?}", s), r#""x\"y'z""#);
 }
+
+#[test]
+fn test_fmt_write() {
+    let_cxx_string!(s = "");
+
+    let name = "world";
+    write!(s, "Hello, {name}!").unwrap();
+    assert_eq!(s.to_str(), Ok("Hello, world!"));
+
+    write!(s, "\nAnd friends!").unwrap();
+    assert_eq!(s.to_str(), Ok("Hello, world!\nAnd friends!"));
+}
+
+#[test]
+fn test_io_write() {
+    let_cxx_string!(s = "");
+    let mut reader: &[u8] = b"Hello, world!";
+
+    std::io::copy(&mut reader, &mut s).unwrap();
+    assert_eq!(s.to_str(), Ok("Hello, world!"));
+}
Repository: dtolnay/cxx
Pull request #1367 (2024-08-13 20:14:23)

Ergonomics: allow constructing `rust::Slice` from any C++ container.
After this commit, it is possible to explicitly construct `rust::Slice` from a reference to any continguous C++ container (any container that exposes `data` and `size` accessors).

The new constructor results in a slightly more ergonomic code, by removing the need to explicit extract and pass `c.data()` and `c.size()` at a callsite of a `rust::Slice` constructor.  The callsites using the new constructor are also more obviously correct, because they doesn't require double-checking that the passed `data` and `size` match.

The implementation of the new constructor mimics `std::span` from C++20, but for C++11 compatibility reimplements `std::size` / `std::ranges::size` in a pedestrian way (same for `std::data` / `std::ranges::data`).


Patch:
diff --git a/include/cxx.h b/include/cxx.h
index 00228255..3414e4c8 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -176,6 +176,9 @@ public:
   Slice() noexcept;
   Slice(T *, std::size_t count) noexcept;
 
+  template <typename C>
+  explicit Slice(C& c) : Slice(c.data(), c.size()) {}
+
   Slice &operator=(const Slice<T> &) &noexcept = default;
   Slice &operator=(Slice<T> &&) &noexcept = default;
 


Test patch:
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 8cf74beb..ca71276f 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -884,6 +884,12 @@ extern "C" const char *cxx_run_test() noexcept {
   rust::String bad_utf16_rstring = rust::String::lossy(bad_utf16_literal);
   ASSERT(bad_utf8_rstring == bad_utf16_rstring);
 
+  std::vector<int> cpp_vec{1, 2, 3};
+  rust::Slice<int> slice_of_cpp_vec(cpp_vec);
+  ASSERT(slice_of_cpp_vec.data() == cpp_vec.data());
+  ASSERT(slice_of_cpp_vec.size() == cpp_vec.size());
+  ASSERT(slice_of_cpp_vec[0] == 1);
+
   rust::Vec<int> vec1{1, 2};
   rust::Vec<int> vec2{3, 4};
   swap(vec1, vec2);
Repository: dtolnay/paste
Pull request #18 (2020-03-19 20:18:41)

Add trybuild tests


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index ac2cb5c..5d7c22b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,6 +15,10 @@ travis-ci = { repository = "dtolnay/paste" }
 paste-impl = { version = "=0.1.7", path = "impl" }
 proc-macro-hack = "0.5.9"
 
+[dev-dependencies]
+rustversion = "1.0"
+trybuild = "1.0"
+
 [workspace]
 members = ["impl"]
 


Test patch:
diff --git a/tests/compiletest.rs b/tests/compiletest.rs
new file mode 100644
index 0000000..f9aea23
--- /dev/null
+++ b/tests/compiletest.rs
@@ -0,0 +1,6 @@
+#[rustversion::attr(not(nightly), ignore)]
+#[test]
+fn ui() {
+    let t = trybuild::TestCases::new();
+    t.compile_fail("tests/ui/*.rs");
+}
diff --git a/tests/ui/no-env-var.rs b/tests/ui/no-env-var.rs
new file mode 100644
index 0000000..87aad35
--- /dev/null
+++ b/tests/ui/no-env-var.rs
@@ -0,0 +1,5 @@
+paste::item! {
+    fn [<a env!("PASTE_UNKNOWN") b>]() {}
+}
+
+fn main() {}
diff --git a/tests/ui/no-env-var.stderr b/tests/ui/no-env-var.stderr
new file mode 100644
index 0000000..2ec206d
--- /dev/null
+++ b/tests/ui/no-env-var.stderr
@@ -0,0 +1,5 @@
+error: no such env var
+ --> $DIR/no-env-var.rs:2:17
+  |
+2 |     fn [<a env!("PASTE_UNKNOWN") b>]() {}
+  |                 ^^^^^^^^^^^^^^^
diff --git a/tests/ui/unexpected-modifier.rs b/tests/ui/unexpected-modifier.rs
new file mode 100644
index 0000000..00a1e99
--- /dev/null
+++ b/tests/ui/unexpected-modifier.rs
@@ -0,0 +1,5 @@
+paste::item! {
+    fn [<:lower x>]() {}
+}
+
+fn main() {}
diff --git a/tests/ui/unexpected-modifier.stderr b/tests/ui/unexpected-modifier.stderr
new file mode 100644
index 0000000..4a518b8
--- /dev/null
+++ b/tests/ui/unexpected-modifier.stderr
@@ -0,0 +1,5 @@
+error: unexpected modifier
+ --> $DIR/unexpected-modifier.rs:2:10
+  |
+2 |     fn [<:lower x>]() {}
+  |          ^^^^^^
diff --git a/tests/ui/unsupported-modifier.rs b/tests/ui/unsupported-modifier.rs
new file mode 100644
index 0000000..9af4c4a
--- /dev/null
+++ b/tests/ui/unsupported-modifier.rs
@@ -0,0 +1,5 @@
+paste::item! {
+    fn [<a:upper>]() {}
+}
+
+fn main() {}
diff --git a/tests/ui/unsupported-modifier.stderr b/tests/ui/unsupported-modifier.stderr
new file mode 100644
index 0000000..6e3da50
--- /dev/null
+++ b/tests/ui/unsupported-modifier.stderr
@@ -0,0 +1,5 @@
+error: unsupported modifier
+ --> $DIR/unsupported-modifier.rs:2:11
+  |
+2 |     fn [<a:upper>]() {}
+  |           ^^^^^^
Repository: dtolnay/paste
Pull request #20 (2020-03-27 20:33:32)

Add :upper modifier
This adds the :upper modifier, with tests. I wasn't sure how you want to handle the nightly test at `tests/ui/unsupported-modifier.rs` so I just left it. I imagine you'll just want to delete it?

This addresses #13 


Patch:
diff --git a/impl/src/lib.rs b/impl/src/lib.rs
index 97190d6..4f19d5a 100644
--- a/impl/src/lib.rs
+++ b/impl/src/lib.rs
@@ -181,6 +181,8 @@ fn paste_segments(span: Span, segments: &[Segment]) -> Result<TokenStream> {
                 };
                 if ident == "lower" {
                     evaluated.push(last.to_lowercase());
+                } else if ident == "upper" {
+                    evaluated.push(last.to_uppercase());
                 } else {
                     return Err(Error::new_spanned(span, "unsupported modifier"));
                 }


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index ce248f9..9d1d7e1 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -199,3 +199,29 @@ fn test_env_to_lower() {
         let _ = Libpaste;
     }
 }
+
+mod test_to_upper {
+    macro_rules! m {
+        ($id:ident) => {
+            paste::item! {
+                const [<MY_ $id:upper _HERE>]: &str = stringify!([<$id:upper>]);
+            }
+        };
+    }
+
+    m!(Test);
+
+    #[test]
+    fn test_to_upper() {
+        assert_eq!(MY_TEST_HERE, "TEST");
+    }
+}
+
+#[test]
+fn test_env_to_upper() {
+    paste::expr! {
+        const [<LIB env!("CARGO_PKG_NAME"):upper>]: &str = "libpaste";
+
+        let _ = LIBPASTE;
+    }
+}
Repository: dtolnay/paste
Pull request #60 (2020-12-10 01:26:54)

Preprocess bracketed paste inside of attribute args
Such as `#[attr(k = [<val $ue>])]`


Patch:
diff --git a/src/lib.rs b/src/lib.rs
index 01d6070..794ef3b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -208,15 +208,12 @@ fn expand(input: TokenStream, contains_paste: &mut bool) -> Result<TokenStream>
                     *contains_paste = true;
                 } else {
                     let mut group_contains_paste = false;
-                    let nested = match delimiter {
-                        Delimiter::Bracket if lookbehind == Lookbehind::Pound => {
-                            expand_attr(content, span, &mut group_contains_paste)?
-                        }
-                        Delimiter::Bracket if lookbehind == Lookbehind::PoundBang => {
-                            expand_attr(content, span, &mut group_contains_paste)?
-                        }
-                        _ => expand(content, &mut group_contains_paste)?,
-                    };
+                    let mut nested = expand(content, &mut group_contains_paste)?;
+                    if delimiter == Delimiter::Bracket
+                        && (lookbehind == Lookbehind::Pound || lookbehind == Lookbehind::PoundBang)
+                    {
+                        nested = expand_attr(nested, span, &mut group_contains_paste)?
+                    }
                     let group = if group_contains_paste {
                         let mut group = Group::new(delimiter, nested);
                         group.set_span(span);


Test patch:
diff --git a/tests/test_attr.rs b/tests/test_attr.rs
index 01abbfe..f2a5bf0 100644
--- a/tests/test_attr.rs
+++ b/tests/test_attr.rs
@@ -12,11 +12,15 @@ fn test_attr() {
 
         #[::paste_test_suite::paste_test(k = "val" "ue")]
         struct C;
+
+        #[paste_test(k = "va" [<l u>] e)]
+        struct D;
     }
 
     let _ = A;
     let _ = B;
     let _ = C;
+    let _ = D;
 }
 
 #[test]
Repository: dtolnay/paste
Pull request #78 (2022-03-27 20:17:55)

Parse unicode character literals
This is especially important because the current proc macro implementation likes making these too much &mdash; https://github.com/rust-lang/rust/pull/95343. In a proc macro `Literal::character('0')` will produce `'\u{30}'` rather than the expected `'0'`.


Patch:
diff --git a/src/lib.rs b/src/lib.rs
index e693cb2..8f7e95f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -155,6 +155,7 @@ use crate::attr::expand_attr;
 use crate::error::{Error, Result};
 use crate::segment::Segment;
 use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
+use std::char;
 use std::iter;
 use std::panic;
 
@@ -364,6 +365,16 @@ fn parse_bracket_as_segments(input: TokenStream, scope: Span) -> Result<Vec<Segm
 
     for segment in &mut segments {
         if let Segment::String(string) = segment {
+            if string.value.starts_with("'\\u{") {
+                let hex = &string.value[4..string.value.len() - 2];
+                if let Ok(unsigned) = u32::from_str_radix(hex, 16) {
+                    if let Some(ch) = char::from_u32(unsigned) {
+                        string.value.clear();
+                        string.value.push(ch);
+                        continue;
+                    }
+                }
+            }
             if string.value.contains(&['#', '\\', '.', '+'][..])
                 || string.value.starts_with("b'")
                 || string.value.starts_with("b\"")


Test patch:
diff --git a/tests/test_expr.rs b/tests/test_expr.rs
index 13a0276..3d3b977 100644
--- a/tests/test_expr.rs
+++ b/tests/test_expr.rs
@@ -40,6 +40,9 @@ fn test_literals() {
 
     let pasted = paste!([<CONST r"0">]);
     assert_eq!(pasted, CONST0);
+
+    let pasted = paste!([<CONST '\u{30}'>]);
+    assert_eq!(pasted, CONST0);
 }
 
 #[test]
Repository: dtolnay/paste
Pull request #86 (2022-08-31 07:01:37)

Produce clone of input tokenstream if contains no paste
This is a partial mitigation for #85. It _only_ helps if the entire macro input contains no pastes `[<`&hellip;`>]`, such as in the minimal repro in that issue. Every other case involving `Delimiter::None` still remains broken due to https://github.com/rust-lang/rust/issues/67062.


Patch:
diff --git a/src/lib.rs b/src/lib.rs
index 82f0815..983ba06 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -164,8 +164,18 @@ use std::panic;
 pub fn paste(input: TokenStream) -> TokenStream {
     let mut contains_paste = false;
     let flatten_single_interpolation = true;
-    match expand(input, &mut contains_paste, flatten_single_interpolation) {
-        Ok(expanded) => expanded,
+    match expand(
+        input.clone(),
+        &mut contains_paste,
+        flatten_single_interpolation,
+    ) {
+        Ok(expanded) => {
+            if contains_paste {
+                expanded
+            } else {
+                input
+            }
+        }
         Err(err) => err.to_compile_error(),
     }
 }


Test patch:
diff --git a/tests/test_expr.rs b/tests/test_expr.rs
index 3d3b977..8ad5de9 100644
--- a/tests/test_expr.rs
+++ b/tests/test_expr.rs
@@ -247,3 +247,26 @@ mod test_local_setter {
         assert_eq!(a.val, 42);
     }
 }
+
+// https://github.com/dtolnay/paste/issues/85
+#[test]
+fn test_top_level_none_delimiter() {
+    macro_rules! clone {
+        ($val:expr) => {
+            paste! {
+                $val.clone()
+            }
+        };
+    }
+
+    #[derive(Clone)]
+    struct A;
+
+    impl A {
+        fn consume_self(self) {
+            let _ = self;
+        }
+    }
+
+    clone!(&A).consume_self();
+}
Repository: dtolnay/cxx
Pull request #586 (2020-12-18 20:58:19)

Add c_str() member function for rust::String
Matches https://en.cppreference.com/w/cpp/string/basic_string/c_str.
#224


Patch:
diff --git a/book/src/binding/string.md b/book/src/binding/string.md
index c4a1b6ad..8dc1ee4b 100644
--- a/book/src/binding/string.md
+++ b/book/src/binding/string.md
@@ -33,6 +33,8 @@ public:
   size_t size() const noexcept;
   size_t length() const noexcept;
 
+  const char *c_str() noexcept;
+
   using iterator = char *;
   iterator begin() noexcept;
   iterator end() noexcept;
diff --git a/include/cxx.h b/include/cxx.h
index f41dde7c..fc630958 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -53,6 +53,8 @@ public:
   std::size_t size() const noexcept;
   std::size_t length() const noexcept;
 
+  const char *c_str() noexcept;
+
   using iterator = char *;
   iterator begin() noexcept;
   iterator end() noexcept;
diff --git a/src/cxx.cc b/src/cxx.cc
index 776663e7..6863129a 100644
--- a/src/cxx.cc
+++ b/src/cxx.cc
@@ -42,6 +42,7 @@ bool cxxbridge1$string$from(rust::String *self, const char *ptr,
 void cxxbridge1$string$drop(rust::String *self) noexcept;
 const char *cxxbridge1$string$ptr(const rust::String *self) noexcept;
 std::size_t cxxbridge1$string$len(const rust::String *self) noexcept;
+void cxxbridge1$string$reserve_total(rust::String *self, size_t cap) noexcept;
 
 // rust::Str
 bool cxxbridge1$str$valid(const char *ptr, std::size_t len) noexcept;
@@ -127,6 +128,14 @@ std::size_t String::length() const noexcept {
   return cxxbridge1$string$len(this);
 }
 
+const char *String::c_str() noexcept {
+  auto len = this->length();
+  cxxbridge1$string$reserve_total(this, len + 1);
+  auto ptr = this->data();
+  const_cast<char *>(ptr)[len] = '\0';
+  return ptr;
+}
+
 String::iterator String::begin() noexcept {
   return const_cast<char *>(this->data());
 }
diff --git a/src/symbols/rust_string.rs b/src/symbols/rust_string.rs
index 944aec44..91fd78a3 100644
--- a/src/symbols/rust_string.rs
+++ b/src/symbols/rust_string.rs
@@ -45,3 +45,8 @@ unsafe extern "C" fn string_ptr(this: &String) -> *const u8 {
 unsafe extern "C" fn string_len(this: &String) -> usize {
     this.len()
 }
+
+#[export_name = "cxxbridge1$string$reserve_total"]
+unsafe extern "C" fn string_reserve_total(this: &mut String, cap: usize) {
+    this.reserve(cap);
+}


Test patch:
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index eeca98a5..ca2bb6a8 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -716,6 +716,12 @@ extern "C" const char *cxx_run_test() noexcept {
     ASSERT((second.*cmp)(sec) == second_sec);
   }
 
+  rust::String cstring = "test";
+  ASSERT(cstring.length() == 4);
+  ASSERT(strncmp(cstring.data(), "test", 4) == 0);
+  ASSERT(strncmp(cstring.c_str(), "test", 5) == 0);
+  ASSERT(cstring.length() == 4);
+
   cxx_test_suite_set_correct();
   return nullptr;
 }