Skip to content

Commit 8ed2fca

Browse files
ecobiubiuB-head
authored andcommitted
Add #[method] and #[base] attribute
godot-rust/gdnative#872
1 parent 04969c0 commit 8ed2fca

File tree

3 files changed

+93
-28
lines changed

3 files changed

+93
-28
lines changed

gdnative-core/src/export/macros.rs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,27 @@ macro_rules! godot_wrap_method_if_deref {
2222
};
2323
}
2424

25+
// The ways of emit warnings is a terrible hack.
26+
// This is because there is no way to emit warnings from macros in stable Rust.
27+
//
28+
// Follow these steps to emit warnings.
29+
// - Detect whether reference types are used in gdnative-derive::methods::derive_methods().
30+
// - Expand the call to the deprecated_reference_return!() macro to user code.
2531
#[doc(hidden)]
2632
#[macro_export]
2733
#[deprecated = "This function does not actually pass by reference to the Godot engine. You can clarify by writing #[export(deref_return)]."]
2834
macro_rules! deprecated_reference_return {
2935
() => {};
3036
}
3137

38+
#[doc(hidden)]
39+
#[macro_export]
40+
macro_rules! godot_wrap_method_void {
41+
($ident:ident, $void:tt) => {
42+
$ident
43+
};
44+
}
45+
3246
#[doc(hidden)]
3347
#[macro_export]
3448
macro_rules! godot_wrap_method_inner {
@@ -38,7 +52,7 @@ macro_rules! godot_wrap_method_inner {
3852
$map_method:ident,
3953
fn $method_name:ident(
4054
$self:ident
41-
, $owner:ident : $owner_ty:ty
55+
$(, #[base] $base:ident : $base_ty:ty)?
4256
$(, $pname:ident : $pty:ty)*
4357
$(, #[opt] $opt_pname:ident : $opt_pty:ty)*
4458
) -> $retty:ty
@@ -67,11 +81,11 @@ macro_rules! godot_wrap_method_inner {
6781
Args { $($pname,)* $($opt_pname,)* }: Args,
6882
) -> $crate::core_types::Variant {
6983
this
70-
.$map_method(|__rust_val, $owner| {
84+
.$map_method(|__rust_val, __base| {
7185
#[allow(unused_unsafe)]
7286
unsafe {
7387
let ret = __rust_val.$method_name(
74-
OwnerArg::from_safe_ref($owner),
88+
$(OwnerArg::from_safe_ref($crate::godot_wrap_method_void!(__base,$base)),)?
7589
$($pname,)*
7690
$($opt_pname,)*
7791
);
@@ -118,7 +132,7 @@ macro_rules! godot_wrap_method {
118132
$is_deref_return:ident,
119133
fn $method_name:ident(
120134
&mut $self:ident
121-
, $owner:ident : $owner_ty:ty
135+
$(, #[base] $base:ident : $base_ty:ty)?
122136
$(, $pname:ident : $pty:ty)*
123137
$(, #[opt] $opt_pname:ident : $opt_pty:ty)*
124138
$(,)?
@@ -130,7 +144,7 @@ macro_rules! godot_wrap_method {
130144
map_mut,
131145
fn $method_name(
132146
$self
133-
, $owner : $owner_ty
147+
$(, #[base] $base : $base_ty)?
134148
$(, $pname : $pty)*
135149
$(, #[opt] $opt_pname : $opt_pty)*
136150
) -> godot_wrap_method_return_type!($($retty)?)
@@ -142,7 +156,7 @@ macro_rules! godot_wrap_method {
142156
$is_deref_return:ident,
143157
fn $method_name:ident(
144158
& $self:ident
145-
, $owner:ident : $owner_ty:ty
159+
$(, #[base] $base:ident : $base_ty:ty)?
146160
$(, $pname:ident : $pty:ty)*
147161
$(, #[opt] $opt_pname:ident : $opt_pty:ty)*
148162
$(,)?
@@ -154,7 +168,7 @@ macro_rules! godot_wrap_method {
154168
map,
155169
fn $method_name(
156170
$self
157-
, $owner : $owner_ty
171+
$(, #[base] $base : $base_ty)?
158172
$(, $pname : $pty)*
159173
$(, #[opt] $opt_pname : $opt_pty)*
160174
) -> godot_wrap_method_return_type!($($retty)?)
@@ -166,7 +180,7 @@ macro_rules! godot_wrap_method {
166180
$is_deref_return:ident,
167181
fn $method_name:ident(
168182
mut $self:ident
169-
, $owner:ident : $owner_ty:ty
183+
$(, #[base] $base:ident : $base_ty:ty)?
170184
$(, $pname:ident : $pty:ty)*
171185
$(, #[opt] $opt_pname:ident : $opt_pty:ty)*
172186
$(,)?
@@ -178,7 +192,7 @@ macro_rules! godot_wrap_method {
178192
map_owned,
179193
fn $method_name(
180194
$self
181-
, $owner : $owner_ty
195+
$(, #[base] $base : $base_ty)?
182196
$(, $pname : $pty)*
183197
$(, #[opt] $opt_pname : $opt_pty)*
184198
) -> godot_wrap_method_return_type!($($retty)?)
@@ -190,7 +204,7 @@ macro_rules! godot_wrap_method {
190204
$is_deref_return:ident,
191205
fn $method_name:ident(
192206
$self:ident
193-
, $owner:ident : $owner_ty:ty
207+
$(, #[base] $base:ident : $base_ty:ty)?
194208
$(, $pname:ident : $pty:ty)*
195209
$(, #[opt] $opt_pname:ident : $opt_pty:ty)*
196210
$(,)?
@@ -202,7 +216,7 @@ macro_rules! godot_wrap_method {
202216
map_owned,
203217
fn $method_name(
204218
$self
205-
, $owner : $owner_ty
219+
$(, #[base] $base : $base_ty)?
206220
$(, $pname : $pty)*
207221
$(, #[opt] $opt_pname : $opt_pty)*
208222
) -> godot_wrap_method_return_type!($($retty)?)

gdnative-derive/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ mod variant;
5050
/// impl gdnative::export::NativeClassMethods for Foo {
5151
/// fn register(builder: &ClassBuilder<Self>) {
5252
/// use gdnative::export::*;
53-
/// builder.method("foo", gdnative::export::godot_wrap_method!(Foo, false, fn foo(&self, _owner: &Reference, bar: i64) -> i64))
53+
/// builder.method("foo", gdnative::export::godot_wrap_method!(Foo, false, fn foo(&self, #[base] _owner: &Reference, bar: i64) -> i64))
5454
/// .with_rpc_mode(RpcMode::Disabled)
5555
/// .done_stateless();
5656
/// }

gdnative-derive/src/methods.rs

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,12 @@ pub(crate) struct ExportMethod {
6060
pub(crate) sig: Signature,
6161
pub(crate) export_args: ExportArgs,
6262
pub(crate) optional_args: Option<usize>,
63+
pub(crate) exist_base_arg: bool,
6364
}
6465

6566
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
6667
pub(crate) struct ExportArgs {
68+
pub(crate) is_old_syntax: bool,
6769
pub(crate) rpc_mode: Option<RpcMode>,
6870
pub(crate) name_override: Option<String>,
6971
pub(crate) is_deref_return: bool,
@@ -80,7 +82,7 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 {
8082
let methods = export
8183
.methods
8284
.into_iter()
83-
.map(|ExportMethod { sig, export_args , optional_args}| {
85+
.map(|ExportMethod { sig, export_args, optional_args, exist_base_arg}| {
8486
let sig_span = sig.ident.span();
8587

8688
let name = sig.ident;
@@ -93,10 +95,18 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 {
9395

9496
let arg_count = sig.inputs.len();
9597

96-
if arg_count < 2 {
98+
if arg_count == 0 {
9799
return syn::Error::new(
98100
sig_span,
99-
"exported methods must take self and owner as arguments",
101+
"exported methods must take self parameter",
102+
)
103+
.to_compile_error();
104+
}
105+
106+
if export_args.is_old_syntax && !exist_base_arg {
107+
return syn::Error::new(
108+
sig_span,
109+
"exported methods must take second parameter",
100110
)
101111
.to_compile_error();
102112
}
@@ -106,7 +116,7 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 {
106116
let max_optional = arg_count - 2; // self and owner
107117
if count > max_optional {
108118
let message = format!(
109-
"there can be at most {} optional arguments, got {}",
119+
"there can be at most {} optional parameters, got {}",
110120
max_optional, count,
111121
);
112122
return syn::Error::new(sig_span, message).to_compile_error();
@@ -121,13 +131,17 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 {
121131

122132
let args = sig.inputs.iter().enumerate().map(|(n, arg)| {
123133
let span = arg.span();
124-
if n < arg_count - optional_args {
134+
if exist_base_arg && n == 1 {
135+
quote_spanned!(span => #[base] #arg ,)
136+
}
137+
else if n < arg_count - optional_args {
125138
quote_spanned!(span => #arg ,)
126139
} else {
127140
quote_spanned!(span => #[opt] #arg ,)
128141
}
129142
});
130143

144+
// See gdnative-core::export::deprecated_reference_return!()
131145
let deprecated = if let syn::ReturnType::Type(_, ty) = &sig.output {
132146
if !is_deref_return && matches!(**ty, syn::Type::Reference(_)) {
133147
quote_spanned!(ret_span=> ::gdnative::export::deprecated_reference_return!();)
@@ -208,9 +222,23 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) {
208222
.last()
209223
.map(|i| i.ident.to_string());
210224

211-
if let Some("export") = last_seg.as_deref() {
212-
let mut _export_args = export_args.get_or_insert_with(ExportArgs::default);
225+
let (is_export, is_old_syntax) = if let Some("export") = last_seg.as_deref()
226+
{
227+
(true, true)
228+
} else if let Some("method") = last_seg.as_deref() {
229+
(true, false)
230+
} else {
231+
(false, false)
232+
};
233+
234+
if is_export {
213235
use syn::{punctuated::Punctuated, Lit, Meta, NestedMeta};
236+
let mut export_args =
237+
export_args.get_or_insert_with(ExportArgs::default);
238+
export_args.is_old_syntax = is_old_syntax;
239+
240+
// Codes like #[macro(path, name = "value")] are accepted.
241+
// Codes like #[path], #[name = "value"] or #[macro("lit")] are not accepted.
214242
let nested_meta_iter = match attr.parse_meta() {
215243
Err(err) => {
216244
errors.push(err);
@@ -260,7 +288,7 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) {
260288
Some(Lit::Str(str)) => {
261289
let value = str.value();
262290
if let Some(mode) = RpcMode::parse(value.as_str()) {
263-
if _export_args.rpc_mode.replace(mode).is_some() {
291+
if export_args.rpc_mode.replace(mode).is_some() {
264292
errors.push(syn::Error::new(
265293
nested_meta.span(),
266294
"rpc mode was set more than once",
@@ -290,7 +318,11 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) {
290318
));
291319
}
292320
Some(Lit::Str(str)) => {
293-
if _export_args.name_override.replace(str.value()).is_some() {
321+
if export_args
322+
.name_override
323+
.replace(str.value())
324+
.is_some()
325+
{
294326
errors.push(syn::Error::new(
295327
nested_meta.span(),
296328
"name was set more than once",
@@ -311,13 +343,13 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) {
311343
nested_meta.span(),
312344
"value for deref_return parameter is not valid",
313345
));
314-
} else if _export_args.is_deref_return {
346+
} else if export_args.is_deref_return {
315347
errors.push(syn::Error::new(
316348
nested_meta.span(),
317349
"deref_return was apply more than once",
318350
));
319351
} else {
320-
_export_args.is_deref_return = true;
352+
export_args.is_deref_return = true;
321353
}
322354
} else {
323355
let msg = format!(
@@ -336,6 +368,7 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) {
336368

337369
if let Some(export_args) = export_args.take() {
338370
let mut optional_args = None;
371+
let mut exist_base_arg = false;
339372

340373
for (n, arg) in method.sig.inputs.iter_mut().enumerate() {
341374
let attrs = match arg {
@@ -344,39 +377,57 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) {
344377
};
345378

346379
let mut is_optional = false;
380+
let mut is_base = false;
347381

348382
attrs.retain(|attr| {
349383
if attr.path.is_ident("opt") {
350384
is_optional = true;
351385
false
386+
} else if attr.path.is_ident("base") {
387+
is_base = true;
388+
false
352389
} else {
353390
true
354391
}
355392
});
356393

394+
// In the old syntax, the second parameter is always the base parameter.
395+
if export_args.is_old_syntax && n == 1 {
396+
is_base = true;
397+
}
398+
357399
if is_optional {
358400
if n < 2 {
359401
errors.push(syn::Error::new(
360402
arg.span(),
361403
"self or owner cannot be optional",
362404
));
363-
continue;
405+
} else {
406+
*optional_args.get_or_insert(0) += 1;
364407
}
365-
366-
*optional_args.get_or_insert(0) += 1;
367408
} else if optional_args.is_some() {
368409
errors.push(syn::Error::new(
369410
arg.span(),
370411
"cannot add required parameters after optional ones",
371412
));
372-
continue;
413+
}
414+
415+
if is_base {
416+
exist_base_arg = true;
417+
if n != 1 {
418+
errors.push(syn::Error::new(
419+
arg.span(),
420+
"base must be the second parameter.",
421+
));
422+
}
373423
}
374424
}
375425

376426
methods_to_export.push(ExportMethod {
377427
sig: method.sig.clone(),
378428
export_args,
379429
optional_args,
430+
exist_base_arg,
380431
});
381432
}
382433

@@ -423,7 +474,7 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) {
423474
continue;
424475
}
425476

426-
// remove "mut" from arguments.
477+
// remove "mut" from parameters.
427478
// give every wildcard a (hopefully) unique name.
428479
method
429480
.sig

0 commit comments

Comments
 (0)