From 2b30f23595199008e5d92f0c72951bac25f94a06 Mon Sep 17 00:00:00 2001 From: Danil Alexeev Date: Fri, 2 May 2025 18:22:49 +0300 Subject: [PATCH] GDScript: Fix `Callable` call error text --- core/variant/variant_callable.cpp | 2 +- modules/gdscript/gdscript_function.h | 3 +- .../gdscript/gdscript_utility_callable.cpp | 2 +- modules/gdscript/gdscript_vm.cpp | 47 +++++++++++++++---- .../errors/callable_call_invalid_arg_type.gd | 3 ++ .../errors/callable_call_invalid_arg_type.out | 2 + .../features/utility_func_as_callable.out | 4 +- 7 files changed, 48 insertions(+), 15 deletions(-) create mode 100644 modules/gdscript/tests/scripts/runtime/errors/callable_call_invalid_arg_type.gd create mode 100644 modules/gdscript/tests/scripts/runtime/errors/callable_call_invalid_arg_type.out diff --git a/core/variant/variant_callable.cpp b/core/variant/variant_callable.cpp index 21f9a4f52b1..3a418c730a4 100644 --- a/core/variant/variant_callable.cpp +++ b/core/variant/variant_callable.cpp @@ -45,7 +45,7 @@ uint32_t VariantCallable::hash() const { } String VariantCallable::get_as_text() const { - return vformat("%s::%s (Callable)", Variant::get_type_name(variant.get_type()), method); + return vformat("%s::%s", Variant::get_type_name(variant.get_type()), method); } CallableCustom::CompareEqualFunc VariantCallable::get_compare_equal_func() const { diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 5638352363c..a21c6c3fb53 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -551,7 +551,8 @@ private: } profile; #endif - _FORCE_INLINE_ String _get_call_error(const String &p_where, const Variant **p_argptrs, const Variant &p_ret, const Callable::CallError &p_err) const; + String _get_call_error(const String &p_where, const Variant **p_argptrs, int p_argcount, const Variant &p_ret, const Callable::CallError &p_err) const; + String _get_callable_call_error(const String &p_where, const Callable &p_callable, const Variant **p_argptrs, int p_argcount, const Variant &p_ret, const Callable::CallError &p_err) const; Variant _get_default_variant_for_data_type(const GDScriptDataType &p_data_type); public: diff --git a/modules/gdscript/gdscript_utility_callable.cpp b/modules/gdscript/gdscript_utility_callable.cpp index 677b32e7942..b2040c3a577 100644 --- a/modules/gdscript/gdscript_utility_callable.cpp +++ b/modules/gdscript/gdscript_utility_callable.cpp @@ -55,7 +55,7 @@ String GDScriptUtilityCallable::get_as_text() const { scope = "@GDScript"; break; } - return vformat("%s::%s (Callable)", scope, function_name); + return vformat("%s::%s", scope, function_name); } CallableCustom::CompareEqualFunc GDScriptUtilityCallable::get_compare_equal_func() const { diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index bb4ed570f0b..48c60f2bcee 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -150,7 +150,7 @@ Variant GDScriptFunction::_get_default_variant_for_data_type(const GDScriptDataT return Variant(); } -String GDScriptFunction::_get_call_error(const String &p_where, const Variant **p_argptrs, const Variant &p_ret, const Callable::CallError &p_err) const { +String GDScriptFunction::_get_call_error(const String &p_where, const Variant **p_argptrs, int p_argcount, const Variant &p_ret, const Callable::CallError &p_err) const { switch (p_err.error) { case Callable::CallError::CALL_OK: return String(); @@ -160,7 +160,8 @@ String GDScriptFunction::_get_call_error(const String &p_where, const Variant ** } return "Invalid call. Nonexistent " + p_where + "."; case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: - ERR_FAIL_COND_V_MSG(p_err.argument < 0 || p_argptrs[p_err.argument] == nullptr, "Bug: Invalid CallError argument index or null pointer.", "Bug: Invalid CallError argument index or null pointer."); + ERR_FAIL_INDEX_V_MSG(p_err.argument, p_argcount, "Bug: Invalid call error argument index.", "Bug: Invalid call error argument index."); + ERR_FAIL_NULL_V_MSG(p_argptrs[p_err.argument], "Bug: Argument is null pointer.", "Bug: Argument is null pointer."); // Handle the Object to Object case separately as we don't have further class details. #ifdef DEBUG_ENABLED if (p_err.expected == Variant::OBJECT && p_argptrs[p_err.argument]->get_type() == p_err.expected) { @@ -185,6 +186,27 @@ String GDScriptFunction::_get_call_error(const String &p_where, const Variant ** return "Bug: Invalid call error code " + itos(p_err.error) + "."; } +String GDScriptFunction::_get_callable_call_error(const String &p_where, const Callable &p_callable, const Variant **p_argptrs, int p_argcount, const Variant &p_ret, const Callable::CallError &p_err) const { + Vector binds; + p_callable.get_bound_arguments_ref(binds); + + int args_unbound = p_callable.get_unbound_arguments_count(); + + if (p_argcount - args_unbound < 0) { + return "Callable unbinds " + itos(args_unbound) + " arguments, but called with " + itos(p_argcount); + } else { + Vector argptrs; + argptrs.resize(p_argcount - args_unbound + binds.size()); + for (int i = 0; i < p_argcount - args_unbound; i++) { + argptrs.write[i] = p_argptrs[i]; + } + for (int i = 0; i < binds.size(); i++) { + argptrs.write[i + p_argcount - args_unbound] = &binds[i]; + } + return _get_call_error(p_where, (const Variant **)argptrs.ptr(), argptrs.size(), p_ret, p_err); + } +} + void (*type_init_function_table[])(Variant *) = { nullptr, // NIL (shouldn't be called). &VariantInitializer::init, // BOOL. @@ -1703,7 +1725,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED if (err.error != Callable::CallError::CALL_OK) { - err_text = _get_call_error("'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs, *dst, err); + err_text = _get_call_error("'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs, argc, *dst, err); OPCODE_BREAK; } #endif @@ -1957,7 +1979,12 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } } } - err_text = _get_call_error("function '" + methodstr + (is_callable ? "" : "' in base '" + basestr) + "'", (const Variant **)argptrs, temp_ret, err); + + if (is_callable) { + err_text = _get_callable_call_error(vformat("function '%s'", methodstr), *base, (const Variant **)argptrs, argc, temp_ret, err); + } else { + err_text = _get_call_error(vformat("function '%s' in base '%s'", methodstr, basestr), (const Variant **)argptrs, argc, temp_ret, err); + } OPCODE_BREAK; } #endif // DEBUG_ENABLED @@ -2043,7 +2070,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } } } - err_text = _get_call_error("function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs, temp_ret, err); + err_text = _get_call_error("function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs, argc, temp_ret, err); OPCODE_BREAK; } #endif @@ -2076,7 +2103,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED if (err.error != Callable::CallError::CALL_OK) { - err_text = _get_call_error("static function '" + methodname->operator String() + "' in type '" + Variant::get_type_name(builtin_type) + "'", argptrs, *ret, err); + err_text = _get_call_error("static function '" + methodname->operator String() + "' in type '" + Variant::get_type_name(builtin_type) + "'", argptrs, argc, *ret, err); OPCODE_BREAK; } #endif @@ -2120,7 +2147,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #endif if (err.error != Callable::CallError::CALL_OK) { - err_text = _get_call_error("static function '" + method->get_name().operator String() + "' in type '" + method->get_instance_class().operator String() + "'", argptrs, *ret, err); + err_text = _get_call_error("static function '" + method->get_name().operator String() + "' in type '" + method->get_instance_class().operator String() + "'", argptrs, argc, *ret, err); OPCODE_BREAK; } @@ -2351,7 +2378,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a // Call provided error string. err_text = vformat(R"*(Error calling utility function "%s()": %s)*", methodstr, *dst); } else { - err_text = _get_call_error(vformat(R"*(utility function "%s()")*", methodstr), (const Variant **)argptrs, *dst, err); + err_text = _get_call_error(vformat(R"*(utility function "%s()")*", methodstr), (const Variant **)argptrs, argc, *dst, err); } OPCODE_BREAK; } @@ -2408,7 +2435,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a // Call provided error string. err_text = vformat(R"*(Error calling GDScript utility function "%s()": %s)*", methodstr, *dst); } else { - err_text = _get_call_error(vformat(R"*(GDScript utility function "%s()")*", methodstr), (const Variant **)argptrs, *dst, err); + err_text = _get_call_error(vformat(R"*(GDScript utility function "%s()")*", methodstr), (const Variant **)argptrs, argc, *dst, err); } OPCODE_BREAK; } @@ -2475,7 +2502,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (err.error != Callable::CallError::CALL_OK) { String methodstr = *methodname; - err_text = _get_call_error("function '" + methodstr + "'", (const Variant **)argptrs, *dst, err); + err_text = _get_call_error("function '" + methodstr + "'", (const Variant **)argptrs, argc, *dst, err); OPCODE_BREAK; } diff --git a/modules/gdscript/tests/scripts/runtime/errors/callable_call_invalid_arg_type.gd b/modules/gdscript/tests/scripts/runtime/errors/callable_call_invalid_arg_type.gd new file mode 100644 index 00000000000..3b874a8758b --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/callable_call_invalid_arg_type.gd @@ -0,0 +1,3 @@ +#debug-only +func test(): + print(load.bind([]).call()) diff --git a/modules/gdscript/tests/scripts/runtime/errors/callable_call_invalid_arg_type.out b/modules/gdscript/tests/scripts/runtime/errors/callable_call_invalid_arg_type.out new file mode 100644 index 00000000000..f564419dcf6 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/callable_call_invalid_arg_type.out @@ -0,0 +1,2 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR at runtime/errors/callable_call_invalid_arg_type.gd:3 on test(): Invalid type in function '@GDScript::load (Callable)'. Cannot convert argument 1 from Array to String. diff --git a/modules/gdscript/tests/scripts/runtime/features/utility_func_as_callable.out b/modules/gdscript/tests/scripts/runtime/features/utility_func_as_callable.out index 91549b93453..dc6bbe7bf02 100644 --- a/modules/gdscript/tests/scripts/runtime/features/utility_func_as_callable.out +++ b/modules/gdscript/tests/scripts/runtime/features/utility_func_as_callable.out @@ -1,6 +1,6 @@ GDTEST_OK -@GlobalScope::print (Callable) -@GDScript::len (Callable) +@GlobalScope::print +@GDScript::len 1 2 3 1 3