diff options
Diffstat (limited to 'modules/gdscript/gdscript_analyzer.cpp')
-rw-r--r-- | modules/gdscript/gdscript_analyzer.cpp | 159 |
1 files changed, 117 insertions, 42 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 4a3a3a4b61..af92450835 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -169,7 +169,9 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_n GDScriptParser::DataType type = make_enum_type(p_enum_name, native_base, p_meta); if (p_meta) { - type.builtin_type = Variant::NIL; // Native enum types are not Dictionaries. + // Native enum types are not dictionaries. + type.builtin_type = Variant::NIL; + type.is_pseudo_type = true; } List<StringName> enum_values; @@ -182,10 +184,29 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_n return type; } +static GDScriptParser::DataType make_builtin_enum_type(const StringName &p_enum_name, Variant::Type p_type, bool p_meta = true) { + GDScriptParser::DataType type = make_enum_type(p_enum_name, Variant::get_type_name(p_type), p_meta); + if (p_meta) { + // Built-in enum types are not dictionaries. + type.builtin_type = Variant::NIL; + type.is_pseudo_type = true; + } + + List<StringName> enum_values; + Variant::get_enumerations_for_enum(p_type, p_enum_name, &enum_values); + + for (const StringName &E : enum_values) { + type.enum_values[E] = Variant::get_enum_value(p_type, p_enum_name, E); + } + + return type; +} + static GDScriptParser::DataType make_global_enum_type(const StringName &p_enum_name, const StringName &p_base, bool p_meta = true) { GDScriptParser::DataType type = make_enum_type(p_enum_name, p_base, p_meta); if (p_meta) { - type.builtin_type = Variant::NIL; // Native enum types are not Dictionaries. + // Global enum types are not dictionaries. + type.builtin_type = Variant::NIL; type.is_pseudo_type = true; } @@ -703,8 +724,8 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type if (first == SNAME("Variant")) { if (p_type->type_chain.size() == 2) { // May be nested enum. - StringName enum_name = p_type->type_chain[1]->name; - StringName qualified_name = String(first) + ENUM_SEPARATOR + String(p_type->type_chain[1]->name); + const StringName enum_name = p_type->type_chain[1]->name; + const StringName qualified_name = String(first) + ENUM_SEPARATOR + String(p_type->type_chain[1]->name); if (CoreConstants::is_global_enum(qualified_name)) { result = make_global_enum_type(enum_name, first, true); return result; @@ -719,21 +740,34 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type result.kind = GDScriptParser::DataType::VARIANT; } else if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) { // Built-in types. - if (p_type->type_chain.size() > 1) { - push_error(R"(Built-in types don't contain nested types.)", p_type->type_chain[1]); + const Variant::Type builtin_type = GDScriptParser::get_builtin_type(first); + + if (p_type->type_chain.size() == 2) { + // May be nested enum. + const StringName enum_name = p_type->type_chain[1]->name; + if (Variant::has_enum(builtin_type, enum_name)) { + result = make_builtin_enum_type(enum_name, builtin_type, true); + return result; + } else { + push_error(vformat(R"(Name "%s" is not a nested type of "%s".)", enum_name, first), p_type->type_chain[1]); + return bad_type; + } + } else if (p_type->type_chain.size() > 2) { + push_error(R"(Built-in types only contain enum types, which do not have nested types.)", p_type->type_chain[2]); return bad_type; } + result.kind = GDScriptParser::DataType::BUILTIN; - result.builtin_type = GDScriptParser::get_builtin_type(first); + result.builtin_type = builtin_type; - if (result.builtin_type == Variant::ARRAY) { + if (builtin_type == Variant::ARRAY) { 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(0, container_type); } } - if (result.builtin_type == Variant::DICTIONARY) { + if (builtin_type == Variant::DICTIONARY) { GDScriptParser::DataType key_type = type_from_metatype(resolve_datatype(p_type->get_container_type_or_null(0))); if (key_type.kind != GDScriptParser::DataType::VARIANT) { key_type.is_constant = false; @@ -940,8 +974,8 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, Finally finally([&]() { ensure_cached_external_parser_for_class(member.get_datatype().class_type, p_class, "Trying to resolve datatype of class member", p_source); GDScriptParser::DataType member_type = member.get_datatype(); - if (member_type.has_container_element_type(0)) { - ensure_cached_external_parser_for_class(member_type.get_container_element_type(0).class_type, p_class, "Trying to resolve datatype of class member", p_source); + for (int i = 0; i < member_type.get_container_element_type_count(); ++i) { + ensure_cached_external_parser_for_class(member_type.get_container_element_type(i).class_type, p_class, "Trying to resolve datatype of class member", p_source); } }); @@ -3816,6 +3850,12 @@ GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const Str } Ref<GDScriptParserRef> GDScriptAnalyzer::ensure_cached_external_parser_for_class(const GDScriptParser::ClassNode *p_class, const GDScriptParser::ClassNode *p_from_class, const char *p_context, const GDScriptParser::Node *p_source) { + // Delicate piece of code that intentionally doesn't use the GDScript cache or `get_depended_parser_for`. + // Search dependencies for the parser that owns `p_class` and make a cache entry for it. + // Required for how we store pointers to classes owned by other parser trees and need to call `resolve_class_member` and such on the same parser tree. + // Since https://github.com/godotengine/godot/pull/94871 there can technically be multiple parsers for the same script in the same parser tree. + // Even if unlikely, getting the wrong parser could lead to strange undefined behavior without errors. + if (p_class == nullptr) { return nullptr; } @@ -3832,8 +3872,6 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::ensure_cached_external_parser_for_class p_from_class = parser->head; } - String script_path = p_class->get_datatype().script_path; - Ref<GDScriptParserRef> parser_ref; for (const GDScriptParser::ClassNode *look_class = p_from_class; look_class != nullptr; look_class = look_class->base_type.class_type) { if (parser->has_class(look_class)) { @@ -3966,13 +4004,36 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod if (base.kind == GDScriptParser::DataType::BUILTIN) { if (base.is_meta_type) { - bool valid = true; - Variant result = Variant::get_constant_value(base.builtin_type, name, &valid); - if (valid) { + bool valid = false; + + if (Variant::has_constant(base.builtin_type, name)) { + valid = true; + + const Variant constant_value = Variant::get_constant_value(base.builtin_type, name); + p_identifier->is_constant = true; - p_identifier->reduced_value = result; - p_identifier->set_datatype(type_from_variant(result, p_identifier)); - } else if (base.is_hard_type()) { + p_identifier->reduced_value = constant_value; + p_identifier->set_datatype(type_from_variant(constant_value, p_identifier)); + } + + if (!valid) { + const StringName enum_name = Variant::get_enum_for_enumeration(base.builtin_type, name); + if (enum_name != StringName()) { + valid = true; + + p_identifier->is_constant = true; + p_identifier->reduced_value = Variant::get_enum_value(base.builtin_type, enum_name, name); + p_identifier->set_datatype(make_builtin_enum_type(enum_name, base.builtin_type, false)); + } + } + + if (!valid && Variant::has_enum(base.builtin_type, name)) { + valid = true; + + p_identifier->set_datatype(make_builtin_enum_type(name, base.builtin_type, true)); + } + + if (!valid && base.is_hard_type()) { #ifdef SUGGEST_GODOT4_RENAMES String rename_hint; if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::RENAMED_IN_GODOT_4_HINT)).booleanize()) { @@ -3981,9 +4042,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name); } } - push_error(vformat(R"(Cannot find constant "%s" on base "%s".%s)", name, base.to_string(), rename_hint), p_identifier); + push_error(vformat(R"(Cannot find member "%s" in base "%s".%s)", name, base.to_string(), rename_hint), p_identifier); #else - push_error(vformat(R"(Cannot find constant "%s" on base "%s".)", name, base.to_string()), p_identifier); + push_error(vformat(R"(Cannot find member "%s" in base "%s".)", name, base.to_string()), p_identifier); #endif // SUGGEST_GODOT4_RENAMES } } else { @@ -4025,9 +4086,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name); } } - push_error(vformat(R"(Cannot find property "%s" on base "%s".%s)", name, base.to_string(), rename_hint), p_identifier); + push_error(vformat(R"(Cannot find member "%s" in base "%s".%s)", name, base.to_string(), rename_hint), p_identifier); #else - push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier); + push_error(vformat(R"(Cannot find member "%s" in base "%s".)", name, base.to_string()), p_identifier); #endif // SUGGEST_GODOT4_RENAMES } } @@ -5568,7 +5629,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo } else { Vector<String> names = String(p_property.class_name).split(ENUM_SEPARATOR); if (names.size() == 2) { - result = make_native_enum_type(names[1], names[0], false); + result = make_enum_type(names[1], names[0], false); result.is_constant = false; } } @@ -5805,8 +5866,6 @@ void GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p #ifdef DEBUG_ENABLED void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_identifier, const String &p_context, const bool p_in_local_scope) { const StringName &name = p_identifier->name; - GDScriptParser::DataType base = parser->current_class->get_datatype(); - GDScriptParser::ClassNode *base_class = base.class_type; { List<MethodInfo> gdscript_funcs; @@ -5834,40 +5893,56 @@ void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_identifier } } + const GDScriptParser::DataType current_class_type = parser->current_class->get_datatype(); if (p_in_local_scope) { - while (base_class != nullptr) { + GDScriptParser::ClassNode *base_class = current_class_type.class_type; + + if (base_class != nullptr) { if (base_class->has_member(name)) { parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE, p_context, p_identifier->name, base_class->get_member(name).get_type_name(), itos(base_class->get_member(name).get_line())); return; } base_class = base_class->base_type.class_type; } + + while (base_class != nullptr) { + if (base_class->has_member(name)) { + String base_class_name = base_class->get_global_name(); + if (base_class_name.is_empty()) { + base_class_name = base_class->fqcn; + } + + parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, base_class->get_member(name).get_type_name(), itos(base_class->get_member(name).get_line()), base_class_name); + return; + } + base_class = base_class->base_type.class_type; + } } - StringName parent = base.native_type; - while (parent != StringName()) { - ERR_FAIL_COND_MSG(!class_exists(parent), "Non-existent native base class."); + StringName native_base_class = current_class_type.native_type; + while (native_base_class != StringName()) { + ERR_FAIL_COND_MSG(!class_exists(native_base_class), "Non-existent native base class."); - if (ClassDB::has_method(parent, name, true)) { - parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "method", parent); + if (ClassDB::has_method(native_base_class, name, true)) { + parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "method", native_base_class); return; - } else if (ClassDB::has_signal(parent, name, true)) { - parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "signal", parent); + } else if (ClassDB::has_signal(native_base_class, name, true)) { + parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "signal", native_base_class); return; - } else if (ClassDB::has_property(parent, name, true)) { - parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "property", parent); + } else if (ClassDB::has_property(native_base_class, name, true)) { + parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "property", native_base_class); return; - } else if (ClassDB::has_integer_constant(parent, name, true)) { - parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "constant", parent); + } else if (ClassDB::has_integer_constant(native_base_class, name, true)) { + parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "constant", native_base_class); return; - } else if (ClassDB::has_enum(parent, name, true)) { - parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "enum", parent); + } else if (ClassDB::has_enum(native_base_class, name, true)) { + parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "enum", native_base_class); return; } - parent = ClassDB::get_parent_class(parent); + native_base_class = ClassDB::get_parent_class(native_base_class); } } -#endif +#endif // DEBUG_ENABLED GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source) { // Unary version. |