Skip to content

Commit 041ac9d

Browse files
mbyxtgross35
authored andcommitted
ctest: fix bug where functions and statics aren't skipped
1 parent 689e54d commit 041ac9d

File tree

9 files changed

+77
-906
lines changed

9 files changed

+77
-906
lines changed

ctest-next/src/generator.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,29 @@ impl TestGenerator {
647647
self
648648
}
649649

650+
/// Configures how Rust type alias names are translated to C.
651+
///
652+
/// # Examples
653+
///
654+
/// ```no_run
655+
/// use ctest_next::TestGenerator;
656+
///
657+
/// let mut cfg = TestGenerator::new();
658+
/// cfg.rename_alias(|c| {
659+
/// (c.ident() == "sighandler_t").then_some("sig_t".to_string())
660+
/// });
661+
/// ```
662+
pub fn rename_alias(&mut self, f: impl Fn(&Type) -> Option<String> + 'static) -> &mut Self {
663+
self.mapped_names.push(Box::new(move |item| {
664+
if let MapInput::Alias(t) = item {
665+
f(t)
666+
} else {
667+
None
668+
}
669+
}));
670+
self
671+
}
672+
650673
/// Configures how a Rust struct field is translated to a C struct field.
651674
///
652675
/// # Examples

ctest-next/src/template.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ impl TestTemplate {
400400
.as_ref()
401401
.is_some_and(|skip| skip(ident))
402402
};
403-
for func in helper.ffi_items.foreign_functions() {
403+
for func in helper.filtered_ffi_items.foreign_functions() {
404404
if should_skip_fn_test(func.ident()) {
405405
continue;
406406
}
@@ -423,7 +423,7 @@ impl TestTemplate {
423423
&mut self,
424424
helper: &TranslateHelper,
425425
) -> Result<(), TranslationError> {
426-
for static_ in helper.ffi_items.foreign_statics() {
426+
for static_ in helper.filtered_ffi_items.foreign_statics() {
427427
let rust_ty = static_.ty.to_token_stream().to_string().into_boxed_str();
428428

429429
let item = TestForeignStatic {

ctest-next/tests/basic.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,12 @@ fn test_skip_simple() {
9191
let library_path = "simple.out.with-skips.a";
9292

9393
let (mut gen_, out_dir) = default_generator(1, "simple.h").unwrap();
94-
gen_.skip_const(|c| c.ident() == "B");
94+
gen_.skip_const(|c| c.ident() == "B" || c.ident() == "A")
95+
.skip_alias(|a| a.ident() == "Byte")
96+
.skip_struct(|s| s.ident() == "Person")
97+
.skip_union(|u| u.ident() == "Word")
98+
.skip_fn(|f| f.ident() == "calloc")
99+
.skip_static(|s| s.ident() == "byte");
95100

96101
check_entrypoint(&mut gen_, out_dir, crate_path, library_path, include_path);
97102
}

ctest-next/tests/input/simple.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
typedef uint8_t Byte;
44

5+
Byte byte = 0x42;
6+
57
struct Person
68
{
79
const char *name;
@@ -17,3 +19,6 @@ union Word
1719

1820
#define A "abc"
1921
#define C_B "bac"
22+
23+
extern void *calloc(size_t num, size_t size);
24+
extern Byte byte;

ctest-next/tests/input/simple.out.with-renames.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,16 @@ union Word ctest_roundtrip__Word(
242242
# pragma warning(disable:4191)
243243
#endif
244244

245+
ctest_void_func ctest_foreign_fn__calloc(void) {
246+
return (ctest_void_func)calloc;
247+
}
248+
245249
#ifdef _MSC_VER
246250
# pragma warning(default:4191)
247251
#endif
252+
253+
// Return a pointer to the static variable content.
254+
void *ctest_static__byte(void) {
255+
// FIXME(ctest): Not correct due to casting the function to a data pointer.
256+
return (void *)&byte;
257+
}

ctest-next/tests/input/simple.out.with-renames.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,28 @@ mod generated_tests {
742742
}
743743
}
744744
}
745+
746+
/// Check if the Rust and C side function pointers point to the same underlying function.
747+
pub fn ctest_foreign_fn_calloc() {
748+
extern "C" {
749+
fn ctest_foreign_fn__calloc() -> unsafe extern "C" fn();
750+
}
751+
let actual = unsafe { ctest_foreign_fn__calloc() } as u64;
752+
let expected = calloc as u64;
753+
check_same(actual, expected, "calloc function pointer");
754+
}
755+
756+
// Tests if the pointer to the static variable matches in both Rust and C.
757+
pub fn ctest_static_byte() {
758+
extern "C" {
759+
fn ctest_static__byte() -> *const Byte;
760+
}
761+
let actual = (&raw const byte).addr();
762+
let expected = unsafe {
763+
ctest_static__byte().addr()
764+
};
765+
check_same(actual, expected, "byte static");
766+
}
745767
}
746768

747769
use generated_tests::*;
@@ -780,4 +802,6 @@ fn run_all() {
780802
ctest_roundtrip_Byte();
781803
ctest_roundtrip_Person();
782804
ctest_roundtrip_Word();
805+
ctest_foreign_fn_calloc();
806+
ctest_static_byte();
783807
}

ctest-next/tests/input/simple.out.with-skips.c

Lines changed: 0 additions & 209 deletions
Original file line numberDiff line numberDiff line change
@@ -9,221 +9,12 @@
99

1010
typedef void (*ctest_void_func)(void);
1111

12-
static char *ctest_const_A_val_static = A;
13-
14-
// Define a function that returns a pointer to the value of the constant to test.
15-
// This will later be called on the Rust side via FFI.
16-
char *ctest_const_cstr__A(void) {
17-
return ctest_const_A_val_static;
18-
}
19-
20-
// Return the size of a type.
21-
uint64_t ctest_size_of__Byte(void) { return sizeof(Byte); }
22-
23-
// Return the alignment of a type.
24-
uint64_t ctest_align_of__Byte(void) { return _Alignof(Byte); }
25-
26-
// Return the size of a type.
27-
uint64_t ctest_size_of__Person(void) { return sizeof(struct Person); }
28-
29-
// Return the alignment of a type.
30-
uint64_t ctest_align_of__Person(void) { return _Alignof(struct Person); }
31-
32-
// Return the size of a type.
33-
uint64_t ctest_size_of__Word(void) { return sizeof(union Word); }
34-
35-
// Return the alignment of a type.
36-
uint64_t ctest_align_of__Word(void) { return _Alignof(union Word); }
37-
38-
// Return `1` if the type is signed, otherwise return `0`.
39-
// Casting -1 to the aliased type if signed evaluates to `-1 < 0`, if unsigned to `MAX_VALUE < 0`
40-
uint32_t ctest_signededness_of__Byte(void) {
41-
Byte all_ones = (Byte) -1;
42-
return all_ones < 0;
43-
}
44-
45-
// Return the offset of a struct/union field.
46-
uint64_t ctest_offset_of__Person__name(void) {
47-
return offsetof(struct Person, name);
48-
}
49-
50-
// Return the size of a struct/union field.
51-
uint64_t ctest_size_of__Person__name(void) {
52-
return sizeof(((struct Person){}).name);
53-
}
54-
55-
// Return the offset of a struct/union field.
56-
uint64_t ctest_offset_of__Person__age(void) {
57-
return offsetof(struct Person, age);
58-
}
59-
60-
// Return the size of a struct/union field.
61-
uint64_t ctest_size_of__Person__age(void) {
62-
return sizeof(((struct Person){}).age);
63-
}
64-
65-
// Return the offset of a struct/union field.
66-
uint64_t ctest_offset_of__Person__job(void) {
67-
return offsetof(struct Person, job);
68-
}
69-
70-
// Return the size of a struct/union field.
71-
uint64_t ctest_size_of__Person__job(void) {
72-
return sizeof(((struct Person){}).job);
73-
}
74-
75-
// Return the offset of a struct/union field.
76-
uint64_t ctest_offset_of__Word__word(void) {
77-
return offsetof(union Word, word);
78-
}
79-
80-
// Return the size of a struct/union field.
81-
uint64_t ctest_size_of__Word__word(void) {
82-
return sizeof(((union Word){}).word);
83-
}
84-
85-
// Return the offset of a struct/union field.
86-
uint64_t ctest_offset_of__Word__byte(void) {
87-
return offsetof(union Word, byte);
88-
}
89-
90-
// Return the size of a struct/union field.
91-
uint64_t ctest_size_of__Word__byte(void) {
92-
return sizeof(((union Word){}).byte);
93-
}
94-
95-
// Return a pointer to a struct/union field.
96-
// This field can have a normal data type, or it could be a function pointer or an array, which
97-
// have different syntax. A typedef is used for convenience, but the syntax must be precomputed.
98-
typedef const char **ctest_field_ty__Person__name;
99-
ctest_field_ty__Person__name
100-
ctest_field_ptr__Person__name(struct Person *b) {
101-
return &b->name;
102-
}
103-
104-
// Return a pointer to a struct/union field.
105-
// This field can have a normal data type, or it could be a function pointer or an array, which
106-
// have different syntax. A typedef is used for convenience, but the syntax must be precomputed.
107-
typedef uint8_t *ctest_field_ty__Person__age;
108-
ctest_field_ty__Person__age
109-
ctest_field_ptr__Person__age(struct Person *b) {
110-
return &b->age;
111-
}
112-
113-
// Return a pointer to a struct/union field.
114-
// This field can have a normal data type, or it could be a function pointer or an array, which
115-
// have different syntax. A typedef is used for convenience, but the syntax must be precomputed.
116-
typedef void (**ctest_field_ty__Person__job)(uint8_t, const char *);
117-
ctest_field_ty__Person__job
118-
ctest_field_ptr__Person__job(struct Person *b) {
119-
return &b->job;
120-
}
121-
122-
// Return a pointer to a struct/union field.
123-
// This field can have a normal data type, or it could be a function pointer or an array, which
124-
// have different syntax. A typedef is used for convenience, but the syntax must be precomputed.
125-
typedef uint16_t *ctest_field_ty__Word__word;
126-
ctest_field_ty__Word__word
127-
ctest_field_ptr__Word__word(union Word *b) {
128-
return &b->word;
129-
}
130-
131-
// Return a pointer to a struct/union field.
132-
// This field can have a normal data type, or it could be a function pointer or an array, which
133-
// have different syntax. A typedef is used for convenience, but the syntax must be precomputed.
134-
typedef Byte (*ctest_field_ty__Word__byte)[2];
135-
ctest_field_ty__Word__byte
136-
ctest_field_ptr__Word__byte(union Word *b) {
137-
return &b->byte;
138-
}
139-
14012
#ifdef _MSC_VER
14113
// Disable signed/unsigned conversion warnings on MSVC.
14214
// These trigger even if the conversion is explicit.
14315
# pragma warning(disable:4365)
14416
#endif
14517

146-
// Tests whether the struct/union/alias `x` when passed by value to C and back to Rust
147-
// remains unchanged.
148-
// It checks if the size is the same as well as if the padding bytes are all in the correct place.
149-
Byte ctest_roundtrip__Byte(
150-
Byte value,
151-
const uint8_t is_padding_byte[sizeof(Byte)],
152-
uint8_t value_bytes[sizeof(Byte)]
153-
) {
154-
int size = (int)sizeof(Byte);
155-
// Mark `p` as volatile so that the C compiler does not optimize away the pattern we create.
156-
// Otherwise the Rust side would not be able to see it.
157-
volatile uint8_t* p = (volatile uint8_t*)&value;
158-
int i = 0;
159-
for (i = 0; i < size; ++i) {
160-
// We skip padding bytes in both Rust and C because writing to it is undefined.
161-
// Instead we just make sure the the placement of the padding bytes remains the same.
162-
if (is_padding_byte[i]) { continue; }
163-
value_bytes[i] = p[i];
164-
// After we check that the pattern remained unchanged from Rust to C, we invert the pattern
165-
// and send it back to Rust to make sure that it remains unchanged from C to Rust.
166-
uint8_t d = (uint8_t)(255) - (uint8_t)(i % 256);
167-
d = d == 0 ? 42: d;
168-
p[i] = d;
169-
}
170-
return value;
171-
}
172-
173-
// Tests whether the struct/union/alias `x` when passed by value to C and back to Rust
174-
// remains unchanged.
175-
// It checks if the size is the same as well as if the padding bytes are all in the correct place.
176-
struct Person ctest_roundtrip__Person(
177-
struct Person value,
178-
const uint8_t is_padding_byte[sizeof(struct Person)],
179-
uint8_t value_bytes[sizeof(struct Person)]
180-
) {
181-
int size = (int)sizeof(struct Person);
182-
// Mark `p` as volatile so that the C compiler does not optimize away the pattern we create.
183-
// Otherwise the Rust side would not be able to see it.
184-
volatile uint8_t* p = (volatile uint8_t*)&value;
185-
int i = 0;
186-
for (i = 0; i < size; ++i) {
187-
// We skip padding bytes in both Rust and C because writing to it is undefined.
188-
// Instead we just make sure the the placement of the padding bytes remains the same.
189-
if (is_padding_byte[i]) { continue; }
190-
value_bytes[i] = p[i];
191-
// After we check that the pattern remained unchanged from Rust to C, we invert the pattern
192-
// and send it back to Rust to make sure that it remains unchanged from C to Rust.
193-
uint8_t d = (uint8_t)(255) - (uint8_t)(i % 256);
194-
d = d == 0 ? 42: d;
195-
p[i] = d;
196-
}
197-
return value;
198-
}
199-
200-
// Tests whether the struct/union/alias `x` when passed by value to C and back to Rust
201-
// remains unchanged.
202-
// It checks if the size is the same as well as if the padding bytes are all in the correct place.
203-
union Word ctest_roundtrip__Word(
204-
union Word value,
205-
const uint8_t is_padding_byte[sizeof(union Word)],
206-
uint8_t value_bytes[sizeof(union Word)]
207-
) {
208-
int size = (int)sizeof(union Word);
209-
// Mark `p` as volatile so that the C compiler does not optimize away the pattern we create.
210-
// Otherwise the Rust side would not be able to see it.
211-
volatile uint8_t* p = (volatile uint8_t*)&value;
212-
int i = 0;
213-
for (i = 0; i < size; ++i) {
214-
// We skip padding bytes in both Rust and C because writing to it is undefined.
215-
// Instead we just make sure the the placement of the padding bytes remains the same.
216-
if (is_padding_byte[i]) { continue; }
217-
value_bytes[i] = p[i];
218-
// After we check that the pattern remained unchanged from Rust to C, we invert the pattern
219-
// and send it back to Rust to make sure that it remains unchanged from C to Rust.
220-
uint8_t d = (uint8_t)(255) - (uint8_t)(i % 256);
221-
d = d == 0 ? 42: d;
222-
p[i] = d;
223-
}
224-
return value;
225-
}
226-
22718
#ifdef _MSC_VER
22819
# pragma warning(default:4365)
22920
#endif

0 commit comments

Comments
 (0)