Skip to content

Commit 4723722

Browse files
Strip r# prefix from raw identifiers in generated C++ names
extern "C++" parameters/fields named with a raw identifier (e.g. r#type, used only to dodge a Rust keyword) were emitted verbatim into generated C++, producing invalid code like `void foo(::std::int32_t r#type) noexcept`. ForeignName::parse is the single place that turns a Rust identifier into its C++-facing name, both for the implicit default name and for explicit #[cxx_name = ...] attributes, so strip the r# prefix there. This mirrors the existing precedent in QualifiedName::parse_unquoted (syntax/qualified.rs) for namespace segments. Fixes #1324 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 986a6dd commit 4723722

2 files changed

Lines changed: 28 additions & 1 deletion

File tree

syntax/names.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ impl ForeignName {
4040
// non-alphanumeric characters (`operator++`).
4141
match Ident::parse_any.parse_str(text) {
4242
Ok(ident) => {
43-
let text = ident.to_string();
43+
let mut text = ident.to_string();
44+
if let Some(unraw) = text.strip_prefix("r#") {
45+
text = unraw.to_owned();
46+
}
4447
Ok(ForeignName { text })
4548
}
4649
Err(err) => Err(Error::new(span, err)),

tests/cxx_gen.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ const BRIDGE0: &str = r#"
1212
}
1313
"#;
1414

15+
const BRIDGE_RAW_IDENTIFIER: &str = r#"
16+
#[cxx::bridge]
17+
mod ffi {
18+
unsafe extern "C++" {
19+
pub fn highlight(r#type: i32);
20+
}
21+
}
22+
"#;
23+
1524
#[test]
1625
fn test_extern_c_function() {
1726
let opt = Opt::default();
@@ -23,6 +32,21 @@ fn test_extern_c_function() {
2332
assert!(output.contains(&format!("void {CXXPREFIX}$do_cpp_thing(::rust::Str foo)")));
2433
}
2534

35+
#[test]
36+
fn test_raw_identifier_param() {
37+
let opt = Opt::default();
38+
let source = BRIDGE_RAW_IDENTIFIER.parse().unwrap();
39+
let generated = generate_header_and_cc(source, &opt).unwrap();
40+
let header = str::from_utf8(&generated.header).unwrap();
41+
let implementation = str::from_utf8(&generated.implementation).unwrap();
42+
43+
assert!(!header.contains("r#"));
44+
assert!(!implementation.contains("r#"));
45+
assert!(implementation.contains(&format!(
46+
"void {CXXPREFIX}$highlight(::std::int32_t type) noexcept",
47+
)));
48+
}
49+
2650
#[test]
2751
fn test_impl_annotation() {
2852
let mut opt = Opt::default();

0 commit comments

Comments
 (0)