Skip to content

Commit

Permalink
Add method attribute
Browse files Browse the repository at this point in the history
Owner argument can be omitted.
  • Loading branch information
B-head committed Apr 30, 2022
1 parent 3724c36 commit e113a6b
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 28 deletions.
36 changes: 25 additions & 11 deletions gdnative-core/src/export/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,27 @@ 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)]."]
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 {
Expand All @@ -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
Expand Down Expand Up @@ -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,)*
);
Expand Down Expand Up @@ -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)*
$(,)?
Expand All @@ -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)?)
Expand All @@ -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)*
$(,)?
Expand All @@ -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)?)
Expand All @@ -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)*
$(,)?
Expand All @@ -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)?)
Expand All @@ -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)*
$(,)?
Expand All @@ -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)?)
Expand Down
2 changes: 1 addition & 1 deletion gdnative-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ mod variant;
/// impl gdnative::export::NativeClassMethods for Foo {
/// fn register(builder: &ClassBuilder<Self>) {
/// 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();
/// }
Expand Down
83 changes: 67 additions & 16 deletions gdnative-derive/src/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,12 @@ pub(crate) struct ExportMethod {
pub(crate) sig: Signature,
pub(crate) export_args: ExportArgs,
pub(crate) optional_args: Option<usize>,
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<RpcMode>,
pub(crate) name_override: Option<String>,
pub(crate) is_deref_return: bool,
Expand All @@ -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;
Expand All @@ -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();
}
Expand All @@ -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();
Expand All @@ -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!();)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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!(
Expand All @@ -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 {
Expand All @@ -344,39 +377,57 @@ 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.",
));
}
}
}

methods_to_export.push(ExportMethod {
sig: method.sig.clone(),
export_args,
optional_args,
exist_base_arg,
});
}

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit e113a6b

Please sign in to comment.