diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 1a1d021dbcdd..6824dfe793e0 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -711,10 +711,15 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc } break; case GDScriptParser::ClassNode::Member::SIGNAL: { // TODO: Cache this in parser to avoid loops like this. - Vector parameters_names; + Vector> parameters_names; parameters_names.resize(member.signal->parameters.size()); for (int j = 0; j < member.signal->parameters.size(); j++) { - parameters_names.write[j] = member.signal->parameters[j]->identifier->name; + Pair ¤t_parameter = parameters_names.write[j]; + + current_parameter.first = member.signal->parameters[j]->identifier->name; + if (member.signal->parameters[j]->datatype_specifier && member.signal->parameters[j]->datatype_specifier->type_chain.size() == 1) { + current_parameter.second = member.signal->parameters[j]->get_datatype(); + } } _signals[member.signal->identifier->name] = parameters_names; } break; @@ -1244,12 +1249,32 @@ bool GDScript::has_script_signal(const StringName &p_signal) const { } void GDScript::_get_script_signal_list(List *r_list, bool p_include_base) const { - for (const KeyValue> &E : _signals) { + for (const KeyValue>> &E : _signals) { MethodInfo mi; mi.name = E.key; + for (int i = 0; i < E.value.size(); i++) { + const Pair ¶meter = E.value[i]; + PropertyInfo arg; - arg.name = E.value[i]; + arg.name = parameter.first; + arg.type = Variant::NIL; + + if (parameter.second.is_set()) { + if (parameter.second.kind == GDScriptParser::DataType::BUILTIN) { + arg.type = parameter.second.builtin_type; + } else if (parameter.second.kind == GDScriptParser::DataType::NATIVE) { + arg.type = Variant::OBJECT; + arg.class_name = parameter.second.native_type; + } else if (parameter.second.kind == GDScriptParser::DataType::SCRIPT) { + arg.type = Variant::OBJECT; + arg.class_name = parameter.second.script_type->get_instance_base_type(); + } else if (parameter.second.kind == GDScriptParser::DataType::CLASS) { + arg.type = Variant::OBJECT; + arg.class_name = parameter.second.class_type->identifier->name; + } + } + mi.arguments.push_back(arg); } r_list->push_back(mi); @@ -1602,7 +1627,7 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { // Signals. const GDScript *sl = sptr; while (sl) { - HashMap>::ConstIterator E = sl->_signals.find(p_name); + HashMap>>::ConstIterator E = sl->_signals.find(p_name); if (E) { r_ret = Signal(this->owner, E->key); return true; //index found diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 0117ed40ab24..00554a3974b0 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -39,6 +39,7 @@ #include "core/object/script_language.h" #include "core/templates/rb_set.h" #include "gdscript_function.h" +#include "gdscript_parser.h" class GDScriptNativeClass : public RefCounted { GDCLASS(GDScriptNativeClass, RefCounted); @@ -96,7 +97,7 @@ class GDScript : public Script { HashMap member_functions; HashMap member_indices; //members are just indices to the instantiated script. HashMap> subclasses; - HashMap> _signals; + HashMap>> _signals; Dictionary rpc_config; #ifdef TOOLS_ENABLED diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 38d5ae6b77cf..a9774dc2634e 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -3023,6 +3023,44 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a update_array_literal_element_type(E.value, par_types[index].get_container_element_type()); } } + + if (is_vararg) { + StringName signal_name; + // signal_name.emit(args...) + if ( + p_call->function_name == "emit" && + p_call->arguments.size() > 0 && + p_call->callee->type == GDScriptParser::Node::SUBSCRIPT && + static_cast(p_call->callee)->base->type == GDScriptParser::Node::IDENTIFIER) { + signal_name = static_cast(static_cast(p_call->callee)->base)->name; + } + // emit_signal(signal_name, args...) + else if ( + p_call->function_name == "emit_signal" && + p_call->arguments.size() >= 1 && + p_call->arguments[0]->type == GDScriptParser::Node::LITERAL + ) { + signal_name = static_cast(p_call->arguments[0])->value; + } + + if (signal_name) { + if (!parser->current_class->has_member(signal_name)) { + push_error(vformat(R"*(Invalid "emit_signal" call", no such a signal named "%s".)*", signal_name), p_call); + } else { + const GDScriptParser::ClassNode::Member ¤t_signal_member = parser->current_class->get_member(signal_name); + + if (current_signal_member.type != GDScriptParser::ClassNode::Member::SIGNAL) { + push_error(vformat(R"*(Invalid "emit_signal" call", "%s" is not a signal.)*", signal_name), p_call); + } else { + const GDScriptParser::SignalNode *current_signal = current_signal_member.signal; + for (int i = 0; i < current_signal->parameters.size(); ++i) { + par_types.push_back(current_signal->parameters[i]->get_datatype()); + } + } + } + } + } + validate_call_arg(par_types, default_arg_count, is_vararg, p_call); if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type) { diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h index c7f40f6e8237..e69de823e20f 100644 --- a/modules/gdscript/gdscript_cache.h +++ b/modules/gdscript/gdscript_cache.h @@ -38,6 +38,7 @@ #include "gdscript.h" #include "scene/resources/packed_scene.h" +class GDScript; class GDScriptAnalyzer; class GDScriptParser; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index e27b977e9d8f..b6c24bca8cb8 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -2431,10 +2431,15 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri const GDScriptParser::SignalNode *signal = member.signal; StringName name = signal->identifier->name; - Vector parameters_names; - parameters_names.resize(signal->parameters.size()); - for (int j = 0; j < signal->parameters.size(); j++) { - parameters_names.write[j] = signal->parameters[j]->identifier->name; + Vector> parameters_names; + parameters_names.resize(member.signal->parameters.size()); + for (int j = 0; j < member.signal->parameters.size(); j++) { + Pair ¤t_parameter = parameters_names.write[j]; + + current_parameter.first = member.signal->parameters[j]->identifier->name; + if (member.signal->parameters[j]->datatype_specifier && member.signal->parameters[j]->datatype_specifier->type_chain.size() == 1) { + current_parameter.second = member.signal->parameters[j]->get_datatype(); + } } p_script->_signals[name] = parameters_names; #ifdef TOOLS_ENABLED diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 146ed10ceba0..d631bc523b5d 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -268,7 +268,12 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p if (j > 0) { symbol.detail += ", "; } - symbol.detail += m.signal->parameters[j]->identifier->name; + const ParameterNode &this_parameter = *m.signal->parameters[j]; + symbol.detail += this_parameter.identifier->name; + if (this_parameter.datatype_specifier && this_parameter.datatype_specifier->type_chain.size() == 1) { + symbol.detail += ": "; + symbol.detail += this_parameter.datatype_specifier->type_chain[0]->name; + } } symbol.detail += ")"; @@ -796,9 +801,14 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode case ClassNode::Member::SIGNAL: { Dictionary api; api["name"] = m.signal->identifier->name; - Array pars; + Dictionary pars; for (int j = 0; j < m.signal->parameters.size(); j++) { - pars.append(String(m.signal->parameters[i]->identifier->name)); + const GDScriptParser::ParameterNode &this_parameter = *m.signal->parameters[i]; + if (this_parameter.datatype_specifier && this_parameter.datatype_specifier->type_chain.size() == 1) { + pars[this_parameter.identifier->name] = this_parameter.datatype_specifier->type_chain[0]->name; + } else { + pars[this_parameter.identifier->name] = Variant::NIL; + } } api["arguments"] = pars; if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.signal->start_line))) {