feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -42,7 +42,7 @@
#include "core/object/class_db.h"
#include "core/object/script_language.h"
#include "core/templates/hash_map.h"
#include "scene/resources/packed_scene.h"
#include "scene/main/node.h"
#if defined(TOOLS_ENABLED) && !defined(DISABLE_DEPRECATED)
#define SUGGEST_GODOT4_RENAMES
@ -148,6 +148,15 @@ static GDScriptParser::DataType make_enum_type(const StringName &p_enum_name, co
return type;
}
static GDScriptParser::DataType make_class_enum_type(const StringName &p_enum_name, GDScriptParser::ClassNode *p_class, const String &p_script_path, bool p_meta = true) {
GDScriptParser::DataType type = make_enum_type(p_enum_name, p_class->fqcn, p_meta);
type.class_type = p_class;
type.script_path = p_script_path;
return type;
}
static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_name, const StringName &p_native_class, bool p_meta = true) {
// Find out which base class declared the enum, so the name is always the same even when coming from other contexts.
StringName native_base = p_native_class;
@ -160,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;
@ -173,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;
}
@ -418,6 +448,12 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
return err;
}
#ifdef DEBUG_ENABLED
if (!parser->_is_tool && ext_parser->get_parser()->_is_tool) {
parser->push_warning(p_class, GDScriptWarning::MISSING_TOOL);
}
#endif
base = ext_parser->get_parser()->head->get_datatype();
} else {
if (p_class->extends.is_empty()) {
@ -445,6 +481,13 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), id);
return err;
}
#ifdef DEBUG_ENABLED
if (!parser->_is_tool && base_parser->get_parser()->_is_tool) {
parser->push_warning(p_class, GDScriptWarning::MISSING_TOOL);
}
#endif
base = base_parser->get_parser()->head->get_datatype();
}
} else if (ProjectSettings::get_singleton()->has_autoload(name) && ProjectSettings::get_singleton()->get_autoload(name).is_singleton) {
@ -465,6 +508,13 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), id);
return err;
}
#ifdef DEBUG_ENABLED
if (!parser->_is_tool && info_parser->get_parser()->_is_tool) {
parser->push_warning(p_class, GDScriptWarning::MISSING_TOOL);
}
#endif
base = info_parser->get_parser()->head->get_datatype();
} else if (class_exists(name)) {
if (Engine::get_singleton()->has_singleton(name)) {
@ -674,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;
@ -690,20 +740,45 @@ 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);
if (result.builtin_type == Variant::ARRAY) {
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = builtin_type;
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 (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;
result.set_container_element_type(0, key_type);
}
GDScriptParser::DataType value_type = type_from_metatype(resolve_datatype(p_type->get_container_type_or_null(1)));
if (value_type.kind != GDScriptParser::DataType::VARIANT) {
value_type.is_constant = false;
result.set_container_element_type(1, value_type);
}
}
} else if (class_exists(first)) {
// Native engine classes.
result.kind = GDScriptParser::DataType::NATIVE;
@ -717,7 +792,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
String ext = path.get_extension();
if (ext == GDScriptLanguage::get_singleton()->get_extension()) {
Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(path);
if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
if (ref.is_null() || ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type);
return bad_type;
}
@ -864,11 +939,16 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::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);
push_error(R"(Typed arrays require exactly one collection element type.)", p_type);
return bad_type;
}
} else if (result.builtin_type == Variant::DICTIONARY) {
if (p_type->container_types.size() != 2) {
push_error(R"(Typed dictionaries require exactly two collection element types.)", p_type);
return bad_type;
}
} else {
push_error("Only arrays can specify collection element types.", p_type);
push_error(R"(Only arrays and dictionaries can specify collection element types.)", p_type);
return bad_type;
}
}
@ -894,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);
}
});
@ -1013,7 +1093,12 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
}
}
if (is_get_node) {
parser->push_warning(member.variable, GDScriptWarning::GET_NODE_DEFAULT_WITHOUT_ONREADY, is_using_shorthand ? "$" : "get_node()");
String offending_syntax = "get_node()";
if (is_using_shorthand) {
GDScriptParser::GetNodeNode *get_node_node = static_cast<GDScriptParser::GetNodeNode *>(expr);
offending_syntax = get_node_node->use_dollar ? "$" : "%";
}
parser->push_warning(member.variable, GDScriptWarning::GET_NODE_DEFAULT_WITHOUT_ONREADY, offending_syntax);
}
}
}
@ -1064,7 +1149,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
check_class_member_name_conflict(p_class, member.m_enum->identifier->name, member.m_enum);
member.m_enum->set_datatype(resolving_datatype);
GDScriptParser::DataType enum_type = make_enum_type(member.m_enum->identifier->name, p_class->fqcn, true);
GDScriptParser::DataType enum_type = make_class_enum_type(member.m_enum->identifier->name, p_class, parser->script_path, true);
const GDScriptParser::EnumNode *prev_enum = current_enum;
current_enum = member.m_enum;
@ -1157,7 +1242,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
// Also update the original references.
member.enum_value.parent_enum->values.set(member.enum_value.index, member.enum_value);
member.enum_value.identifier->set_datatype(make_enum_type(UNNAMED_ENUM, p_class->fqcn, false));
member.enum_value.identifier->set_datatype(make_class_enum_type(UNNAMED_ENUM, p_class, parser->script_path, false));
} break;
case GDScriptParser::ClassNode::Member::CLASS:
check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class);
@ -1906,6 +1991,11 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
if (has_specified_type && specified_type.has_container_element_type(0)) {
update_array_literal_element_type(array, specified_type.get_container_element_type(0));
}
} else if (p_assignable->initializer->type == GDScriptParser::Node::DICTIONARY) {
GDScriptParser::DictionaryNode *dictionary = static_cast<GDScriptParser::DictionaryNode *>(p_assignable->initializer);
if (has_specified_type && specified_type.has_container_element_types()) {
update_dictionary_literal_element_type(dictionary, specified_type.get_container_element_type_or_variant(0), specified_type.get_container_element_type_or_variant(1));
}
}
if (is_constant && !p_assignable->initializer->is_constant) {
@ -1967,7 +2057,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(0) && !initializer_type.has_container_element_type(0)) {
} else if ((specified_type.has_container_element_type(0) && !initializer_type.has_container_element_type(0)) || (specified_type.has_container_element_type(1) && !initializer_type.has_container_element_type(1))) {
mark_node_unsafe(p_assignable->initializer);
#ifdef DEBUG_ENABLED
} else if (specified_type.builtin_type == Variant::INT && initializer_type.builtin_type == Variant::FLOAT) {
@ -2209,8 +2299,12 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
} else if (!is_type_compatible(specified_type, variable_type)) {
p_for->use_conversion_assign = true;
}
if (p_for->list && p_for->list->type == GDScriptParser::Node::ARRAY) {
update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_for->list), specified_type);
if (p_for->list) {
if (p_for->list->type == GDScriptParser::Node::ARRAY) {
update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_for->list), specified_type);
} else if (p_for->list->type == GDScriptParser::Node::DICTIONARY) {
update_dictionary_literal_element_type(static_cast<GDScriptParser::DictionaryNode *>(p_for->list), specified_type, GDScriptParser::DataType::get_variant_type());
}
}
}
p_for->variable->set_datatype(specified_type);
@ -2412,6 +2506,9 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
} else {
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));
} else if (p_return->return_value->type == GDScriptParser::Node::DICTIONARY && has_expected_type && expected_type.has_container_element_types()) {
update_dictionary_literal_element_type(static_cast<GDScriptParser::DictionaryNode *>(p_return->return_value),
expected_type.get_container_element_type_or_variant(0), expected_type.get_container_element_type_or_variant(1));
}
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");
@ -2658,6 +2755,54 @@ void GDScriptAnalyzer::update_array_literal_element_type(GDScriptParser::ArrayNo
p_array->set_datatype(array_type);
}
// When a dictionary literal is stored (or passed as function argument) to a typed context, we then assume the dictionary is typed.
// This function determines which type is that (if any).
void GDScriptAnalyzer::update_dictionary_literal_element_type(GDScriptParser::DictionaryNode *p_dictionary, const GDScriptParser::DataType &p_key_element_type, const GDScriptParser::DataType &p_value_element_type) {
GDScriptParser::DataType expected_key_type = p_key_element_type;
GDScriptParser::DataType expected_value_type = p_value_element_type;
expected_key_type.container_element_types.clear(); // Nested types (like `Dictionary[String, Array[int]]`) are not currently supported.
expected_value_type.container_element_types.clear();
for (int i = 0; i < p_dictionary->elements.size(); i++) {
GDScriptParser::ExpressionNode *key_element_node = p_dictionary->elements[i].key;
if (key_element_node->is_constant) {
update_const_expression_builtin_type(key_element_node, expected_key_type, "include");
}
const GDScriptParser::DataType &actual_key_type = key_element_node->get_datatype();
if (actual_key_type.has_no_type() || actual_key_type.is_variant() || !actual_key_type.is_hard_type()) {
mark_node_unsafe(key_element_node);
} else if (!is_type_compatible(expected_key_type, actual_key_type, true, p_dictionary)) {
if (is_type_compatible(actual_key_type, expected_key_type)) {
mark_node_unsafe(key_element_node);
} else {
push_error(vformat(R"(Cannot have a key of type "%s" in a dictionary of type "Dictionary[%s, %s]".)", actual_key_type.to_string(), expected_key_type.to_string(), expected_value_type.to_string()), key_element_node);
return;
}
}
GDScriptParser::ExpressionNode *value_element_node = p_dictionary->elements[i].value;
if (value_element_node->is_constant) {
update_const_expression_builtin_type(value_element_node, expected_value_type, "include");
}
const GDScriptParser::DataType &actual_value_type = value_element_node->get_datatype();
if (actual_value_type.has_no_type() || actual_value_type.is_variant() || !actual_value_type.is_hard_type()) {
mark_node_unsafe(value_element_node);
} else if (!is_type_compatible(expected_value_type, actual_value_type, true, p_dictionary)) {
if (is_type_compatible(actual_value_type, expected_value_type)) {
mark_node_unsafe(value_element_node);
} else {
push_error(vformat(R"(Cannot have a value of type "%s" in a dictionary of type "Dictionary[%s, %s]".)", actual_value_type.to_string(), expected_key_type.to_string(), expected_value_type.to_string()), value_element_node);
return;
}
}
}
GDScriptParser::DataType dictionary_type = p_dictionary->get_datatype();
dictionary_type.set_container_element_type(0, expected_key_type);
dictionary_type.set_container_element_type(1, expected_value_type);
p_dictionary->set_datatype(dictionary_type);
}
void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assignment) {
reduce_expression(p_assignment->assigned_value);
@ -2750,9 +2895,12 @@ 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.
// Check if assigned value is an array/dictionary literal, so we can make it a typed container too if appropriate.
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));
} else if (p_assignment->assigned_value->type == GDScriptParser::Node::DICTIONARY && assignee_type.is_hard_type() && assignee_type.has_container_element_types()) {
update_dictionary_literal_element_type(static_cast<GDScriptParser::DictionaryNode *>(p_assignment->assigned_value),
assignee_type.get_container_element_type_or_variant(0), assignee_type.get_container_element_type_or_variant(1));
}
if (p_assignment->operation == GDScriptParser::AssignmentNode::OP_NONE && assignee_type.is_hard_type() && p_assignment->assigned_value->is_constant) {
@ -2830,8 +2978,8 @@ 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(0) && !op_type.has_container_element_type(0)) {
// typed array assignee and untyped array result
} else if ((assignee_type.has_container_element_type(0) && !op_type.has_container_element_type(0)) || (assignee_type.has_container_element_type(1) && !op_type.has_container_element_type(1))) {
// Typed assignee and untyped result.
mark_node_unsafe(p_assignment);
}
}
@ -3029,10 +3177,13 @@ const char *check_for_renamed_identifier(String identifier, GDScriptParser::Node
void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_await, bool p_is_root) {
bool all_is_constant = true;
HashMap<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing.
HashMap<int, GDScriptParser::DictionaryNode *> dictionaries; // Same, but for dictionaries.
for (int i = 0; i < p_call->arguments.size(); i++) {
reduce_expression(p_call->arguments[i]);
if (p_call->arguments[i]->type == GDScriptParser::Node::ARRAY) {
arrays[i] = static_cast<GDScriptParser::ArrayNode *>(p_call->arguments[i]);
} else if (p_call->arguments[i]->type == GDScriptParser::Node::DICTIONARY) {
dictionaries[i] = static_cast<GDScriptParser::DictionaryNode *>(p_call->arguments[i]);
}
all_is_constant = all_is_constant && p_call->arguments[i]->is_constant;
}
@ -3225,6 +3376,26 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
push_error(vformat(R"(No constructor of "%s" matches the signature "%s".)", Variant::get_type_name(builtin_type), signature), p_call);
}
}
#ifdef DEBUG_ENABLED
// Consider `Signal(self, "my_signal")` as an implicit use of the signal.
if (builtin_type == Variant::SIGNAL && p_call->arguments.size() >= 2) {
const GDScriptParser::ExpressionNode *object_arg = p_call->arguments[0];
if (object_arg && object_arg->type == GDScriptParser::Node::SELF) {
const GDScriptParser::ExpressionNode *signal_arg = p_call->arguments[1];
if (signal_arg && signal_arg->is_constant) {
const StringName &signal_name = signal_arg->reduced_value;
if (parser->current_class->has_member(signal_name)) {
const GDScriptParser::ClassNode::Member &member = parser->current_class->get_member(signal_name);
if (member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
member.signal->usages++;
}
}
}
}
}
#endif
p_call->set_datatype(call_type);
return;
} else if (GDScriptUtilityFunctions::function_exists(function_name)) {
@ -3417,6 +3588,14 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
update_array_literal_element_type(E.value, par_types.get(index).get_container_element_type(0));
}
}
for (const KeyValue<int, GDScriptParser::DictionaryNode *> &E : dictionaries) {
int index = E.key;
if (index < par_types.size() && par_types.get(index).is_hard_type() && par_types.get(index).has_container_element_types()) {
GDScriptParser::DataType key = par_types.get(index).get_container_element_type_or_variant(0);
GDScriptParser::DataType value = par_types.get(index).get_container_element_type_or_variant(1);
update_dictionary_literal_element_type(E.value, key, value);
}
}
validate_call_arg(par_types, default_arg_count, method_flags.has_flag(METHOD_FLAG_VARARG), p_call);
if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type) {
@ -3459,6 +3638,20 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
parser->push_warning(p_call, GDScriptWarning::STATIC_CALLED_ON_INSTANCE, p_call->function_name, caller_type);
}
// Consider `emit_signal()`, `connect()`, and `disconnect()` as implicit uses of the signal.
if (is_self && (p_call->function_name == SNAME("emit_signal") || p_call->function_name == SNAME("connect") || p_call->function_name == SNAME("disconnect")) && !p_call->arguments.is_empty()) {
const GDScriptParser::ExpressionNode *signal_arg = p_call->arguments[0];
if (signal_arg && signal_arg->is_constant) {
const StringName &signal_name = signal_arg->reduced_value;
if (parser->current_class->has_member(signal_name)) {
const GDScriptParser::ClassNode::Member &member = parser->current_class->get_member(signal_name);
if (member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
member.signal->usages++;
}
}
}
}
#endif // DEBUG_ENABLED
call_type = return_type;
@ -3502,7 +3695,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string();
#ifdef SUGGEST_GODOT4_RENAMES
String rename_hint;
if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::RENAMED_IN_GODOT_4_HINT)).booleanize()) {
if (GLOBAL_GET("debug/gdscript/warnings/renamed_in_godot_4_hint")) {
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) + "()");
@ -3547,6 +3740,11 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_cast->operand), cast_type.get_container_element_type(0));
}
if (p_cast->operand->type == GDScriptParser::Node::DICTIONARY && cast_type.has_container_element_types()) {
update_dictionary_literal_element_type(static_cast<GDScriptParser::DictionaryNode *>(p_cast->operand),
cast_type.get_container_element_type_or_variant(0), cast_type.get_container_element_type_or_variant(1));
}
if (!cast_type.is_variant()) {
GDScriptParser::DataType op_type = p_cast->operand->get_datatype();
if (op_type.is_variant() || !op_type.is_hard_type()) {
@ -3657,6 +3855,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;
}
@ -3673,8 +3877,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)) {
@ -3755,8 +3957,9 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::find_cached_external_parser_for_class(c
Ref<GDScript> GDScriptAnalyzer::get_depended_shallow_script(const String &p_path, Error &r_error) {
// To keep a local cache of the parser for resolving external nodes later.
parser->get_depended_parser_for(p_path);
Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_path, r_error, parser->script_path);
const String path = ResourceUID::ensure_path(p_path);
parser->get_depended_parser_for(path);
Ref<GDScript> scr = GDScriptCache::get_shallow_script(path, r_error, parser->script_path);
return scr;
}
@ -3807,24 +4010,47 @@ 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()) {
if (GLOBAL_GET("debug/gdscript/warnings/renamed_in_godot_4_hint")) {
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"(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 {
@ -3860,15 +4086,15 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
if (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()) {
if (GLOBAL_GET("debug/gdscript/warnings/renamed_in_godot_4_hint")) {
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"(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
}
}
@ -4099,7 +4325,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
const GDScriptParser::EnumNode::Value &element = current_enum->values[i];
if (element.identifier->name == p_identifier->name) {
StringName enum_name = current_enum->identifier ? current_enum->identifier->name : UNNAMED_ENUM;
GDScriptParser::DataType type = make_enum_type(enum_name, parser->current_class->fqcn, false);
GDScriptParser::DataType type = make_class_enum_type(enum_name, parser->current_class, parser->script_path, false);
if (element.parent_enum->identifier) {
type.enum_type = element.parent_enum->identifier->name;
}
@ -4288,11 +4514,11 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
result.builtin_type = Variant::OBJECT;
result.native_type = SNAME("Node");
if (ResourceLoader::get_resource_type(autoload.path) == "GDScript") {
Ref<GDScriptParserRef> singl_parser = parser->get_depended_parser_for(autoload.path);
if (singl_parser.is_valid()) {
Error err = singl_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
Ref<GDScriptParserRef> single_parser = parser->get_depended_parser_for(autoload.path);
if (single_parser.is_valid()) {
Error err = single_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
if (err == OK) {
result = type_from_metatype(singl_parser->get_parser()->head->get_datatype());
result = type_from_metatype(single_parser->get_parser()->head->get_datatype());
}
}
} else if (ResourceLoader::get_resource_type(autoload.path) == "PackedScene") {
@ -4302,11 +4528,11 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
if (node != nullptr) {
Ref<GDScript> scr = node->get_script();
if (scr.is_valid()) {
Ref<GDScriptParserRef> singl_parser = parser->get_depended_parser_for(scr->get_script_path());
if (singl_parser.is_valid()) {
Error err = singl_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
Ref<GDScriptParserRef> single_parser = parser->get_depended_parser_for(scr->get_script_path());
if (single_parser.is_valid()) {
Error err = single_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
if (err == OK) {
result = type_from_metatype(singl_parser->get_parser()->head->get_datatype());
result = type_from_metatype(single_parser->get_parser()->head->get_datatype());
}
}
}
@ -4376,7 +4602,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
// Not found.
#ifdef SUGGEST_GODOT4_RENAMES
String rename_hint;
if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::RENAMED_IN_GODOT_4_HINT)).booleanize()) {
if (GLOBAL_GET("debug/gdscript/warnings/renamed_in_godot_4_hint")) {
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);
@ -4571,10 +4797,23 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
reduce_identifier_from_base(p_subscript->attribute, &base_type);
GDScriptParser::DataType attr_type = p_subscript->attribute->get_datatype();
if (attr_type.is_set()) {
valid = !attr_type.is_pseudo_type || p_can_be_pseudo_type;
result_type = attr_type;
p_subscript->is_constant = p_subscript->attribute->is_constant;
p_subscript->reduced_value = p_subscript->attribute->reduced_value;
if (base_type.builtin_type == Variant::DICTIONARY && base_type.has_container_element_types()) {
Variant::Type key_type = base_type.get_container_element_type_or_variant(0).builtin_type;
valid = key_type == Variant::NIL || key_type == Variant::STRING || key_type == Variant::STRING_NAME;
if (base_type.has_container_element_type(1)) {
result_type = base_type.get_container_element_type(1);
result_type.type_source = base_type.type_source;
} else {
result_type.builtin_type = Variant::NIL;
result_type.kind = GDScriptParser::DataType::VARIANT;
result_type.type_source = GDScriptParser::DataType::UNDETECTED;
}
} else {
valid = !attr_type.is_pseudo_type || p_can_be_pseudo_type;
result_type = attr_type;
p_subscript->is_constant = p_subscript->attribute->is_constant;
p_subscript->reduced_value = p_subscript->attribute->reduced_value;
}
} else if (!base_type.is_meta_type || !base_type.is_constant) {
valid = base_type.kind != GDScriptParser::DataType::BUILTIN;
#ifdef DEBUG_ENABLED
@ -4681,8 +4920,40 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::SIGNAL:
case Variant::STRING_NAME:
break;
// Here for completeness.
// Support depends on if the dictionary has a typed key, otherwise anything is valid.
case Variant::DICTIONARY:
if (base_type.has_container_element_type(0)) {
GDScriptParser::DataType key_type = base_type.get_container_element_type(0);
switch (index_type.builtin_type) {
// Null value will be treated as an empty object, allow.
case Variant::NIL:
error = key_type.builtin_type != Variant::OBJECT;
break;
// Objects are parsed for validity in a similar manner to container types.
case Variant::OBJECT:
if (key_type.builtin_type == Variant::OBJECT) {
error = !key_type.can_reference(index_type);
} else {
error = key_type.builtin_type != Variant::NIL;
}
break;
// String and StringName interchangeable in this context.
case Variant::STRING:
case Variant::STRING_NAME:
error = key_type.builtin_type != Variant::STRING_NAME && key_type.builtin_type != Variant::STRING;
break;
// Ints are valid indices for floats, but not the other way around.
case Variant::INT:
error = key_type.builtin_type != Variant::INT && key_type.builtin_type != Variant::FLOAT;
break;
// All other cases require the types to match exactly.
default:
error = key_type.builtin_type != index_type.builtin_type;
break;
}
}
break;
// Here for completeness.
case Variant::VARIANT_MAX:
break;
}
@ -4771,7 +5042,6 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::PROJECTION:
case Variant::PLANE:
case Variant::COLOR:
case Variant::DICTIONARY:
case Variant::OBJECT:
result_type.kind = GDScriptParser::DataType::VARIANT;
result_type.type_source = GDScriptParser::DataType::UNDETECTED;
@ -4786,6 +5056,16 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
result_type.type_source = GDScriptParser::DataType::UNDETECTED;
}
break;
// Can have two element types, but we only care about the value.
case Variant::DICTIONARY:
if (base_type.has_container_element_type(1)) {
result_type = base_type.get_container_element_type(1);
result_type.type_source = base_type.type_source;
} else {
result_type.kind = GDScriptParser::DataType::VARIANT;
result_type.type_source = GDScriptParser::DataType::UNDETECTED;
}
break;
// Here for completeness.
case Variant::VARIANT_MAX:
break;
@ -4965,7 +5245,9 @@ Variant GDScriptAnalyzer::make_array_reduced_value(GDScriptParser::ArrayNode *p_
}
Variant GDScriptAnalyzer::make_dictionary_reduced_value(GDScriptParser::DictionaryNode *p_dictionary, bool &is_reduced) {
Dictionary dictionary;
Dictionary dictionary = p_dictionary->get_datatype().has_container_element_types()
? make_dictionary_from_element_datatype(p_dictionary->get_datatype().get_container_element_type_or_variant(0), p_dictionary->get_datatype().get_container_element_type_or_variant(1))
: Dictionary();
for (int i = 0; i < p_dictionary->elements.size(); i++) {
const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
@ -5052,6 +5334,49 @@ Array GDScriptAnalyzer::make_array_from_element_datatype(const GDScriptParser::D
return array;
}
Dictionary GDScriptAnalyzer::make_dictionary_from_element_datatype(const GDScriptParser::DataType &p_key_element_datatype, const GDScriptParser::DataType &p_value_element_datatype, const GDScriptParser::Node *p_source_node) {
Dictionary dictionary;
StringName key_name;
Variant key_script;
StringName value_name;
Variant value_script;
if (p_key_element_datatype.builtin_type == Variant::OBJECT) {
Ref<Script> script_type = p_key_element_datatype.script_type;
if (p_key_element_datatype.kind == GDScriptParser::DataType::CLASS && script_type.is_null()) {
Error err = OK;
Ref<GDScript> scr = get_depended_shallow_script(p_key_element_datatype.script_path, err);
if (err) {
push_error(vformat(R"(Error while getting cache for script "%s".)", p_key_element_datatype.script_path), p_source_node);
return dictionary;
}
script_type.reference_ptr(scr->find_class(p_key_element_datatype.class_type->fqcn));
}
key_name = p_key_element_datatype.native_type;
key_script = script_type;
}
if (p_value_element_datatype.builtin_type == Variant::OBJECT) {
Ref<Script> script_type = p_value_element_datatype.script_type;
if (p_value_element_datatype.kind == GDScriptParser::DataType::CLASS && script_type.is_null()) {
Error err = OK;
Ref<GDScript> scr = get_depended_shallow_script(p_value_element_datatype.script_path, err);
if (err) {
push_error(vformat(R"(Error while getting cache for script "%s".)", p_value_element_datatype.script_path), p_source_node);
return dictionary;
}
script_type.reference_ptr(scr->find_class(p_value_element_datatype.class_type->fqcn));
}
value_name = p_value_element_datatype.native_type;
value_script = script_type;
}
dictionary.set_typed(p_key_element_datatype.builtin_type, key_name, key_script, p_value_element_datatype.builtin_type, value_name, value_script);
return dictionary;
}
Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNode *p_variable) {
Variant result = Variant();
@ -5067,6 +5392,10 @@ Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNo
if (datatype.kind == GDScriptParser::DataType::BUILTIN && datatype.builtin_type != Variant::OBJECT) {
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 if (datatype.builtin_type == Variant::DICTIONARY && datatype.has_container_element_types()) {
GDScriptParser::DataType key = datatype.get_container_element_type_or_variant(0);
GDScriptParser::DataType value = datatype.get_container_element_type_or_variant(1);
result = make_dictionary_from_element_datatype(key, value);
} else {
VariantInternal::initialize(&result, datatype.builtin_type);
}
@ -5095,6 +5424,22 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
} else if (array.get_typed_builtin() != Variant::NIL) {
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::DICTIONARY) {
const Dictionary &dict = p_value;
if (dict.get_typed_key_script()) {
result.set_container_element_type(0, type_from_metatype(make_script_meta_type(dict.get_typed_key_script())));
} else if (dict.get_typed_key_class_name()) {
result.set_container_element_type(0, type_from_metatype(make_native_meta_type(dict.get_typed_key_class_name())));
} else if (dict.get_typed_key_builtin() != Variant::NIL) {
result.set_container_element_type(0, type_from_metatype(make_builtin_meta_type((Variant::Type)dict.get_typed_key_builtin())));
}
if (dict.get_typed_value_script()) {
result.set_container_element_type(1, type_from_metatype(make_script_meta_type(dict.get_typed_value_script())));
} else if (dict.get_typed_value_class_name()) {
result.set_container_element_type(1, type_from_metatype(make_native_meta_type(dict.get_typed_value_class_name())));
} else if (dict.get_typed_value_builtin() != Variant::NIL) {
result.set_container_element_type(1, type_from_metatype(make_builtin_meta_type((Variant::Type)dict.get_typed_value_builtin())));
}
} else if (p_value.get_type() == Variant::OBJECT) {
// Object is treated as a native type, not a builtin type.
result.kind = GDScriptParser::DataType::NATIVE;
@ -5227,6 +5572,60 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
}
elem_type.is_constant = false;
result.set_container_element_type(0, elem_type);
} else if (p_property.type == Variant::DICTIONARY && p_property.hint == PROPERTY_HINT_DICTIONARY_TYPE) {
// Check element type.
StringName key_elem_type_name = p_property.hint_string.get_slice(";", 0);
GDScriptParser::DataType key_elem_type;
key_elem_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
Variant::Type key_elem_builtin_type = GDScriptParser::get_builtin_type(key_elem_type_name);
if (key_elem_builtin_type < Variant::VARIANT_MAX) {
// Builtin type.
key_elem_type.kind = GDScriptParser::DataType::BUILTIN;
key_elem_type.builtin_type = key_elem_builtin_type;
} else if (class_exists(key_elem_type_name)) {
key_elem_type.kind = GDScriptParser::DataType::NATIVE;
key_elem_type.builtin_type = Variant::OBJECT;
key_elem_type.native_type = key_elem_type_name;
} else if (ScriptServer::is_global_class(key_elem_type_name)) {
// Just load this as it shouldn't be a GDScript.
Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(key_elem_type_name));
key_elem_type.kind = GDScriptParser::DataType::SCRIPT;
key_elem_type.builtin_type = Variant::OBJECT;
key_elem_type.native_type = script->get_instance_base_type();
key_elem_type.script_type = script;
} else {
ERR_FAIL_V_MSG(result, "Could not find element type from property hint of a typed dictionary.");
}
key_elem_type.is_constant = false;
StringName value_elem_type_name = p_property.hint_string.get_slice(";", 1);
GDScriptParser::DataType value_elem_type;
value_elem_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
Variant::Type value_elem_builtin_type = GDScriptParser::get_builtin_type(value_elem_type_name);
if (value_elem_builtin_type < Variant::VARIANT_MAX) {
// Builtin type.
value_elem_type.kind = GDScriptParser::DataType::BUILTIN;
value_elem_type.builtin_type = value_elem_builtin_type;
} else if (class_exists(value_elem_type_name)) {
value_elem_type.kind = GDScriptParser::DataType::NATIVE;
value_elem_type.builtin_type = Variant::OBJECT;
value_elem_type.native_type = value_elem_type_name;
} else if (ScriptServer::is_global_class(value_elem_type_name)) {
// Just load this as it shouldn't be a GDScript.
Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(value_elem_type_name));
value_elem_type.kind = GDScriptParser::DataType::SCRIPT;
value_elem_type.builtin_type = Variant::OBJECT;
value_elem_type.native_type = script->get_instance_base_type();
value_elem_type.script_type = script;
} else {
ERR_FAIL_V_MSG(result, "Could not find element type from property hint of a typed dictionary.");
}
value_elem_type.is_constant = false;
result.set_container_element_type(0, key_elem_type);
result.set_container_element_type(1, value_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()) {
@ -5236,7 +5635,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;
}
}
@ -5473,8 +5872,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;
@ -5502,40 +5899,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.
@ -5647,6 +6060,15 @@ bool GDScriptAnalyzer::check_type_compatibility(const GDScriptParser::DataType &
valid = p_target.get_container_element_type(0) == p_source.get_container_element_type(0);
}
}
if (valid && p_target.builtin_type == Variant::DICTIONARY && p_source.builtin_type == Variant::DICTIONARY) {
// Check the element types.
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);
}
if (valid && p_target.has_container_element_type(1) && p_source.has_container_element_type(1)) {
valid = p_target.get_container_element_type(1) == p_source.get_container_element_type(1);
}
}
return valid;
}