From e113a6b970a03ebca6faa2bda33a9460b5f254c4 Mon Sep 17 00:00:00 2001 From: B_head Date: Sat, 30 Apr 2022 19:37:56 +0900 Subject: [PATCH] Add method attribute Owner argument can be omitted. --- gdnative-core/src/export/macros.rs | 36 +++++++++---- gdnative-derive/src/lib.rs | 2 +- gdnative-derive/src/methods.rs | 83 ++++++++++++++++++++++++------ 3 files changed, 93 insertions(+), 28 deletions(-) diff --git a/gdnative-core/src/export/macros.rs b/gdnative-core/src/export/macros.rs index df11a9372..083cd9e40 100644 --- a/gdnative-core/src/export/macros.rs +++ b/gdnative-core/src/export/macros.rs @@ -22,6 +22,12 @@ macro_rules! godot_wrap_method_if_deref { }; } +// The ways of emit warnings is a terrible hack. +// This is because there is no way to emit warnings from macros in stable Rust. +// +// Follow these steps to emit warnings. +// - Detect whether reference types are used in gdnative-derive::methods::derive_methods(). +// - Expand the call to the deprecated_reference_return!() macro to user code. #[doc(hidden)] #[macro_export] #[deprecated = "This function does not actually pass by reference to the Godot engine. You can clarify by writing #[export(deref_return)]."] @@ -29,6 +35,14 @@ macro_rules! deprecated_reference_return { () => {}; } +#[doc(hidden)] +#[macro_export] +macro_rules! godot_wrap_method_void { + ($ident:ident, $void:tt) => { + $ident + }; +} + #[doc(hidden)] #[macro_export] macro_rules! godot_wrap_method_inner { @@ -38,7 +52,7 @@ macro_rules! godot_wrap_method_inner { $map_method:ident, fn $method_name:ident( $self:ident - , $owner:ident : $owner_ty:ty + $(, #[base] $base:ident : $base_ty:ty)? $(, $pname:ident : $pty:ty)* $(, #[opt] $opt_pname:ident : $opt_pty:ty)* ) -> $retty:ty @@ -67,11 +81,11 @@ macro_rules! godot_wrap_method_inner { Args { $($pname,)* $($opt_pname,)* }: Args, ) -> $crate::core_types::Variant { this - .$map_method(|__rust_val, $owner| { + .$map_method(|__rust_val, __base| { #[allow(unused_unsafe)] unsafe { let ret = __rust_val.$method_name( - OwnerArg::from_safe_ref($owner), + $(OwnerArg::from_safe_ref($crate::godot_wrap_method_void!(__base,$base)),)? $($pname,)* $($opt_pname,)* ); @@ -118,7 +132,7 @@ macro_rules! godot_wrap_method { $is_deref_return:ident, fn $method_name:ident( &mut $self:ident - , $owner:ident : $owner_ty:ty + $(, #[base] $base:ident : $base_ty:ty)? $(, $pname:ident : $pty:ty)* $(, #[opt] $opt_pname:ident : $opt_pty:ty)* $(,)? @@ -130,7 +144,7 @@ macro_rules! godot_wrap_method { map_mut, fn $method_name( $self - , $owner : $owner_ty + $(, #[base] $base : $base_ty)? $(, $pname : $pty)* $(, #[opt] $opt_pname : $opt_pty)* ) -> godot_wrap_method_return_type!($($retty)?) @@ -142,7 +156,7 @@ macro_rules! godot_wrap_method { $is_deref_return:ident, fn $method_name:ident( & $self:ident - , $owner:ident : $owner_ty:ty + $(, #[base] $base:ident : $base_ty:ty)? $(, $pname:ident : $pty:ty)* $(, #[opt] $opt_pname:ident : $opt_pty:ty)* $(,)? @@ -154,7 +168,7 @@ macro_rules! godot_wrap_method { map, fn $method_name( $self - , $owner : $owner_ty + $(, #[base] $base : $base_ty)? $(, $pname : $pty)* $(, #[opt] $opt_pname : $opt_pty)* ) -> godot_wrap_method_return_type!($($retty)?) @@ -166,7 +180,7 @@ macro_rules! godot_wrap_method { $is_deref_return:ident, fn $method_name:ident( mut $self:ident - , $owner:ident : $owner_ty:ty + $(, #[base] $base:ident : $base_ty:ty)? $(, $pname:ident : $pty:ty)* $(, #[opt] $opt_pname:ident : $opt_pty:ty)* $(,)? @@ -178,7 +192,7 @@ macro_rules! godot_wrap_method { map_owned, fn $method_name( $self - , $owner : $owner_ty + $(, #[base] $base : $base_ty)? $(, $pname : $pty)* $(, #[opt] $opt_pname : $opt_pty)* ) -> godot_wrap_method_return_type!($($retty)?) @@ -190,7 +204,7 @@ macro_rules! godot_wrap_method { $is_deref_return:ident, fn $method_name:ident( $self:ident - , $owner:ident : $owner_ty:ty + $(, #[base] $base:ident : $base_ty:ty)? $(, $pname:ident : $pty:ty)* $(, #[opt] $opt_pname:ident : $opt_pty:ty)* $(,)? @@ -202,7 +216,7 @@ macro_rules! godot_wrap_method { map_owned, fn $method_name( $self - , $owner : $owner_ty + $(, #[base] $base : $base_ty)? $(, $pname : $pty)* $(, #[opt] $opt_pname : $opt_pty)* ) -> godot_wrap_method_return_type!($($retty)?) diff --git a/gdnative-derive/src/lib.rs b/gdnative-derive/src/lib.rs index e37c3c006..c3b2e3a7d 100644 --- a/gdnative-derive/src/lib.rs +++ b/gdnative-derive/src/lib.rs @@ -50,7 +50,7 @@ mod variant; /// impl gdnative::export::NativeClassMethods for Foo { /// fn register(builder: &ClassBuilder) { /// use gdnative::export::*; -/// builder.method("foo", gdnative::export::godot_wrap_method!(Foo, false, fn foo(&self, _owner: &Reference, bar: i64) -> i64)) +/// builder.method("foo", gdnative::export::godot_wrap_method!(Foo, false, fn foo(&self, #[base] _owner: &Reference, bar: i64) -> i64)) /// .with_rpc_mode(RpcMode::Disabled) /// .done_stateless(); /// } diff --git a/gdnative-derive/src/methods.rs b/gdnative-derive/src/methods.rs index 6671ac1d6..84470e1ed 100644 --- a/gdnative-derive/src/methods.rs +++ b/gdnative-derive/src/methods.rs @@ -60,10 +60,12 @@ pub(crate) struct ExportMethod { pub(crate) sig: Signature, pub(crate) export_args: ExportArgs, pub(crate) optional_args: Option, + pub(crate) exist_base_arg: bool, } #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)] pub(crate) struct ExportArgs { + pub(crate) is_old_syntax: bool, pub(crate) rpc_mode: Option, pub(crate) name_override: Option, pub(crate) is_deref_return: bool, @@ -80,7 +82,7 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 { let methods = export .methods .into_iter() - .map(|ExportMethod { sig, export_args , optional_args}| { + .map(|ExportMethod { sig, export_args, optional_args, exist_base_arg}| { let sig_span = sig.ident.span(); let name = sig.ident; @@ -93,10 +95,18 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 { let arg_count = sig.inputs.len(); - if arg_count < 2 { + if arg_count == 0 { return syn::Error::new( sig_span, - "exported methods must take self and owner as arguments", + "exported methods must take self parameter", + ) + .to_compile_error(); + } + + if export_args.is_old_syntax && !exist_base_arg { + return syn::Error::new( + sig_span, + "exported methods must take second parameter", ) .to_compile_error(); } @@ -106,7 +116,7 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 { let max_optional = arg_count - 2; // self and owner if count > max_optional { let message = format!( - "there can be at most {} optional arguments, got {}", + "there can be at most {} optional parameters, got {}", max_optional, count, ); return syn::Error::new(sig_span, message).to_compile_error(); @@ -121,13 +131,17 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 { let args = sig.inputs.iter().enumerate().map(|(n, arg)| { let span = arg.span(); - if n < arg_count - optional_args { + if exist_base_arg && n == 1 { + quote_spanned!(span => #[base] #arg ,) + } + else if n < arg_count - optional_args { quote_spanned!(span => #arg ,) } else { quote_spanned!(span => #[opt] #arg ,) } }); + // See gdnative-core::export::deprecated_reference_return!() let deprecated = if let syn::ReturnType::Type(_, ty) = &sig.output { if !is_deref_return && matches!(**ty, syn::Type::Reference(_)) { quote_spanned!(ret_span=> ::gdnative::export::deprecated_reference_return!();) @@ -208,9 +222,23 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) { .last() .map(|i| i.ident.to_string()); - if let Some("export") = last_seg.as_deref() { - let mut _export_args = export_args.get_or_insert_with(ExportArgs::default); + let (is_export, is_old_syntax) = if let Some("export") = last_seg.as_deref() + { + (true, true) + } else if let Some("method") = last_seg.as_deref() { + (true, false) + } else { + (false, false) + }; + + if is_export { use syn::{punctuated::Punctuated, Lit, Meta, NestedMeta}; + let mut export_args = + export_args.get_or_insert_with(ExportArgs::default); + export_args.is_old_syntax = is_old_syntax; + + // Codes like #[macro(path, name = "value")] are accepted. + // Codes like #[path], #[name = "value"] or #[macro("lit")] are not accepted. let nested_meta_iter = match attr.parse_meta() { Err(err) => { errors.push(err); @@ -260,7 +288,7 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) { Some(Lit::Str(str)) => { let value = str.value(); if let Some(mode) = RpcMode::parse(value.as_str()) { - if _export_args.rpc_mode.replace(mode).is_some() { + if export_args.rpc_mode.replace(mode).is_some() { errors.push(syn::Error::new( nested_meta.span(), "rpc mode was set more than once", @@ -290,7 +318,11 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) { )); } Some(Lit::Str(str)) => { - if _export_args.name_override.replace(str.value()).is_some() { + if export_args + .name_override + .replace(str.value()) + .is_some() + { errors.push(syn::Error::new( nested_meta.span(), "name was set more than once", @@ -311,13 +343,13 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) { nested_meta.span(), "value for deref_return parameter is not valid", )); - } else if _export_args.is_deref_return { + } else if export_args.is_deref_return { errors.push(syn::Error::new( nested_meta.span(), "deref_return was apply more than once", )); } else { - _export_args.is_deref_return = true; + export_args.is_deref_return = true; } } else { let msg = format!( @@ -336,6 +368,7 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) { if let Some(export_args) = export_args.take() { let mut optional_args = None; + let mut exist_base_arg = false; for (n, arg) in method.sig.inputs.iter_mut().enumerate() { let attrs = match arg { @@ -344,32 +377,49 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) { }; let mut is_optional = false; + let mut is_base = false; attrs.retain(|attr| { if attr.path.is_ident("opt") { is_optional = true; false + } else if attr.path.is_ident("base") { + is_base = true; + false } else { true } }); + // In the old syntax, the second parameter is always the base parameter. + if export_args.is_old_syntax && n == 1 { + is_base = true; + } + if is_optional { if n < 2 { errors.push(syn::Error::new( arg.span(), "self or owner cannot be optional", )); - continue; + } else { + *optional_args.get_or_insert(0) += 1; } - - *optional_args.get_or_insert(0) += 1; } else if optional_args.is_some() { errors.push(syn::Error::new( arg.span(), "cannot add required parameters after optional ones", )); - continue; + } + + if is_base { + exist_base_arg = true; + if n != 1 { + errors.push(syn::Error::new( + arg.span(), + "base must be the second parameter.", + )); + } } } @@ -377,6 +427,7 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) { sig: method.sig.clone(), export_args, optional_args, + exist_base_arg, }); } @@ -423,7 +474,7 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) { continue; } - // remove "mut" from arguments. + // remove "mut" from parameters. // give every wildcard a (hopefully) unique name. method .sig