Skip to content

Commit 81fd6d0

Browse files
committed
Allow #[cfg] to be used with #[signal]
1 parent ce5e8be commit 81fd6d0

File tree

2 files changed

+88
-20
lines changed

2 files changed

+88
-20
lines changed

godot-macros/src/class/godot_api.rs

+50-20
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,35 @@ impl BoundAttr {
6767
}
6868
}
6969

70+
/// Holds information known from a signal's definition
71+
struct SignalDefinition {
72+
/// The signal's function signature.
73+
signature: Function,
74+
75+
/// The signal's non-gdext attributes (all except #[signal]).
76+
external_attributes: Vec<Attribute>,
77+
}
78+
7079
/// Codegen for `#[godot_api] impl MyType`
7180
fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
7281
let class_name = util::validate_impl(&decl, None, "godot_api")?;
7382
let class_name_obj = util::class_name_obj(&class_name);
7483
let (funcs, signals) = process_godot_fns(&mut decl)?;
7584

85+
let mut signal_cfg_attrs: Vec<Vec<&Attribute>> = Vec::new();
7686
let mut signal_name_strs: Vec<String> = Vec::new();
7787
let mut signal_parameters_count: Vec<usize> = Vec::new();
7888
let mut signal_parameters: Vec<TokenStream> = Vec::new();
7989

80-
for signature in signals {
90+
for signal in signals.iter() {
91+
let SignalDefinition {
92+
signature,
93+
external_attributes,
94+
} = signal;
8195
let mut param_types: Vec<TyExpr> = Vec::new();
8296
let mut param_names: Vec<String> = Vec::new();
8397

84-
for param in signature.params.inner {
98+
for param in signature.params.inner.iter() {
8599
match &param.0 {
86100
FnParam::Typed(param) => {
87101
param_types.push(param.ty.clone());
@@ -103,6 +117,13 @@ fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
103117
]
104118
};
105119

120+
// Transport #[cfg] attrs to the FFI glue to ensure signals which were conditionally
121+
// removed from compilation don't cause errors.
122+
signal_cfg_attrs.push(
123+
util::extract_cfg_attrs(external_attributes)
124+
.into_iter()
125+
.collect(),
126+
);
106127
signal_name_strs.push(signature.name.to_string());
107128
signal_parameters_count.push(param_names.len());
108129
signal_parameters.push(param_array_decl);
@@ -164,20 +185,23 @@ fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
164185
use ::godot::sys;
165186

166187
#(
167-
let parameters_info: [::godot::builtin::meta::PropertyInfo; #signal_parameters_count] = #signal_parameters;
168-
169-
let mut parameters_info_sys: [::godot::sys::GDExtensionPropertyInfo; #signal_parameters_count] =
170-
std::array::from_fn(|i| parameters_info[i].property_sys());
171-
172-
let signal_name = ::godot::builtin::StringName::from(#signal_name_strs);
173-
174-
sys::interface_fn!(classdb_register_extension_class_signal)(
175-
sys::get_library(),
176-
#class_name_obj.string_sys(),
177-
signal_name.string_sys(),
178-
parameters_info_sys.as_ptr(),
179-
sys::GDExtensionInt::from(#signal_parameters_count as i64),
180-
);
188+
#(#signal_cfg_attrs)*
189+
{
190+
let parameters_info: [::godot::builtin::meta::PropertyInfo; #signal_parameters_count] = #signal_parameters;
191+
192+
let mut parameters_info_sys: [::godot::sys::GDExtensionPropertyInfo; #signal_parameters_count] =
193+
std::array::from_fn(|i| parameters_info[i].property_sys());
194+
195+
let signal_name = ::godot::builtin::StringName::from(#signal_name_strs);
196+
197+
sys::interface_fn!(classdb_register_extension_class_signal)(
198+
sys::get_library(),
199+
#class_name_obj.string_sys(),
200+
signal_name.string_sys(),
201+
parameters_info_sys.as_ptr(),
202+
sys::GDExtensionInt::from(#signal_parameters_count as i64),
203+
);
204+
};
181205
)*
182206
}
183207
}
@@ -203,9 +227,11 @@ fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
203227
Ok(result)
204228
}
205229

206-
fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<FuncDefinition>, Vec<Function>), Error> {
230+
fn process_godot_fns(
231+
decl: &mut Impl,
232+
) -> Result<(Vec<FuncDefinition>, Vec<SignalDefinition>), Error> {
207233
let mut func_definitions = vec![];
208-
let mut signal_signatures = vec![];
234+
let mut signal_definitions = vec![];
209235

210236
let mut removed_indexes = vec![];
211237
for (index, item) in decl.body_items.iter_mut().enumerate() {
@@ -259,9 +285,13 @@ fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<FuncDefinition>, Vec<Functi
259285
if method.return_ty.is_some() {
260286
return attr.bail("return types are not supported", method);
261287
}
288+
let external_attributes = method.attributes.clone();
262289
let sig = util::reduce_to_signature(method);
263290

264-
signal_signatures.push(sig.clone());
291+
signal_definitions.push(SignalDefinition {
292+
signature: sig,
293+
external_attributes,
294+
});
265295
removed_indexes.push(index);
266296
}
267297
BoundAttrType::Const(_) => {
@@ -280,7 +310,7 @@ fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<FuncDefinition>, Vec<Functi
280310
decl.body_items.remove(index);
281311
}
282312

283-
Ok((func_definitions, signal_signatures))
313+
Ok((func_definitions, signal_definitions))
284314
}
285315

286316
fn process_godot_constants(decl: &mut Impl) -> Result<Vec<Constant>, Error> {

itest/rust/src/register_tests/func_test.rs

+38
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,25 @@ impl GdSelfReference {
152152
#[cfg(all())]
153153
fn signal_recognized_with_simple_path_attribute_below_signal_attr();
154154

155+
#[signal]
156+
fn cfg_removes_duplicate_signal();
157+
158+
#[cfg(any())]
159+
#[signal]
160+
fn cfg_removes_duplicate_signal();
161+
162+
#[signal]
163+
#[cfg(any())]
164+
fn cfg_removes_duplicate_signal();
165+
166+
#[cfg(any())]
167+
#[signal]
168+
fn cfg_removes_signal();
169+
170+
#[signal]
171+
#[cfg(any())]
172+
fn cfg_removes_signal();
173+
155174
#[func]
156175
fn fail_to_update_internal_value_due_to_conflicting_borrow(
157176
&mut self,
@@ -208,6 +227,11 @@ fn class_has_method<T: GodotClass>(name: &str) -> bool {
208227
.done()
209228
}
210229

230+
/// Checks at runtime if a class has a given signal through [ClassDb].
231+
fn class_has_signal<T: GodotClass>(name: &str) -> bool {
232+
ClassDb::singleton().class_has_signal(T::class_name().to_string_name(), name.into())
233+
}
234+
211235
#[itest]
212236
fn cfg_doesnt_interfere_with_valid_method_impls() {
213237
// If we re-implement this method but the re-implementation is removed, that should keep the non-removed implementation.
@@ -235,3 +259,17 @@ fn cfg_removes_or_keeps_methods() {
235259
));
236260
assert!(!class_has_method::<GdSelfReference>("cfg_removes_function"));
237261
}
262+
263+
#[itest]
264+
fn cfg_removes_or_keeps_signals() {
265+
assert!(class_has_signal::<GdSelfReference>(
266+
"signal_recognized_with_simple_path_attribute_above_signal_attr"
267+
));
268+
assert!(class_has_signal::<GdSelfReference>(
269+
"signal_recognized_with_simple_path_attribute_below_signal_attr"
270+
));
271+
assert!(class_has_signal::<GdSelfReference>(
272+
"cfg_removes_duplicate_signal"
273+
));
274+
assert!(!class_has_signal::<GdSelfReference>("cfg_removes_signal"));
275+
}

0 commit comments

Comments
 (0)