Skip to content

Commit

Permalink
many changes, wow
Browse files Browse the repository at this point in the history
  • Loading branch information
0x53A committed Jan 24, 2025
1 parent eef5faf commit 2101017
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 48 deletions.
2 changes: 1 addition & 1 deletion godot-macros/src/class/data_models/field_var.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ impl GetterSetterImpl {
};

let func_registered_name_const =
make_function_registered_name_constant(class_name, &function_name, &None, &vec![]);
make_function_registered_name_constant(class_name, &function_name, None, &[]);

let signature = util::parse_signature(signature);
let export_token = make_method_registration(
Expand Down
49 changes: 17 additions & 32 deletions godot-macros/src/class/data_models/inherent_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ use crate::class::{
SignatureInfo, TransferMode,
};
use crate::util::{
bail, c_str, format_function_registered_name_struct_name, ident,
make_function_registered_name_constants, require_api_version, KvParser,
bail, c_str, error_fn, format_function_registered_name_struct_name, ident,
make_function_registered_name_constants, replace_class_in_path, require_api_version, KvParser,
};
use crate::{handle_mutually_exclusive_keys, util, ParseResult};

use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, TokenStream};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::spanned::Spanned;
use quote::{format_ident, quote};
use venial::{Path, PathSegment};
use venial::Path;

/// Attribute for user-declared function.
enum ItemAttrType {
Expand Down Expand Up @@ -88,13 +88,24 @@ 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")))]
let docs = quote! {};

// This is the container struct that holds the names of all registered #[func]s.
// (The struct is declared by the macro derive_godot_class.)
let class_functions_name = format_function_registered_name_struct_name(&class_name);
// As the impl block could be of the form "path::class", and we add a second impl block below, we need the full path, not just the class name.
let this_class_full_path = impl_block.self_ty.as_path().ok_or(error_fn(
"unexpected: the function already checked 'as_path' above in validate_impl",
&impl_block,
))?;
let class_functions_path: Path =
replace_class_in_path(this_class_full_path, class_functions_name);
// For each #[func] in this impl block, we create one constant.
let func_export_name_constants = make_function_registered_name_constants(&funcs, &class_name);

let signal_registrations = make_signal_registrations(signals, &class_name_obj);

#[cfg(feature = "codegen-full")]
Expand Down Expand Up @@ -125,32 +136,6 @@ pub fn transform_inherent_impl(
});
};

let class_functions_name = format_function_registered_name_struct_name(&class_name);
let class_functions_path: Path = match impl_block.self_ty.as_path().unwrap().segments.as_slice()
{
[] => panic!("unexpected: empty path (this should have already been checked above)"),
[_single] => Path {
segments: vec![PathSegment {
ident: class_functions_name,
generic_args: None,
tk_separator_colons: None,
}],
},
[path @ .., _last] => {
let mut segments = vec![];
segments.extend(path.iter().cloned());
segments.push(PathSegment {
ident: class_functions_name,
generic_args: None,
tk_separator_colons: Some([
Punct::new(':', Spacing::Joint),
Punct::new(':', Spacing::Alone),
]),
});
Path { segments }
}
};

if !meta.secondary {
// We are the primary `impl` block.

Expand Down
57 changes: 44 additions & 13 deletions godot-macros/src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@

use crate::class::FuncDefinition;
use crate::ParseResult;
use proc_macro2::{Delimiter, Group, Ident, Literal, TokenStream, TokenTree};
use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, TokenStream, TokenTree};
use quote::spanned::Spanned;
use quote::{format_ident, quote, ToTokens, TokenStreamExt};
use venial::Attribute;
use venial::{Attribute, Path, PathSegment};

mod kv_parser;
mod list_parser;
Expand Down Expand Up @@ -248,9 +248,11 @@ pub(crate) fn extract_cfg_attrs(
let Some(attr_name) = attr.get_single_path_segment() else {
return false;
};
// #[cfg(condition)]
if attr_name == "cfg" {
return true;
}
// #[cfg_attr(condition, attributes...)], note that there can be multiple attributes seperated by comma.
if attr_name == "cfg_attr" && attr.value.to_token_stream().to_string().contains("cfg(") {
return true;
}
Expand Down Expand Up @@ -328,11 +330,12 @@ pub fn make_function_registered_name_constants(
// the constant needs the same #[cfg] attribute(s) as the function, so that it is only active if the function is also active.
let cfg_attributes = extract_cfg_attrs(&func.external_attributes)
.into_iter()
.collect();
.collect::<Vec<_>>();

make_function_registered_name_constant(
class_name,
&func.signature_info.method_name,
&func.registered_name,
func.registered_name.as_ref(),
&cfg_attributes,
)
})
Expand All @@ -346,19 +349,17 @@ pub fn make_function_registered_name_constants(
pub fn make_function_registered_name_constant(
class_name: &Ident,
func_name: &Ident,
registered_name: &Option<String>,
attributes: &Vec<&Attribute>,
registered_name: Option<&String>,
attributes: &[&Attribute],
) -> TokenStream {
let const_name = format_function_registered_name_constant_name(class_name, func_name);
let const_value = match &registered_name {
Some(renamed) => renamed.to_string(),
None => func_name.to_string(),
};

let doc_comment = format!(
"The rust function `{}` is registered with godot as `{}`.",
func_name, const_value
);
let doc_comment =
format!("The Rust function `{func_name}` is registered with Godot as `{const_value}`.");

quote! {
#(#attributes)*
Expand All @@ -369,15 +370,45 @@ pub fn make_function_registered_name_constant(
}
}

/// Converts "path::class" to "path::new_class".
pub fn replace_class_in_path(path: Path, new_class: Ident) -> Path {
match path.segments.as_slice() {
// Can't happen, you have at least one segment (the class name).
[] => panic!("unexpected: empty path"),

[_single] => Path {
segments: vec![PathSegment {
ident: new_class,
generic_args: None,
tk_separator_colons: None,
}],
},

[path @ .., _last] => {
let mut segments = vec![];
segments.extend(path.iter().cloned());
segments.push(PathSegment {
ident: new_class,
generic_args: None,
tk_separator_colons: Some([
Punct::new(':', Spacing::Joint),
Punct::new(':', Spacing::Alone),
]),
});
Path { segments }
}
}
}

/// Returns the name of the constant that will be autogenerated.
pub fn format_function_registered_name_constant_name(
class_name: &Ident,
_class_name: &Ident,
func_name: &Ident,
) -> Ident {
format_ident!("__gdext_func_{}_{}", class_name, func_name)
format_ident!("{func_name}")
}

/// Returns the name of the dummy struct that's used as container for all function name constants.
pub fn format_function_registered_name_struct_name(class_name: &Ident) -> Ident {
format_ident!("{class_name}_Functions")
format_ident!("__gdext_{class_name}_Functions")
}
9 changes: 7 additions & 2 deletions itest/godot/ManualFfiTests.gd
Original file line number Diff line number Diff line change
Expand Up @@ -392,8 +392,11 @@ func test_get_set():
assert(obj.is_get_called())


# Validates the shape of the Class defined in Rust:
# In Rust we declared a single property (int_val) and two functions (f1 and f2).
# In addition, Godot defines a property with the name of the Class, which acts as the top-level category in the inspector.
func test_RenamedFunc_shape():
# note: RenamedFunc is in property_test.rs
# Note: RenamedFunc is in property_test.rs.
var obj: RenamedFunc = RenamedFunc.new()

# Get baseline Node properties and methods
Expand Down Expand Up @@ -426,8 +429,10 @@ func test_RenamedFunc_shape():

obj.free()


# Validates that the property has been linked to the correct rust get/set functions.
func test_RenamedFunc_get_set():
# note: RenamedFunc is in property_test.rs
# Note: RenamedFunc is in property_test.rs.
var obj: RenamedFunc = RenamedFunc.new()

assert_eq(obj.int_val, 0)
Expand Down

0 comments on commit 2101017

Please sign in to comment.