From 203eeec33c1b34286ba7454fb786649b841d8727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Thu, 12 Dec 2024 12:58:31 -0800 Subject: [PATCH] Add rename rule for generated associated constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In FFI Rust code it is often necessary to use constants instead of enums for compatibility reasons. The most natural thing to do to somewhat preserve grouping is to use associated constants. E.g.: ```rust #[repr(C)] struct Foo {} impl Foo { pub const FLAG1: u32 = 10; pub const FLAG2: u32 = 11; ... ``` For the above, the generated constants would be called Foo_FLAG1 & Foo_FLAG2. Many Linux core C libraries adhere to a slightly different casing, however, where the type prefix is fully upper cased: FOO_FLAG1. Currently, it does not seem possible to generate such constants. This change introduces a new config option to structured types that enables this use case: the struct.rename_associated_constant option expects a rename rule (as other rename related config options) and applies it to the struct base name as used in conjunction with associated constants to form the final name of the constant. Signed-off-by: Daniel Müller --- docs.md | 6 ++++++ src/bindgen/config.rs | 3 +++ src/bindgen/ir/constant.rs | 17 +++++++++++++++- template.toml | 1 + .../rename_associated_constant.c.sym | 3 +++ .../expectations/rename_associated_constant.c | 12 +++++++++++ .../rename_associated_constant.compat.c | 20 +++++++++++++++++++ .../rename_associated_constant.cpp | 17 ++++++++++++++++ .../rename_associated_constant.pyx | 14 +++++++++++++ .../rename_associated_constant_both.c | 12 +++++++++++ .../rename_associated_constant_both.compat.c | 20 +++++++++++++++++++ .../rename_associated_constant_tag.c | 12 +++++++++++ .../rename_associated_constant_tag.compat.c | 20 +++++++++++++++++++ .../rename_associated_constant_tag.pyx | 14 +++++++++++++ tests/rust/rename_associated_constant.rs | 11 ++++++++++ tests/rust/rename_associated_constant.toml | 2 ++ 16 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 tests/expectations-symbols/rename_associated_constant.c.sym create mode 100644 tests/expectations/rename_associated_constant.c create mode 100644 tests/expectations/rename_associated_constant.compat.c create mode 100644 tests/expectations/rename_associated_constant.cpp create mode 100644 tests/expectations/rename_associated_constant.pyx create mode 100644 tests/expectations/rename_associated_constant_both.c create mode 100644 tests/expectations/rename_associated_constant_both.compat.c create mode 100644 tests/expectations/rename_associated_constant_tag.c create mode 100644 tests/expectations/rename_associated_constant_tag.compat.c create mode 100644 tests/expectations/rename_associated_constant_tag.pyx create mode 100644 tests/rust/rename_associated_constant.rs create mode 100644 tests/rust/rename_associated_constant.toml diff --git a/docs.md b/docs.md index 3d00d814d..14e7d119f 100644 --- a/docs.md +++ b/docs.md @@ -837,6 +837,12 @@ deprecated_with_notes = "DEPRECATED_STRUCT_WITH_NOTE" # default: false # associated_constants_in_body: false +# The rename rule to apply to the struct name used for prefixing associated +# constants. +# +# default: "None" +rename_associated_constant = "None" + # Whether to derive a simple constructor that takes a value for every field. # default: false derive_constructor = true diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs index 31316503d..28bb0eae3 100644 --- a/src/bindgen/config.rs +++ b/src/bindgen/config.rs @@ -493,6 +493,9 @@ pub struct StructConfig { /// Whether associated constants should be in the body. Only applicable to /// non-transparent structs, and in C++-only. pub associated_constants_in_body: bool, + /// The rename rule to apply to the struct name used for prefixing associated + /// constants + pub rename_associated_constant: RenameRule, /// The way to annotate this struct as #[must_use]. pub must_use: Option, /// The way to annotation this function as #[deprecated] without notes diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs index c7b7dd1a2..f47db1e77 100644 --- a/src/bindgen/ir/constant.rs +++ b/src/bindgen/ir/constant.rs @@ -18,6 +18,7 @@ use crate::bindgen::ir::{ }; use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::library::Library; +use crate::bindgen::rename::{IdentifierType, RenameRule}; use crate::bindgen::writer::SourceWriter; use crate::bindgen::Bindings; @@ -666,7 +667,21 @@ impl Constant { Cow::Borrowed(self.export_name()) } else { let associated_name = match associated_to_struct { - Some(s) => Cow::Borrowed(s.export_name()), + Some(s) => { + let name = s.export_name(); + let rules = s + .annotations + .parse_atom::("rename-associated-constant"); + let rules = rules + .as_ref() + .unwrap_or(&config.structure.rename_associated_constant); + + if let Some(r) = rules.not_none() { + r.apply(name, IdentifierType::Type) + } else { + Cow::Borrowed(name) + } + } None => { let mut name = self.associated_to.as_ref().unwrap().name().to_owned(); config.export.rename(&mut name); diff --git a/template.toml b/template.toml index a2f18a68d..ff3b32322 100644 --- a/template.toml +++ b/template.toml @@ -95,6 +95,7 @@ rename_fields = "None" # must_use = "MUST_USE_STRUCT" # deprecated = "DEPRECATED_STRUCT" # deprecated_with_note = "DEPRECATED_STRUCT_WITH_NOTE" +rename_associated_constant = "None" derive_constructor = false derive_eq = false derive_neq = false diff --git a/tests/expectations-symbols/rename_associated_constant.c.sym b/tests/expectations-symbols/rename_associated_constant.c.sym new file mode 100644 index 000000000..f018156fc --- /dev/null +++ b/tests/expectations-symbols/rename_associated_constant.c.sym @@ -0,0 +1,3 @@ +{ +root; +}; \ No newline at end of file diff --git a/tests/expectations/rename_associated_constant.c b/tests/expectations/rename_associated_constant.c new file mode 100644 index 000000000..fce0955bd --- /dev/null +++ b/tests/expectations/rename_associated_constant.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include + +typedef struct { + +} Foo; +#define FOO_GA 10 +#define FOO_ZO 3.14 + +void root(Foo x); diff --git a/tests/expectations/rename_associated_constant.compat.c b/tests/expectations/rename_associated_constant.compat.c new file mode 100644 index 000000000..7160635e5 --- /dev/null +++ b/tests/expectations/rename_associated_constant.compat.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +typedef struct { + +} Foo; +#define FOO_GA 10 +#define FOO_ZO 3.14 + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void root(Foo x); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/rename_associated_constant.cpp b/tests/expectations/rename_associated_constant.cpp new file mode 100644 index 000000000..8d8bb9f0f --- /dev/null +++ b/tests/expectations/rename_associated_constant.cpp @@ -0,0 +1,17 @@ +#include +#include +#include +#include +#include + +struct Foo { + +}; +constexpr static const int32_t FOO_GA = 10; +constexpr static const float FOO_ZO = 3.14; + +extern "C" { + +void root(Foo x); + +} // extern "C" diff --git a/tests/expectations/rename_associated_constant.pyx b/tests/expectations/rename_associated_constant.pyx new file mode 100644 index 000000000..d6560451a --- /dev/null +++ b/tests/expectations/rename_associated_constant.pyx @@ -0,0 +1,14 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + ctypedef struct Foo: + pass + const int32_t FOO_GA # = 10 + const float FOO_ZO # = 3.14 + + void root(Foo x); diff --git a/tests/expectations/rename_associated_constant_both.c b/tests/expectations/rename_associated_constant_both.c new file mode 100644 index 000000000..29c4ef9c5 --- /dev/null +++ b/tests/expectations/rename_associated_constant_both.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include + +typedef struct Foo { + +} Foo; +#define FOO_GA 10 +#define FOO_ZO 3.14 + +void root(struct Foo x); diff --git a/tests/expectations/rename_associated_constant_both.compat.c b/tests/expectations/rename_associated_constant_both.compat.c new file mode 100644 index 000000000..1f3cd1e0b --- /dev/null +++ b/tests/expectations/rename_associated_constant_both.compat.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +typedef struct Foo { + +} Foo; +#define FOO_GA 10 +#define FOO_ZO 3.14 + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void root(struct Foo x); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/rename_associated_constant_tag.c b/tests/expectations/rename_associated_constant_tag.c new file mode 100644 index 000000000..db78b0c6e --- /dev/null +++ b/tests/expectations/rename_associated_constant_tag.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include + +struct Foo { + +}; +#define FOO_GA 10 +#define FOO_ZO 3.14 + +void root(struct Foo x); diff --git a/tests/expectations/rename_associated_constant_tag.compat.c b/tests/expectations/rename_associated_constant_tag.compat.c new file mode 100644 index 000000000..7c8fa2adb --- /dev/null +++ b/tests/expectations/rename_associated_constant_tag.compat.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +struct Foo { + +}; +#define FOO_GA 10 +#define FOO_ZO 3.14 + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void root(struct Foo x); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/rename_associated_constant_tag.pyx b/tests/expectations/rename_associated_constant_tag.pyx new file mode 100644 index 000000000..6d4c628e2 --- /dev/null +++ b/tests/expectations/rename_associated_constant_tag.pyx @@ -0,0 +1,14 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + cdef struct Foo: + pass + const int32_t FOO_GA # = 10 + const float FOO_ZO # = 3.14 + + void root(Foo x); diff --git a/tests/rust/rename_associated_constant.rs b/tests/rust/rename_associated_constant.rs new file mode 100644 index 000000000..80c18fbe9 --- /dev/null +++ b/tests/rust/rename_associated_constant.rs @@ -0,0 +1,11 @@ +/// cbindgen:rename-associated-constant=UpperCase +#[repr(C)] +struct Foo {} + +impl Foo { + pub const GA: i32 = 10; + pub const ZO: f32 = 3.14; +} + +#[no_mangle] +pub extern "C" fn root(x: Foo) { } diff --git a/tests/rust/rename_associated_constant.toml b/tests/rust/rename_associated_constant.toml new file mode 100644 index 000000000..adc7ef3bd --- /dev/null +++ b/tests/rust/rename_associated_constant.toml @@ -0,0 +1,2 @@ +[struct] +rename_associated_constant = "UpperCase"