From e8a3c98dafa7f4abd3db9a30922a9f6220611c6f Mon Sep 17 00:00:00 2001 From: Lukas Rieger Date: Mon, 20 Jan 2025 03:22:45 +0100 Subject: [PATCH] #[var(get=f, set=f)]: properly handle #[func]s that have been renamed with (rename=godot_name) --- .../src/class/data_models/field_var.rs | 5 ++ .../src/class/data_models/inherent_impl.rs | 12 ++++- .../src/class/data_models/property.rs | 22 ++++++--- godot-macros/src/util/mod.rs | 49 +++++++++++++++++++ itest/rust/src/object_tests/property_test.rs | 22 +++++++++ 5 files changed, 102 insertions(+), 8 deletions(-) diff --git a/godot-macros/src/class/data_models/field_var.rs b/godot-macros/src/class/data_models/field_var.rs index a79304dd9..682cfce67 100644 --- a/godot-macros/src/class/data_models/field_var.rs +++ b/godot-macros/src/class/data_models/field_var.rs @@ -12,6 +12,7 @@ use crate::class::{ into_signature_info, make_existence_check, make_method_registration, Field, FieldHint, FuncDefinition, }; +use crate::util::make_function_registered_name_constant; use crate::util::KvParser; use crate::{util, ParseResult}; @@ -200,10 +201,14 @@ impl GetterSetterImpl { } } + let constant_declaration = + make_function_registered_name_constant(class_name, &function_name, &None, &vec![]); + let function_impl = quote! { pub #signature { #function_body } + #constant_declaration }; let signature = util::parse_signature(signature); diff --git a/godot-macros/src/class/data_models/inherent_impl.rs b/godot-macros/src/class/data_models/inherent_impl.rs index 4eeff41f2..e8c9d7321 100644 --- a/godot-macros/src/class/data_models/inherent_impl.rs +++ b/godot-macros/src/class/data_models/inherent_impl.rs @@ -10,7 +10,9 @@ use crate::class::{ make_signal_registrations, ConstDefinition, FuncDefinition, RpcAttr, RpcMode, SignalDefinition, SignatureInfo, TransferMode, }; -use crate::util::{bail, c_str, ident, require_api_version, KvParser}; +use crate::util::{ + bail, c_str, ident, make_function_registered_name_constants, require_api_version, KvParser, +}; use crate::{handle_mutually_exclusive_keys, util, ParseResult}; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; @@ -84,6 +86,8 @@ pub fn transform_inherent_impl( let (funcs, signals) = process_godot_fns(&class_name, &mut impl_block, meta.secondary)?; let consts = process_godot_constants(&mut impl_block)?; + let func_export_name_constants = make_function_registered_name_constants(&funcs, &class_name); + #[cfg(all(feature = "register-docs", since_api = "4.3"))] let docs = crate::docs::make_inherent_impl_docs(&funcs, &consts, &signals); #[cfg(not(all(feature = "register-docs", since_api = "4.3")))] @@ -174,6 +178,9 @@ pub fn transform_inherent_impl( #trait_impl #fill_storage #class_registration + impl #class_name{ + #( #func_export_name_constants )* + } }; Ok(result) @@ -184,6 +191,9 @@ pub fn transform_inherent_impl( let result = quote! { #impl_block #fill_storage + impl #class_name{ + #( #func_export_name_constants )* + } }; Ok(result) diff --git a/godot-macros/src/class/data_models/property.rs b/godot-macros/src/class/data_models/property.rs index be4077964..6907a1951 100644 --- a/godot-macros/src/class/data_models/property.rs +++ b/godot-macros/src/class/data_models/property.rs @@ -8,6 +8,7 @@ //! Parsing the `var` and `export` attributes on fields. use crate::class::{Field, FieldVar, Fields, GetSet, GetterSetterImpl, UsageFlags}; +use crate::util::{format_function_registered_name_constant, ident}; use proc_macro2::{Ident, TokenStream}; use quote::quote; @@ -134,22 +135,24 @@ pub fn make_property_impl(class_name: &Ident, fields: &Fields) -> TokenStream { }, }; - let getter_name = make_getter_setter( + let getter_tokens = make_getter_setter( getter.to_impl(class_name, GetSet::Get, field), &mut getter_setter_impls, &mut export_tokens, + class_name, ); - let setter_name = make_getter_setter( + let setter_tokens = make_getter_setter( setter.to_impl(class_name, GetSet::Set, field), &mut getter_setter_impls, &mut export_tokens, + class_name, ); export_tokens.push(quote! { ::godot::register::private::#registration_fn::<#class_name, #field_type>( #field_name, - #getter_name, - #setter_name, + #getter_tokens, + #setter_tokens, #hint, #usage_flags, ); @@ -177,7 +180,8 @@ fn make_getter_setter( getter_setter_impl: Option, getter_setter_impls: &mut Vec, export_tokens: &mut Vec, -) -> String { + class_name: &Ident, +) -> TokenStream { if let Some(getter_impl) = getter_setter_impl { let GetterSetterImpl { function_name, @@ -188,8 +192,12 @@ fn make_getter_setter( getter_setter_impls.push(function_impl); export_tokens.push(export_token); - function_name.to_string() + let getter_setter_name = function_name.to_string(); + + let getter_setter_fn_const = + format_function_registered_name_constant(class_name, &ident(&getter_setter_name)); + quote! { #class_name::#getter_setter_fn_const } } else { - String::new() + quote! { "" } } } diff --git a/godot-macros/src/util/mod.rs b/godot-macros/src/util/mod.rs index f88525733..e4ab42243 100644 --- a/godot-macros/src/util/mod.rs +++ b/godot-macros/src/util/mod.rs @@ -7,10 +7,12 @@ // Note: some code duplication with godot-codegen crate. +use crate::class::FuncDefinition; use crate::ParseResult; use proc_macro2::{Delimiter, Group, Ident, Literal, TokenStream, TokenTree}; use quote::spanned::Spanned; use quote::{format_ident, quote, ToTokens, TokenStreamExt}; +use venial::Attribute; mod kv_parser; mod list_parser; @@ -303,3 +305,50 @@ pub fn venial_parse_meta( venial::parse_item(input) } + +// ---------------------------------------------------------------------------------------------------------------------------------------------- + +// util functions for handling #[func]s and #[var(get=f, set=f)] + +pub fn make_function_registered_name_constants( + funcs: &[FuncDefinition], + class_name: &Ident, +) -> Vec { + funcs + .iter() + .map(|func| { + make_function_registered_name_constant( + class_name, + &func.signature_info.method_name, + &func.registered_name, + &func.external_attributes, + ) + }) + .collect() +} + +/// funcs can be renamed with `#[func(rename=new_name) fn f();`. +/// To be able to access the renamed function name at a later point, it is saved in a string constant. +pub fn make_function_registered_name_constant( + class_name: &Ident, + func_name: &Ident, + exported_name: &Option, + attributes: &Vec, +) -> TokenStream { + let const_name = format_function_registered_name_constant(class_name, func_name); + let const_value = match &exported_name { + Some(renamed) => renamed.to_string(), + None => func_name.to_string(), + }; + + quote! { + #(#attributes)* + #[doc(hidden)] + #[allow(non_upper_case_globals)] + pub const #const_name: &str = #const_value; + } +} + +pub fn format_function_registered_name_constant(class_name: &Ident, func_name: &Ident) -> Ident { + format_ident!("__gdext_func_{}_{}", class_name, func_name) +} diff --git a/itest/rust/src/object_tests/property_test.rs b/itest/rust/src/object_tests/property_test.rs index 49b3b4f1d..7d89d8aeb 100644 --- a/itest/rust/src/object_tests/property_test.rs +++ b/itest/rust/src/object_tests/property_test.rs @@ -482,3 +482,25 @@ fn override_export() { fn check_property(property: &Dictionary, key: &str, expected: impl ToGodot) { assert_eq!(property.get_or_nil(key), expected.to_variant()); } + +// ---------------------------------------------------------------------------------------------------------------------------------------------- + +#[derive(GodotClass)] +#[class(base=Node, init)] +struct RenamedFunc { + #[var(get = get_int_val, set = set_int_val)] + int_val: i32, +} + +#[godot_api] +impl RenamedFunc { + #[func(rename=f1)] + pub fn get_int_val(&self) -> i32 { + self.int_val + } + + #[func(rename=f2)] + pub fn set_int_val(&mut self, val: i32) { + self.int_val = val; + } +}