diff options
Diffstat (limited to 'modules/gdscript/gdscript_analyzer.cpp')
-rw-r--r-- | modules/gdscript/gdscript_analyzer.cpp | 277 |
1 files changed, 188 insertions, 89 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index f3f95ba50b..98d4a19a87 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -31,6 +31,7 @@ #include "gdscript_analyzer.h" #include "gdscript.h" +#include "gdscript_utility_callable.h" #include "gdscript_utility_functions.h" #include "core/config/engine.h" @@ -360,7 +361,7 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c push_error(vformat(R"(Class "%s" hides a built-in type.)", class_name), p_class->identifier); } else if (class_exists(class_name)) { push_error(vformat(R"(Class "%s" hides a native class.)", class_name), p_class->identifier); - } else if (ScriptServer::is_global_class(class_name) && (ScriptServer::get_global_class_path(class_name) != parser->script_path || p_class != parser->head)) { + } else if (ScriptServer::is_global_class(class_name) && (!GDScript::is_canonically_equal_paths(ScriptServer::get_global_class_path(class_name), parser->script_path) || p_class != parser->head)) { push_error(vformat(R"(Class "%s" hides a global script class.)", class_name), p_class->identifier); } else if (ProjectSettings::get_singleton()->has_autoload(class_name) && ProjectSettings::get_singleton()->get_autoload(class_name).is_singleton) { push_error(vformat(R"(Class "%s" hides an autoload singleton.)", class_name), p_class->identifier); @@ -424,7 +425,7 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c if (ScriptServer::is_global_class(name)) { String base_path = ScriptServer::get_global_class_path(name); - if (base_path == parser->script_path) { + if (GDScript::is_canonically_equal_paths(base_path, parser->script_path)) { base = parser->head->get_datatype(); } else { Ref<GDScriptParserRef> base_parser = get_parser_for(base_path); @@ -685,10 +686,10 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type result.builtin_type = GDScriptParser::get_builtin_type(first); if (result.builtin_type == Variant::ARRAY) { - GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->container_type)); + GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->get_container_type_or_null(0))); if (container_type.kind != GDScriptParser::DataType::VARIANT) { container_type.is_constant = false; - result.set_container_element_type(container_type); + result.set_container_element_type(0, container_type); } } } else if (class_exists(first)) { @@ -697,7 +698,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type result.builtin_type = Variant::OBJECT; result.native_type = first; } else if (ScriptServer::is_global_class(first)) { - if (parser->script_path == ScriptServer::get_global_class_path(first)) { + if (GDScript::is_canonically_equal_paths(parser->script_path, ScriptServer::get_global_class_path(first))) { result = parser->head->get_datatype(); } else { String path = ScriptServer::get_global_class_path(first); @@ -829,15 +830,23 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type } } - if (result.builtin_type != Variant::ARRAY && p_type->container_type != nullptr) { - push_error("Only arrays can specify the collection element type.", p_type); + if (!p_type->container_types.is_empty()) { + if (result.builtin_type == Variant::ARRAY) { + if (p_type->container_types.size() != 1) { + push_error("Arrays require exactly one collection element type.", p_type); + return bad_type; + } + } else { + push_error("Only arrays can specify collection element types.", p_type); + return bad_type; + } } p_type->set_datatype(result); return result; } -void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, StringName p_name, const GDScriptParser::Node *p_source) { +void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, const StringName &p_name, const GDScriptParser::Node *p_source) { ERR_FAIL_COND(!p_class->has_member(p_name)); resolve_class_member(p_class, p_class->members_indices[p_name], p_source); } @@ -1585,7 +1594,13 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * GDScriptParser::FunctionNode *previous_function = parser->current_function; parser->current_function = p_function; bool previous_static_context = static_context; - static_context = p_function->is_static; + if (p_is_lambda) { + // For lambdas this is determined from the context, the `static` keyword is not allowed. + p_function->is_static = static_context; + } else { + // For normal functions, this is determined in the parser by the `static` keyword. + static_context = p_function->is_static; + } GDScriptParser::DataType prev_datatype = p_function->get_datatype(); @@ -1885,8 +1900,8 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) { GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer); - if (has_specified_type && specified_type.has_container_element_type()) { - update_array_literal_element_type(array, specified_type.get_container_element_type()); + if (has_specified_type && specified_type.has_container_element_type(0)) { + update_array_literal_element_type(array, specified_type.get_container_element_type(0)); } } @@ -1949,7 +1964,7 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi } else { push_error(vformat(R"(Cannot assign a value of type %s to %s "%s" with specified type %s.)", initializer_type.to_string(), p_kind, p_assignable->identifier->name, specified_type.to_string()), p_assignable->initializer); } - } else if (specified_type.has_container_element_type() && !initializer_type.has_container_element_type()) { + } else if (specified_type.has_container_element_type(0) && !initializer_type.has_container_element_type(0)) { mark_node_unsafe(p_assignable->initializer); #ifdef DEBUG_ENABLED } else if (specified_type.builtin_type == Variant::INT && initializer_type.builtin_type == Variant::FLOAT) { @@ -2121,8 +2136,8 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { if (list_type.is_variant()) { variable_type.kind = GDScriptParser::DataType::VARIANT; mark_node_unsafe(p_for->list); - } else if (list_type.has_container_element_type()) { - variable_type = list_type.get_container_element_type(); + } else if (list_type.has_container_element_type(0)) { + variable_type = list_type.get_container_element_type(0); variable_type.type_source = list_type.type_source; } else if (list_type.is_typed_container_type()) { variable_type = list_type.get_typed_container_type(); @@ -2371,8 +2386,8 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) { result.builtin_type = Variant::NIL; result.is_constant = true; } else { - if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type()) { - update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type()); + if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type(0)) { + update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type(0)); } if (has_expected_type && expected_type.is_hard_type() && p_return->return_value->is_constant) { update_const_expression_builtin_type(p_return->return_value, expected_type, "return"); @@ -2592,7 +2607,7 @@ void GDScriptAnalyzer::update_const_expression_builtin_type(GDScriptParser::Expr // This function determines which type is that (if any). void GDScriptAnalyzer::update_array_literal_element_type(GDScriptParser::ArrayNode *p_array, const GDScriptParser::DataType &p_element_type) { GDScriptParser::DataType expected_type = p_element_type; - expected_type.unset_container_element_type(); // Nested types (like `Array[Array[int]]`) are not currently supported. + expected_type.container_element_types.clear(); // Nested types (like `Array[Array[int]]`) are not currently supported. for (int i = 0; i < p_array->elements.size(); i++) { GDScriptParser::ExpressionNode *element_node = p_array->elements[i]; @@ -2615,7 +2630,7 @@ void GDScriptAnalyzer::update_array_literal_element_type(GDScriptParser::ArrayNo } GDScriptParser::DataType array_type = p_array->get_datatype(); - array_type.set_container_element_type(expected_type); + array_type.set_container_element_type(0, expected_type); p_array->set_datatype(array_type); } @@ -2662,8 +2677,8 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig } // Check if assigned value is an array literal, so we can make it a typed array too if appropriate. - if (p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY && assignee_type.is_hard_type() && assignee_type.has_container_element_type()) { - update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value), assignee_type.get_container_element_type()); + if (p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY && assignee_type.is_hard_type() && assignee_type.has_container_element_type(0)) { + update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value), assignee_type.get_container_element_type(0)); } if (p_assignment->operation == GDScriptParser::AssignmentNode::OP_NONE && assignee_type.is_hard_type() && p_assignment->assigned_value->is_constant) { @@ -2741,7 +2756,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig // weak non-variant assignee and incompatible result downgrades_assignee = true; } - } else if (assignee_type.has_container_element_type() && !op_type.has_container_element_type()) { + } else if (assignee_type.has_container_element_type(0) && !op_type.has_container_element_type(0)) { // typed array assignee and untyped array result mark_node_unsafe(p_assignment); } @@ -3305,8 +3320,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a // If the function requires typed arrays we must make literals be typed. for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) { int index = E.key; - if (index < par_types.size() && par_types[index].is_hard_type() && par_types[index].has_container_element_type()) { - update_array_literal_element_type(E.value, par_types[index].get_container_element_type()); + if (index < par_types.size() && par_types[index].is_hard_type() && par_types[index].has_container_element_type(0)) { + update_array_literal_element_type(E.value, par_types[index].get_container_element_type(0)); } } validate_call_arg(par_types, default_arg_count, method_flags.has_flag(METHOD_FLAG_VARARG), p_call); @@ -3317,15 +3332,16 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a } if (is_self && static_context && !method_flags.has_flag(METHOD_FLAG_STATIC)) { - if (parser->current_function) { - // Get the parent function above any lambda. - GDScriptParser::FunctionNode *parent_function = parser->current_function; - while (parent_function->source_lambda) { - parent_function = parent_function->source_lambda->parent_function; - } + // Get the parent function above any lambda. + GDScriptParser::FunctionNode *parent_function = parser->current_function; + while (parent_function && parent_function->source_lambda) { + parent_function = parent_function->source_lambda->parent_function; + } + + if (parent_function) { push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call); } else { - push_error(vformat(R"*(Cannot call non-static function "%s()" for static variable initializer.)*", p_call->function_name), p_call); + push_error(vformat(R"*(Cannot call non-static function "%s()" from a static variable initializer.)*", p_call->function_name), p_call); } } else if (!is_self && base_type.is_meta_type && !method_flags.has_flag(METHOD_FLAG_STATIC)) { base_type.is_meta_type = false; // For `to_string()`. @@ -3395,8 +3411,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a if (!found && (is_self || (base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN))) { String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string(); #ifdef SUGGEST_GODOT4_RENAMES - String rename_hint = String(); - if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GODOT_4_HINT)).booleanize()) { + String rename_hint; + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::RENAMED_IN_GODOT_4_HINT)).booleanize()) { const char *renamed_function_name = check_for_renamed_identifier(p_call->function_name, p_call->type); if (renamed_function_name) { rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", String(renamed_function_name) + "()"); @@ -3437,8 +3453,8 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) { } } - if (p_cast->operand->type == GDScriptParser::Node::ARRAY && cast_type.has_container_element_type()) { - update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_cast->operand), cast_type.get_container_element_type()); + if (p_cast->operand->type == GDScriptParser::Node::ARRAY && cast_type.has_container_element_type(0)) { + update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_cast->operand), cast_type.get_container_element_type(0)); } if (!cast_type.is_variant()) { @@ -3605,8 +3621,8 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod p_identifier->set_datatype(type_from_variant(result, p_identifier)); } else if (base.is_hard_type()) { #ifdef SUGGEST_GODOT4_RENAMES - String rename_hint = String(); - if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GODOT_4_HINT)).booleanize()) { + String rename_hint; + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::RENAMED_IN_GODOT_4_HINT)).booleanize()) { const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type); if (renamed_identifier_name) { rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name); @@ -3621,7 +3637,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod switch (base.builtin_type) { case Variant::NIL: { if (base.is_hard_type()) { - push_error(vformat(R"(Invalid get index "%s" on base Nil)", name), p_identifier); + push_error(vformat(R"(Cannot get property "%s" on a null object.)", name), p_identifier); } return; } @@ -3643,10 +3659,14 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod return; } } + if (Variant::has_builtin_method(base.builtin_type, name)) { + p_identifier->set_datatype(make_callable_type(Variant::get_builtin_method_info(base.builtin_type, name))); + return; + } if (base.is_hard_type()) { #ifdef SUGGEST_GODOT4_RENAMES - String rename_hint = String(); - if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GODOT_4_HINT)).booleanize()) { + String rename_hint; + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::RENAMED_IN_GODOT_4_HINT)).booleanize()) { const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type); if (renamed_identifier_name) { rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name); @@ -3762,6 +3782,60 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod } } + // Check non-GDScript scripts. + Ref<Script> script_type = base.script_type; + + if (base_class == nullptr && script_type.is_valid()) { + List<PropertyInfo> property_list; + script_type->get_script_property_list(&property_list); + + for (const PropertyInfo &property_info : property_list) { + if (property_info.name != p_identifier->name) { + continue; + } + + const GDScriptParser::DataType property_type = GDScriptAnalyzer::type_from_property(property_info, false, false); + + p_identifier->set_datatype(property_type); + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE; + return; + } + + MethodInfo method_info = script_type->get_method_info(p_identifier->name); + + if (method_info.name == p_identifier->name) { + p_identifier->set_datatype(make_callable_type(method_info)); + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_FUNCTION; + return; + } + + List<MethodInfo> signal_list; + script_type->get_script_signal_list(&signal_list); + + for (const MethodInfo &signal_info : signal_list) { + if (signal_info.name != p_identifier->name) { + continue; + } + + const GDScriptParser::DataType signal_type = make_signal_type(signal_info); + + p_identifier->set_datatype(signal_type); + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL; + return; + } + + HashMap<StringName, Variant> constant_map; + script_type->get_constants(&constant_map); + + if (constant_map.has(p_identifier->name)) { + Variant constant = constant_map.get(p_identifier->name); + + p_identifier->set_datatype(make_builtin_meta_type(constant.get_type())); + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; + return; + } + } + // Check native members. No need for native class recursion because Node exposes all Object's properties. const StringName &native = base.native_type; @@ -3908,15 +3982,16 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident bool source_is_variable = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE; bool source_is_signal = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_SIGNAL; if ((source_is_variable || source_is_signal) && static_context) { - if (parser->current_function) { - // Get the parent function above any lambda. - GDScriptParser::FunctionNode *parent_function = parser->current_function; - while (parent_function->source_lambda) { - parent_function = parent_function->source_lambda->parent_function; - } + // Get the parent function above any lambda. + GDScriptParser::FunctionNode *parent_function = parser->current_function; + while (parent_function && parent_function->source_lambda) { + parent_function = parent_function->source_lambda->parent_function; + } + + if (parent_function) { push_error(vformat(R"*(Cannot access %s "%s" from the static function "%s()".)*", source_is_signal ? "signal" : "instance variable", p_identifier->name, parent_function->identifier->name), p_identifier); } else { - push_error(vformat(R"*(Cannot access %s "%s" for a static variable initializer.)*", source_is_signal ? "signal" : "instance variable", p_identifier->name), p_identifier); + push_error(vformat(R"*(Cannot access %s "%s" from a static variable initializer.)*", source_is_signal ? "signal" : "instance variable", p_identifier->name), p_identifier); } } @@ -4043,6 +4118,19 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident return; } + if (Variant::has_utility_function(name) || GDScriptUtilityFunctions::function_exists(name)) { + p_identifier->is_constant = true; + p_identifier->reduced_value = Callable(memnew(GDScriptUtilityCallable(name))); + MethodInfo method_info; + if (GDScriptUtilityFunctions::function_exists(name)) { + method_info = GDScriptUtilityFunctions::get_function_info(name); + } else { + method_info = Variant::get_utility_function_info(name); + } + p_identifier->set_datatype(make_callable_type(method_info)); + return; + } + // Allow "Variant" here since it might be used for nested enums. if (can_be_builtin && name == SNAME("Variant")) { GDScriptParser::DataType variant; @@ -4055,23 +4143,18 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident } // Not found. - // Check if it's a builtin function. - if (GDScriptUtilityFunctions::function_exists(name)) { - push_error(vformat(R"(Built-in function "%s" cannot be used as an identifier.)", name), p_identifier); - } else { #ifdef SUGGEST_GODOT4_RENAMES - String rename_hint = String(); - if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GODOT_4_HINT)).booleanize()) { - const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type); - if (renamed_identifier_name) { - rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name); - } + String rename_hint; + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::RENAMED_IN_GODOT_4_HINT)).booleanize()) { + const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type); + if (renamed_identifier_name) { + rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name); } - push_error(vformat(R"(Identifier "%s" not declared in the current scope.%s)", name, rename_hint), p_identifier); + } + push_error(vformat(R"(Identifier "%s" not declared in the current scope.%s)", name, rename_hint), p_identifier); #else - push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier); + push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier); #endif // SUGGEST_GODOT4_RENAMES - } GDScriptParser::DataType dummy; dummy.kind = GDScriptParser::DataType::VARIANT; p_identifier->set_datatype(dummy); // Just so type is set to something. @@ -4136,8 +4219,8 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) { } else { // TODO: Don't load if validating: use completion cache. - // Must load GDScript and PackedScenes separately to permit cyclic references - // as ResourceLoader::load() detect and reject those. + // Must load GDScript separately to permit cyclic references + // as ResourceLoader::load() detects and rejects those. if (ResourceLoader::get_resource_type(p_preload->resolved_path) == "GDScript") { Error err = OK; Ref<GDScript> res = GDScriptCache::get_shallow_script(p_preload->resolved_path, err, parser->script_path); @@ -4145,13 +4228,6 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) { if (err != OK) { push_error(vformat(R"(Could not preload resource script "%s".)", p_preload->resolved_path), p_preload->path); } - } else if (ResourceLoader::get_resource_type(p_preload->resolved_path) == "PackedScene") { - Error err = OK; - Ref<PackedScene> res = GDScriptCache::get_packed_scene(p_preload->resolved_path, err, parser->script_path); - p_preload->resource = res; - if (err != OK) { - push_error(vformat(R"(Could not preload resource scene "%s".)", p_preload->resolved_path), p_preload->path); - } } else { p_preload->resource = ResourceLoader::load(p_preload->resolved_path); if (p_preload->resource.is_null()) { @@ -4343,7 +4419,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri } } else if (base_type.kind != GDScriptParser::DataType::BUILTIN && !index_type.is_variant()) { if (index_type.builtin_type != Variant::STRING && index_type.builtin_type != Variant::STRING_NAME) { - push_error(vformat(R"(Only String or StringName can be used as index for type "%s", but received a "%s".)", base_type.to_string(), index_type.to_string()), p_subscript->index); + push_error(vformat(R"(Only "String" or "StringName" can be used as index for type "%s", but received "%s".)", base_type.to_string(), index_type.to_string()), p_subscript->index); } } @@ -4424,8 +4500,8 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri break; // Can have an element type. case Variant::ARRAY: - if (base_type.has_container_element_type()) { - result_type = base_type.get_container_element_type(); + if (base_type.has_container_element_type(0)) { + result_type = base_type.get_container_element_type(0); result_type.type_source = base_type.type_source; } else { result_type.kind = GDScriptParser::DataType::VARIANT; @@ -4589,7 +4665,7 @@ Variant GDScriptAnalyzer::make_expression_reduced_value(GDScriptParser::Expressi } Variant GDScriptAnalyzer::make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced) { - Array array = p_array->get_datatype().has_container_element_type() ? make_array_from_element_datatype(p_array->get_datatype().get_container_element_type()) : Array(); + Array array = p_array->get_datatype().has_container_element_type(0) ? make_array_from_element_datatype(p_array->get_datatype().get_container_element_type(0)) : Array(); array.resize(p_array->elements.size()); for (int i = 0; i < p_array->elements.size(); i++) { @@ -4711,8 +4787,8 @@ Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNo GDScriptParser::DataType datatype = p_variable->get_datatype(); if (datatype.is_hard_type()) { if (datatype.kind == GDScriptParser::DataType::BUILTIN && datatype.builtin_type != Variant::OBJECT) { - if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type()) { - result = make_array_from_element_datatype(datatype.get_container_element_type()); + if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type(0)) { + result = make_array_from_element_datatype(datatype.get_container_element_type(0)); } else { VariantInternal::initialize(&result, datatype.builtin_type); } @@ -4739,11 +4815,11 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va if (p_value.get_type() == Variant::ARRAY) { const Array &array = p_value; if (array.get_typed_script()) { - result.set_container_element_type(type_from_metatype(make_script_meta_type(array.get_typed_script()))); + result.set_container_element_type(0, type_from_metatype(make_script_meta_type(array.get_typed_script()))); } else if (array.get_typed_class_name()) { - result.set_container_element_type(type_from_metatype(make_native_meta_type(array.get_typed_class_name()))); + result.set_container_element_type(0, type_from_metatype(make_native_meta_type(array.get_typed_class_name()))); } else if (array.get_typed_builtin() != Variant::NIL) { - result.set_container_element_type(type_from_metatype(make_builtin_meta_type((Variant::Type)array.get_typed_builtin()))); + result.set_container_element_type(0, type_from_metatype(make_builtin_meta_type((Variant::Type)array.get_typed_builtin()))); } } else if (p_value.get_type() == Variant::OBJECT) { // Object is treated as a native type, not a builtin type. @@ -4834,8 +4910,19 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo } result.builtin_type = p_property.type; if (p_property.type == Variant::OBJECT) { - result.kind = GDScriptParser::DataType::NATIVE; - result.native_type = p_property.class_name == StringName() ? SNAME("Object") : p_property.class_name; + if (ScriptServer::is_global_class(p_property.class_name)) { + result.kind = GDScriptParser::DataType::SCRIPT; + result.script_path = ScriptServer::get_global_class_path(p_property.class_name); + result.native_type = ScriptServer::get_global_class_native_base(p_property.class_name); + + Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_property.class_name)); + if (scr.is_valid()) { + result.script_type = scr; + } + } else { + result.kind = GDScriptParser::DataType::NATIVE; + result.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name; + } } else { result.kind = GDScriptParser::DataType::BUILTIN; result.builtin_type = p_property.type; @@ -4865,7 +4952,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo ERR_FAIL_V_MSG(result, "Could not find element type from property hint of a typed array."); } elem_type.is_constant = false; - result.set_container_element_type(elem_type); + result.set_container_element_type(0, elem_type); } else if (p_property.type == Variant::INT) { // Check if it's enum. if ((p_property.usage & PROPERTY_USAGE_CLASS_IS_ENUM) && p_property.class_name != StringName()) { @@ -5217,7 +5304,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator bool hard_operation = p_a.is_hard_type() && p_b.is_hard_type(); if (p_operation == Variant::OP_ADD && a_type == Variant::ARRAY && b_type == Variant::ARRAY) { - if (p_a.has_container_element_type() && p_b.has_container_element_type() && p_a.get_container_element_type() == p_b.get_container_element_type()) { + if (p_a.has_container_element_type(0) && p_b.has_container_element_type(0) && p_a.get_container_element_type(0) == p_b.get_container_element_type(0)) { r_valid = true; result = p_a; result.type_source = hard_operation ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED; @@ -5241,8 +5328,21 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator return result; } -// TODO: Add safe/unsafe return variable (for variant cases) bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion, const GDScriptParser::Node *p_source_node) { +#ifdef DEBUG_ENABLED + if (p_source_node) { + if (p_target.kind == GDScriptParser::DataType::ENUM) { + if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::INT) { + parser->push_warning(p_source_node, GDScriptWarning::INT_AS_ENUM_WITHOUT_CAST); + } + } + } +#endif + return check_type_compatibility(p_target, p_source, p_allow_implicit_conversion, p_source_node); +} + +// TODO: Add safe/unsafe return variable (for variant cases) +bool GDScriptAnalyzer::check_type_compatibility(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion, const GDScriptParser::Node *p_source_node) { // These return "true" so it doesn't affect users negatively. ERR_FAIL_COND_V_MSG(!p_target.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset target type"); ERR_FAIL_COND_V_MSG(!p_source.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset value type"); @@ -5268,8 +5368,8 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ } if (valid && p_target.builtin_type == Variant::ARRAY && p_source.builtin_type == Variant::ARRAY) { // Check the element type. - if (p_target.has_container_element_type() && p_source.has_container_element_type()) { - valid = p_target.get_container_element_type() == p_source.get_container_element_type(); + if (p_target.has_container_element_type(0) && p_source.has_container_element_type(0)) { + valid = p_target.get_container_element_type(0) == p_source.get_container_element_type(0); } } return valid; @@ -5277,11 +5377,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ if (p_target.kind == GDScriptParser::DataType::ENUM) { if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::INT) { -#ifdef DEBUG_ENABLED - if (p_source_node) { - parser->push_warning(p_source_node, GDScriptWarning::INT_AS_ENUM_WITHOUT_CAST); - } -#endif return true; } if (p_source.kind == GDScriptParser::DataType::ENUM) { @@ -5459,12 +5554,15 @@ void GDScriptAnalyzer::resolve_pending_lambda_bodies() { } GDScriptParser::LambdaNode *previous_lambda = current_lambda; + bool previous_static_context = static_context; List<GDScriptParser::LambdaNode *> lambdas = pending_body_resolution_lambdas; pending_body_resolution_lambdas.clear(); for (GDScriptParser::LambdaNode *lambda : lambdas) { current_lambda = lambda; + static_context = lambda->function->is_static; + resolve_function_body(lambda->function, true); int captures_amount = lambda->captures.size(); @@ -5493,6 +5591,7 @@ void GDScriptAnalyzer::resolve_pending_lambda_bodies() { } current_lambda = previous_lambda; + static_context = previous_static_context; } bool GDScriptAnalyzer::class_exists(const StringName &p_class) const { |