Add support for default arguments in utility functions

Co-authored-by: Leo Belda <leo.belda@wanadoo.fr>
This commit is contained in:
Chaosus 2024-08-01 13:35:07 +03:00
parent 3978628c6c
commit bf4ff9b792
10 changed files with 366 additions and 314 deletions

View File

@ -580,14 +580,20 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
bool vararg = Variant::is_utility_function_vararg(name);
func["is_vararg"] = Variant::is_utility_function_vararg(name);
func["hash"] = Variant::get_utility_function_hash(name);
Array arguments;
const Vector<Variant> def_args = Variant::get_utility_function_default_arguments(name);
int argcount = Variant::get_utility_function_argument_count(name);
for (int i = 0; i < argcount; i++) {
Dictionary arg;
String argname = vararg ? "arg" + itos(i + 1) : Variant::get_utility_function_argument_name(name, i);
arg["name"] = argname;
arg["type"] = get_builtin_or_variant_type_name(Variant::get_utility_function_argument_type(name, i));
//no default value support in utility functions
const int dargidx = Variant::get_utility_function_default_argument_index(name, i);
if (dargidx >= 0) {
arg["default_value"] = def_args[dargidx].get_construct_string();
}
arguments.push_back(arg);
}

View File

@ -551,7 +551,7 @@ public:
static uint32_t rand();
static _ALWAYS_INLINE_ double randd() { return (double)rand() / (double)Math::RANDOM_32BIT_MAX; }
static _ALWAYS_INLINE_ float randf() { return (float)rand() / (float)Math::RANDOM_32BIT_MAX; }
static double randfn(double mean, double deviation);
static double randfn(double mean = 0.0, double deviation = 1.0);
static double random(double from, double to);
static float random(float from, float to);

View File

@ -752,6 +752,8 @@ public:
static int get_utility_function_argument_count(const StringName &p_name);
static Variant::Type get_utility_function_argument_type(const StringName &p_name, int p_arg);
static String get_utility_function_argument_name(const StringName &p_name, int p_arg);
static Vector<Variant> get_utility_function_default_arguments(const StringName &p_name);
static int get_utility_function_default_argument_index(const StringName &p_name, int p_arg);
static bool has_utility_function_return_value(const StringName &p_name);
static Variant::Type get_utility_function_return_type(const StringName &p_name);
static bool is_utility_function_vararg(const StringName &p_name);

View File

@ -1228,47 +1228,19 @@ bool VariantUtilityFunctions::is_same(const Variant &p_a, const Variant &p_b) {
return p_a.identity_compare(p_b);
}
#ifdef DEBUG_METHODS_ENABLED
#define VCALLR *ret = p_func(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...)
#define VCALL p_func(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...)
#else
#define VCALLR *ret = p_func(VariantCaster<P>::cast(*p_args[Is])...)
#define VCALL p_func(VariantCaster<P>::cast(*p_args[Is])...)
#endif
template <typename R, typename... P, size_t... Is>
static _FORCE_INLINE_ void call_helperpr(R (*p_func)(P...), Variant *ret, const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) {
r_error.error = Callable::CallError::CALL_OK;
VCALLR;
(void)p_args; // avoid gcc warning
(void)r_error;
}
template <typename R, typename... P, size_t... Is>
static _FORCE_INLINE_ void validated_call_helperpr(R (*p_func)(P...), Variant *ret, const Variant **p_args, IndexSequence<Is...>) {
*ret = p_func(VariantCaster<P>::cast(*p_args[Is])...);
(void)p_args;
}
template <typename R, typename... P, size_t... Is>
static _FORCE_INLINE_ void ptr_call_helperpr(R (*p_func)(P...), void *ret, const void **p_args, IndexSequence<Is...>) {
PtrToArg<R>::encode(p_func(PtrToArg<P>::convert(p_args[Is])...), ret);
(void)p_args;
}
template <typename R, typename... P>
static _FORCE_INLINE_ void call_helperr(R (*p_func)(P...), Variant *ret, const Variant **p_args, Callable::CallError &r_error) {
call_helperpr(p_func, ret, p_args, r_error, BuildIndexSequence<sizeof...(P)>{});
static _FORCE_INLINE_ void call_helperr(R (*p_func)(P...), Variant *ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) {
call_with_variant_args_static_ret_dv(p_func, p_args, p_argcount, *ret, r_error, p_defvals);
}
template <typename R, typename... P>
static _FORCE_INLINE_ void validated_call_helperr(R (*p_func)(P...), Variant *ret, const Variant **p_args) {
validated_call_helperpr(p_func, ret, p_args, BuildIndexSequence<sizeof...(P)>{});
call_with_validated_variant_args_static_method_ret(p_func, p_args, ret);
}
template <typename R, typename... P>
static _FORCE_INLINE_ void ptr_call_helperr(R (*p_func)(P...), void *ret, const void **p_args) {
ptr_call_helperpr(p_func, ret, p_args, BuildIndexSequence<sizeof...(P)>{});
call_with_ptr_args_static_method_ret<R, P...>(p_func, p_args, ret);
}
template <typename R, typename... P>
@ -1288,20 +1260,6 @@ static _FORCE_INLINE_ Variant::Type get_ret_type_helperr(R (*p_func)(P...)) {
// WITHOUT RET
template <typename... P, size_t... Is>
static _FORCE_INLINE_ void call_helperp(void (*p_func)(P...), const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) {
r_error.error = Callable::CallError::CALL_OK;
VCALL;
(void)p_args;
(void)r_error;
}
template <typename... P, size_t... Is>
static _FORCE_INLINE_ void validated_call_helperp(void (*p_func)(P...), const Variant **p_args, IndexSequence<Is...>) {
p_func(VariantCaster<P>::cast(*p_args[Is])...);
(void)p_args;
}
template <typename... P, size_t... Is>
static _FORCE_INLINE_ void ptr_call_helperp(void (*p_func)(P...), const void **p_args, IndexSequence<Is...>) {
p_func(PtrToArg<P>::convert(p_args[Is])...);
@ -1309,18 +1267,18 @@ static _FORCE_INLINE_ void ptr_call_helperp(void (*p_func)(P...), const void **p
}
template <typename... P>
static _FORCE_INLINE_ void call_helper(void (*p_func)(P...), const Variant **p_args, Callable::CallError &r_error) {
call_helperp(p_func, p_args, r_error, BuildIndexSequence<sizeof...(P)>{});
static _FORCE_INLINE_ void call_helper(void (*p_func)(P...), const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) {
call_with_variant_args_static_dv(p_func, p_args, p_argcount, r_error, p_defvals);
}
template <typename... P>
static _FORCE_INLINE_ void validated_call_helper(void (*p_func)(P...), const Variant **p_args) {
validated_call_helperp(p_func, p_args, BuildIndexSequence<sizeof...(P)>{});
call_with_validated_variant_args_static_method(p_func, p_args);
}
template <typename... P>
static _FORCE_INLINE_ void ptr_call_helper(void (*p_func)(P...), const void **p_args) {
ptr_call_helperp(p_func, p_args, BuildIndexSequence<sizeof...(P)>{});
call_with_ptr_args_static_method<P...>(p_func, p_args);
}
template <typename... P>
@ -1341,8 +1299,8 @@ static _FORCE_INLINE_ Variant::Type get_ret_type_helper(void (*p_func)(P...)) {
#define FUNCBINDR(m_func, m_args, m_category) \
class Func_##m_func { \
public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \
call_helperr(VariantUtilityFunctions::m_func, r_ret, p_args, r_error); \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
call_helperr(VariantUtilityFunctions::m_func, r_ret, p_args, p_argcount, varray(), r_error); \
} \
static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \
validated_call_helperr(VariantUtilityFunctions::m_func, r_ret, p_args); \
@ -1365,12 +1323,41 @@ static _FORCE_INLINE_ Variant::Type get_ret_type_helper(void (*p_func)(P...)) {
static bool is_vararg() { return false; } \
static Variant::UtilityFunctionType get_type() { return m_category; } \
}; \
register_utility_function<Func_##m_func>(#m_func, m_args)
register_utility_function<Func_##m_func>(#m_func, m_args, varray())
#define FUNCBINDRD(m_func, m_args, m_defvals, m_category) \
class Func_##m_func { \
public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
call_helperr(VariantUtilityFunctions::m_func, r_ret, p_args, p_argcount, p_defvals, r_error); \
} \
static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \
validated_call_helperr(VariantUtilityFunctions::m_func, r_ret, p_args); \
} \
static void ptrcall(void *ret, const void **p_args, int p_argcount) { \
ptr_call_helperr(VariantUtilityFunctions::m_func, ret, p_args); \
} \
static int get_argument_count() { \
return get_arg_count_helperr(VariantUtilityFunctions::m_func); \
} \
static Variant::Type get_argument_type(int p_arg) { \
return get_arg_type_helperr(VariantUtilityFunctions::m_func, p_arg); \
} \
static Variant::Type get_return_type() { \
return get_ret_type_helperr(VariantUtilityFunctions::m_func); \
} \
static bool has_return_type() { \
return true; \
} \
static bool is_vararg() { return false; } \
static Variant::UtilityFunctionType get_type() { return m_category; } \
}; \
register_utility_function<Func_##m_func>(#m_func, m_args, m_defvals)
#define FUNCBINDVR(m_func, m_args, m_category) \
class Func_##m_func { \
public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
r_error.error = Callable::CallError::CALL_OK; \
*r_ret = VariantUtilityFunctions::m_func(*p_args[0], r_error); \
} \
@ -1397,12 +1384,12 @@ static _FORCE_INLINE_ Variant::Type get_ret_type_helper(void (*p_func)(P...)) {
static bool is_vararg() { return false; } \
static Variant::UtilityFunctionType get_type() { return m_category; } \
}; \
register_utility_function<Func_##m_func>(#m_func, m_args)
register_utility_function<Func_##m_func>(#m_func, m_args, varray())
#define FUNCBINDVR2(m_func, m_args, m_category) \
class Func_##m_func { \
public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
r_error.error = Callable::CallError::CALL_OK; \
*r_ret = VariantUtilityFunctions::m_func(*p_args[0], *p_args[1], r_error); \
} \
@ -1431,12 +1418,12 @@ static _FORCE_INLINE_ Variant::Type get_ret_type_helper(void (*p_func)(P...)) {
static bool is_vararg() { return false; } \
static Variant::UtilityFunctionType get_type() { return m_category; } \
}; \
register_utility_function<Func_##m_func>(#m_func, m_args)
register_utility_function<Func_##m_func>(#m_func, m_args, varray())
#define FUNCBINDVR3(m_func, m_args, m_category) \
class Func_##m_func { \
public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
r_error.error = Callable::CallError::CALL_OK; \
*r_ret = VariantUtilityFunctions::m_func(*p_args[0], *p_args[1], *p_args[2], r_error); \
} \
@ -1465,12 +1452,12 @@ static _FORCE_INLINE_ Variant::Type get_ret_type_helper(void (*p_func)(P...)) {
static bool is_vararg() { return false; } \
static Variant::UtilityFunctionType get_type() { return m_category; } \
}; \
register_utility_function<Func_##m_func>(#m_func, m_args)
register_utility_function<Func_##m_func>(#m_func, m_args, varray())
#define FUNCBINDVARARG(m_func, m_args, m_category) \
class Func_##m_func { \
public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
r_error.error = Callable::CallError::CALL_OK; \
*r_ret = VariantUtilityFunctions::m_func(p_args, p_argcount, r_error); \
} \
@ -1510,12 +1497,12 @@ static _FORCE_INLINE_ Variant::Type get_ret_type_helper(void (*p_func)(P...)) {
return m_category; \
} \
}; \
register_utility_function<Func_##m_func>(#m_func, m_args)
register_utility_function<Func_##m_func>(#m_func, m_args, varray())
#define FUNCBINDVARARGS(m_func, m_args, m_category) \
class Func_##m_func { \
public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
r_error.error = Callable::CallError::CALL_OK; \
*r_ret = VariantUtilityFunctions::m_func(p_args, p_argcount, r_error); \
} \
@ -1555,12 +1542,12 @@ static _FORCE_INLINE_ Variant::Type get_ret_type_helper(void (*p_func)(P...)) {
return m_category; \
} \
}; \
register_utility_function<Func_##m_func>(#m_func, m_args)
register_utility_function<Func_##m_func>(#m_func, m_args, varray())
#define FUNCBINDVARARGV(m_func, m_args, m_category) \
class Func_##m_func { \
public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
r_error.error = Callable::CallError::CALL_OK; \
VariantUtilityFunctions::m_func(p_args, p_argcount, r_error); \
} \
@ -1599,13 +1586,13 @@ static _FORCE_INLINE_ Variant::Type get_ret_type_helper(void (*p_func)(P...)) {
return m_category; \
} \
}; \
register_utility_function<Func_##m_func>(#m_func, m_args)
register_utility_function<Func_##m_func>(#m_func, m_args, varray())
#define FUNCBIND(m_func, m_args, m_category) \
class Func_##m_func { \
public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \
call_helper(VariantUtilityFunctions::m_func, p_args, r_error); \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
call_helper(VariantUtilityFunctions::m_func, p_args, p_argcount, varray(), r_error); \
} \
static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \
validated_call_helper(VariantUtilityFunctions::m_func, p_args); \
@ -1628,12 +1615,13 @@ static _FORCE_INLINE_ Variant::Type get_ret_type_helper(void (*p_func)(P...)) {
static bool is_vararg() { return false; } \
static Variant::UtilityFunctionType get_type() { return m_category; } \
}; \
register_utility_function<Func_##m_func>(#m_func, m_args)
register_utility_function<Func_##m_func>(#m_func, m_args, varray())
struct VariantUtilityFunctionInfo {
void (*call_utility)(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = nullptr;
void (*call_utility)(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) = nullptr;
Variant::ValidatedUtilityFunction validated_call_utility = nullptr;
Variant::PTRUtilityFunction ptr_call_utility = nullptr;
Vector<Variant> default_arguments;
Vector<String> argnames;
bool is_vararg = false;
bool returns_value = false;
@ -1647,7 +1635,7 @@ static OAHashMap<StringName, VariantUtilityFunctionInfo> utility_function_table;
static List<StringName> utility_function_name_table;
template <typename T>
static void register_utility_function(const String &p_name, const Vector<String> &argnames) {
static void register_utility_function(const String &p_name, const Vector<String> &argnames, const Vector<Variant> &p_def_args) {
String name = p_name;
if (name.begins_with("_")) {
name = name.substr(1, name.length() - 1);
@ -1660,6 +1648,7 @@ static void register_utility_function(const String &p_name, const Vector<String>
bfi.validated_call_utility = T::validated_call;
bfi.ptr_call_utility = T::ptrcall;
bfi.is_vararg = T::is_vararg();
bfi.default_arguments = p_def_args;
bfi.argnames = argnames;
bfi.argcount = T::get_argument_count();
if (!bfi.is_vararg) {
@ -1786,7 +1775,7 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(randf, sarray(), Variant::UTILITY_FUNC_TYPE_RANDOM);
FUNCBINDR(randi_range, sarray("from", "to"), Variant::UTILITY_FUNC_TYPE_RANDOM);
FUNCBINDR(randf_range, sarray("from", "to"), Variant::UTILITY_FUNC_TYPE_RANDOM);
FUNCBINDR(randfn, sarray("mean", "deviation"), Variant::UTILITY_FUNC_TYPE_RANDOM);
FUNCBINDRD(randfn, sarray("mean", "deviation"), varray(0.0, 1.0), Variant::UTILITY_FUNC_TYPE_RANDOM);
FUNCBIND(seed, sarray("base"), Variant::UTILITY_FUNC_TYPE_RANDOM);
FUNCBINDR(rand_from_seed, sarray("seed"), Variant::UTILITY_FUNC_TYPE_RANDOM);
@ -1843,9 +1832,9 @@ void Variant::call_utility_function(const StringName &p_name, Variant *r_ret, co
return;
}
if (unlikely(!bfi->is_vararg && p_argcount < bfi->argcount)) {
if (unlikely(!bfi->is_vararg && p_argcount < bfi->argcount - bfi->default_arguments.size())) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.expected = bfi->argcount;
r_error.expected = bfi->argcount - bfi->default_arguments.size();
return;
}
@ -1855,7 +1844,7 @@ void Variant::call_utility_function(const StringName &p_name, Variant *r_ret, co
return;
}
bfi->call_utility(r_ret, p_args, p_argcount, r_error);
bfi->call_utility(r_ret, p_args, p_argcount, bfi->default_arguments, r_error);
}
bool Variant::has_utility_function(const StringName &p_name) {
@ -1907,6 +1896,7 @@ MethodInfo Variant::get_utility_function_info(const StringName &p_name) {
arg.name = bfi->argnames[i];
info.arguments.push_back(arg);
}
info.default_arguments = bfi->default_arguments;
}
return info;
}
@ -1920,6 +1910,24 @@ int Variant::get_utility_function_argument_count(const StringName &p_name) {
return bfi->argcount;
}
Vector<Variant> Variant::get_utility_function_default_arguments(const StringName &p_name) {
const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name);
if (!bfi) {
return varray();
}
return bfi->default_arguments;
}
int Variant::get_utility_function_default_argument_index(const StringName &p_name, int p_arg) {
const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name);
if (!bfi) {
return false;
}
return p_arg - (bfi->argcount - bfi->default_arguments.size());
}
Variant::Type Variant::get_utility_function_argument_type(const StringName &p_name, int p_arg) {
const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name);
if (!bfi) {

View File

@ -119,7 +119,7 @@ struct VariantUtilityFunctions {
static void randomize();
static int64_t randi();
static double randf();
static double randfn(double mean, double deviation);
static double randfn(double mean = 0.0, double deviation = 1.0);
static int64_t randi_range(int64_t from, int64_t to);
static double randf_range(double from, double to);
static void seed(int64_t s);

View File

@ -1033,8 +1033,8 @@
</method>
<method name="randfn">
<return type="float" />
<param index="0" name="mean" type="float" />
<param index="1" name="deviation" type="float" />
<param index="0" name="mean" type="float" default="0.0" />
<param index="1" name="deviation" type="float" default="1.0" />
<description>
Returns a [url=https://en.wikipedia.org/wiki/Normal_distribution]normally-distributed[/url], pseudo-random floating-point value from the specified [param mean] and a standard [param deviation]. This is also known as a Gaussian distribution.
[b]Note:[/b] This method uses the [url=https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform]Box-Muller transform[/url] algorithm.

View File

@ -981,6 +981,7 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
if (Variant::is_utility_function_vararg(E)) {
md.qualifiers = "vararg";
} else {
const Vector<Variant> def_args = Variant::get_utility_function_default_arguments(E);
for (int i = 0; i < Variant::get_utility_function_argument_count(E); i++) {
PropertyInfo pi;
pi.type = Variant::get_utility_function_argument_type(E, i);
@ -990,6 +991,12 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
}
DocData::ArgumentDoc ad;
DocData::argument_doc_from_arginfo(ad, pi);
const int dargidx = Variant::get_utility_function_default_argument_index(E, i);
if (dargidx >= 0) {
ad.default_value = DocData::get_default_value_string(def_args[dargidx]);
}
md.arguments.push_back(ad);
}
}

View File

@ -77,6 +77,7 @@ static MethodInfo info_from_utility_func(const StringName &p_function) {
pi.type = Variant::get_utility_function_argument_type(p_function, i);
info.arguments.push_back(pi);
}
info.default_arguments = Variant::get_utility_function_default_arguments(p_function);
}
return info;

View File

@ -621,6 +621,20 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {
gen->write_construct(result, GDScriptParser::get_builtin_type(call->function_name), arguments);
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {
int arg_count = Variant::get_utility_function_argument_count(call->function_name);
if (call->arguments.size() < arg_count) {
Vector<Variant> def_args = Variant::get_utility_function_default_arguments(call->function_name);
for (int i = call->arguments.size(); i < arg_count; i++) {
const int dargidx = Variant::get_utility_function_default_argument_index(call->function_name, i);
if (dargidx >= 0) {
arguments.push_back(codegen.add_constant(def_args[dargidx]));
}
}
}
// Variant utility function.
gen->write_call_utility(result, call->function_name, arguments);
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) {

View File

@ -2167,12 +2167,19 @@ TEST_CASE("[Variant] Utility functions") {
if (Variant::is_utility_function_vararg(E)) {
md.is_vararg = true;
} else {
const Vector<Variant> def_args = Variant::get_utility_function_default_arguments(E);
for (int i = 0; i < Variant::get_utility_function_argument_count(E); i++) {
ArgumentData arg;
arg.type = Variant::get_utility_function_argument_type(E, i);
arg.name = Variant::get_utility_function_argument_name(E, i);
arg.position = i;
const int dargidx = Variant::get_utility_function_default_argument_index(E, i);
if (dargidx >= 0) {
arg.has_defval = true;
arg.defval = def_args[dargidx];
}
md.arguments.push_back(arg);
}
}
@ -2187,6 +2194,13 @@ TEST_CASE("[Variant] Utility functions") {
TEST_COND((arg.name.is_empty() || arg.name.begins_with("_unnamed_arg")),
vformat("Unnamed argument in position %d of function '%s'.", arg.position, E.name));
if (arg.has_defval) {
const bool arg_defval_assignable_to_type = arg.type == arg.defval.get_type();
TEST_COND(!arg_defval_assignable_to_type,
vformat("Invalid default value for parameter '%s' of function '%s'.", arg.name, E.name));
}
}
}
}