Skip to content

Commit 315a6da

Browse files
committed
fix(cpp): escape cpp keywords
Fixes #1419 Signed-off-by: Bailey Hayes <[email protected]>
1 parent 799fdb6 commit 315a6da

File tree

2 files changed

+117
-13
lines changed

2 files changed

+117
-13
lines changed

crates/cpp/src/lib.rs

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -797,8 +797,23 @@ struct CppInterfaceGenerator<'a> {
797797

798798
impl CppInterfaceGenerator<'_> {
799799
fn types(&mut self, iface: InterfaceId) {
800-
let iface = &self.resolve().interfaces[iface];
801-
for (name, id) in iface.types.iter() {
800+
let iface_data = &self.resolve().interfaces[iface];
801+
802+
// First pass: emit forward declarations for all resources
803+
// This ensures resources can reference each other in method signatures
804+
for (name, id) in iface_data.types.iter() {
805+
let ty = &self.resolve().types[*id];
806+
if matches!(&ty.kind, TypeDefKind::Resource) {
807+
let pascal = name.to_upper_camel_case();
808+
let guest_import = self.gen.imported_interfaces.contains(&iface);
809+
let namespc = namespace(self.resolve, &ty.owner, !guest_import, &self.gen.opts);
810+
self.gen.h_src.change_namespace(&namespc);
811+
uwriteln!(self.gen.h_src.src, "class {pascal};");
812+
}
813+
}
814+
815+
// Second pass: emit full type definitions
816+
for (name, id) in iface_data.types.iter() {
802817
self.define_type(name, *id);
803818
}
804819
}
@@ -1055,7 +1070,7 @@ impl CppInterfaceGenerator<'_> {
10551070
""
10561071
};
10571072
res.arguments.push((
1058-
name.to_snake_case(),
1073+
to_c_ident(name),
10591074
self.type_name(param, &res.namespace, Flavor::Argument(abi_variant)) + is_pointer,
10601075
));
10611076
}
@@ -1662,7 +1677,7 @@ impl CppInterfaceGenerator<'_> {
16621677
uwriteln!(self.gen.h_src.src, "struct {pascal} {{");
16631678
for field in record.fields.iter() {
16641679
let typename = self.type_name(&field.ty, namespc, flavor);
1665-
let fname = field.name.to_snake_case();
1680+
let fname = to_c_ident(&field.name);
16661681
uwriteln!(self.gen.h_src.src, "{typename} {fname};");
16671682
}
16681683
uwriteln!(self.gen.h_src.src, "}};");
@@ -1703,7 +1718,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a>
17031718
for field in record.fields.iter() {
17041719
Self::docs(&mut self.gen.h_src.src, &field.docs);
17051720
let typename = self.type_name(&field.ty, &namespc, Flavor::InStruct);
1706-
let fname = field.name.to_snake_case();
1721+
let fname = to_c_ident(&field.name);
17071722
uwriteln!(self.gen.h_src.src, "{typename} {fname};");
17081723
}
17091724
uwriteln!(self.gen.h_src.src, "}};");
@@ -1884,7 +1899,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a>
18841899
uwriteln!(self.gen.h_src.src, "k_None = 0,");
18851900
for (n, field) in flags.flags.iter().enumerate() {
18861901
Self::docs(&mut self.gen.h_src.src, &field.docs);
1887-
let fname = field.name.to_pascal_case();
1902+
let fname = to_c_ident(&field.name).to_pascal_case();
18881903
uwriteln!(self.gen.h_src.src, "k{fname} = (1ULL<<{n}),");
18891904
}
18901905
uwriteln!(self.gen.h_src.src, "}};");
@@ -1926,7 +1941,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a>
19261941
let mut all_types = String::new();
19271942
for case in variant.cases.iter() {
19281943
Self::docs(&mut self.gen.h_src.src, &case.docs);
1929-
let case_pascal = case.name.to_pascal_case();
1944+
let case_pascal = to_c_ident(&case.name).to_pascal_case();
19301945
if !all_types.is_empty() {
19311946
all_types += ", ";
19321947
}
@@ -1985,7 +2000,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a>
19852000
uwriteln!(
19862001
self.gen.h_src.src,
19872002
" k{} = {i},",
1988-
case.name.to_pascal_case(),
2003+
to_c_ident(&case.name).to_pascal_case(),
19892004
);
19902005
}
19912006
uwriteln!(self.gen.h_src.src, "}};\n");
@@ -2655,7 +2670,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> {
26552670
uwriteln!(self.src, "case {}: {{", i);
26562671
if let Some(ty) = case.ty.as_ref() {
26572672
let ty = self.gen.type_name(ty, &self.namespace, Flavor::InStruct);
2658-
let case = format!("{elem_ns}::{}", case.name.to_pascal_case());
2673+
let case =
2674+
format!("{elem_ns}::{}", to_c_ident(&case.name).to_pascal_case());
26592675
uwriteln!(
26602676
self.src,
26612677
"{} &{} = std::get<{case}>({}.variants).value;",
@@ -2686,24 +2702,26 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> {
26862702
let resultno = self.tmp();
26872703
let result = format!("variant{resultno}");
26882704

2689-
uwriteln!(self.src, "{ty} {result};");
2690-
26912705
let op0 = &operands[0];
26922706

2707+
// Use std::optional to avoid default constructor issues
2708+
self.gen.gen.dependencies.needs_optional = true;
2709+
uwriteln!(self.src, "std::optional<{ty}> {result}_opt;");
26932710
uwriteln!(self.src, "switch ({op0}) {{");
26942711
for (i, (case, (block, block_results))) in
26952712
variant.cases.iter().zip(blocks).enumerate()
26962713
{
2697-
let tp = case.name.clone().to_pascal_case();
2714+
let tp = to_c_ident(&case.name).to_pascal_case();
26982715
uwriteln!(self.src, "case {i}: {{ {block}");
26992716
uwriteln!(
27002717
self.src,
2701-
"{result}.variants = {ty}::{tp}{{{}}};",
2718+
"{result}_opt = {ty}{{{{{ty}::{tp}{{{}}}}}}};",
27022719
move_if_necessary(&block_results.first().cloned().unwrap_or_default())
27032720
);
27042721
uwriteln!(self.src, "}} break;");
27052722
}
27062723
uwriteln!(self.src, "}}");
2724+
uwriteln!(self.src, "{ty} {result} = std::move(*{result}_opt);");
27072725

27082726
results.push(result);
27092727
}

tests/codegen/cpp-http-owned.wit

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Test file for C++ code generation fixes:
2+
// 1. C++ keyword escaping (parameter named 'this')
3+
// 2. Forward declarations for resources
4+
5+
package test:cpp-http-owned;
6+
7+
world cpp-http-owned {
8+
// Test 1: Resource with forward reference
9+
// IncomingBody returns FutureTrailers (forward declaration needed)
10+
import test-interface: interface {
11+
resource incoming-body {
12+
// Static method with 'this' parameter (C++ keyword)
13+
finish: static func(this: incoming-body) -> future-trailers;
14+
}
15+
16+
resource future-trailers {
17+
get: func() -> option<string>;
18+
}
19+
20+
resource request {
21+
// Method that returns another resource (forward declaration test)
22+
consume: func() -> incoming-body;
23+
}
24+
}
25+
26+
// Test 2: Export interface with Owned types
27+
export test-exports: interface {
28+
resource request-data {
29+
constructor();
30+
}
31+
32+
resource response {
33+
constructor();
34+
}
35+
36+
// Function requiring ::Owned syntax
37+
handle: func(req: request-data, resp: response);
38+
}
39+
40+
// More C++ keywords as parameters
41+
export keyword-test: func(this: string, class: u32, template: bool) -> string;
42+
43+
// Test 3: C++ keywords in records, enums, flags, and variants
44+
export keyword-types: interface {
45+
// Record with C++ keyword field names
46+
record field-keywords {
47+
class: string,
48+
return: u32,
49+
default: bool,
50+
case: s64,
51+
namespace: string,
52+
}
53+
54+
// Enum with C++ keyword case names
55+
enum case-keywords {
56+
class,
57+
return,
58+
default,
59+
case,
60+
template,
61+
}
62+
63+
// Flags with C++ keyword names
64+
flags flag-keywords {
65+
class,
66+
return,
67+
default,
68+
case,
69+
namespace,
70+
}
71+
72+
// Variant with C++ keyword case names
73+
variant variant-keywords {
74+
class(string),
75+
return(u32),
76+
default,
77+
case(bool),
78+
}
79+
80+
// Functions using these types
81+
test-record: func(input: field-keywords) -> field-keywords;
82+
test-enum: func(input: case-keywords) -> case-keywords;
83+
test-flags: func(input: flag-keywords) -> flag-keywords;
84+
test-variant: func(input: variant-keywords) -> variant-keywords;
85+
}
86+
}

0 commit comments

Comments
 (0)