feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue