diff --git a/godot-macros/src/class/data_models/field_var.rs b/godot-macros/src/class/data_models/field_var.rs index 3066713e9..267ae0de9 100644 --- a/godot-macros/src/class/data_models/field_var.rs +++ b/godot-macros/src/class/data_models/field_var.rs @@ -202,7 +202,7 @@ impl GetterSetterImpl { // Ideally, we'd be able to place #[cfg_attr] on #[var(get)] and #[var(set)] to be able to match a // #[cfg()] (for instance) placed on the getter/setter function, but that is not currently supported. external_attributes: Vec::new(), - rename: None, + registered_name: None, is_script_virtual: false, rpc_info: None, }, diff --git a/godot-macros/src/class/data_models/func.rs b/godot-macros/src/class/data_models/func.rs index e19763636..f26bee138 100644 --- a/godot-macros/src/class/data_models/func.rs +++ b/godot-macros/src/class/data_models/func.rs @@ -15,11 +15,18 @@ use quote::{format_ident, quote}; pub struct FuncDefinition { /// Refined signature, with higher level info and renamed parameters. pub signature_info: SignatureInfo, + /// The function's non-gdext attributes (all except #[func]). pub external_attributes: Vec, + /// The name the function will be exposed as in Godot. If `None`, the Rust function name is used. - pub rename: Option, + /// + /// This can differ from the name in [`signature_info`] if the user has used `#[func(rename)]` or for script-virtual functions. + pub registered_name: Option, + + /// True for script-virtual functions. pub is_script_virtual: bool, + /// Information about the RPC configuration, if provided. pub rpc_info: Option, } @@ -84,8 +91,8 @@ pub fn make_method_registration( // String literals let method_name = &signature_info.method_name; let class_name_str = class_name.to_string(); - let method_name_str = if let Some(rename) = func_definition.rename { - rename + let method_name_str = if let Some(updated_name) = func_definition.registered_name { + updated_name } else { method_name.to_string() }; diff --git a/godot-macros/src/class/data_models/inherent_impl.rs b/godot-macros/src/class/data_models/inherent_impl.rs index 5bde682aa..187911395 100644 --- a/godot-macros/src/class/data_models/inherent_impl.rs +++ b/godot-macros/src/class/data_models/inherent_impl.rs @@ -9,7 +9,7 @@ use crate::class::{ make_signal_registrations, ConstDefinition, FuncDefinition, RpcAttr, RpcMode, SignalDefinition, SignatureInfo, TransferMode, }; -use crate::util::{bail, ident, require_api_version, KvParser}; +use crate::util::{bail, c_str, ident, require_api_version, KvParser}; use crate::{handle_mutually_exclusive_keys, util, ParseResult}; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; @@ -89,7 +89,7 @@ pub fn transform_inherent_impl(mut impl_block: venial::Impl) -> ParseResult = funcs .into_iter() .map(|func_def| make_method_registration(&class_name, func_def)) - .collect::>>()?; // <- FIXME transpose this + .collect::>>()?; let constant_registration = make_constant_registration(consts, &class_name, &class_name_obj)?; @@ -210,7 +210,7 @@ fn process_godot_fns( func_definitions.push(FuncDefinition { signature_info, external_attributes, - rename: func.rename, + registered_name: func.rename, is_script_virtual: func.is_virtual, rpc_info, }); @@ -311,9 +311,12 @@ fn add_virtual_script_call( let class_name_str = class_name.to_string(); let early_bound_name = format_ident!("__earlybound_{}", &function.name); - let method_name_str = rename - .clone() - .unwrap_or_else(|| format!("_{}", function.name)); + + let method_name_str = match rename { + Some(rename) => rename.clone(), + None => format!("_{}", function.name), + }; + let method_name_cstr = c_str(&method_name_str); let sig_tuple = signature_info.tuple_type(); let arg_names = &signature_info.param_idents; @@ -329,7 +332,7 @@ fn add_virtual_script_call( let code = quote! { let object_ptr = #object_ptr; - let method_sname = ::godot::builtin::StringName::from(#method_name_str); + let method_sname = ::godot::builtin::StringName::from(#method_name_cstr); let method_sname_ptr = method_sname.string_sys(); let has_virtual_override = unsafe { ::godot::private::has_virtual_script_method(object_ptr, method_sname_ptr) }; @@ -377,7 +380,7 @@ where index += 1; let Some(attr_name) = attr.get_single_path_segment() else { - // Attribute of the form #[segmented::path] can't be what we are looking for + // Attribute of the form #[segmented::path] can't be what we are looking for. continue; }; @@ -412,7 +415,7 @@ where // #[rpc] name if name == "rpc" => { - // Safe unwrap since #[rpc] must be present if we got to this point + // Safe unwrap, since #[rpc] must be present if we got to this point. let mut parser = KvParser::parse(attributes, "rpc")?.unwrap(); let rpc_mode = handle_mutually_exclusive_keys( @@ -445,9 +448,14 @@ where let rpc_attr = match (config_expr, (&rpc_mode, &transfer_mode, &call_local, &channel)) { // Ok: Only `config = [expr]` is present. (Some(expr), (None, None, None, None)) => RpcAttr::Expression(expr), + // Err: `config = [expr]` is present along other parameters, which is not allowed. - (Some(_), _) => return bail!(&*item, "`#[rpc(config = ...)]` is mutually exclusive with any other parameters(`any_peer`, `reliable`, `call_local`, `channel = 0`)"), - // Ok: `config` is not present, any combination of the other parameters is allowed.. + (Some(_), _) => return bail!( + &*item, + "`#[rpc(config = ...)]` is mutually exclusive with any other parameters(`any_peer`, `reliable`, `call_local`, `channel = 0`)" + ), + + // Ok: `config` is not present, any combination of the other parameters is allowed. _ => RpcAttr::SeparatedArgs { rpc_mode, transfer_mode, @@ -476,19 +484,21 @@ where let attr_name = attr_name.clone(); - // Remaining code no longer has attribute -- rest stays - attributes.remove(index - 1); // -1 because we bumped the index at the beginning of the loop + // Remaining code no longer has attribute -- rest stays. + attributes.remove(index - 1); // -1 because we bumped the index at the beginning of the loop. index -= 1; let (new_name, new_attr) = match (found, parsed_attr) { - // First attribute + // First attribute. (None, parsed) => (attr_name, parsed), + // Regardless of the order, if we found both `#[func]` and `#[rpc]`, we can just merge them. (Some((found_name, AttrParseResult::Func(func))), AttrParseResult::Rpc(rpc)) | (Some((found_name, AttrParseResult::Rpc(rpc))), AttrParseResult::Func(func)) => ( ident(&format!("{found_name}_{attr_name}")), AttrParseResult::FuncRpc(func, rpc), ), + // We found two incompatible attributes. (Some((found_name, _)), _) => { return bail!(&*item, "The attributes `{found_name}` and `{attr_name}` cannot be used in the same declaration")?; diff --git a/godot-macros/src/class/data_models/rpc.rs b/godot-macros/src/class/data_models/rpc.rs index 442b4c88b..c08d80b20 100644 --- a/godot-macros/src/class/data_models/rpc.rs +++ b/godot-macros/src/class/data_models/rpc.rs @@ -145,7 +145,7 @@ fn make_rpc_registration(func_def: &FuncDefinition) -> Option { } }; - let method_name_str = if let Some(rename) = &func_def.rename { + let method_name_str = if let Some(rename) = &func_def.registered_name { rename.to_string() } else { func_def.signature_info.method_name.to_string() diff --git a/godot-macros/src/docs.rs b/godot-macros/src/docs.rs index 8f7702c41..52557f268 100644 --- a/godot-macros/src/docs.rs +++ b/godot-macros/src/docs.rs @@ -247,7 +247,7 @@ pub fn make_virtual_method_docs(method: Function) -> Option { pub fn make_method_docs(method: &FuncDefinition) -> Option { let desc = make_docs_from_attributes(&method.external_attributes)?; let name = method - .rename + .registered_name .clone() .unwrap_or_else(|| method.signature_info.method_name.to_string()); let ret = method.signature_info.ret_type.to_token_stream().to_string();