Skip to content

Commit

Permalink
Follow-up changes to "func collections"
Browse files Browse the repository at this point in the history
  • Loading branch information
Bromeon committed Feb 1, 2025
1 parent 0edf6e3 commit 22127f5
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 94 deletions.
11 changes: 6 additions & 5 deletions godot-macros/src/class/data_models/field_var.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::class::{
into_signature_info, make_existence_check, make_method_registration, Field, FieldHint,
FuncDefinition,
};
use crate::util::make_func_name_constant;
use crate::util::make_funcs_collection_constant;
use crate::util::KvParser;
use crate::{util, ParseResult};

Expand Down Expand Up @@ -167,7 +167,7 @@ pub struct GetterSetterImpl {
pub function_name: Ident,
pub function_impl: TokenStream,
pub export_token: TokenStream,
pub func_name_const: TokenStream,
pub funcs_collection_constant: TokenStream,
}

impl GetterSetterImpl {
Expand Down Expand Up @@ -208,7 +208,8 @@ impl GetterSetterImpl {
}
};

let func_name_const = make_func_name_constant(class_name, &function_name, None, &[]);
let funcs_collection_constant =
make_funcs_collection_constant(class_name, &function_name, None, &[]);

let signature = util::parse_signature(signature);
let export_token = make_method_registration(
Expand All @@ -234,7 +235,7 @@ impl GetterSetterImpl {
function_name,
function_impl,
export_token,
func_name_const,
funcs_collection_constant,
}
}

Expand All @@ -243,7 +244,7 @@ impl GetterSetterImpl {
function_name: function_name.clone(),
function_impl: TokenStream::new(),
export_token: make_existence_check(function_name),
func_name_const: TokenStream::new(),
funcs_collection_constant: TokenStream::new(),
}
}
}
Expand Down
32 changes: 14 additions & 18 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,14 @@ use crate::class::{
SignatureInfo, TransferMode,
};
use crate::util::{
bail, c_str, error_fn, format_func_name_struct_name, ident, make_func_name_constants,
bail, c_str, format_funcs_collection_struct, ident, make_funcs_collection_constants,
replace_class_in_path, require_api_version, KvParser,
};
use crate::{handle_mutually_exclusive_keys, util, ParseResult};

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

/// Attribute for user-declared function.
enum ItemAttrType {
Expand Down Expand Up @@ -79,6 +78,7 @@ pub struct InherentImplAttr {
pub fn transform_inherent_impl(
meta: InherentImplAttr,
mut impl_block: venial::Impl,
self_path: venial::Path,
) -> ParseResult<TokenStream> {
let class_name = util::validate_impl(&impl_block, None, "godot_api")?;
let class_name_obj = util::class_name_obj(&class_name);
Expand All @@ -93,19 +93,15 @@ pub fn transform_inherent_impl(
#[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_func_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_name_constants = make_func_name_constants(&funcs, &class_name);
// Container struct holding names of all registered #[func]s.
// The struct is declared by #[derive(GodotClass)].
let funcs_collection = {
let struct_name = format_funcs_collection_struct(&class_name);
replace_class_in_path(self_path, struct_name)
};

// For each #[func] in this impl block, create one constant.
let func_name_constants = make_funcs_collection_constants(&funcs, &class_name);
let signal_registrations = make_signal_registrations(signals, &class_name_obj);

#[cfg(feature = "codegen-full")]
Expand Down Expand Up @@ -181,8 +177,8 @@ pub fn transform_inherent_impl(
#trait_impl
#fill_storage
#class_registration
impl #class_functions_path {
#( #func_name_constants )*
impl #funcs_collection {
#( #func_name_constants )*
}
};

Expand All @@ -194,8 +190,8 @@ pub fn transform_inherent_impl(
let result = quote! {
#impl_block
#fill_storage
impl #class_functions_path {
#( #func_name_constants )*
impl #funcs_collection {
#( #func_name_constants )*
}
};

Expand Down
48 changes: 20 additions & 28 deletions godot-macros/src/class/data_models/property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

//! Parsing the `var` and `export` attributes on fields.
//! Parses the `#[var]` and `#[export]` attributes on fields.
use crate::class::{Field, FieldVar, Fields, GetSet, GetterSetterImpl, UsageFlags};
use crate::util::{format_func_name_constant_name, format_func_name_struct_name, ident};
use crate::util::{format_funcs_collection_constant, format_funcs_collection_struct};
use proc_macro2::{Ident, TokenStream};
use quote::quote;

Expand Down Expand Up @@ -136,7 +136,7 @@ pub fn make_property_impl(class_name: &Ident, fields: &Fields) -> TokenStream {
},
};

// Note: (getter/setter)_tokens can be either a path ``Class_Functions::constant_name`` or an empty string ``""``.
// Note: {getter,setter}_tokens can be either a path `Class_Functions::constant_name` or an empty string `""`.

let getter_tokens = make_getter_setter(
getter.to_impl(class_name, GetSet::Get, field),
Expand Down Expand Up @@ -164,9 +164,9 @@ pub fn make_property_impl(class_name: &Ident, fields: &Fields) -> TokenStream {
});
}

// For each generated #[func], add a const.
// This is the name of the container struct, which is declared by the derive macro GodotClass.
let class_functions_name = format_func_name_struct_name(class_name);
// For each generated #[func], add a const declaration.
// This is the name of the container struct, which is declared by #[derive(GodotClass)].
let class_functions_name = format_funcs_collection_struct(class_name);

quote! {
impl #class_name {
Expand Down Expand Up @@ -196,26 +196,18 @@ fn make_getter_setter(
export_tokens: &mut Vec<TokenStream>,
class_name: &Ident,
) -> TokenStream {
if let Some(getter_impl) = getter_setter_impl {
let GetterSetterImpl {
function_name,
function_impl,
export_token,
func_name_const,
} = getter_impl;

getter_setter_impls.push(function_impl);
func_name_consts.push(func_name_const);
export_tokens.push(export_token);

let getter_setter_name = function_name.to_string();

let class_functions_name = format_func_name_struct_name(class_name);

let getter_setter_fn_const =
format_func_name_constant_name(class_name, &ident(&getter_setter_name));
quote! { #class_functions_name::#getter_setter_fn_const }
} else {
quote! { "" }
}
let Some(gs) = getter_setter_impl else {
return quote! { "" };
};

getter_setter_impls.push(gs.function_impl);
func_name_consts.push(gs.funcs_collection_constant);
export_tokens.push(gs.export_token);

// Getters/setters are, like #[func]s, subject to additional code generation: a constant inside a "funcs collection" struct
// stores their Godot name and can be used as an indirection to refer to their true name from other procedural macros.
let funcs_collection = format_funcs_collection_struct(class_name);
let constant = format_funcs_collection_constant(class_name, &gs.function_name);

quote! { #funcs_collection::#constant }
}
15 changes: 8 additions & 7 deletions godot-macros/src/class/derive_godot_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use crate::class::{
FieldVar, Fields, SignatureInfo,
};
use crate::util::{
bail, error, format_func_name_struct_name, ident, path_ends_with_complex, require_api_version,
KvParser,
bail, error, format_funcs_collection_struct, ident, path_ends_with_complex,
require_api_version, KvParser,
};
use crate::{handle_mutually_exclusive_keys, util, ParseResult};

Expand Down Expand Up @@ -137,11 +137,12 @@ pub fn derive_godot_class(item: venial::Item) -> ParseResult<TokenStream> {
modifiers.push(quote! { with_tool })
}

// Declares a dummy struct that, for each #[func], holds a constant that maps the rust name to the name under which it is registered in godot.
let class_functions_name = format_func_name_struct_name(class_name);
let class_functions_struct = quote! {
// Declares a "funcs collection" struct that, for holds a constant for each #[func].
// That constant maps the Rust name (constant ident) to the Godot registered name (string value).
let funcs_collection_struct_name = format_funcs_collection_struct(class_name);
let funcs_collection_struct = quote! {
#[doc(hidden)]
pub struct #class_functions_name { }
pub struct #funcs_collection_struct_name {}
};

Ok(quote! {
Expand All @@ -167,7 +168,7 @@ pub fn derive_godot_class(item: venial::Item) -> ParseResult<TokenStream> {
type Exportable = <<Self as ::godot::obj::GodotClass>::Base as ::godot::obj::Bounds>::Exportable;
}

#class_functions_struct
#funcs_collection_struct
#godot_init_impl
#godot_withbase_impl
#godot_exports_impl
Expand Down
4 changes: 2 additions & 2 deletions godot-macros/src/class/godot_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub fn attribute_godot_api(
)?;
}

if decl.self_ty.as_path().is_none() {
let Some(self_path) = decl.self_ty.as_path() else {
return bail!(decl, "invalid Self type for #[godot_api] impl");
};

Expand All @@ -57,7 +57,7 @@ pub fn attribute_godot_api(
transform_trait_impl(decl)
} else {
match parse_inherent_impl_attr(meta) {
Ok(meta) => transform_inherent_impl(meta, decl),
Ok(meta) => transform_inherent_impl(meta, decl, self_path),
Err(err) => Err(err),
}
}
Expand Down
49 changes: 28 additions & 21 deletions godot-macros/src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use crate::ParseResult;
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, Path, PathSegment};

mod kv_parser;
mod list_parser;
Expand Down Expand Up @@ -248,14 +247,17 @@ 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.

// #[cfg_attr(condition, attributes...)]. Multiple attributes can be seperated by comma.
if attr_name == "cfg_attr" && attr.value.to_token_stream().to_string().contains("cfg(") {
return true;
}

false
})
}
Expand Down Expand Up @@ -320,7 +322,10 @@ pub fn venial_parse_meta(

// util functions for handling #[func]s and #[var(get=f, set=f)]

pub fn make_func_name_constants(funcs: &[FuncDefinition], class_name: &Ident) -> Vec<TokenStream> {
pub fn make_funcs_collection_constants(
funcs: &[FuncDefinition],
class_name: &Ident,
) -> Vec<TokenStream> {
funcs
.iter()
.map(|func| {
Expand All @@ -329,7 +334,7 @@ pub fn make_func_name_constants(funcs: &[FuncDefinition], class_name: &Ident) ->
.into_iter()
.collect::<Vec<_>>();

make_func_name_constant(
make_funcs_collection_constant(
class_name,
&func.signature_info.method_name,
func.registered_name.as_ref(),
Expand All @@ -339,15 +344,17 @@ pub fn make_func_name_constants(funcs: &[FuncDefinition], class_name: &Ident) ->
.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_func_name_constant(
/// Returns a `const` declaration for the funcs collection struct.
///
/// User-defined functions can be renamed with `#[func(rename=new_name)]`. To be able to access the renamed function name from another macro,
/// a constant is used as indirection.
pub fn make_funcs_collection_constant(
class_name: &Ident,
func_name: &Ident,
registered_name: Option<&String>,
attributes: &[&Attribute],
attributes: &[&venial::Attribute],
) -> TokenStream {
let const_name = format_func_name_constant_name(class_name, func_name);
let const_name = format_funcs_collection_constant(class_name, func_name);
let const_value = match &registered_name {
Some(renamed) => renamed.to_string(),
None => func_name.to_string(),
Expand All @@ -365,14 +372,14 @@ pub fn make_func_name_constant(
}
}

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

[_single] => Path {
segments: vec![PathSegment {
[_single] => venial::Path {
segments: vec![venial::PathSegment {
ident: new_class,
generic_args: None,
tk_separator_colons: None,
Expand All @@ -382,25 +389,25 @@ pub fn replace_class_in_path(path: Path, new_class: Ident) -> Path {
[path @ .., _last] => {
let mut segments = vec![];
segments.extend(path.iter().cloned());
segments.push(PathSegment {
segments.push(venial::PathSegment {
ident: new_class,
generic_args: None,
tk_separator_colons: Some([
Punct::new(':', Spacing::Joint),
Punct::new(':', Spacing::Alone),
]),
});
Path { segments }
venial::Path { segments }
}
}
}

/// Returns the name of the constant that will be autogenerated.
pub fn format_func_name_constant_name(_class_name: &Ident, func_name: &Ident) -> Ident {
/// Returns the name of the constant inside the func "collection" struct.
pub fn format_funcs_collection_constant(_class_name: &Ident, func_name: &Ident) -> Ident {
format_ident!("{func_name}")
}

/// Returns the name of the dummy struct that's used as container for all function name constants.
pub fn format_func_name_struct_name(class_name: &Ident) -> Ident {
format_ident!("__gdext_{class_name}_Functions")
/// Returns the name of the struct used as collection for all function name constants.
pub fn format_funcs_collection_struct(class_name: &Ident) -> Ident {
format_ident!("__gdext_{class_name}_Funcs")
}
Loading

0 comments on commit 22127f5

Please sign in to comment.