Skip to content

Commit 779dc2b

Browse files
youknowonetgross35
authored andcommitted
[ctest] support r#keyword
1 parent 30fd290 commit 779dc2b

6 files changed

Lines changed: 128 additions & 12 deletions

File tree

ctest/src/ast/field.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,69 @@ impl Field {
1313
pub fn ident(&self) -> &str {
1414
&self.ident
1515
}
16+
17+
/// Return the identifier escaped for Rust source output when needed.
18+
pub fn rust_ident(&self) -> String {
19+
if is_rust_keyword(&self.ident) {
20+
format!("r#{}", self.ident)
21+
} else {
22+
self.ident.to_string()
23+
}
24+
}
25+
}
26+
27+
fn is_rust_keyword(ident: &str) -> bool {
28+
matches!(
29+
ident,
30+
"as" | "break"
31+
| "const"
32+
| "continue"
33+
| "crate"
34+
| "else"
35+
| "enum"
36+
| "extern"
37+
| "false"
38+
| "fn"
39+
| "for"
40+
| "if"
41+
| "impl"
42+
| "in"
43+
| "let"
44+
| "loop"
45+
| "match"
46+
| "mod"
47+
| "move"
48+
| "mut"
49+
| "pub"
50+
| "ref"
51+
| "return"
52+
| "self"
53+
| "Self"
54+
| "static"
55+
| "struct"
56+
| "super"
57+
| "trait"
58+
| "true"
59+
| "type"
60+
| "unsafe"
61+
| "use"
62+
| "where"
63+
| "while"
64+
| "async"
65+
| "await"
66+
| "dyn"
67+
| "abstract"
68+
| "become"
69+
| "box"
70+
| "do"
71+
| "final"
72+
| "macro"
73+
| "override"
74+
| "priv"
75+
| "typeof"
76+
| "unsized"
77+
| "virtual"
78+
| "yield"
79+
| "try"
80+
)
1681
}

ctest/src/ffi_items.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,17 @@ fn collect_fields(fields: &Punctuated<syn::Field, syn::Token![,]>) -> Vec<Field>
9696
fields
9797
.iter()
9898
.filter_map(|field| {
99-
field.ident.as_ref().map(|ident| Field {
100-
public: is_visible(&field.vis),
101-
ident: ident.to_string().into_boxed_str(),
102-
ty: field.ty.clone(),
99+
field.ident.as_ref().map(|ident| {
100+
let ident = ident.to_string();
101+
Field {
102+
public: is_visible(&field.vis),
103+
ident: ident
104+
.strip_prefix("r#")
105+
.unwrap_or(&ident)
106+
.to_string()
107+
.into_boxed_str(),
108+
ty: field.ty.clone(),
109+
}
103110
})
104111
})
105112
.collect()

ctest/templates/test.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -188,19 +188,19 @@ mod generated_tests {
188188
let uninit_ty = uninit_ty.as_ptr();
189189

190190
{# /* SAFETY: we assume the field access doesn't wrap */ #}
191-
let ty_ptr = unsafe { &raw const (*uninit_ty).{{ item.field.ident() }} };
191+
let ty_ptr = unsafe { &raw const (*uninit_ty).{{ item.field.rust_ident() }} };
192192
{# /* SAFETY: we assume that all zeros is a valid bitpattern for `ty_ptr`, otherwise the
193193
* test should be skipped. */ #}
194194
let val = unsafe { ty_ptr.read_unaligned() };
195195

196196
{# /* SAFETY: FFI call with no preconditions */ #}
197197
let ctest_field_offset = unsafe { ctest_offset_of__{{ item.id }}__{{ item.field.ident() }}() };
198-
check_same(offset_of!({{ item.id }}, {{ item.field.ident() }}) as u64, ctest_field_offset,
199-
"field offset `{{ item.field.ident() }}` of `{{ item.id }}`");
198+
check_same(offset_of!({{ item.id }}, {{ item.field.rust_ident() }}) as u64, ctest_field_offset,
199+
"field offset `{{ item.field.rust_ident() }}` of `{{ item.id }}`");
200200
{# /* SAFETY: FFI call with no preconditions */ #}
201201
let ctest_field_size = unsafe { ctest_size_of__{{ item.id }}__{{ item.field.ident() }}() };
202202
check_same(size_of_val(&val) as u64, ctest_field_size,
203-
"field size `{{ item.field.ident() }}` of `{{ item.id }}`");
203+
"field size `{{ item.field.rust_ident() }}` of `{{ item.id }}`");
204204
}
205205
{%- endfor +%}
206206

@@ -217,12 +217,12 @@ mod generated_tests {
217217
let ty_ptr = uninit_ty.as_ptr();
218218
// SAFETY: We don't read `field_ptr`, only compare the pointer itself.
219219
// The assumption is made that this does not wrap the address space.
220-
let field_ptr = unsafe { &raw const ((*ty_ptr).{{ item.field.ident() }}) };
220+
let field_ptr = unsafe { &raw const ((*ty_ptr).{{ item.field.rust_ident() }}) };
221221

222222
// SAFETY: FFI call with no preconditions
223223
let ctest_field_ptr = unsafe { ctest_field_ptr__{{ item.id }}__{{ item.field.ident() }}(ty_ptr) };
224224
check_same(field_ptr.cast(), ctest_field_ptr,
225-
"field pointer access `{{ item.field.ident() }}` of `{{ item.id }}`");
225+
"field pointer access `{{ item.field.rust_ident() }}` of `{{ item.id }}`");
226226
}
227227

228228
{%- endfor +%}
@@ -254,11 +254,11 @@ mod generated_tests {
254254
let bar = bar.as_ptr();
255255
{%- for field in item.fields +%}
256256

257-
let ty_ptr = unsafe { &raw const ((*bar).{{ field.ident() }}) };
257+
let ty_ptr = unsafe { &raw const ((*bar).{{ field.rust_ident() }}) };
258258
let val = unsafe { ty_ptr.read_unaligned() };
259259

260260
let size = size_of_val(&val);
261-
let off = offset_of!({{ item.id }}, {{ field.ident() }});
261+
let off = offset_of!({{ item.id }}, {{ field.rust_ident() }});
262262
v.push((off, size));
263263
{%- endfor +%}
264264
{# /* This vector contains `true` if the byte is padding and `false` if the byte is not

ctest/tests/basic.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,38 @@ fn test_entrypoint_invalid_syntax() {
174174

175175
assert!(fails)
176176
}
177+
178+
#[test]
179+
fn test_raw_identifier_field() {
180+
let include_path = PathBuf::from("tests/input");
181+
let crate_path = include_path.join("raw_ident.rs");
182+
let library_path = "raw_ident.out.a";
183+
184+
let (mut gen_, out_dir) = default_generator(1, Some("raw_ident.h")).unwrap();
185+
gen_.rename_struct_ty(|ty| Some(ty.to_string()));
186+
let output_file = gen_.generate_files(&crate_path, library_path).unwrap();
187+
188+
let rust_output = fs::read_to_string(output_file.with_extension("rs")).unwrap();
189+
let c_output = fs::read_to_string(output_file.with_extension("c")).unwrap();
190+
191+
assert!(rust_output.contains("(*uninit_ty).r#type"));
192+
assert!(rust_output.contains("offset_of!(RawIdent, r#type)"));
193+
assert!(rust_output.contains("ctest_offset_of__RawIdent__type"));
194+
assert!(rust_output.contains("ctest_field_ptr__RawIdent__type"));
195+
196+
assert!(c_output.contains(", type)"));
197+
assert!(c_output.contains("->type"));
198+
assert!(!c_output.contains("r#type"));
199+
assert!(c_output.contains("ctest_offset_of__RawIdent__type"));
200+
assert!(c_output.contains("ctest_field_ptr__RawIdent__type"));
201+
202+
if env::var("TARGET_PLATFORM") == env::var("HOST_PLATFORM") {
203+
generate_test(&mut gen_, &crate_path, library_path).unwrap();
204+
let test_binary = __compile_test(&out_dir, crate_path, library_path).unwrap();
205+
let result = __run_test(test_binary);
206+
if let Err(err) = &result {
207+
eprintln!("Test failed: {err:?}");
208+
}
209+
assert!(result.is_ok());
210+
}
211+
}

ctest/tests/input/raw_ident.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#include <stdint.h>
2+
3+
typedef struct {
4+
int32_t type;
5+
} RawIdent;

ctest/tests/input/raw_ident.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#[repr(C)]
2+
pub struct RawIdent {
3+
pub r#type: i32,
4+
}

0 commit comments

Comments
 (0)