Skip to content

Commit fbdb0ac

Browse files
committed
Fully proceduralize method exports
Converted the remaining macro_rules! used by the `#[methods]` macro into procedural macros, increasing flexibility for future expansion and reducing reliance on `#[doc(hidden)]` macro items. This makes it easier to support more combinations of method signatures, without having to manually write out n^2 patterns. A shim for the legacy `godot_wrap_method` macro is kept for compatibility. It can be removed in the next breaking release.
1 parent 46baf14 commit fbdb0ac

File tree

8 files changed

+383
-330
lines changed

8 files changed

+383
-330
lines changed

gdnative-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type-tag-fallback = []
1818

1919
[dependencies]
2020
gdnative-sys = { path = "../gdnative-sys", version = "=0.11.0" }
21+
gdnative-derive = { path = "../gdnative-derive", version = "=0.11.0" }
2122
gdnative-impl-proc-macros = { path = "../impl/proc-macros", version = "=0.11.0" }
2223
ahash = "0.8"
2324
approx = "0.5"

gdnative-core/src/export/macros.rs

Lines changed: 2 additions & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -1,222 +1,2 @@
1-
#![macro_use]
2-
3-
#[doc(hidden)]
4-
#[macro_export]
5-
macro_rules! godot_wrap_method_if_deref {
6-
(true, $ret:expr) => {
7-
std::ops::Deref::deref(&$ret)
8-
};
9-
(false, $ret:expr) => {
10-
$ret
11-
};
12-
}
13-
14-
// The ways of emit warnings is a terrible hack.
15-
// This is because there is no way to emit warnings from macros in stable Rust.
16-
//
17-
// Follow these steps to emit warnings.
18-
// - Detect whether reference types are used in gdnative-derive::methods::derive_methods().
19-
// - Expand the call to the deprecated_reference_return!() macro to user code.
20-
#[doc(hidden)]
21-
#[macro_export]
22-
#[deprecated = "This function does not actually pass by reference to the Godot engine. You can clarify by writing #[method(deref_return)]."]
23-
macro_rules! deprecated_reference_return {
24-
() => {};
25-
}
26-
27-
#[doc(hidden)]
28-
#[macro_export]
29-
#[deprecated = "\n#[export] is deprecated and will be removed in a future godot-rust version. Use #[method] instead. \n\
30-
For more information, see https://godot-rust.github.io/docs/gdnative/derive/derive.NativeClass.html."]
31-
macro_rules! deprecated_export_syntax {
32-
() => {};
33-
}
34-
35-
#[doc(hidden)]
36-
#[macro_export]
37-
macro_rules! godot_wrap_method_void {
38-
($ident:ident, $void:tt) => {
39-
$ident
40-
};
41-
}
42-
43-
#[doc(hidden)]
44-
#[macro_export]
45-
macro_rules! godot_wrap_method_inner {
46-
(
47-
$type_name:ty,
48-
$is_deref_return:ident,
49-
$map_method:ident,
50-
fn $method_name:ident(
51-
$self:ident
52-
$(, #[base] $base:ident : $base_ty:ty)?
53-
$(, $pname:ident : $pty:ty)*
54-
$(, #[opt] $opt_pname:ident : $opt_pty:ty)*
55-
) -> $retty:ty
56-
) => {
57-
{
58-
#[derive(Copy, Clone, Default)]
59-
struct ThisMethod;
60-
61-
use $crate::export::{NativeClass, OwnerArg};
62-
use $crate::object::{Instance, TInstance};
63-
use ::gdnative::derive::FromVarargs;
64-
65-
#[derive(FromVarargs)]
66-
#[allow(clippy::used_underscore_binding)]
67-
struct Args {
68-
$($pname: $pty,)*
69-
$(#[opt] $opt_pname: $opt_pty,)*
70-
}
71-
72-
#[allow(unused_variables, unused_assignments, unused_mut)]
73-
impl $crate::export::StaticArgsMethod<$type_name> for ThisMethod {
74-
type Args = Args;
75-
fn call(
76-
&self,
77-
this: TInstance<'_, $type_name, $crate::object::ownership::Shared>,
78-
Args { $($pname,)* $($opt_pname,)* }: Args,
79-
) -> $crate::core_types::Variant {
80-
this
81-
.$map_method(|__rust_val, __base| {
82-
#[allow(unused_unsafe)]
83-
unsafe {
84-
let ret = __rust_val.$method_name(
85-
$(OwnerArg::from_safe_ref($crate::godot_wrap_method_void!(__base,$base)),)?
86-
$($pname,)*
87-
$($opt_pname,)*
88-
);
89-
gdnative::core_types::OwnedToVariant::owned_to_variant(
90-
$crate::godot_wrap_method_if_deref!($is_deref_return, ret)
91-
)
92-
}
93-
})
94-
.unwrap_or_else(|err| {
95-
$crate::godot_error!("gdnative-core: method call failed with error: {}", err);
96-
$crate::godot_error!("gdnative-core: check module level documentation on gdnative::user_data for more information");
97-
$crate::core_types::Variant::nil()
98-
})
99-
}
100-
101-
fn site() -> Option<$crate::log::Site<'static>> {
102-
Some($crate::godot_site!($type_name::$method_name))
103-
}
104-
}
105-
106-
$crate::export::StaticArgs::new(ThisMethod)
107-
}
108-
};
109-
}
110-
111-
#[doc(hidden)]
112-
#[macro_export]
113-
macro_rules! godot_wrap_method_return_type {
114-
() => {
115-
()
116-
};
117-
($retty:ty) => {
118-
$retty: ty
119-
};
120-
}
121-
122-
/// Convenience macro to wrap an object's method into a function pointer
123-
/// that can be passed to the engine when registering a class.
124-
#[macro_export]
125-
macro_rules! godot_wrap_method {
126-
// mutable
127-
(
128-
$type_name:ty,
129-
$is_deref_return:ident,
130-
fn $method_name:ident(
131-
&mut $self:ident
132-
$(, #[base] $base:ident : $base_ty:ty)?
133-
$(, $pname:ident : $pty:ty)*
134-
$(, #[opt] $opt_pname:ident : $opt_pty:ty)*
135-
$(,)?
136-
) $(-> $retty:ty)?
137-
) => {
138-
$crate::godot_wrap_method_inner!(
139-
$type_name,
140-
$is_deref_return,
141-
map_mut,
142-
fn $method_name(
143-
$self
144-
$(, #[base] $base : $base_ty)?
145-
$(, $pname : $pty)*
146-
$(, #[opt] $opt_pname : $opt_pty)*
147-
) -> godot_wrap_method_return_type!($($retty)?)
148-
)
149-
};
150-
// immutable
151-
(
152-
$type_name:ty,
153-
$is_deref_return:ident,
154-
fn $method_name:ident(
155-
& $self:ident
156-
$(, #[base] $base:ident : $base_ty:ty)?
157-
$(, $pname:ident : $pty:ty)*
158-
$(, #[opt] $opt_pname:ident : $opt_pty:ty)*
159-
$(,)?
160-
) $(-> $retty:ty)?
161-
) => {
162-
$crate::godot_wrap_method_inner!(
163-
$type_name,
164-
$is_deref_return,
165-
map,
166-
fn $method_name(
167-
$self
168-
$(, #[base] $base : $base_ty)?
169-
$(, $pname : $pty)*
170-
$(, #[opt] $opt_pname : $opt_pty)*
171-
) -> godot_wrap_method_return_type!($($retty)?)
172-
)
173-
};
174-
// owned
175-
(
176-
$type_name:ty,
177-
$is_deref_return:ident,
178-
fn $method_name:ident(
179-
mut $self:ident
180-
$(, #[base] $base:ident : $base_ty:ty)?
181-
$(, $pname:ident : $pty:ty)*
182-
$(, #[opt] $opt_pname:ident : $opt_pty:ty)*
183-
$(,)?
184-
) $(-> $retty:ty)?
185-
) => {
186-
$crate::godot_wrap_method_inner!(
187-
$type_name,
188-
$is_deref_return,
189-
map_owned,
190-
fn $method_name(
191-
$self
192-
$(, #[base] $base : $base_ty)?
193-
$(, $pname : $pty)*
194-
$(, #[opt] $opt_pname : $opt_pty)*
195-
) -> godot_wrap_method_return_type!($($retty)?)
196-
)
197-
};
198-
// owned
199-
(
200-
$type_name:ty,
201-
$is_deref_return:ident,
202-
fn $method_name:ident(
203-
$self:ident
204-
$(, #[base] $base:ident : $base_ty:ty)?
205-
$(, $pname:ident : $pty:ty)*
206-
$(, #[opt] $opt_pname:ident : $opt_pty:ty)*
207-
$(,)?
208-
) $(-> $retty:ty)?
209-
) => {
210-
$crate::godot_wrap_method_inner!(
211-
$type_name,
212-
$is_deref_return,
213-
map_owned,
214-
fn $method_name(
215-
$self
216-
$(, #[base] $base : $base_ty)?
217-
$(, $pname : $pty)*
218-
$(, #[opt] $opt_pname : $opt_pty)*
219-
) -> godot_wrap_method_return_type!($($retty)?)
220-
)
221-
};
222-
}
1+
#[doc(inline)]
2+
pub use gdnative_derive::godot_wrap_method;

gdnative-core/src/export/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,10 @@ pub(crate) mod type_tag;
2626

2727
pub mod user_data;
2828

29-
pub use crate::godot_wrap_method;
30-
#[allow(deprecated)]
31-
pub use crate::{deprecated_export_syntax, deprecated_reference_return};
3229
pub use class::*;
3330
pub use class_builder::*;
31+
#[doc(inline)]
32+
pub use gdnative_derive::godot_wrap_method;
3433
pub use method::*;
3534
pub use property::*;
3635
pub use signal::*;

gdnative-core/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ pub extern crate libc;
3737
#[macro_use]
3838
extern crate approx;
3939

40+
#[doc(inline)]
41+
pub use gdnative_derive::godot_wrap_method;
42+
43+
/// Derive macros and macro attributes.
44+
#[doc(inline)]
45+
pub use gdnative_derive as derive;
46+
4047
// Macros have to be processed before they are used.
4148
mod macros;
4249

gdnative-derive/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ proc-macro = true
1818
syn = { version = "1", features = ["full", "extra-traits", "visit"] }
1919
quote = "1"
2020
proc-macro2 = "1"
21+
proc-macro-crate = "1"
2122

2223
[dev-dependencies]
2324
# This is included for the doc tests.

gdnative-derive/src/lib.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,24 @@ pub fn derive_from_varargs(input: TokenStream) -> TokenStream {
482482
}
483483
}
484484

485+
/// Convenience macro to wrap an object's method into a `Method` implementor
486+
/// that can be passed to the engine when registering a class.
487+
#[proc_macro]
488+
#[deprecated = "The legacy manual export macro is deprecated and will be removed in a future godot-rust version. \
489+
Either use the `#[methods]` attribute macro, or implement the `Method` trait manually instead."]
490+
pub fn godot_wrap_method(input: TokenStream) -> TokenStream {
491+
match methods::expand_godot_wrap_method(input.into()) {
492+
Ok(stream) => stream.into(),
493+
Err(xs) => {
494+
let mut tokens = TokenStream2::new();
495+
for err in xs {
496+
tokens.extend(err.to_compile_error());
497+
}
498+
tokens.into()
499+
}
500+
}
501+
}
502+
485503
/// Returns a standard header for derived implementations.
486504
///
487505
/// Adds the `automatically_derived` attribute and prevents common lints from triggering
@@ -496,3 +514,37 @@ fn automatically_derived() -> proc_macro2::TokenStream {
496514
#[allow(nonstandard_style, unused, clippy::style, clippy::complexity, clippy::perf, clippy::pedantic)]
497515
}
498516
}
517+
518+
/// Returns the (possibly renamed or imported as `gdnative`) identifier of the `gdnative_core` crate.
519+
fn crate_gdnative_core() -> proc_macro2::TokenStream {
520+
let found_crate = proc_macro_crate::crate_name("gdnative-core")
521+
.or_else(|_| proc_macro_crate::crate_name("gdnative"))
522+
.expect("crate not found");
523+
524+
match found_crate {
525+
proc_macro_crate::FoundCrate::Itself => quote!(crate),
526+
proc_macro_crate::FoundCrate::Name(name) => {
527+
let ident = proc_macro2::Ident::new(&name, proc_macro2::Span::call_site());
528+
quote!( #ident )
529+
}
530+
}
531+
}
532+
533+
/// Hack to emit a warning in expression position through `deprecated`.
534+
/// This is because there is no way to emit warnings from macros in stable Rust.
535+
fn emit_warning<S: std::fmt::Display>(
536+
span: proc_macro2::Span,
537+
warning_name: &str,
538+
message: S,
539+
) -> proc_macro2::TokenStream {
540+
let warning_name = proc_macro2::Ident::new(warning_name, span);
541+
let message = message.to_string();
542+
543+
quote_spanned! { span =>
544+
{
545+
#[deprecated = #message]
546+
fn #warning_name() {}
547+
#warning_name()
548+
}
549+
}
550+
}

0 commit comments

Comments
 (0)