Skip to content

Commit ce6a756

Browse files
committed
Allow #[cfg] to be used in #[godot_api] virtual impls
1 parent 3491d7b commit ce6a756

File tree

4 files changed

+93
-7
lines changed

4 files changed

+93
-7
lines changed

godot-macros/src/class/godot_api.rs

+49-7
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ fn transform_trait_impl(original_impl: Impl) -> Result<TokenStream, Error> {
398398
let mut on_notification_fn = quote! { None };
399399

400400
let mut virtual_methods = vec![];
401+
let mut virtual_method_cfg_attrs = vec![];
401402
let mut virtual_method_names = vec![];
402403

403404
let prv = quote! { ::godot::private };
@@ -409,52 +410,85 @@ fn transform_trait_impl(original_impl: Impl) -> Result<TokenStream, Error> {
409410
continue;
410411
};
411412

413+
// Transport #[cfg] attributes to the virtual method's FFI glue, to ensure it won't be
414+
// registered in Godot if conditionally removed from compilation.
415+
let cfg_attrs = util::extract_cfg_attrs(&method.attributes)
416+
.into_iter()
417+
.collect::<Vec<_>>();
412418
let method_name = method.name.to_string();
413419
match method_name.as_str() {
414420
"register_class" => {
415421
register_class_impl = quote! {
422+
#(#cfg_attrs)*
416423
impl ::godot::obj::cap::GodotRegisterClass for #class_name {
417424
fn __godot_register_class(builder: &mut ::godot::builder::GodotBuilder<Self>) {
418425
<Self as #trait_name>::register_class(builder)
419426
}
420427
}
421428
};
422429

430+
// Use 'match' as a way to only return 'Some(...)' if the given cfg attrs allow.
431+
// Needs '#[allow(unreachable_patterns)]' to avoid warnings about the last arm.
423432
register_fn = quote! {
424-
Some(#prv::ErasedRegisterFn {
425-
raw: #prv::callbacks::register_class_by_builder::<#class_name>
426-
})
433+
match () {
434+
#(#cfg_attrs)*
435+
() => Some(#prv::ErasedRegisterFn {
436+
raw: #prv::callbacks::register_class_by_builder::<#class_name>
437+
}),
438+
_ => None,
439+
}
427440
};
428441
}
429442

430443
"init" => {
431444
godot_init_impl = quote! {
445+
#(#cfg_attrs)*
432446
impl ::godot::obj::cap::GodotInit for #class_name {
433447
fn __godot_init(base: ::godot::obj::Base<Self::Base>) -> Self {
434448
<Self as #trait_name>::init(base)
435449
}
436450
}
437451
};
438-
create_fn = quote! { Some(#prv::callbacks::create::<#class_name>) };
452+
create_fn = quote! {
453+
match () {
454+
#(#cfg_attrs)*
455+
() => Some(#prv::callbacks::create::<#class_name>),
456+
_ => None,
457+
}
458+
};
439459
if cfg!(since_api = "4.2") {
440-
recreate_fn = quote! { Some(#prv::callbacks::recreate::<#class_name>) };
460+
recreate_fn = quote! {
461+
match () {
462+
#(#cfg_attrs)*
463+
() => Some(#prv::callbacks::recreate::<#class_name>),
464+
_ => None,
465+
}
466+
};
441467
}
442468
}
443469

444470
"to_string" => {
445471
to_string_impl = quote! {
472+
#(#cfg_attrs)*
446473
impl ::godot::obj::cap::GodotToString for #class_name {
447474
fn __godot_to_string(&self) -> ::godot::builtin::GodotString {
448475
<Self as #trait_name>::to_string(self)
449476
}
450477
}
451478
};
452479

453-
to_string_fn = quote! { Some(#prv::callbacks::to_string::<#class_name>) };
480+
to_string_fn = quote! {
481+
match () {
482+
#(#cfg_attrs)*
483+
() => Some(#prv::callbacks::to_string::<#class_name>),
484+
_ => None,
485+
}
486+
};
454487
}
455488

456489
"on_notification" => {
457490
on_notification_impl = quote! {
491+
#(#cfg_attrs)*
458492
impl ::godot::obj::cap::GodotNotification for #class_name {
459493
fn __godot_notification(&mut self, what: i32) {
460494
if ::godot::private::is_class_inactive(Self::__config().is_tool) {
@@ -467,7 +501,11 @@ fn transform_trait_impl(original_impl: Impl) -> Result<TokenStream, Error> {
467501
};
468502

469503
on_notification_fn = quote! {
470-
Some(#prv::callbacks::on_notification::<#class_name>)
504+
match () {
505+
#(#cfg_attrs)*
506+
() => Some(#prv::callbacks::on_notification::<#class_name>),
507+
_ => None,
508+
}
471509
};
472510
}
473511

@@ -487,6 +525,7 @@ fn transform_trait_impl(original_impl: Impl) -> Result<TokenStream, Error> {
487525
} else {
488526
format!("_{method_name}")
489527
};
528+
virtual_method_cfg_attrs.push(cfg_attrs);
490529
virtual_method_names.push(virtual_method_name);
491530
virtual_methods.push(method);
492531
}
@@ -517,6 +556,7 @@ fn transform_trait_impl(original_impl: Impl) -> Result<TokenStream, Error> {
517556

518557
match name {
519558
#(
559+
#(#virtual_method_cfg_attrs)*
520560
#virtual_method_names => #virtual_method_callbacks,
521561
)*
522562
_ => None,
@@ -526,6 +566,8 @@ fn transform_trait_impl(original_impl: Impl) -> Result<TokenStream, Error> {
526566

527567
::godot::sys::plugin_add!(__GODOT_PLUGIN_REGISTRY in #prv; #prv::ClassPlugin {
528568
class_name: #class_name_obj,
569+
#[allow(unreachable_patterns)] // due to the cfg-based match statements
570+
#[allow(clippy::match_single_binding)] // avoid warning on single-arm matches
529571
component: #prv::PluginComponent::UserVirtuals {
530572
user_register_fn: #register_fn,
531573
user_create_fn: #create_fn,

godot-macros/src/util/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,15 @@ pub(crate) fn path_ends_with(path: &[TokenTree], expected: &str) -> bool {
227227
.unwrap_or(false)
228228
}
229229

230+
pub(crate) fn extract_cfg_attrs(
231+
attrs: &[venial::Attribute],
232+
) -> impl IntoIterator<Item = &venial::Attribute> {
233+
attrs.iter().filter(|attr| {
234+
attr.get_single_path_segment()
235+
.map_or(false, |name| name == "cfg")
236+
})
237+
}
238+
230239
pub(crate) struct DeclInfo {
231240
pub where_: Option<WhereClause>,
232241
pub generic_params: Option<GenericParamList>,

itest/rust/src/object_tests/virtual_methods_test.rs

+5
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ impl Node2DVirtual for ReadyVirtualTest {
7575
fn ready(&mut self) {
7676
self.implementation_value += 1;
7777
}
78+
79+
#[cfg(any())]
80+
fn to_string(&self) -> GodotString {
81+
panic!("Removed by #[cfg]")
82+
}
7883
}
7984

8085
// ----------------------------------------------------------------------------------------------------------------------------------------------

itest/rust/src/register_tests/func_test.rs

+30
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,34 @@ impl RefCountedVirtual for GdSelfReference {
147147
base,
148148
}
149149
}
150+
151+
#[cfg(any())]
152+
fn init(base: Base<Self::Base>) -> Self {
153+
panic!("Removed by #[cfg]")
154+
}
155+
156+
#[cfg(all())]
157+
fn to_string(&self) -> GodotString {
158+
GodotString::new()
159+
}
160+
161+
#[cfg(any())]
162+
fn register_class() {
163+
panic!("Removed by #[cfg]");
164+
}
165+
166+
#[cfg(all())]
167+
fn on_notification(&mut self, _: godot::engine::notify::ObjectNotification) {
168+
godot_print!("Hello!");
169+
}
170+
171+
#[cfg(any())]
172+
fn on_notification(&mut self, _: godot::engine::notify::ObjectNotification) {
173+
panic!("Removed by #[cfg]");
174+
}
175+
176+
#[cfg(any())]
177+
fn cfg_removes_this() {
178+
panic!("Removed by #[cfg]");
179+
}
150180
}

0 commit comments

Comments
 (0)