mirror of
https://github.com/godotengine/godot.git
synced 2025-10-19 16:03:29 +00:00
Merge pull request #82808 from dalexeev/gds-vararg
GDScript: Add support for variadic functions
This commit is contained in:
commit
0f05e91889
33 changed files with 416 additions and 65 deletions
|
@ -107,6 +107,8 @@ public:
|
|||
bool is_experimental = false;
|
||||
String experimental_message;
|
||||
Vector<ArgumentDoc> arguments;
|
||||
// NOTE: Only for GDScript for now. The rest argument is not saved to the XML file.
|
||||
ArgumentDoc rest_argument;
|
||||
Vector<int> errors_returned;
|
||||
String keywords;
|
||||
bool operator<(const MethodDoc &p_method) const {
|
||||
|
|
|
@ -314,12 +314,15 @@ List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_me
|
|||
}
|
||||
|
||||
if (check_signal) {
|
||||
if (mi.arguments.size() != effective_args.size()) {
|
||||
const unsigned min_argc = mi.arguments.size() - mi.default_arguments.size();
|
||||
const unsigned max_argc = (mi.flags & METHOD_FLAG_VARARG) ? UINT_MAX : mi.arguments.size();
|
||||
|
||||
if (effective_args.size() < min_argc || effective_args.size() > max_argc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool type_mismatch = false;
|
||||
for (int64_t i = 0; i < mi.arguments.size(); ++i) {
|
||||
for (int64_t i = 0; i < effective_args.size() && i < mi.arguments.size(); ++i) {
|
||||
Variant::Type stype = effective_args[i].first;
|
||||
Variant::Type mtype = mi.arguments[i].type;
|
||||
|
||||
|
@ -609,10 +612,14 @@ String ConnectDialog::get_signature(const MethodInfo &p_method, PackedStringArra
|
|||
String arg_name = pi.name.is_empty() ? "arg" + itos(i) : pi.name;
|
||||
signature.append(arg_name + ": " + type_name);
|
||||
if (r_arg_names) {
|
||||
r_arg_names->push_back(arg_name + ":" + type_name);
|
||||
r_arg_names->push_back(arg_name + ": " + type_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (p_method.flags & METHOD_FLAG_VARARG) {
|
||||
signature.append(p_method.arguments.is_empty() ? "..." : ", ...");
|
||||
}
|
||||
|
||||
signature.append(")");
|
||||
return String().join(signature);
|
||||
}
|
||||
|
@ -1044,7 +1051,7 @@ void ConnectionsDock::_make_or_edit_connection() {
|
|||
}
|
||||
|
||||
for (int i = 0; i < cd.binds.size(); i++) {
|
||||
script_function_args.push_back("extra_arg_" + itos(i) + ":" + Variant::get_type_name(cd.binds[i].get_type()));
|
||||
script_function_args.push_back("extra_arg_" + itos(i) + ": " + Variant::get_type_name(cd.binds[i].get_type()));
|
||||
}
|
||||
|
||||
EditorNode::get_singleton()->emit_signal(SNAME("script_add_function_request"), target, cd.method, script_function_args);
|
||||
|
@ -1182,9 +1189,9 @@ bool ConnectionsDock::_is_connection_inherited(Connection &p_connection) {
|
|||
* Open connection dialog with TreeItem data to CREATE a brand-new connection.
|
||||
*/
|
||||
void ConnectionsDock::_open_connection_dialog(TreeItem &p_item) {
|
||||
Dictionary sinfo = p_item.get_metadata(0);
|
||||
String signal_name = sinfo["name"];
|
||||
PackedStringArray signal_args = sinfo["args"];
|
||||
const Dictionary sinfo = p_item.get_metadata(0);
|
||||
const StringName signal_name = sinfo["name"];
|
||||
const PackedStringArray signal_args = sinfo["args"];
|
||||
|
||||
Node *dst_node = selected_node->get_owner() ? selected_node->get_owner() : selected_node;
|
||||
if (!dst_node || dst_node->get_script().is_null()) {
|
||||
|
@ -1193,12 +1200,12 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &p_item) {
|
|||
|
||||
ConnectDialog::ConnectionData cd;
|
||||
cd.source = selected_node;
|
||||
cd.signal = StringName(signal_name);
|
||||
cd.signal = signal_name;
|
||||
cd.target = dst_node;
|
||||
cd.method = ConnectDialog::generate_method_callback_name(cd.source, signal_name, cd.target);
|
||||
connect_dialog->init(cd, signal_args);
|
||||
connect_dialog->set_title(TTR("Connect a Signal to a Method"));
|
||||
connect_dialog->popup_dialog(signal_name + "(" + String(", ").join(signal_args) + ")");
|
||||
connect_dialog->popup_dialog(signal_name.operator String() + "(" + String(", ").join(signal_args) + ")");
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1215,12 +1222,12 @@ void ConnectionsDock::_open_edit_connection_dialog(TreeItem &p_item) {
|
|||
Node *dst = Object::cast_to<Node>(cd.target);
|
||||
|
||||
if (src && dst) {
|
||||
const String &signal_name_ref = cd.signal;
|
||||
PackedStringArray signal_args = signal_item->get_metadata(0).operator Dictionary()["args"];
|
||||
const StringName &signal_name = cd.signal;
|
||||
const PackedStringArray signal_args = signal_item->get_metadata(0).operator Dictionary()["args"];
|
||||
|
||||
connect_dialog->set_title(vformat(TTR("Edit Connection: '%s'"), cd.signal));
|
||||
connect_dialog->popup_dialog(signal_name_ref);
|
||||
connect_dialog->init(cd, signal_args, true);
|
||||
connect_dialog->set_title(vformat(TTR("Edit Connection: '%s'"), cd.signal));
|
||||
connect_dialog->popup_dialog(signal_name.operator String() + "(" + String(", ").join(signal_args) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -582,14 +582,20 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview
|
|||
for (int j = 0; j < p_method.arguments.size(); j++) {
|
||||
const DocData::ArgumentDoc &argument = p_method.arguments[j];
|
||||
|
||||
class_desc->push_color(theme_cache.text_color);
|
||||
|
||||
if (j > 0) {
|
||||
class_desc->push_color(theme_cache.symbol_color);
|
||||
class_desc->add_text(", ");
|
||||
class_desc->pop(); // color
|
||||
}
|
||||
|
||||
class_desc->push_color(theme_cache.text_color);
|
||||
class_desc->add_text(argument.name);
|
||||
class_desc->pop(); // color
|
||||
|
||||
class_desc->push_color(theme_cache.symbol_color);
|
||||
class_desc->add_text(colon_nbsp);
|
||||
class_desc->pop(); // color
|
||||
|
||||
_add_type(argument.type, argument.enumeration, argument.is_bitfield);
|
||||
|
||||
if (!argument.default_value.is_empty()) {
|
||||
|
@ -601,13 +607,11 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview
|
|||
class_desc->add_text(_fix_constant(argument.default_value));
|
||||
class_desc->pop(); // color
|
||||
}
|
||||
|
||||
class_desc->pop(); // color
|
||||
}
|
||||
|
||||
if (is_vararg) {
|
||||
if (!p_method.arguments.is_empty()) {
|
||||
class_desc->push_color(theme_cache.text_color);
|
||||
class_desc->push_color(theme_cache.symbol_color);
|
||||
class_desc->add_text(", ");
|
||||
class_desc->pop(); // color
|
||||
}
|
||||
|
@ -615,6 +619,22 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview
|
|||
class_desc->push_color(theme_cache.symbol_color);
|
||||
class_desc->add_text("...");
|
||||
class_desc->pop(); // color
|
||||
|
||||
const DocData::ArgumentDoc &rest_argument = p_method.rest_argument;
|
||||
|
||||
class_desc->push_color(theme_cache.text_color);
|
||||
class_desc->add_text(rest_argument.name.is_empty() ? "args" : rest_argument.name);
|
||||
class_desc->pop(); // color
|
||||
|
||||
class_desc->push_color(theme_cache.symbol_color);
|
||||
class_desc->add_text(colon_nbsp);
|
||||
class_desc->pop(); // color
|
||||
|
||||
if (rest_argument.type.is_empty()) {
|
||||
_add_type("Array");
|
||||
} else {
|
||||
_add_type(rest_argument.type, rest_argument.enumeration, rest_argument.is_bitfield);
|
||||
}
|
||||
}
|
||||
|
||||
class_desc->push_color(theme_cache.symbol_color);
|
||||
|
@ -1558,14 +1578,20 @@ void EditorHelp::_update_doc() {
|
|||
for (int j = 0; j < signal.arguments.size(); j++) {
|
||||
const DocData::ArgumentDoc &argument = signal.arguments[j];
|
||||
|
||||
class_desc->push_color(theme_cache.text_color);
|
||||
|
||||
if (j > 0) {
|
||||
class_desc->push_color(theme_cache.symbol_color);
|
||||
class_desc->add_text(", ");
|
||||
class_desc->pop(); // color
|
||||
}
|
||||
|
||||
class_desc->push_color(theme_cache.text_color);
|
||||
class_desc->add_text(argument.name);
|
||||
class_desc->pop(); // color
|
||||
|
||||
class_desc->push_color(theme_cache.symbol_color);
|
||||
class_desc->add_text(colon_nbsp);
|
||||
class_desc->pop(); // color
|
||||
|
||||
_add_type(argument.type, argument.enumeration, argument.is_bitfield);
|
||||
|
||||
// Signals currently do not support default argument values, neither the core nor GDScript.
|
||||
|
@ -1579,8 +1605,6 @@ void EditorHelp::_update_doc() {
|
|||
class_desc->add_text(_fix_constant(argument.default_value));
|
||||
class_desc->pop(); // color
|
||||
}
|
||||
|
||||
class_desc->pop(); // color
|
||||
}
|
||||
|
||||
class_desc->push_color(theme_cache.symbol_color);
|
||||
|
@ -2002,14 +2026,20 @@ void EditorHelp::_update_doc() {
|
|||
for (int j = 0; j < annotation.arguments.size(); j++) {
|
||||
const DocData::ArgumentDoc &argument = annotation.arguments[j];
|
||||
|
||||
class_desc->push_color(theme_cache.text_color);
|
||||
|
||||
if (j > 0) {
|
||||
class_desc->push_color(theme_cache.symbol_color);
|
||||
class_desc->add_text(", ");
|
||||
class_desc->pop(); // color
|
||||
}
|
||||
|
||||
class_desc->push_color(theme_cache.text_color);
|
||||
class_desc->add_text(argument.name);
|
||||
class_desc->pop(); // color
|
||||
|
||||
class_desc->push_color(theme_cache.symbol_color);
|
||||
class_desc->add_text(colon_nbsp);
|
||||
class_desc->pop(); // color
|
||||
|
||||
_add_type(argument.type, argument.enumeration, argument.is_bitfield);
|
||||
|
||||
if (!argument.default_value.is_empty()) {
|
||||
|
@ -2021,13 +2051,11 @@ void EditorHelp::_update_doc() {
|
|||
class_desc->add_text(_fix_constant(argument.default_value));
|
||||
class_desc->pop(); // color
|
||||
}
|
||||
|
||||
class_desc->pop(); // color
|
||||
}
|
||||
|
||||
if (annotation.qualifiers.contains("vararg")) {
|
||||
if (!annotation.arguments.is_empty()) {
|
||||
class_desc->push_color(theme_cache.text_color);
|
||||
class_desc->push_color(theme_cache.symbol_color);
|
||||
class_desc->add_text(", ");
|
||||
class_desc->pop(); // color
|
||||
}
|
||||
|
@ -2035,6 +2063,22 @@ void EditorHelp::_update_doc() {
|
|||
class_desc->push_color(theme_cache.symbol_color);
|
||||
class_desc->add_text("...");
|
||||
class_desc->pop(); // color
|
||||
|
||||
const DocData::ArgumentDoc &rest_argument = annotation.rest_argument;
|
||||
|
||||
class_desc->push_color(theme_cache.text_color);
|
||||
class_desc->add_text(rest_argument.name.is_empty() ? "args" : rest_argument.name);
|
||||
class_desc->pop(); // color
|
||||
|
||||
class_desc->push_color(theme_cache.symbol_color);
|
||||
class_desc->add_text(colon_nbsp);
|
||||
class_desc->pop(); // color
|
||||
|
||||
if (rest_argument.type.is_empty()) {
|
||||
_add_type("Array");
|
||||
} else {
|
||||
_add_type(rest_argument.type, rest_argument.enumeration, rest_argument.is_bitfield);
|
||||
}
|
||||
}
|
||||
|
||||
class_desc->push_color(theme_cache.symbol_color);
|
||||
|
@ -3730,10 +3774,13 @@ EditorHelpBit::HelpData EditorHelpBit::_get_method_help_data(const StringName &p
|
|||
}
|
||||
current.doc_type = { method.return_type, method.return_enum, method.return_is_bitfield };
|
||||
for (const DocData::ArgumentDoc &argument : method.arguments) {
|
||||
const DocType argument_type = { argument.type, argument.enumeration, argument.is_bitfield };
|
||||
current.arguments.push_back({ argument.name, argument_type, argument.default_value });
|
||||
const DocType argument_doc_type = { argument.type, argument.enumeration, argument.is_bitfield };
|
||||
current.arguments.push_back({ argument.name, argument_doc_type, argument.default_value });
|
||||
}
|
||||
current.qualifiers = method.qualifiers;
|
||||
const DocData::ArgumentDoc &rest_argument = method.rest_argument;
|
||||
const DocType rest_argument_doc_type = { rest_argument.type, rest_argument.enumeration, rest_argument.is_bitfield };
|
||||
current.rest_argument = { rest_argument.name, rest_argument_doc_type, rest_argument.default_value };
|
||||
|
||||
if (method.name == p_method_name) {
|
||||
result = current;
|
||||
|
@ -3895,6 +3942,7 @@ void EditorHelpBit::_update_labels() {
|
|||
|
||||
title->pop(); // font
|
||||
|
||||
const Color text_color = get_theme_color(SNAME("text_color"), SNAME("EditorHelp"));
|
||||
const Color symbol_color = get_theme_color(SNAME("symbol_color"), SNAME("EditorHelp"));
|
||||
const Color value_color = get_theme_color(SNAME("value_color"), SNAME("EditorHelp"));
|
||||
const Color qualifier_color = get_theme_color(SNAME("qualifier_color"), SNAME("EditorHelp"));
|
||||
|
@ -3970,10 +4018,14 @@ void EditorHelpBit::_update_labels() {
|
|||
const ArgumentData &argument = help_data.arguments[i];
|
||||
|
||||
if (i > 0) {
|
||||
title->push_color(symbol_color);
|
||||
title->add_text(", ");
|
||||
title->pop(); // color
|
||||
}
|
||||
|
||||
title->push_color(text_color);
|
||||
title->add_text(argument.name);
|
||||
title->pop(); // color
|
||||
|
||||
title->push_color(symbol_color);
|
||||
title->add_text(colon_nbsp);
|
||||
|
@ -3994,12 +4046,30 @@ void EditorHelpBit::_update_labels() {
|
|||
|
||||
if (help_data.qualifiers.contains("vararg")) {
|
||||
if (!help_data.arguments.is_empty()) {
|
||||
title->push_color(symbol_color);
|
||||
title->add_text(", ");
|
||||
title->pop(); // color
|
||||
}
|
||||
|
||||
title->push_color(symbol_color);
|
||||
title->add_text("...");
|
||||
title->pop(); // color
|
||||
|
||||
const ArgumentData &rest_argument = help_data.rest_argument;
|
||||
|
||||
title->push_color(text_color);
|
||||
title->add_text(rest_argument.name.is_empty() ? "args" : rest_argument.name);
|
||||
title->pop(); // color
|
||||
|
||||
title->push_color(symbol_color);
|
||||
title->add_text(colon_nbsp);
|
||||
title->pop(); // color
|
||||
|
||||
if (rest_argument.doc_type.type.is_empty()) {
|
||||
_add_type_to_title({ "Array", "", false });
|
||||
} else {
|
||||
_add_type_to_title(rest_argument.doc_type);
|
||||
}
|
||||
}
|
||||
|
||||
title->push_color(symbol_color);
|
||||
|
|
|
@ -302,6 +302,7 @@ class EditorHelpBit : public VBoxContainer {
|
|||
DocType doc_type;
|
||||
String value;
|
||||
Vector<ArgumentData> arguments;
|
||||
ArgumentData rest_argument;
|
||||
String qualifiers;
|
||||
String resource_path;
|
||||
};
|
||||
|
|
|
@ -287,8 +287,16 @@ void PropertySelector::_update_search() {
|
|||
}
|
||||
}
|
||||
|
||||
if (mi.flags & METHOD_FLAG_VARARG) {
|
||||
desc += mi.arguments.is_empty() ? "..." : ", ...";
|
||||
}
|
||||
|
||||
desc += ")";
|
||||
|
||||
if (mi.flags & METHOD_FLAG_VARARG) {
|
||||
desc += " vararg";
|
||||
}
|
||||
|
||||
if (mi.flags & METHOD_FLAG_CONST) {
|
||||
desc += " const";
|
||||
}
|
||||
|
|
|
@ -408,13 +408,25 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_
|
|||
method_doc.is_experimental = m_func->doc_data.is_experimental;
|
||||
method_doc.experimental_message = m_func->doc_data.experimental_message;
|
||||
|
||||
// Currently, an abstract function cannot be static.
|
||||
if (m_func->is_vararg()) {
|
||||
if (!method_doc.qualifiers.is_empty()) {
|
||||
method_doc.qualifiers += " ";
|
||||
}
|
||||
method_doc.qualifiers += "vararg";
|
||||
method_doc.rest_argument.name = m_func->rest_parameter->identifier->name;
|
||||
_doctype_from_gdtype(m_func->rest_parameter->get_datatype(), method_doc.rest_argument.type, method_doc.rest_argument.enumeration);
|
||||
}
|
||||
if (m_func->is_abstract) {
|
||||
method_doc.qualifiers = "abstract";
|
||||
} else if (m_func->is_static) {
|
||||
method_doc.qualifiers = "static";
|
||||
} else {
|
||||
method_doc.qualifiers = "";
|
||||
if (!method_doc.qualifiers.is_empty()) {
|
||||
method_doc.qualifiers += " ";
|
||||
}
|
||||
method_doc.qualifiers += "abstract";
|
||||
}
|
||||
if (m_func->is_static) {
|
||||
if (!method_doc.qualifiers.is_empty()) {
|
||||
method_doc.qualifiers += " ";
|
||||
}
|
||||
method_doc.qualifiers += "static";
|
||||
}
|
||||
|
||||
if (func_name == "_init") {
|
||||
|
|
|
@ -492,7 +492,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
|
|||
k--;
|
||||
}
|
||||
|
||||
if (str[k] == '.') {
|
||||
if (str[k] == '.' && (k < 1 || str[k - 1] != '.')) {
|
||||
in_member_variable = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1798,6 +1798,33 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
|
|||
}
|
||||
}
|
||||
|
||||
if (p_function->is_vararg()) {
|
||||
resolve_parameter(p_function->rest_parameter);
|
||||
if (p_function->rest_parameter->datatype_specifier != nullptr) {
|
||||
GDScriptParser::DataType specified_type = p_function->rest_parameter->get_datatype();
|
||||
if (specified_type.kind != GDScriptParser::DataType::BUILTIN || specified_type.builtin_type != Variant::ARRAY) {
|
||||
push_error(vformat(R"(The rest parameter type must be "Array", but "%s" is specified.)", specified_type.to_string()), p_function->rest_parameter->datatype_specifier);
|
||||
} else if ((specified_type.has_container_element_type(0) && !specified_type.get_container_element_type(0).is_variant())) {
|
||||
push_error(R"(Typed arrays are currently not supported for the rest parameter.)", p_function->rest_parameter->datatype_specifier);
|
||||
}
|
||||
} else {
|
||||
GDScriptParser::DataType inferred_type;
|
||||
inferred_type.type_source = GDScriptParser::DataType::INFERRED;
|
||||
inferred_type.kind = GDScriptParser::DataType::BUILTIN;
|
||||
inferred_type.builtin_type = Variant::ARRAY;
|
||||
p_function->rest_parameter->set_datatype(inferred_type);
|
||||
#ifdef DEBUG_ENABLED
|
||||
parser->push_warning(p_function->rest_parameter, GDScriptWarning::UNTYPED_DECLARATION, "Parameter", p_function->rest_parameter->identifier->name);
|
||||
#endif
|
||||
}
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (p_function->rest_parameter->usages == 0 && !String(p_function->rest_parameter->identifier->name).begins_with("_") && !p_function->is_abstract) {
|
||||
parser->push_warning(p_function->rest_parameter->identifier, GDScriptWarning::UNUSED_PARAMETER, function_visible_name, p_function->rest_parameter->identifier->name);
|
||||
}
|
||||
is_shadowing(p_function->rest_parameter->identifier, "function parameter", true);
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
if (!p_is_lambda && function_name == GDScriptLanguage::get_singleton()->strings._init) {
|
||||
// Constructor.
|
||||
GDScriptParser::DataType return_type = parser->current_class->get_datatype();
|
||||
|
@ -1864,15 +1891,23 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
|
|||
}
|
||||
}
|
||||
|
||||
int par_count_diff = p_function->parameters.size() - parameters_types.size();
|
||||
valid = valid && par_count_diff >= 0;
|
||||
valid = valid && default_value_count >= default_par_count + par_count_diff;
|
||||
int parent_min_argc = parameters_types.size() - default_par_count;
|
||||
int parent_max_argc = (method_flags & METHOD_FLAG_VARARG) ? INT_MAX : parameters_types.size();
|
||||
int current_min_argc = p_function->parameters.size() - default_value_count;
|
||||
int current_max_argc = p_function->is_vararg() ? INT_MAX : p_function->parameters.size();
|
||||
|
||||
// `[current_min_argc..current_max_argc]` must include `[parent_min_argc..parent_max_argc]`.
|
||||
valid = valid && current_min_argc <= parent_min_argc && parent_max_argc <= current_max_argc;
|
||||
|
||||
if (valid) {
|
||||
int i = 0;
|
||||
for (const GDScriptParser::DataType &parent_par_type : parameters_types) {
|
||||
if (i >= p_function->parameters.size()) {
|
||||
break;
|
||||
}
|
||||
const GDScriptParser::DataType ¤t_par_type = p_function->parameters[i]->datatype;
|
||||
i++;
|
||||
// Check parameter type contravariance.
|
||||
GDScriptParser::DataType current_par_type = p_function->parameters[i++]->get_datatype();
|
||||
if (parent_par_type.is_variant() && parent_par_type.is_hard_type()) {
|
||||
// `is_type_compatible()` returns `true` if one of the types is `Variant`.
|
||||
// Don't allow narrowing a hard `Variant`.
|
||||
|
@ -1902,6 +1937,12 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
|
|||
|
||||
j++;
|
||||
}
|
||||
if (method_flags & METHOD_FLAG_VARARG) {
|
||||
if (!parameters_types.is_empty()) {
|
||||
parent_signature += ", ";
|
||||
}
|
||||
parent_signature += "...";
|
||||
}
|
||||
parent_signature += ") -> ";
|
||||
|
||||
const String return_type = parent_return_type.to_string_strict();
|
||||
|
@ -5791,6 +5832,9 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
|
|||
r_default_arg_count++;
|
||||
}
|
||||
}
|
||||
if (found_function->is_vararg()) {
|
||||
r_method_flags.set_flag(METHOD_FLAG_VARARG);
|
||||
}
|
||||
r_return_type = p_is_constructor ? p_base_type : found_function->get_datatype();
|
||||
r_return_type.is_meta_type = false;
|
||||
r_return_type.is_coroutine = found_function->is_coroutine;
|
||||
|
|
|
@ -2338,6 +2338,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
|
|||
codegen.generator->write_start(p_script, func_name, is_static, rpc_config, return_type);
|
||||
|
||||
int optional_parameters = 0;
|
||||
GDScriptCodeGenerator::Address vararg_addr;
|
||||
|
||||
if (p_func) {
|
||||
for (int i = 0; i < p_func->parameters.size(); i++) {
|
||||
|
@ -2353,6 +2354,11 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
|
|||
}
|
||||
}
|
||||
|
||||
if (p_func->is_vararg()) {
|
||||
vararg_addr = codegen.add_local(p_func->rest_parameter->identifier->name, _gdtype_from_datatype(p_func->rest_parameter->get_datatype(), codegen.script));
|
||||
method_info.flags |= METHOD_FLAG_VARARG;
|
||||
}
|
||||
|
||||
method_info.default_arguments.append_array(p_func->default_arg_values);
|
||||
}
|
||||
|
||||
|
@ -2519,6 +2525,10 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
|
|||
gd_function->return_type.kind = GDScriptDataType::BUILTIN;
|
||||
gd_function->return_type.builtin_type = Variant::NIL;
|
||||
}
|
||||
|
||||
if (p_func->is_vararg()) {
|
||||
gd_function->_vararg_index = vararg_addr.address;
|
||||
}
|
||||
}
|
||||
|
||||
gd_function->method_info = method_info;
|
||||
|
|
|
@ -778,7 +778,7 @@ static String _make_arguments_hint(const MethodInfo &p_info, int p_arg_idx, bool
|
|||
if (p_arg_idx >= p_info.arguments.size()) {
|
||||
arghint += String::chr(0xFFFF);
|
||||
}
|
||||
arghint += "...";
|
||||
arghint += "...args: Array"; // `MethodInfo` does not support the rest parameter name.
|
||||
if (p_arg_idx >= p_info.arguments.size()) {
|
||||
arghint += String::chr(0xFFFF);
|
||||
}
|
||||
|
@ -796,9 +796,9 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
|
|||
arghint = "(";
|
||||
} else {
|
||||
if (p_function->get_datatype().builtin_type == Variant::NIL) {
|
||||
arghint = "void " + p_function->identifier->name.operator String() + "(";
|
||||
arghint = "void " + p_function->identifier->name + "(";
|
||||
} else {
|
||||
arghint = p_function->get_datatype().to_string() + " " + p_function->identifier->name.operator String() + "(";
|
||||
arghint = p_function->get_datatype().to_string() + " " + p_function->identifier->name + "(";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -870,6 +870,20 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
|
|||
}
|
||||
}
|
||||
|
||||
if (p_function->is_vararg()) {
|
||||
if (!p_function->parameters.is_empty()) {
|
||||
arghint += ", ";
|
||||
}
|
||||
if (p_arg_idx >= p_function->parameters.size()) {
|
||||
arghint += String::chr(0xFFFF);
|
||||
}
|
||||
const GDScriptParser::ParameterNode *rest_param = p_function->rest_parameter;
|
||||
arghint += "..." + rest_param->identifier->name + ": " + rest_param->get_datatype().to_string();
|
||||
if (p_arg_idx >= p_function->parameters.size()) {
|
||||
arghint += String::chr(0xFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
arghint += ")";
|
||||
|
||||
return arghint;
|
||||
|
@ -3610,6 +3624,15 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
|
|||
method_hint += ": " + _get_visual_datatype(mi.arguments[i], true, class_name);
|
||||
}
|
||||
}
|
||||
if (mi.flags & METHOD_FLAG_VARARG) {
|
||||
if (!mi.arguments.is_empty()) {
|
||||
method_hint += ", ";
|
||||
}
|
||||
method_hint += "...args"; // `MethodInfo` does not support the rest parameter name.
|
||||
if (use_type_hint) {
|
||||
method_hint += ": Array";
|
||||
}
|
||||
}
|
||||
method_hint += ")";
|
||||
if (use_type_hint) {
|
||||
method_hint += " -> " + _get_visual_datatype(mi.return_val, false, class_name);
|
||||
|
|
|
@ -248,8 +248,9 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
|
|||
void GDScriptFunctionState::_clear_stack() {
|
||||
if (state.stack_size) {
|
||||
Variant *stack = (Variant *)state.stack.ptr();
|
||||
// The first 3 are special addresses and not copied to the state, so we skip them here.
|
||||
for (int i = 3; i < state.stack_size; i++) {
|
||||
// First `GDScriptFunction::FIXED_ADDRESSES_MAX` stack addresses are special
|
||||
// and not copied to the state, so we skip them here.
|
||||
for (int i = GDScriptFunction::FIXED_ADDRESSES_MAX; i < state.stack_size; i++) {
|
||||
stack[i].~Variant();
|
||||
}
|
||||
state.stack_size = 0;
|
||||
|
|
|
@ -458,6 +458,7 @@ private:
|
|||
GDScript *_script = nullptr;
|
||||
int _initial_line = 0;
|
||||
int _argument_count = 0;
|
||||
int _vararg_index = -1;
|
||||
int _stack_size = 0;
|
||||
int _instruction_args_size = 0;
|
||||
|
||||
|
@ -579,6 +580,7 @@ public:
|
|||
_FORCE_INLINE_ StringName get_source() const { return source; }
|
||||
_FORCE_INLINE_ GDScript *get_script() const { return _script; }
|
||||
_FORCE_INLINE_ bool is_static() const { return _static; }
|
||||
_FORCE_INLINE_ bool is_vararg() const { return _vararg_index >= 0; }
|
||||
_FORCE_INLINE_ MethodInfo get_method_info() const { return method_info; }
|
||||
_FORCE_INLINE_ int get_argument_count() const { return _argument_count; }
|
||||
_FORCE_INLINE_ Variant get_rpc_config() const { return rpc_config; }
|
||||
|
|
|
@ -1632,20 +1632,40 @@ void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNod
|
|||
// Allow for trailing comma.
|
||||
break;
|
||||
}
|
||||
|
||||
bool is_rest = false;
|
||||
if (match(GDScriptTokenizer::Token::PERIOD_PERIOD_PERIOD)) {
|
||||
is_rest = true;
|
||||
}
|
||||
|
||||
ParameterNode *parameter = parse_parameter();
|
||||
if (parameter == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (p_function->is_vararg()) {
|
||||
push_error("Cannot have parameters after the rest parameter.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parameter->initializer != nullptr) {
|
||||
if (is_rest) {
|
||||
push_error("The rest parameter cannot have a default value.");
|
||||
continue;
|
||||
}
|
||||
default_used = true;
|
||||
} else {
|
||||
if (default_used) {
|
||||
if (default_used && !is_rest) {
|
||||
push_error("Cannot have mandatory parameters after optional parameters.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (p_function->parameters_indices.has(parameter->identifier->name)) {
|
||||
push_error(vformat(R"(Parameter with name "%s" was already declared for this %s.)", parameter->identifier->name, p_type));
|
||||
} else if (is_rest) {
|
||||
p_function->rest_parameter = parameter;
|
||||
p_body->add_local(parameter, current_function);
|
||||
} else {
|
||||
p_function->parameters_indices[parameter->identifier->name] = p_function->parameters.size();
|
||||
p_function->parameters.push_back(parameter);
|
||||
|
@ -1669,7 +1689,7 @@ void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNod
|
|||
if (!p_function->is_static) {
|
||||
push_error(R"(Static constructor must be declared static.)");
|
||||
}
|
||||
if (p_function->parameters.size() != 0) {
|
||||
if (!p_function->parameters.is_empty() || p_function->is_vararg()) {
|
||||
push_error(R"(Static constructor cannot have parameters.)");
|
||||
}
|
||||
current_class->has_static_data = true;
|
||||
|
@ -4233,6 +4253,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
|
|||
{ nullptr, nullptr, PREC_NONE }, // SEMICOLON,
|
||||
{ nullptr, &GDScriptParser::parse_attribute, PREC_ATTRIBUTE }, // PERIOD,
|
||||
{ nullptr, nullptr, PREC_NONE }, // PERIOD_PERIOD,
|
||||
{ nullptr, nullptr, PREC_NONE }, // PERIOD_PERIOD_PERIOD,
|
||||
{ nullptr, nullptr, PREC_NONE }, // COLON,
|
||||
{ &GDScriptParser::parse_get_node, nullptr, PREC_NONE }, // DOLLAR,
|
||||
{ nullptr, nullptr, PREC_NONE }, // FORWARD_ARROW,
|
||||
|
|
|
@ -851,6 +851,7 @@ public:
|
|||
IdentifierNode *identifier = nullptr;
|
||||
Vector<ParameterNode *> parameters;
|
||||
HashMap<StringName, int> parameters_indices;
|
||||
ParameterNode *rest_parameter = nullptr;
|
||||
TypeNode *return_type = nullptr;
|
||||
SuiteNode *body = nullptr;
|
||||
bool is_abstract = false;
|
||||
|
@ -869,6 +870,8 @@ public:
|
|||
bool resolved_signature = false;
|
||||
bool resolved_body = false;
|
||||
|
||||
_FORCE_INLINE_ bool is_vararg() const { return rest_parameter != nullptr; }
|
||||
|
||||
FunctionNode() {
|
||||
type = FUNCTION;
|
||||
}
|
||||
|
|
|
@ -135,6 +135,7 @@ static const char *token_names[] = {
|
|||
";", // SEMICOLON,
|
||||
".", // PERIOD,
|
||||
"..", // PERIOD_PERIOD,
|
||||
"...", // PERIOD_PERIOD_PERIOD,
|
||||
":", // COLON,
|
||||
"$", // DOLLAR,
|
||||
"->", // FORWARD_ARROW,
|
||||
|
@ -1501,6 +1502,10 @@ GDScriptTokenizer::Token GDScriptTokenizerText::scan() {
|
|||
case '.':
|
||||
if (_peek() == '.') {
|
||||
_advance();
|
||||
if (_peek() == '.') {
|
||||
_advance();
|
||||
return make_token(Token::PERIOD_PERIOD_PERIOD);
|
||||
}
|
||||
return make_token(Token::PERIOD_PERIOD);
|
||||
} else if (is_digit(_peek())) {
|
||||
// Number starting with '.'.
|
||||
|
|
|
@ -139,6 +139,7 @@ public:
|
|||
SEMICOLON,
|
||||
PERIOD,
|
||||
PERIOD_PERIOD,
|
||||
PERIOD_PERIOD_PERIOD,
|
||||
COLON,
|
||||
DOLLAR,
|
||||
FORWARD_ARROW,
|
||||
|
|
|
@ -554,10 +554,12 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
} else {
|
||||
if (p_argcount != _argument_count) {
|
||||
if (p_argcount > _argument_count) {
|
||||
if (!is_vararg()) {
|
||||
r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
|
||||
r_err.expected = _argument_count;
|
||||
call_depth--;
|
||||
return _get_default_variant_for_data_type(return_type);
|
||||
}
|
||||
} else if (p_argcount < _argument_count - _default_arg_count) {
|
||||
r_err.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
|
||||
r_err.expected = _argument_count - _default_arg_count;
|
||||
|
@ -568,21 +570,21 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
}
|
||||
}
|
||||
|
||||
// Add 3 here for self, class, and nil.
|
||||
alloca_size = sizeof(Variant *) * 3 + sizeof(Variant *) * _instruction_args_size + sizeof(Variant) * _stack_size;
|
||||
alloca_size = sizeof(Variant *) * FIXED_ADDRESSES_MAX + sizeof(Variant *) * _instruction_args_size + sizeof(Variant) * _stack_size;
|
||||
|
||||
uint8_t *aptr = (uint8_t *)alloca(alloca_size);
|
||||
stack = (Variant *)aptr;
|
||||
|
||||
for (int i = 0; i < p_argcount; i++) {
|
||||
const int non_vararg_arg_count = MIN(p_argcount, _argument_count);
|
||||
for (int i = 0; i < non_vararg_arg_count; i++) {
|
||||
if (!argument_types[i].has_type) {
|
||||
memnew_placement(&stack[i + 3], Variant(*p_args[i]));
|
||||
memnew_placement(&stack[i + FIXED_ADDRESSES_MAX], Variant(*p_args[i]));
|
||||
continue;
|
||||
}
|
||||
// If types already match, don't call Variant::construct(). Constructors of some types
|
||||
// (e.g. packed arrays) do copies, whereas they pass by reference when inside a Variant.
|
||||
if (argument_types[i].is_type(*p_args[i], false)) {
|
||||
memnew_placement(&stack[i + 3], Variant(*p_args[i]));
|
||||
memnew_placement(&stack[i + FIXED_ADDRESSES_MAX], Variant(*p_args[i]));
|
||||
continue;
|
||||
}
|
||||
if (!argument_types[i].is_type(*p_args[i], true)) {
|
||||
|
@ -597,11 +599,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
const GDScriptDataType &arg_key_type = argument_types[i].get_container_element_type_or_variant(0);
|
||||
const GDScriptDataType &arg_value_type = argument_types[i].get_container_element_type_or_variant(1);
|
||||
Dictionary dict(p_args[i]->operator Dictionary(), arg_key_type.builtin_type, arg_key_type.native_type, arg_key_type.script_type, arg_value_type.builtin_type, arg_value_type.native_type, arg_value_type.script_type);
|
||||
memnew_placement(&stack[i + 3], Variant(dict));
|
||||
memnew_placement(&stack[i + FIXED_ADDRESSES_MAX], Variant(dict));
|
||||
} else if (argument_types[i].builtin_type == Variant::ARRAY && argument_types[i].has_container_element_type(0)) {
|
||||
const GDScriptDataType &arg_type = argument_types[i].container_element_types[0];
|
||||
Array array(p_args[i]->operator Array(), arg_type.builtin_type, arg_type.native_type, arg_type.script_type);
|
||||
memnew_placement(&stack[i + 3], Variant(array));
|
||||
memnew_placement(&stack[i + FIXED_ADDRESSES_MAX], Variant(array));
|
||||
} else {
|
||||
Variant variant;
|
||||
Variant::construct(argument_types[i].builtin_type, variant, &p_args[i], 1, r_err);
|
||||
|
@ -612,16 +614,27 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
call_depth--;
|
||||
return _get_default_variant_for_data_type(return_type);
|
||||
}
|
||||
memnew_placement(&stack[i + 3], Variant(variant));
|
||||
memnew_placement(&stack[i + FIXED_ADDRESSES_MAX], Variant(variant));
|
||||
}
|
||||
} else {
|
||||
memnew_placement(&stack[i + 3], Variant(*p_args[i]));
|
||||
memnew_placement(&stack[i + FIXED_ADDRESSES_MAX], Variant(*p_args[i]));
|
||||
}
|
||||
}
|
||||
for (int i = p_argcount + 3; i < _stack_size; i++) {
|
||||
for (int i = non_vararg_arg_count + FIXED_ADDRESSES_MAX; i < _stack_size; i++) {
|
||||
memnew_placement(&stack[i], Variant);
|
||||
}
|
||||
|
||||
if (is_vararg()) {
|
||||
Array vararg;
|
||||
stack[_vararg_index] = vararg;
|
||||
if (p_argcount > _argument_count) {
|
||||
vararg.resize(p_argcount - _argument_count);
|
||||
for (int i = 0; i < p_argcount - _argument_count; i++) {
|
||||
vararg[i] = *p_args[i + _argument_count];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_instruction_args_size) {
|
||||
instruction_args = (Variant **)&aptr[sizeof(Variant) * _stack_size];
|
||||
} else {
|
||||
|
@ -2559,8 +2572,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
|
||||
gdfs->state.stack.resize(alloca_size);
|
||||
|
||||
// First 3 stack addresses are special, so we just skip them here.
|
||||
for (int i = 3; i < _stack_size; i++) {
|
||||
// First `FIXED_ADDRESSES_MAX` stack addresses are special, so we just skip them here.
|
||||
for (int i = FIXED_ADDRESSES_MAX; i < _stack_size; i++) {
|
||||
memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i]));
|
||||
}
|
||||
gdfs->state.stack_size = _stack_size;
|
||||
|
|
|
@ -508,9 +508,22 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
|
|||
parameters += " = " + parameter->initializer->reduced_value.to_json_string();
|
||||
}
|
||||
}
|
||||
if (p_func->is_vararg()) {
|
||||
if (!p_func->parameters.is_empty()) {
|
||||
parameters += ", ";
|
||||
}
|
||||
const ParameterNode *rest_param = p_func->rest_parameter;
|
||||
parameters += "..." + rest_param->identifier->name + ": " + rest_param->get_datatype().to_string();
|
||||
}
|
||||
r_symbol.detail += parameters + ")";
|
||||
if (p_func->get_datatype().is_hard_type()) {
|
||||
r_symbol.detail += " -> " + p_func->get_datatype().to_string();
|
||||
|
||||
const DataType return_type = p_func->get_datatype();
|
||||
if (return_type.is_hard_type()) {
|
||||
if (return_type.kind == DataType::BUILTIN && return_type.builtin_type == Variant::NIL) {
|
||||
r_symbol.detail += " -> void";
|
||||
} else {
|
||||
r_symbol.detail += " -> " + return_type.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
List<GDScriptParser::SuiteNode *> function_nodes;
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
class A:
|
||||
func f1(x: int, ...args: Array) -> void:
|
||||
prints(x, args)
|
||||
|
||||
func f2(x: int, ...args: Array) -> void:
|
||||
prints(x, args)
|
||||
|
||||
class B extends A:
|
||||
func f1(x: int, y: int, ...args: Array) -> void:
|
||||
prints(x, y, args)
|
||||
|
||||
func f2(x: int) -> void:
|
||||
print(x)
|
||||
|
||||
func g(...args: int):
|
||||
pass
|
||||
|
||||
func h(...args: Array[int]):
|
||||
pass
|
||||
|
||||
func test():
|
||||
pass
|
|
@ -0,0 +1,5 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
>> ERROR at line 15: The rest parameter type must be "Array", but "int" is specified.
|
||||
>> ERROR at line 18: Typed arrays are currently not supported for the rest parameter.
|
||||
>> ERROR at line 9: The function signature doesn't match the parent. Parent signature is "f1(int, ...) -> void".
|
||||
>> ERROR at line 12: The function signature doesn't match the parent. Parent signature is "f2(int, ...) -> void".
|
|
@ -0,0 +1,5 @@
|
|||
func f(...args, extra_arg):
|
||||
pass
|
||||
|
||||
func test():
|
||||
pass
|
|
@ -0,0 +1,2 @@
|
|||
GDTEST_PARSER_ERROR
|
||||
Cannot have parameters after the rest parameter.
|
|
@ -0,0 +1,5 @@
|
|||
func f(...args, ...more_args):
|
||||
pass
|
||||
|
||||
func test():
|
||||
pass
|
|
@ -0,0 +1,2 @@
|
|||
GDTEST_PARSER_ERROR
|
||||
Cannot have parameters after the rest parameter.
|
|
@ -0,0 +1,5 @@
|
|||
func f(...args = []):
|
||||
pass
|
||||
|
||||
func test():
|
||||
pass
|
|
@ -0,0 +1,2 @@
|
|||
GDTEST_PARSER_ERROR
|
||||
The rest parameter cannot have a default value.
|
|
@ -0,0 +1,5 @@
|
|||
static func _static_init(...args) -> void:
|
||||
pass
|
||||
|
||||
func test():
|
||||
pass
|
|
@ -0,0 +1,2 @@
|
|||
GDTEST_PARSER_ERROR
|
||||
Static constructor cannot have parameters.
|
|
@ -4,6 +4,7 @@ abstract class A:
|
|||
|
||||
# No `UNUSED_PARAMETER` warning.
|
||||
abstract func func_with_param(param: int) -> int
|
||||
abstract func func_with_rest_param(...args: Array) -> int
|
||||
abstract func func_with_semicolon() -> int;
|
||||
abstract func func_1() -> int; abstract func func_2() -> int
|
||||
abstract func func_without_return_type()
|
||||
|
@ -23,6 +24,7 @@ class C extends B:
|
|||
return "text_2c"
|
||||
|
||||
func func_with_param(param: int) -> int: return param
|
||||
func func_with_rest_param(...args: Array) -> int: return args.size()
|
||||
func func_with_semicolon() -> int: return 0
|
||||
func func_1() -> int: return 0
|
||||
func func_2() -> int: return 0
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
class A:
|
||||
func f(x: int) -> void:
|
||||
print(x)
|
||||
|
||||
class B extends A:
|
||||
func f(x: int, ...args: Array) -> void:
|
||||
prints(x, args)
|
||||
|
||||
class C extends B:
|
||||
func f(x: int, y: int = 0, ...args: Array) -> void:
|
||||
prints(x, y, args)
|
||||
|
||||
class D extends C:
|
||||
func f(...args: Array) -> void:
|
||||
print(args)
|
||||
|
||||
func test_func(x: int, y: int = 0, ...args: Array) -> void:
|
||||
prints(x, y, args)
|
||||
|
||||
var test_lambda := func (x: int, y: int = 0, ...args: Array) -> void:
|
||||
prints(x, y, args)
|
||||
|
||||
func test():
|
||||
for method in get_method_list():
|
||||
if str(method.name).begins_with("test_"):
|
||||
print(Utils.get_method_signature(method))
|
||||
|
||||
test_func(1)
|
||||
test_func(1, 2)
|
||||
test_func(1, 2, 3)
|
||||
test_func(1, 2, 3, 4)
|
||||
test_func(1, 2, 3, 4, 5)
|
||||
|
||||
test_lambda.call(1)
|
||||
test_lambda.call(1, 2)
|
||||
test_lambda.call(1, 2, 3)
|
||||
test_lambda.call(1, 2, 3, 4)
|
||||
test_lambda.call(1, 2, 3, 4, 5)
|
|
@ -0,0 +1,12 @@
|
|||
GDTEST_OK
|
||||
func test_func(x: int, y: int = 0, ...args) -> void
|
||||
1 0 []
|
||||
1 2 []
|
||||
1 2 [3]
|
||||
1 2 [3, 4]
|
||||
1 2 [3, 4, 5]
|
||||
1 0 []
|
||||
1 2 []
|
||||
1 2 [3]
|
||||
1 2 [3, 4]
|
||||
1 2 [3, 4, 5]
|
|
@ -117,6 +117,10 @@ static func get_method_signature(method: Dictionary, is_signal: bool = false) ->
|
|||
if i >= mandatory_argc:
|
||||
result += " = " + var_to_str(default_args[i - mandatory_argc])
|
||||
|
||||
if method.flags & METHOD_FLAG_VARARG:
|
||||
# `MethodInfo` does not support the rest parameter name.
|
||||
result += "...args" if args.is_empty() else ", ...args"
|
||||
|
||||
result += ")"
|
||||
if is_signal:
|
||||
if get_type(method.return, true) != "void":
|
||||
|
|
|
@ -207,6 +207,10 @@ static void disassemble_function(const GDScriptFunction *p_func, const Vector<St
|
|||
arg_string += arg_info.name;
|
||||
is_first_arg = false;
|
||||
}
|
||||
if (p_func->is_vararg()) {
|
||||
// `MethodInfo` does not support the rest parameter name.
|
||||
arg_string += (p_func->get_argument_count() == 0) ? "...args" : ", ...args";
|
||||
}
|
||||
|
||||
print_line(vformat("Function %s(%s)", p_func->get_name(), arg_string));
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue