Skip to content

Commit aa88d90

Browse files
committed
ctest: add foreign static test
1 parent 7f3d696 commit aa88d90

File tree

12 files changed

+154
-8
lines changed

12 files changed

+154
-8
lines changed

ctest-next/src/ast/static_variable.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub struct Static {
1212
pub(crate) ident: BoxStr,
1313
pub(crate) link_name: Option<BoxStr>,
1414
pub(crate) ty: syn::Type,
15+
pub(crate) mutable: bool,
1516
}
1617

1718
impl Static {

ctest-next/src/ffi_items.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ impl FfiItems {
6363
}
6464

6565
/// Return a list of all foreign statics found mapped by their ABI.
66-
#[cfg_attr(not(test), expect(unused))]
6766
pub(crate) fn foreign_statics(&self) -> &Vec<Static> {
6867
&self.foreign_statics
6968
}
@@ -159,13 +158,15 @@ fn visit_foreign_item_static(table: &mut FfiItems, i: &syn::ForeignItemStatic, a
159158
let ident = i.ident.to_string().into_boxed_str();
160159
let ty = i.ty.deref().clone();
161160
let link_name = extract_single_link_name(&i.attrs);
161+
let mutable = matches!(i.mutability, syn::StaticMutability::Mut(_));
162162

163163
table.foreign_statics.push(Static {
164164
public,
165165
abi,
166166
ident,
167167
link_name,
168168
ty,
169+
mutable,
169170
});
170171
}
171172

ctest-next/src/template.rs

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use quote::ToTokens;
44
use syn::spanned::Spanned;
55

66
use crate::ffi_items::FfiItems;
7-
use crate::translator::Translator;
7+
use crate::translator::{TranslationErrorKind, Translator};
88
use crate::{
99
BoxStr, Field, MapInput, Result, TestGenerator, TranslationError, VolatileItemKind, cdecl,
1010
};
@@ -50,6 +50,7 @@ impl CTestTemplate {
5050
/// Stores all information necessary for generation of tests for all items.
5151
#[derive(Clone, Debug, Default)]
5252
pub(crate) struct TestTemplate {
53+
pub foreign_static_tests: Vec<TestForeignStatic>,
5354
pub field_ptr_tests: Vec<TestFieldPtr>,
5455
pub field_size_offset_tests: Vec<TestFieldSizeOffset>,
5556
pub roundtrip_tests: Vec<TestRoundtrip>,
@@ -75,6 +76,7 @@ impl TestTemplate {
7576
template.populate_field_size_offset_tests(&helper)?;
7677
template.populate_field_ptr_tests(&helper)?;
7778
template.populate_roundtrip_tests(&helper)?;
79+
template.populate_foreign_static_tests(&helper)?;
7880

7981
Ok(template)
8082
}
@@ -380,6 +382,64 @@ impl TestTemplate {
380382

381383
Ok(())
382384
}
385+
386+
/// Populates tests for foreign statics, keeping track of the names of each test.
387+
fn populate_foreign_static_tests(
388+
&mut self,
389+
helper: &TranslateHelper,
390+
) -> Result<(), TranslationError> {
391+
for static_ in helper.ffi_items.foreign_statics() {
392+
let is_volatile = helper
393+
.generator
394+
.volatile_items
395+
.iter()
396+
.any(|is_volatile| is_volatile(VolatileItemKind::Static(static_.clone())));
397+
398+
let c_ty = helper.c_type(static_)?.into_boxed_str();
399+
let rust_ty = static_.ty.to_token_stream().to_string().into_boxed_str();
400+
401+
let return_type = if rust_ty.contains("extern") {
402+
helper.translator.translate_type(&static_.ty)?
403+
} else {
404+
cdecl::ptr(
405+
helper.translator.translate_type(&static_.ty)?,
406+
cdecl::Constness::Mut,
407+
)
408+
};
409+
let return_type = cdecl::cdecl(
410+
&return_type,
411+
format!("ctest_static_ty__{}", static_.ident()),
412+
)
413+
.map_err(|_| {
414+
TranslationError::new(
415+
TranslationErrorKind::InvalidReturn,
416+
&static_.ty.to_token_stream().to_string(),
417+
static_.ty.span(),
418+
)
419+
})?;
420+
421+
let item = TestForeignStatic {
422+
test_name: static_test_ident(static_.ident()),
423+
id: static_.ident().into(),
424+
c_val: helper.c_ident(static_).into_boxed_str(),
425+
rust_ty: rust_ty.into(),
426+
mutable: if static_.mutable { "mut" } else { "const" }.into(),
427+
volatile_keyword: { if is_volatile { "volatile " } else { "" }.into() },
428+
c_mutable: if c_ty.contains("const") || static_.mutable {
429+
""
430+
} else {
431+
"const "
432+
}
433+
.into(),
434+
return_type: return_type.into(),
435+
};
436+
437+
self.foreign_static_tests.push(item.clone());
438+
self.test_idents.push(item.test_name);
439+
}
440+
441+
Ok(())
442+
}
383443
}
384444

385445
/* Many test structures have the following fields:
@@ -457,6 +517,18 @@ pub(crate) struct TestRoundtrip {
457517
pub is_alias: bool,
458518
}
459519

520+
#[derive(Clone, Debug)]
521+
pub(crate) struct TestForeignStatic {
522+
pub test_name: BoxStr,
523+
pub id: BoxStr,
524+
pub c_val: BoxStr,
525+
pub rust_ty: BoxStr,
526+
pub mutable: BoxStr,
527+
pub c_mutable: BoxStr,
528+
pub return_type: BoxStr,
529+
pub volatile_keyword: BoxStr,
530+
}
531+
460532
fn signededness_test_ident(ident: &str) -> BoxStr {
461533
format!("ctest_signededness_{ident}").into()
462534
}
@@ -485,6 +557,10 @@ fn roundtrip_test_ident(ident: &str) -> BoxStr {
485557
format!("ctest_roundtrip_{ident}").into()
486558
}
487559

560+
fn static_test_ident(ident: &str) -> BoxStr {
561+
format!("ctest_static_{ident}").into()
562+
}
563+
488564
/// Wrap methods that depend on both ffi items and the generator.
489565
pub(crate) struct TranslateHelper<'a> {
490566
ffi_items: &'a FfiItems,

ctest-next/templates/test.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,19 @@ ctest_field_ptr__{{ item.id }}__{{ item.field.ident() }}({{ item.c_ty }} *b) {
118118
#ifdef _MSC_VER
119119
# pragma warning(default:4365)
120120
#endif
121+
122+
{%- for static_ in ctx.foreign_static_tests +%}
123+
124+
// Return a pointer to the static variable content.
125+
{%- if static_.rust_ty.contains("extern") +%}
126+
typedef {{ static_.return_type }};
127+
ctest_static_ty__{{ static_.id }} ctest_static__{{ static_.id }}(void) {
128+
return {{ static_.c_val }};
129+
}
130+
{%- else +%}
131+
typedef {{ static_.volatile_keyword }}{{ static_.c_mutable }}{{ static_.return_type }};
132+
ctest_static_ty__{{ static_.id }} ctest_static__{{ static_.id }}(void) {
133+
return &{{ static_.c_val }};
134+
}
135+
{%- endif +%}
136+
{%- endfor +%}

ctest-next/templates/test.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ mod generated_tests {
1010
#![allow(non_snake_case)]
1111
#![deny(improper_ctypes_definitions)]
1212
#[allow(unused_imports)]
13-
use std::ffi::{CStr, c_int, c_char};
13+
use std::ffi::{CStr, c_int, c_char, c_uint};
1414
use std::fmt::{Debug, LowerHex};
1515
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
1616
#[allow(unused_imports)]
@@ -314,6 +314,37 @@ mod generated_tests {
314314
}
315315
}
316316
{%- endfor +%}
317+
318+
{%- for static_ in ctx.foreign_static_tests +%}
319+
320+
// Tests if the pointer to the static variable matches in both Rust and C.
321+
pub fn {{ static_.test_name }}() {
322+
{%- if static_.rust_ty.contains("extern") +%}
323+
extern "C" {
324+
fn ctest_static__{{ static_.id }}() -> {{ static_.rust_ty }};
325+
}
326+
unsafe {
327+
// We must use addr_of! here because of https://github.com/rust-lang/rust/issues/114447
328+
let actual = std::mem::transmute_copy::<_, usize>(&*std::ptr::addr_of!({{ static_.id }}));
329+
let expected = {
330+
let val = ctest_static__{{ static_.id }}();
331+
std::mem::transmute_copy::<_, usize>(&val)
332+
};
333+
check_same(actual, expected, "{{ static_.id }} static");
334+
}
335+
{%- else +%}
336+
extern "C" {
337+
fn ctest_static__{{ static_.id }}() -> *{{ static_.mutable }} {{ static_.rust_ty }};
338+
}
339+
unsafe {
340+
// We must use addr_of! here because of https://github.com/rust-lang/rust/issues/114447
341+
check_same(std::ptr::addr_of!({{ static_.id }}) as usize,
342+
ctest_static__{{ static_.id }}() as usize,
343+
"{{ static_.id }} static");
344+
}
345+
{%- endif +%}
346+
}
347+
{%- endfor +%}
317348
}
318349

319350
use generated_tests::*;

ctest-next/tests/input/hierarchy.out.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,9 @@ in6_addr ctest_roundtrip__in6_addr(
6464
#ifdef _MSC_VER
6565
# pragma warning(default:4365)
6666
#endif
67+
68+
// Return a pointer to the static variable content.
69+
typedef const in6_addr *ctest_static_ty__in6addr_any;
70+
ctest_static_ty__in6addr_any ctest_static__in6addr_any(void) {
71+
return &in6addr_any;
72+
}

ctest-next/tests/input/hierarchy.out.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ mod generated_tests {
77
#![allow(non_snake_case)]
88
#![deny(improper_ctypes_definitions)]
99
#[allow(unused_imports)]
10-
use std::ffi::{CStr, c_int, c_char};
10+
use std::ffi::{CStr, c_char};
1111
use std::fmt::{Debug, LowerHex};
1212
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
1313
#[allow(unused_imports)]
@@ -213,6 +213,19 @@ mod generated_tests {
213213
}
214214
}
215215
}
216+
217+
// Tests if the pointer to the static variable matches in both Rust and C.
218+
pub fn ctest_static_in6addr_any() {
219+
extern "C" {
220+
fn ctest_static__in6addr_any() -> *const in6_addr;
221+
}
222+
unsafe {
223+
// We must use addr_of! here because of https://github.com/rust-lang/rust/issues/114447
224+
check_same(std::ptr::addr_of!(in6addr_any) as usize,
225+
ctest_static__in6addr_any() as usize,
226+
"in6addr_any static");
227+
}
228+
}
216229
}
217230

218231
use generated_tests::*;
@@ -236,4 +249,5 @@ fn run_all() {
236249
ctest_size_align_in6_addr();
237250
ctest_signededness_in6_addr();
238251
ctest_roundtrip_in6_addr();
252+
ctest_static_in6addr_any();
239253
}

ctest-next/tests/input/hierarchy/foo.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ pub const ON: bool = true;
66
unsafe extern "C" {
77
fn malloc(size: usize) -> *mut c_void;
88

9-
static in6addr_any: in6_addr;
9+
pub static in6addr_any: in6_addr;
1010
}

ctest-next/tests/input/macro.out.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ mod generated_tests {
77
#![allow(non_snake_case)]
88
#![deny(improper_ctypes_definitions)]
99
#[allow(unused_imports)]
10-
use std::ffi::{CStr, c_int, c_char};
10+
use std::ffi::{CStr, c_char};
1111
use std::fmt::{Debug, LowerHex};
1212
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
1313
#[allow(unused_imports)]

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ mod generated_tests {
77
#![allow(non_snake_case)]
88
#![deny(improper_ctypes_definitions)]
99
#[allow(unused_imports)]
10-
use std::ffi::{CStr, c_int, c_char};
10+
use std::ffi::{CStr, c_char};
1111
use std::fmt::{Debug, LowerHex};
1212
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
1313
#[allow(unused_imports)]

0 commit comments

Comments
 (0)