Skip to content

Commit 04c2323

Browse files
bors[bot]jbarnoud
andauthored
Merge #207
207: Add return values when generating virtual methods r=Bromeon a=jbarnoud Fixes #190 Some node methods are expected to return values. The corresponding virtual methods, however, do not reflect that. For instance, this is the virtual method `Node.get_configuration_warnings` before this commit: ```rust fn get_configuration_warnings(&self) { unimplemented!() } ``` This is the same method with this commit: ```rust fn get_configuration_warnings(&self) -> PackedStringArray { unimplemented!() } ``` This commit adapts what I think is the relevant code from `godot_codegen::class_generator::make_method_definition` into `make_virtual_method`. Note that I am very unfamiliar with most of the code I had to touch here as well as with the project. Co-authored-by: Jonathan Barnoud <[email protected]>
2 parents e0d45a3 + e81d5d1 commit 04c2323

File tree

2 files changed

+83
-16
lines changed

2 files changed

+83
-16
lines changed

godot-codegen/src/class_generator.rs

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,7 @@ fn make_method_definition(
584584
__call_fn(__method_bind, #receiver_arg, __args_ptr, return_ptr);
585585
};
586586

587+
let is_virtual = false;
587588
make_function_definition(
588589
method_name_str,
589590
special_cases::is_private(class_name, &method.name),
@@ -594,6 +595,7 @@ fn make_method_definition(
594595
init_code,
595596
&varcall_invocation,
596597
&ptrcall_invocation,
598+
is_virtual,
597599
ctx,
598600
)
599601
}
@@ -634,6 +636,7 @@ fn make_builtin_method_definition(
634636
__call_fn(#receiver_arg, __args_ptr, return_ptr, __args.len() as i32);
635637
};
636638

639+
let is_virtual = false;
637640
make_function_definition(
638641
method_name_str,
639642
special_cases::is_private(class_name, &method.name),
@@ -644,6 +647,7 @@ fn make_builtin_method_definition(
644647
init_code,
645648
&ptrcall_invocation,
646649
&ptrcall_invocation,
650+
is_virtual,
647651
ctx,
648652
)
649653
}
@@ -669,8 +673,9 @@ pub(crate) fn make_utility_function_definition(
669673
__call_fn(return_ptr, __args_ptr, __args.len() as i32);
670674
};
671675

676+
let is_virtual = false;
672677
make_function_definition(
673-
&function.name,
678+
function_name_str,
674679
false,
675680
TokenStream::new(),
676681
&function.arguments,
@@ -679,6 +684,7 @@ pub(crate) fn make_utility_function_definition(
679684
init_code,
680685
&invocation,
681686
&invocation,
687+
is_virtual,
682688
ctx,
683689
)
684690
}
@@ -714,6 +720,7 @@ fn make_function_definition(
714720
init_code: TokenStream,
715721
varcall_invocation: &TokenStream,
716722
ptrcall_invocation: &TokenStream,
723+
is_virtual: bool,
717724
ctx: &mut Context,
718725
) -> TokenStream {
719726
let vis = if is_private {
@@ -761,10 +768,17 @@ fn make_function_definition(
761768
ptrcall_invocation,
762769
prepare_arg_types,
763770
error_fn_context,
771+
is_virtual,
764772
ctx,
765773
);
766774

767-
if let Some(variant_ffi) = variant_ffi.as_ref() {
775+
if is_virtual {
776+
quote! {
777+
fn #fn_name( #receiver #( #params, )* ) #return_decl {
778+
#call_code
779+
}
780+
}
781+
} else if let Some(variant_ffi) = variant_ffi.as_ref() {
768782
// varcall (using varargs)
769783
let sys_method = &variant_ffi.sys_method;
770784
quote! {
@@ -864,13 +878,15 @@ fn make_params(
864878
[params, variant_types, arg_exprs, arg_names]
865879
}
866880

881+
#[allow(clippy::too_many_arguments)]
867882
fn make_return(
868883
return_value: Option<&MethodReturn>,
869884
variant_ffi: Option<&VariantFfi>,
870885
varcall_invocation: &TokenStream,
871886
ptrcall_invocation: &TokenStream,
872887
prepare_arg_types: TokenStream,
873888
error_fn_context: TokenStream, // only for panic message
889+
is_virtual: bool,
874890
ctx: &mut Context,
875891
) -> (TokenStream, TokenStream) {
876892
let return_decl: TokenStream;
@@ -885,8 +901,13 @@ fn make_return(
885901
return_ty = None;
886902
}
887903

888-
let call = match (variant_ffi, return_ty) {
889-
(Some(variant_ffi), Some(return_ty)) => {
904+
let call = match (is_virtual, variant_ffi, return_ty) {
905+
(true, _, _) => {
906+
quote! {
907+
unimplemented!()
908+
}
909+
}
910+
(false, Some(variant_ffi), Some(return_ty)) => {
890911
// If the return type is not Variant, then convert to concrete target type
891912
let return_expr = match return_ty {
892913
RustTy::BuiltinIdent(ident) if ident == "Variant" => quote! { variant },
@@ -908,7 +929,7 @@ fn make_return(
908929
#return_expr
909930
}
910931
}
911-
(Some(_), None) => {
932+
(false, Some(_), None) => {
912933
// Note: __err may remain unused if the #call does not handle errors (e.g. utility fn, ptrcall, ...)
913934
// TODO use Result instead of panic on error
914935
quote! {
@@ -921,22 +942,22 @@ fn make_return(
921942
}
922943
}
923944
}
924-
(None, Some(RustTy::EngineClass { tokens, .. })) => {
945+
(false, None, Some(RustTy::EngineClass { tokens, .. })) => {
925946
let return_ty = tokens;
926947
quote! {
927948
<#return_ty>::from_sys_init_opt(|return_ptr| {
928949
#ptrcall_invocation
929950
})
930951
}
931952
}
932-
(None, Some(return_ty)) => {
953+
(false, None, Some(return_ty)) => {
933954
quote! {
934955
<#return_ty as sys::GodotFfi>::from_sys_init_default(|return_ptr| {
935956
#ptrcall_invocation
936957
})
937958
}
938959
}
939-
(None, None) => {
960+
(false, None, None) => {
940961
quote! {
941962
let return_ptr = std::ptr::null_mut();
942963
#ptrcall_invocation
@@ -983,19 +1004,35 @@ fn special_virtual_methods() -> TokenStream {
9831004
}
9841005

9851006
fn make_virtual_method(class_method: &ClassMethod, ctx: &mut Context) -> TokenStream {
986-
let method_name = ident(virtual_method_name(class_method));
1007+
let method_name = virtual_method_name(class_method);
9871008

9881009
// Virtual methods are never static.
9891010
assert!(!class_method.is_static);
9901011

9911012
let receiver = make_receiver_self_param(false, class_method.is_const);
992-
let [params, _, _, _] = make_params(&class_method.arguments, class_method.is_vararg, ctx);
9931013

994-
quote! {
995-
fn #method_name ( #receiver #( #params , )* ) {
996-
unimplemented!()
997-
}
998-
}
1014+
// make_return requests these token streams, but they won't be used for
1015+
// virtual methods. We can provide empty streams.
1016+
let varcall_invocation = TokenStream::new();
1017+
let ptrcall_invocation = TokenStream::new();
1018+
let init_code = TokenStream::new();
1019+
let variant_ffi = None;
1020+
1021+
let is_virtual = true;
1022+
let is_private = false;
1023+
make_function_definition(
1024+
method_name,
1025+
is_private,
1026+
receiver,
1027+
&class_method.arguments,
1028+
class_method.return_value.as_ref(),
1029+
variant_ffi,
1030+
init_code,
1031+
&varcall_invocation,
1032+
&ptrcall_invocation,
1033+
is_virtual,
1034+
ctx,
1035+
)
9991036
}
10001037

10011038
fn make_all_virtual_methods(

itest/rust/src/virtual_methods_test.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ use crate::TestContext;
1010
use godot::bind::{godot_api, GodotClass};
1111
use godot::builtin::GodotString;
1212
use godot::engine::node::InternalMode;
13-
use godot::engine::{Node, Node2D, Node2DVirtual, RefCounted, RefCountedVirtual};
13+
use godot::engine::{Node, Node2D, Node2DVirtual, NodeVirtual, RefCounted, RefCountedVirtual};
1414
use godot::obj::{Base, Gd, Share};
15+
use godot::prelude::PackedStringArray;
1516
use godot::test::itest;
1617

1718
/// Simple class, that deliberately has no constructor accessible from GDScript
@@ -91,6 +92,26 @@ impl Node2DVirtual for TreeVirtualTest {
9192
}
9293
}
9394

95+
#[derive(GodotClass, Debug)]
96+
#[class(base=Node)]
97+
struct ReturnVirtualTest {
98+
#[base]
99+
base: Base<Node>,
100+
}
101+
102+
#[godot_api]
103+
impl NodeVirtual for ReturnVirtualTest {
104+
fn init(base: Base<Node>) -> Self {
105+
ReturnVirtualTest { base }
106+
}
107+
108+
fn get_configuration_warnings(&self) -> PackedStringArray {
109+
let mut output = PackedStringArray::new();
110+
output.push("Hello".into());
111+
output
112+
}
113+
}
114+
94115
// ----------------------------------------------------------------------------------------------------------------------------------------------
95116

96117
#[itest]
@@ -215,3 +236,12 @@ fn test_tree_enters_exits(test_context: &TestContext) {
215236
assert_eq!(obj.bind().tree_enters, 2);
216237
assert_eq!(obj.bind().tree_exits, 1);
217238
}
239+
240+
#[itest]
241+
fn test_virtual_method_with_return(_test_context: &TestContext) {
242+
let obj = Gd::<ReturnVirtualTest>::new_default();
243+
let output = obj.bind().get_configuration_warnings();
244+
assert!(output.contains("Hello".into()));
245+
assert_eq!(output.len(), 1);
246+
obj.free();
247+
}

0 commit comments

Comments
 (0)