summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript_analyzer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript_analyzer.cpp')
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp978
1 files changed, 655 insertions, 323 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 96bd8aafad..983a19470a 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -30,6 +30,9 @@
#include "gdscript_analyzer.h"
+#include "gdscript.h"
+#include "gdscript_utility_functions.h"
+
#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/core_constants.h"
@@ -39,8 +42,6 @@
#include "core/object/class_db.h"
#include "core/object/script_language.h"
#include "core/templates/hash_map.h"
-#include "gdscript.h"
-#include "gdscript_utility_functions.h"
#include "scene/resources/packed_scene.h"
#if defined(TOOLS_ENABLED) && !defined(DISABLE_DEPRECATED)
@@ -247,7 +248,7 @@ Error GDScriptAnalyzer::check_native_member_name_conflict(const StringName &p_me
return ERR_PARSE_ERROR;
}
- if (GDScriptParser::get_builtin_type(p_member_name) != Variant::VARIANT_MAX) {
+ if (GDScriptParser::get_builtin_type(p_member_name) < Variant::VARIANT_MAX) {
push_error(vformat(R"(The member "%s" cannot have the same name as a builtin type.)", p_member_name), p_member_node);
return ERR_PARSE_ERROR;
}
@@ -385,6 +386,7 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
if (!p_class->extends_used) {
result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
result.kind = GDScriptParser::DataType::NATIVE;
+ result.builtin_type = Variant::OBJECT;
result.native_type = SNAME("RefCounted");
} else {
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@@ -458,7 +460,12 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
}
base = info_parser->get_parser()->head->get_datatype();
} else if (class_exists(name)) {
+ if (Engine::get_singleton()->has_singleton(name)) {
+ push_error(vformat(R"(Cannot inherit native class "%s" because it is an engine singleton.)", name), id);
+ return ERR_PARSE_ERROR;
+ }
base.kind = GDScriptParser::DataType::NATIVE;
+ base.builtin_type = Variant::OBJECT;
base.native_type = name;
} else {
// Look for other classes in script.
@@ -612,144 +619,178 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
return result;
}
- StringName first = p_type->type_chain[0]->name;
+ const GDScriptParser::IdentifierNode *first_id = p_type->type_chain[0];
+ StringName first = first_id->name;
+ bool type_found = false;
- 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);
- if (CoreConstants::is_global_enum(qualified_name)) {
- result = make_global_enum_type(enum_name, first, true);
- return result;
+ if (first_id->suite && first_id->suite->has_local(first)) {
+ const GDScriptParser::SuiteNode::Local &local = first_id->suite->get_local(first);
+ if (local.type == GDScriptParser::SuiteNode::Local::CONSTANT) {
+ result = local.get_datatype();
+ if (!result.is_set()) {
+ // Don't try to resolve it as the constant can be declared below.
+ push_error(vformat(R"(Local constant "%s" is not resolved at this point.)", first), first_id);
+ return bad_type;
+ }
+ if (result.is_meta_type) {
+ type_found = true;
+ } else if (Ref<Script>(local.constant->initializer->reduced_value).is_valid()) {
+ Ref<GDScript> gdscript = local.constant->initializer->reduced_value;
+ if (gdscript.is_valid()) {
+ Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_script_path());
+ if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
+ push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_script_path()), first_id);
+ return bad_type;
+ }
+ result = ref->get_parser()->head->get_datatype();
+ } else {
+ result = make_script_meta_type(local.constant->initializer->reduced_value);
+ }
+ type_found = true;
} else {
- push_error(vformat(R"(Name "%s" is not a nested type of "Variant".)", enum_name), p_type->type_chain[1]);
+ push_error(vformat(R"(Local constant "%s" is not a valid type.)", first), first_id);
return bad_type;
}
- } else if (p_type->type_chain.size() > 2) {
- push_error(R"(Variant only contains enum types, which do not have nested types.)", p_type->type_chain[2]);
- return bad_type;
- }
- result.kind = GDScriptParser::DataType::VARIANT;
- } else if (first == SNAME("Object")) {
- // Object is treated like a native type, not a built-in.
- result.kind = GDScriptParser::DataType::NATIVE;
- result.builtin_type = Variant::OBJECT;
- result.native_type = SNAME("Object");
- } 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]);
+ } else {
+ push_error(vformat(R"(Local %s "%s" cannot be used as a type.)", local.get_name(), first), first_id);
return bad_type;
}
- result.kind = GDScriptParser::DataType::BUILTIN;
- result.builtin_type = GDScriptParser::get_builtin_type(first);
+ }
- if (result.builtin_type == Variant::ARRAY) {
- GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->container_type));
- if (container_type.kind != GDScriptParser::DataType::VARIANT) {
- container_type.is_constant = false;
- result.set_container_element_type(container_type);
- }
- }
- } else if (class_exists(first)) {
- // Native engine classes.
- result.kind = GDScriptParser::DataType::NATIVE;
- result.builtin_type = Variant::OBJECT;
- result.native_type = first;
- } else if (ScriptServer::is_global_class(first)) {
- if (parser->script_path == ScriptServer::get_global_class_path(first)) {
- result = parser->head->get_datatype();
- } else {
- String path = ScriptServer::get_global_class_path(first);
- String ext = path.get_extension();
- if (ext == GDScriptLanguage::get_singleton()->get_extension()) {
- Ref<GDScriptParserRef> ref = get_parser_for(path);
- if (!ref.is_valid() || 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);
+ if (!type_found) {
+ 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);
+ if (CoreConstants::is_global_enum(qualified_name)) {
+ result = make_global_enum_type(enum_name, first, true);
+ return result;
+ } else {
+ push_error(vformat(R"(Name "%s" is not a nested type of "Variant".)", enum_name), p_type->type_chain[1]);
return bad_type;
}
- result = ref->get_parser()->head->get_datatype();
- } else {
- result = make_script_meta_type(ResourceLoader::load(path, "Script"));
+ } else if (p_type->type_chain.size() > 2) {
+ push_error(R"(Variant only contains enum types, which do not have nested types.)", p_type->type_chain[2]);
+ return bad_type;
}
- }
- } else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) {
- const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(first);
- Ref<GDScriptParserRef> ref = get_parser_for(autoload.path);
- if (ref.is_null()) {
- push_error(vformat(R"(The referenced autoload "%s" (from "%s") could not be loaded.)", first, autoload.path), p_type);
- return bad_type;
- }
- if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
- push_error(vformat(R"(Could not parse singleton "%s" from "%s".)", first, autoload.path), p_type);
- return bad_type;
- }
- result = ref->get_parser()->head->get_datatype();
- } else if (ClassDB::has_enum(parser->current_class->base_type.native_type, first)) {
- // Native enum in current class.
- result = make_native_enum_type(first, parser->current_class->base_type.native_type);
- } else if (CoreConstants::is_global_enum(first)) {
- if (p_type->type_chain.size() > 1) {
- push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[1]);
- return bad_type;
- }
- result = make_global_enum_type(first, StringName());
- } else {
- // Classes in current scope.
- List<GDScriptParser::ClassNode *> script_classes;
- bool found = false;
- get_class_node_current_scope_classes(parser->current_class, &script_classes);
- for (GDScriptParser::ClassNode *script_class : script_classes) {
- if (found) {
- break;
+ 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]);
+ return bad_type;
}
+ result.kind = GDScriptParser::DataType::BUILTIN;
+ result.builtin_type = GDScriptParser::get_builtin_type(first);
- if (script_class->identifier && script_class->identifier->name == first) {
- result = script_class->get_datatype();
- break;
+ if (result.builtin_type == Variant::ARRAY) {
+ GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->container_type));
+ if (container_type.kind != GDScriptParser::DataType::VARIANT) {
+ container_type.is_constant = false;
+ result.set_container_element_type(container_type);
+ }
+ }
+ } else if (class_exists(first)) {
+ // Native engine classes.
+ result.kind = GDScriptParser::DataType::NATIVE;
+ result.builtin_type = Variant::OBJECT;
+ result.native_type = first;
+ } else if (ScriptServer::is_global_class(first)) {
+ if (parser->script_path == ScriptServer::get_global_class_path(first)) {
+ result = parser->head->get_datatype();
+ } else {
+ String path = ScriptServer::get_global_class_path(first);
+ String ext = path.get_extension();
+ if (ext == GDScriptLanguage::get_singleton()->get_extension()) {
+ Ref<GDScriptParserRef> ref = get_parser_for(path);
+ if (!ref.is_valid() || 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;
+ }
+ result = ref->get_parser()->head->get_datatype();
+ } else {
+ result = make_script_meta_type(ResourceLoader::load(path, "Script"));
+ }
}
- if (script_class->members_indices.has(first)) {
- resolve_class_member(script_class, first, p_type);
+ } else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) {
+ const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(first);
+ Ref<GDScriptParserRef> ref = get_parser_for(autoload.path);
+ if (ref.is_null()) {
+ push_error(vformat(R"(The referenced autoload "%s" (from "%s") could not be loaded.)", first, autoload.path), p_type);
+ return bad_type;
+ }
+ if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
+ push_error(vformat(R"(Could not parse singleton "%s" from "%s".)", first, autoload.path), p_type);
+ return bad_type;
+ }
+ result = ref->get_parser()->head->get_datatype();
+ } else if (ClassDB::has_enum(parser->current_class->base_type.native_type, first)) {
+ // Native enum in current class.
+ result = make_native_enum_type(first, parser->current_class->base_type.native_type);
+ } else if (CoreConstants::is_global_enum(first)) {
+ if (p_type->type_chain.size() > 1) {
+ push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[1]);
+ return bad_type;
+ }
+ result = make_global_enum_type(first, StringName());
+ } else {
+ // Classes in current scope.
+ List<GDScriptParser::ClassNode *> script_classes;
+ bool found = false;
+ get_class_node_current_scope_classes(parser->current_class, &script_classes);
+ for (GDScriptParser::ClassNode *script_class : script_classes) {
+ if (found) {
+ break;
+ }
- GDScriptParser::ClassNode::Member member = script_class->get_member(first);
- switch (member.type) {
- case GDScriptParser::ClassNode::Member::CLASS:
- result = member.get_datatype();
- found = true;
- break;
- case GDScriptParser::ClassNode::Member::ENUM:
- result = member.get_datatype();
- found = true;
- break;
- case GDScriptParser::ClassNode::Member::CONSTANT:
- if (member.get_datatype().is_meta_type) {
+ if (script_class->identifier && script_class->identifier->name == first) {
+ result = script_class->get_datatype();
+ break;
+ }
+ if (script_class->members_indices.has(first)) {
+ resolve_class_member(script_class, first, p_type);
+
+ GDScriptParser::ClassNode::Member member = script_class->get_member(first);
+ switch (member.type) {
+ case GDScriptParser::ClassNode::Member::CLASS:
result = member.get_datatype();
found = true;
break;
- } else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) {
- Ref<GDScript> gdscript = member.constant->initializer->reduced_value;
- if (gdscript.is_valid()) {
- Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_script_path());
- if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
- push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_script_path()), p_type);
- return bad_type;
- }
- result = ref->get_parser()->head->get_datatype();
- } else {
- result = make_script_meta_type(member.constant->initializer->reduced_value);
- }
+ case GDScriptParser::ClassNode::Member::ENUM:
+ result = member.get_datatype();
found = true;
break;
- }
- [[fallthrough]];
- default:
- push_error(vformat(R"("%s" is a %s but does not contain a type.)", first, member.get_type_name()), p_type);
- return bad_type;
+ case GDScriptParser::ClassNode::Member::CONSTANT:
+ if (member.get_datatype().is_meta_type) {
+ result = member.get_datatype();
+ found = true;
+ break;
+ } else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) {
+ Ref<GDScript> gdscript = member.constant->initializer->reduced_value;
+ if (gdscript.is_valid()) {
+ Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_script_path());
+ if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
+ push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_script_path()), p_type);
+ return bad_type;
+ }
+ result = ref->get_parser()->head->get_datatype();
+ } else {
+ result = make_script_meta_type(member.constant->initializer->reduced_value);
+ }
+ found = true;
+ break;
+ }
+ [[fallthrough]];
+ default:
+ push_error(vformat(R"("%s" is a %s but does not contain a type.)", first, member.get_type_name()), p_type);
+ return bad_type;
+ }
}
}
}
}
+
if (!result.is_set()) {
push_error(vformat(R"(Could not find type "%s" in the current scope.)", first), p_type);
return bad_type;
@@ -869,7 +910,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
for (GDScriptParser::AnnotationNode *&E : member_node->annotations) {
if (E->name == SNAME("@warning_ignore")) {
resolve_annotation(E);
- E->apply(parser, member.variable);
+ E->apply(parser, member.variable, p_class);
}
}
for (GDScriptWarning::Code ignored_warning : member_node->ignored_warnings) {
@@ -881,18 +922,23 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
case GDScriptParser::ClassNode::Member::VARIABLE: {
bool previous_static_context = static_context;
static_context = member.variable->is_static;
+
check_class_member_name_conflict(p_class, member.variable->identifier->name, member.variable);
+
member.variable->set_datatype(resolving_datatype);
resolve_variable(member.variable, false);
+ resolve_pending_lambda_bodies();
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.variable->annotations) {
if (E->name != SNAME("@warning_ignore")) {
resolve_annotation(E);
- E->apply(parser, member.variable);
+ E->apply(parser, member.variable, p_class);
}
}
+
static_context = previous_static_context;
+
#ifdef DEBUG_ENABLED
if (member.variable->exported && member.variable->onready) {
parser->push_warning(member.variable, GDScriptWarning::ONREADY_WITH_EXPORT);
@@ -939,7 +985,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.constant->annotations) {
resolve_annotation(E);
- E->apply(parser, member.constant);
+ E->apply(parser, member.constant, p_class);
}
} break;
case GDScriptParser::ClassNode::Member::SIGNAL: {
@@ -955,15 +1001,21 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
GDScriptParser::ParameterNode *param = member.signal->parameters[j];
GDScriptParser::DataType param_type = type_from_metatype(resolve_datatype(param->datatype_specifier));
param->set_datatype(param_type);
- mi.arguments.push_back(PropertyInfo(param_type.builtin_type, param->identifier->name));
- // TODO: add signal parameter default values
+#ifdef DEBUG_ENABLED
+ if (param->datatype_specifier == nullptr) {
+ parser->push_warning(param, GDScriptWarning::UNTYPED_DECLARATION, "Parameter", param->identifier->name);
+ }
+#endif
+ mi.arguments.push_back(param_type.to_property_info(param->identifier->name));
+ // Signals do not support parameter default values.
}
member.signal->set_datatype(make_signal_type(mi));
+ member.signal->method_info = mi;
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.signal->annotations) {
resolve_annotation(E);
- E->apply(parser, member.signal);
+ E->apply(parser, member.signal, p_class);
}
} break;
case GDScriptParser::ClassNode::Member::ENUM: {
@@ -1011,13 +1063,13 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.m_enum->annotations) {
resolve_annotation(E);
- E->apply(parser, member.m_enum);
+ E->apply(parser, member.m_enum, p_class);
}
} break;
case GDScriptParser::ClassNode::Member::FUNCTION:
for (GDScriptParser::AnnotationNode *&E : member.function->annotations) {
resolve_annotation(E);
- E->apply(parser, member.function);
+ E->apply(parser, member.function, p_class);
}
resolve_function_signature(member.function, p_source);
break;
@@ -1227,23 +1279,21 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.function->annotations) {
resolve_annotation(E);
- E->apply(parser, member.function);
+ E->apply(parser, member.function, p_class);
}
resolve_function_body(member.function);
} else if (member.type == GDScriptParser::ClassNode::Member::VARIABLE && member.variable->property != GDScriptParser::VariableNode::PROP_NONE) {
if (member.variable->property == GDScriptParser::VariableNode::PROP_INLINE) {
if (member.variable->getter != nullptr) {
- member.variable->getter->set_datatype(member.variable->datatype);
+ member.variable->getter->return_type = member.variable->datatype_specifier;
+ member.variable->getter->set_datatype(member.get_datatype());
resolve_function_body(member.variable->getter);
}
if (member.variable->setter != nullptr) {
- resolve_function_signature(member.variable->setter);
-
- if (member.variable->setter->parameters.size() > 0) {
- member.variable->setter->parameters[0]->datatype_specifier = member.variable->datatype_specifier;
- member.variable->setter->parameters[0]->set_datatype(member.get_datatype());
- }
+ ERR_CONTINUE(member.variable->setter->parameters.is_empty());
+ member.variable->setter->parameters[0]->datatype_specifier = member.variable->datatype_specifier;
+ member.variable->setter->parameters[0]->set_datatype(member.get_datatype());
resolve_function_body(member.variable->setter);
}
@@ -1251,7 +1301,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co
} else if (member.type == GDScriptParser::ClassNode::Member::GROUP) {
// Apply annotation (`@export_{category,group,subgroup}`).
resolve_annotation(member.annotation);
- member.annotation->apply(parser, nullptr);
+ member.annotation->apply(parser, nullptr, p_class);
}
}
@@ -1344,6 +1394,11 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co
}
}
+ if (!pending_body_resolution_lambdas.is_empty()) {
+ ERR_PRINT("GDScript bug (please report): Not all pending lambda bodies were resolved in time.");
+ resolve_pending_lambda_bodies();
+ }
+
parser->current_class = previous_class;
}
@@ -1361,7 +1416,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, bo
}
void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root) {
- ERR_FAIL_COND_MSG(p_node == nullptr, "Trying to resolve type of a null node.");
+ ERR_FAIL_NULL_MSG(p_node, "Trying to resolve type of a null node.");
switch (p_node->type) {
case GDScriptParser::Node::NONE:
@@ -1530,7 +1585,13 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
GDScriptParser::FunctionNode *previous_function = parser->current_function;
parser->current_function = p_function;
bool previous_static_context = static_context;
- static_context = p_function->is_static;
+ if (p_is_lambda) {
+ // For lambdas this is determined from the context, the `static` keyword is not allowed.
+ p_function->is_static = static_context;
+ } else {
+ // For normal functions, this is determined in the parser by the `static` keyword.
+ static_context = p_function->is_static;
+ }
GDScriptParser::DataType prev_datatype = p_function->get_datatype();
@@ -1542,21 +1603,26 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
int default_value_count = 0;
#endif // TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
+ String function_visible_name = function_name;
+ if (function_name == StringName()) {
+ function_visible_name = p_is_lambda ? "<anonymous lambda>" : "<unknown function>";
+ }
+#endif
+
for (int i = 0; i < p_function->parameters.size(); i++) {
resolve_parameter(p_function->parameters[i]);
#ifdef DEBUG_ENABLED
if (p_function->parameters[i]->usages == 0 && !String(p_function->parameters[i]->identifier->name).begins_with("_")) {
- String visible_name = function_name;
- if (function_name == StringName()) {
- visible_name = p_is_lambda ? "<anonymous lambda>" : "<unknown function>";
- }
- parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, visible_name, p_function->parameters[i]->identifier->name);
+ parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, function_visible_name, p_function->parameters[i]->identifier->name);
}
- is_shadowing(p_function->parameters[i]->identifier, "function parameter");
+ is_shadowing(p_function->parameters[i]->identifier, "function parameter", true);
#endif // DEBUG_ENABLED
-#ifdef TOOLS_ENABLED
+
if (p_function->parameters[i]->initializer) {
+#ifdef TOOLS_ENABLED
default_value_count++;
+#endif // TOOLS_ENABLED
if (p_function->parameters[i]->initializer->is_constant) {
p_function->default_arg_values.push_back(p_function->parameters[i]->initializer->reduced_value);
@@ -1564,7 +1630,6 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
p_function->default_arg_values.push_back(Variant()); // Prevent shift.
}
}
-#endif // TOOLS_ENABLED
}
if (!p_is_lambda && function_name == GDScriptLanguage::get_singleton()->strings._init) {
@@ -1610,20 +1675,46 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
GDScriptParser::DataType parent_return_type;
List<GDScriptParser::DataType> parameters_types;
int default_par_count = 0;
- bool is_static = false;
- bool is_vararg = false;
+ BitField<MethodFlags> method_flags;
StringName native_base;
- if (!p_is_lambda && get_function_signature(p_function, false, base_type, function_name, parent_return_type, parameters_types, default_par_count, is_static, is_vararg, &native_base)) {
- bool valid = p_function->is_static == is_static;
- valid = valid && parent_return_type == p_function->get_datatype();
+ if (!p_is_lambda && get_function_signature(p_function, false, base_type, function_name, parent_return_type, parameters_types, default_par_count, method_flags, &native_base)) {
+ bool valid = p_function->is_static == method_flags.has_flag(METHOD_FLAG_STATIC);
+
+ if (p_function->return_type != nullptr) {
+ // Check return type covariance.
+ GDScriptParser::DataType return_type = p_function->get_datatype();
+ if (return_type.is_variant()) {
+ // `is_type_compatible()` returns `true` if one of the types is `Variant`.
+ // Don't allow an explicitly specified `Variant` if the parent return type is narrower.
+ valid = valid && parent_return_type.is_variant();
+ } else if (return_type.kind == GDScriptParser::DataType::BUILTIN && return_type.builtin_type == Variant::NIL) {
+ // `is_type_compatible()` returns `true` if target is an `Object` and source is `null`.
+ // Don't allow `void` if the parent return type is a hard non-`void` type.
+ if (parent_return_type.is_hard_type() && !(parent_return_type.kind == GDScriptParser::DataType::BUILTIN && parent_return_type.builtin_type == Variant::NIL)) {
+ valid = false;
+ }
+ } else {
+ valid = valid && is_type_compatible(parent_return_type, return_type);
+ }
+ }
int par_count_diff = p_function->parameters.size() - parameters_types.size();
valid = valid && par_count_diff >= 0;
valid = valid && default_value_count >= default_par_count + par_count_diff;
- int i = 0;
- for (const GDScriptParser::DataType &par_type : parameters_types) {
- valid = valid && par_type == p_function->parameters[i++]->get_datatype();
+ if (valid) {
+ int i = 0;
+ for (const GDScriptParser::DataType &parent_par_type : parameters_types) {
+ // Check parameter type contravariance.
+ GDScriptParser::DataType current_par_type = p_function->parameters[i++]->get_datatype();
+ if (parent_par_type.is_variant() && parent_par_type.is_hard_type()) {
+ // `is_type_compatible()` returns `true` if one of the types is `Variant`.
+ // Don't allow narrowing a hard `Variant`.
+ valid = valid && current_par_type.is_variant();
+ } else {
+ valid = valid && is_type_compatible(current_par_type, parent_par_type);
+ }
+ }
}
if (!valid) {
@@ -1647,7 +1738,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
}
parent_signature += ") -> ";
- const String return_type = parent_return_type.is_hard_type() ? parent_return_type.to_string() : "Variant";
+ const String return_type = parent_return_type.to_string_strict();
if (return_type == "null") {
parent_signature += "void";
} else {
@@ -1665,6 +1756,12 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
#endif // TOOLS_ENABLED
}
+#ifdef DEBUG_ENABLED
+ if (p_function->return_type == nullptr) {
+ parser->push_warning(p_function, GDScriptWarning::UNTYPED_DECLARATION, "Function", function_visible_name);
+ }
+#endif
+
if (p_function->get_datatype().is_resolving()) {
p_function->set_datatype(prev_datatype);
}
@@ -1692,6 +1789,9 @@ void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_fun
GDScriptParser::FunctionNode *previous_function = parser->current_function;
parser->current_function = p_function;
+ bool previous_static_context = static_context;
+ static_context = p_function->is_static;
+
resolve_suite(p_function->body);
if (!p_function->get_datatype().is_hard_type() && p_function->body->get_datatype().is_set()) {
@@ -1707,6 +1807,7 @@ void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_fun
parser->ignored_warnings = previously_ignored_warnings;
#endif
parser->current_function = previous_function;
+ static_context = previous_static_context;
}
void GDScriptAnalyzer::decide_suite_type(GDScriptParser::Node *p_suite, GDScriptParser::Node *p_statement) {
@@ -1742,7 +1843,7 @@ void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : stmt->annotations) {
resolve_annotation(E);
- E->apply(parser, stmt);
+ E->apply(parser, stmt, nullptr);
}
#ifdef DEBUG_ENABLED
@@ -1753,6 +1854,7 @@ void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
#endif // DEBUG_ENABLED
resolve_node(stmt);
+ resolve_pending_lambda_bodies();
#ifdef DEBUG_ENABLED
parser->ignored_warnings = previously_ignored_warnings;
@@ -1768,6 +1870,15 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
bool is_constant = p_assignable->type == GDScriptParser::Node::CONSTANT;
+#ifdef DEBUG_ENABLED
+ if (p_assignable->identifier != nullptr && p_assignable->identifier->suite != nullptr && p_assignable->identifier->suite->parent_block != nullptr) {
+ if (p_assignable->identifier->suite->parent_block->has_local(p_assignable->identifier->name)) {
+ const GDScriptParser::SuiteNode::Local &local = p_assignable->identifier->suite->parent_block->get_local(p_assignable->identifier->name);
+ parser->push_warning(p_assignable->identifier, GDScriptWarning::CONFUSABLE_LOCAL_DECLARATION, local.get_name(), p_assignable->identifier->name);
+ }
+ }
+#endif
+
GDScriptParser::DataType specified_type;
bool has_specified_type = p_assignable->datatype_specifier != nullptr;
if (has_specified_type) {
@@ -1854,6 +1965,18 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
}
}
+#ifdef DEBUG_ENABLED
+ if (!has_specified_type) {
+ const bool is_parameter = p_assignable->type == GDScriptParser::Node::PARAMETER;
+ const String declaration_type = is_constant ? "Constant" : (is_parameter ? "Parameter" : "Variable");
+ if (p_assignable->infer_datatype || is_constant) {
+ parser->push_warning(p_assignable, GDScriptWarning::INFERRED_DECLARATION, declaration_type, p_assignable->identifier->name);
+ } else {
+ parser->push_warning(p_assignable, GDScriptWarning::UNTYPED_DECLARATION, declaration_type, p_assignable->identifier->name);
+ }
+ }
+#endif
+
type.is_constant = is_constant;
type.is_read_only = false;
p_assignable->set_datatype(type);
@@ -1870,9 +1993,8 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
} else if (p_variable->assignments == 0) {
parser->push_warning(p_variable, GDScriptWarning::UNASSIGNED_VARIABLE, p_variable->identifier->name);
}
-
- is_shadowing(p_variable->identifier, kind);
}
+ is_shadowing(p_variable->identifier, kind, p_is_local);
#endif
}
@@ -1885,9 +2007,8 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant
if (p_constant->usages == 0) {
parser->push_warning(p_constant, GDScriptWarning::UNUSED_LOCAL_CONSTANT, p_constant->identifier->name);
}
-
- is_shadowing(p_constant->identifier, kind);
}
+ is_shadowing(p_constant->identifier, kind, p_is_local);
#endif
}
@@ -1990,13 +2111,16 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
}
GDScriptParser::DataType variable_type;
+ String list_visible_type = "<unresolved type>";
if (list_resolved) {
variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
variable_type.kind = GDScriptParser::DataType::BUILTIN;
variable_type.builtin_type = Variant::INT;
+ list_visible_type = "Array[int]"; // NOTE: `range()` has `Array` return type.
} else if (p_for->list) {
resolve_node(p_for->list, false);
GDScriptParser::DataType list_type = p_for->list->get_datatype();
+ list_visible_type = list_type.to_string();
if (!list_type.is_hard_type()) {
mark_node_unsafe(p_for->list);
}
@@ -2025,9 +2149,8 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
GDScriptParser::DataType return_type;
List<GDScriptParser::DataType> par_types;
int default_arg_count = 0;
- bool is_static = false;
- bool is_vararg = false;
- if (get_function_signature(p_for->list, false, list_type, CoreStringNames::get_singleton()->_iter_get, return_type, par_types, default_arg_count, is_static, is_vararg)) {
+ BitField<MethodFlags> method_flags;
+ if (get_function_signature(p_for->list, false, list_type, CoreStringNames::get_singleton()->_iter_get, return_type, par_types, default_arg_count, method_flags)) {
variable_type = return_type;
variable_type.type_source = list_type.type_source;
} else if (!list_type.is_hard_type()) {
@@ -2041,15 +2164,46 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
push_error(vformat(R"(Unable to iterate on value of type "%s".)", list_type.to_string()), p_for->list);
}
}
+
if (p_for->variable) {
- p_for->variable->set_datatype(variable_type);
+ if (p_for->datatype_specifier) {
+ GDScriptParser::DataType specified_type = type_from_metatype(resolve_datatype(p_for->datatype_specifier));
+ if (!specified_type.is_variant()) {
+ if (variable_type.is_variant() || !variable_type.is_hard_type()) {
+ mark_node_unsafe(p_for->variable);
+ p_for->use_conversion_assign = true;
+ } else if (!is_type_compatible(specified_type, variable_type, true, p_for->variable)) {
+ if (is_type_compatible(variable_type, specified_type)) {
+ mark_node_unsafe(p_for->variable);
+ p_for->use_conversion_assign = true;
+ } else {
+ push_error(vformat(R"(Unable to iterate on value of type "%s" with variable of type "%s".)", list_visible_type, specified_type.to_string()), p_for->datatype_specifier);
+ }
+ } 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);
+ }
+ }
+ p_for->variable->set_datatype(specified_type);
+ } else {
+ p_for->variable->set_datatype(variable_type);
+#ifdef DEBUG_ENABLED
+ if (variable_type.is_hard_type()) {
+ parser->push_warning(p_for->variable, GDScriptWarning::INFERRED_DECLARATION, R"("for" iterator variable)", p_for->variable->name);
+ } else {
+ parser->push_warning(p_for->variable, GDScriptWarning::UNTYPED_DECLARATION, R"("for" iterator variable)", p_for->variable->name);
+ }
+#endif
+ }
}
resolve_suite(p_for->loop);
p_for->set_datatype(p_for->loop->get_datatype());
#ifdef DEBUG_ENABLED
if (p_for->variable) {
- is_shadowing(p_for->variable, R"("for" iterator variable)");
+ is_shadowing(p_for->variable, R"("for" iterator variable)", true);
}
#endif
}
@@ -2098,6 +2252,10 @@ void GDScriptAnalyzer::resolve_match_branch(GDScriptParser::MatchBranchNode *p_m
resolve_match_pattern(p_match_branch->patterns[i], p_match_test);
}
+ if (p_match_branch->guard_body) {
+ resolve_suite(p_match_branch->guard_body);
+ }
+
resolve_suite(p_match_branch->block);
decide_suite_type(p_match_branch, p_match_branch->block);
@@ -2145,7 +2303,7 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
}
p_match_pattern->bind->set_datatype(result);
#ifdef DEBUG_ENABLED
- is_shadowing(p_match_pattern->bind, "pattern bind");
+ is_shadowing(p_match_pattern->bind, "pattern bind", true);
if (p_match_pattern->bind->usages == 0 && !String(p_match_pattern->bind->name).begins_with("_")) {
parser->push_warning(p_match_pattern->bind, GDScriptWarning::UNUSED_VARIABLE, p_match_pattern->bind->name);
}
@@ -2348,6 +2506,14 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre
case GDScriptParser::Node::WHILE:
ERR_FAIL_MSG("Reaching unreachable case");
}
+
+ if (p_expression->get_datatype().kind == GDScriptParser::DataType::UNRESOLVED) {
+ // Prevent `is_type_compatible()` errors for incomplete expressions.
+ // The error can still occur if `reduce_*()` is called directly.
+ GDScriptParser::DataType dummy;
+ dummy.kind = GDScriptParser::DataType::VARIANT;
+ p_expression->set_datatype(dummy);
+ }
}
void GDScriptAnalyzer::reduce_array(GDScriptParser::ArrayNode *p_array) {
@@ -2431,24 +2597,31 @@ void GDScriptAnalyzer::update_const_expression_builtin_type(GDScriptParser::Expr
// When an array literal is stored (or passed as function argument) to a typed context, we then assume the array is typed.
// This function determines which type is that (if any).
void GDScriptAnalyzer::update_array_literal_element_type(GDScriptParser::ArrayNode *p_array, const GDScriptParser::DataType &p_element_type) {
+ GDScriptParser::DataType expected_type = p_element_type;
+ expected_type.unset_container_element_type(); // Nested types (like `Array[Array[int]]`) are not currently supported.
+
for (int i = 0; i < p_array->elements.size(); i++) {
GDScriptParser::ExpressionNode *element_node = p_array->elements[i];
if (element_node->is_constant) {
- update_const_expression_builtin_type(element_node, p_element_type, "include");
+ update_const_expression_builtin_type(element_node, expected_type, "include");
}
- const GDScriptParser::DataType &element_type = element_node->get_datatype();
- if (element_type.has_no_type() || element_type.is_variant() || !element_type.is_hard_type()) {
+ const GDScriptParser::DataType &actual_type = element_node->get_datatype();
+ if (actual_type.has_no_type() || actual_type.is_variant() || !actual_type.is_hard_type()) {
mark_node_unsafe(element_node);
continue;
}
- if (!is_type_compatible(p_element_type, element_type, true, p_array)) {
- push_error(vformat(R"(Cannot have an element of type "%s" in an array of type "Array[%s]".)", element_type.to_string(), p_element_type.to_string()), element_node);
+ if (!is_type_compatible(expected_type, actual_type, true, p_array)) {
+ if (is_type_compatible(actual_type, expected_type)) {
+ mark_node_unsafe(element_node);
+ continue;
+ }
+ push_error(vformat(R"(Cannot have an element of type "%s" in an array of type "Array[%s]".)", actual_type.to_string(), expected_type.to_string()), element_node);
return;
}
}
GDScriptParser::DataType array_type = p_array->get_datatype();
- array_type.set_container_element_type(p_element_type);
+ array_type.set_container_element_type(expected_type);
p_array->set_datatype(array_type);
}
@@ -2462,9 +2635,15 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
GDScriptParser::DataType assignee_type = p_assignment->assignee->get_datatype();
- if (assignee_type.is_constant || (p_assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT && static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee)->base->is_constant)) {
+ if (assignee_type.is_constant) {
push_error("Cannot assign a new value to a constant.", p_assignment->assignee);
return;
+ } else if (p_assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT && static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee)->base->is_constant) {
+ const GDScriptParser::DataType &base_type = static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee)->base->datatype;
+ if (base_type.kind != GDScriptParser::DataType::SCRIPT && base_type.kind != GDScriptParser::DataType::CLASS) { // Static variables.
+ push_error("Cannot assign a new value to a constant.", p_assignment->assignee);
+ return;
+ }
} else if (assignee_type.is_read_only) {
push_error("Cannot assign a new value to a read-only property.", p_assignment->assignee);
return;
@@ -2489,7 +2668,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
}
// Check if assigned value is an array literal, so we can make it a typed array too if appropriate.
- if (p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY && assignee_type.has_container_element_type()) {
+ if (p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY && assignee_type.is_hard_type() && assignee_type.has_container_element_type()) {
update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value), assignee_type.get_container_element_type());
}
@@ -2768,19 +2947,20 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
if (!p_call->is_super && callee_type == GDScriptParser::Node::IDENTIFIER) {
// Call to name directly.
StringName function_name = p_call->function_name;
- Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name);
+ if (function_name == SNAME("Object")) {
+ push_error(R"*(Invalid constructor "Object()", use "Object.new()" instead.)*", p_call);
+ p_call->set_datatype(call_type);
+ return;
+ }
+
+ Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name);
if (builtin_type < Variant::VARIANT_MAX) {
// Is a builtin constructor.
call_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
call_type.kind = GDScriptParser::DataType::BUILTIN;
call_type.builtin_type = builtin_type;
- if (builtin_type == Variant::OBJECT) {
- call_type.kind = GDScriptParser::DataType::NATIVE;
- call_type.native_type = function_name; // "Object".
- }
-
bool safe_to_fold = true;
switch (builtin_type) {
// Those are stored by reference so not suited for compile-time construction.
@@ -2816,7 +2996,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
switch (err.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
- push_error(vformat(R"(Invalid argument for %s constructor: argument %d should be "%s" but is "%s".)", Variant::get_type_name(builtin_type), err.argument + 1,
+ push_error(vformat(R"*(Invalid argument for "%s()" constructor: argument %d should be "%s" but is "%s".)*", Variant::get_type_name(builtin_type), err.argument + 1,
Variant::get_type_name(Variant::Type(err.expected)), p_call->arguments[err.argument]->get_datatype().to_string()),
p_call->arguments[err.argument]);
break;
@@ -2832,10 +3012,10 @@ 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->callee);
} break;
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
- push_error(vformat(R"(Too many arguments for %s constructor. Received %d but expected %d.)", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
+ push_error(vformat(R"*(Too many arguments for "%s()" constructor. Received %d but expected %d.)*", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
break;
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
- push_error(vformat(R"(Too few arguments for %s constructor. Received %d but expected %d.)", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
+ push_error(vformat(R"*(Too few arguments for "%s()" constructor. Received %d but expected %d.)*", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
break;
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
@@ -2846,21 +3026,41 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
break;
}
} else {
- // TODO: Check constructors without constants.
-
// If there's one argument, try to use copy constructor (those aren't explicitly defined).
if (p_call->arguments.size() == 1) {
GDScriptParser::DataType arg_type = p_call->arguments[0]->get_datatype();
- if (arg_type.is_variant()) {
- mark_node_unsafe(p_call->arguments[0]);
- } else {
+ if (arg_type.is_hard_type() && !arg_type.is_variant()) {
if (arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == builtin_type) {
// Okay.
p_call->set_datatype(call_type);
return;
}
+ } else {
+#ifdef DEBUG_ENABLED
+ mark_node_unsafe(p_call);
+ // Constructors support overloads.
+ Vector<String> types;
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+ if (i != builtin_type && Variant::can_convert_strict((Variant::Type)i, builtin_type)) {
+ types.push_back(Variant::get_type_name((Variant::Type)i));
+ }
+ }
+ String expected_types = function_name;
+ if (types.size() == 1) {
+ expected_types += "\" or \"" + types[0];
+ } else if (types.size() >= 2) {
+ for (int i = 0; i < types.size() - 1; i++) {
+ expected_types += "\", \"" + types[i];
+ }
+ expected_types += "\", or \"" + types[types.size() - 1];
+ }
+ parser->push_warning(p_call->arguments[0], GDScriptWarning::UNSAFE_CALL_ARGUMENT, "1", "constructor", function_name, expected_types, "Variant");
+#endif
+ p_call->set_datatype(call_type);
+ return;
}
}
+
List<MethodInfo> constructors;
Variant::get_constructor_list(builtin_type, &constructors);
bool match = false;
@@ -2877,14 +3077,14 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
for (int i = 0; i < p_call->arguments.size(); i++) {
GDScriptParser::DataType par_type = type_from_property(info.arguments[i], true);
-
- if (!is_type_compatible(par_type, p_call->arguments[i]->get_datatype(), true)) {
+ GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype();
+ if (!is_type_compatible(par_type, arg_type, true)) {
types_match = false;
break;
#ifdef DEBUG_ENABLED
} else {
- if (par_type.builtin_type == Variant::INT && p_call->arguments[i]->get_datatype().builtin_type == Variant::FLOAT && builtin_type != Variant::INT) {
- parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name);
+ if (par_type.builtin_type == Variant::INT && arg_type.builtin_type == Variant::FLOAT && builtin_type != Variant::INT) {
+ parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, function_name);
}
#endif
}
@@ -2892,9 +3092,19 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
if (types_match) {
for (int i = 0; i < p_call->arguments.size(); i++) {
+ GDScriptParser::DataType par_type = type_from_property(info.arguments[i], true);
if (p_call->arguments[i]->is_constant) {
- update_const_expression_builtin_type(p_call->arguments[i], type_from_property(info.arguments[i], true), "pass");
+ update_const_expression_builtin_type(p_call->arguments[i], par_type, "pass");
}
+#ifdef DEBUG_ENABLED
+ if (!(par_type.is_variant() && par_type.is_hard_type())) {
+ GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype();
+ if (arg_type.is_variant() || !arg_type.is_hard_type()) {
+ mark_node_unsafe(p_call);
+ parser->push_warning(p_call->arguments[i], GDScriptWarning::UNSAFE_CALL_ARGUMENT, itos(i + 1), "constructor", function_name, par_type.to_string(), arg_type.to_string_strict());
+ }
+ }
+#endif
}
match = true;
call_type = type_from_property(info.return_val);
@@ -2935,12 +3145,16 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
GDScriptUtilityFunctions::get_function(function_name)(&value, (const Variant **)args.ptr(), args.size(), err);
switch (err.error) {
- case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
- PropertyInfo wrong_arg = function_info.arguments[err.argument];
- push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*", function_name, err.argument + 1,
- type_from_property(wrong_arg, true).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
- p_call->arguments[err.argument]);
- } break;
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
+ if (value.get_type() == Variant::STRING && !value.operator String().is_empty()) {
+ push_error(vformat(R"*(Invalid argument for "%s()" function: %s)*", function_name, value), p_call->arguments[err.argument]);
+ } else {
+ // Do not use `type_from_property()` for expected type, since utility functions use their own checks.
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*", function_name, err.argument + 1,
+ Variant::get_type_name((Variant::Type)err.expected), p_call->arguments[err.argument]->get_datatype().to_string()),
+ p_call->arguments[err.argument]);
+ }
+ break;
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call);
break;
@@ -2982,18 +3196,16 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
Variant::call_utility_function(function_name, &value, (const Variant **)args.ptr(), args.size(), err);
switch (err.error) {
- case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
- String expected_type_name;
- if (err.argument < function_info.arguments.size()) {
- expected_type_name = type_from_property(function_info.arguments[err.argument], true).to_string();
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
+ if (value.get_type() == Variant::STRING && !value.operator String().is_empty()) {
+ push_error(vformat(R"*(Invalid argument for "%s()" function: %s)*", function_name, value), p_call->arguments[err.argument]);
} else {
- expected_type_name = Variant::get_type_name((Variant::Type)err.expected);
+ // Do not use `type_from_property()` for expected type, since utility functions use their own checks.
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*", function_name, err.argument + 1,
+ Variant::get_type_name((Variant::Type)err.expected), p_call->arguments[err.argument]->get_datatype().to_string()),
+ p_call->arguments[err.argument]);
}
-
- push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*", function_name, err.argument + 1,
- expected_type_name, p_call->arguments[err.argument]->get_datatype().to_string()),
- p_call->arguments[err.argument]);
- } break;
+ break;
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call);
break;
@@ -3028,7 +3240,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
base_type.is_meta_type = false;
is_self = true;
- if (p_call->callee == nullptr && !lambda_stack.is_empty()) {
+ if (p_call->callee == nullptr && current_lambda != nullptr) {
push_error("Cannot use `super()` inside a lambda.", p_call);
}
} else if (callee_type == GDScriptParser::Node::IDENTIFIER) {
@@ -3076,44 +3288,56 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
return;
}
- bool is_static = false;
- bool is_vararg = false;
int default_arg_count = 0;
+ BitField<MethodFlags> method_flags;
GDScriptParser::DataType return_type;
List<GDScriptParser::DataType> par_types;
bool is_constructor = (base_type.is_meta_type || (p_call->callee && p_call->callee->type == GDScriptParser::Node::IDENTIFIER)) && p_call->function_name == SNAME("new");
- if (get_function_signature(p_call, is_constructor, base_type, p_call->function_name, return_type, par_types, default_arg_count, is_static, is_vararg)) {
- // If the function require typed arrays we must make literals be typed.
+ if (is_constructor && Engine::get_singleton()->has_singleton(base_type.native_type)) {
+ push_error(vformat(R"(Cannot construct native class "%s" because it is an engine singleton.)", base_type.native_type), p_call);
+ p_call->set_datatype(call_type);
+ return;
+ }
+
+ if (get_function_signature(p_call, is_constructor, base_type, p_call->function_name, return_type, par_types, default_arg_count, method_flags)) {
+ // If the method is implemented in the class hierarchy, the virtual flag will not be set for that MethodInfo and the search stops there.
+ // Virtual check only possible for super() calls because class hierarchy is known. Node/Objects may have scripts attached we don't know of at compile-time.
+ if (p_call->is_super && method_flags.has_flag(METHOD_FLAG_VIRTUAL)) {
+ push_error(vformat(R"*(Cannot call the parent class' virtual function "%s()" because it hasn't been defined.)*", p_call->function_name), p_call);
+ }
+
+ // If the function requires typed arrays we must make literals be typed.
for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) {
int index = E.key;
- if (index < par_types.size() && par_types[index].has_container_element_type()) {
+ if (index < par_types.size() && par_types[index].is_hard_type() && par_types[index].has_container_element_type()) {
update_array_literal_element_type(E.value, par_types[index].get_container_element_type());
}
}
- validate_call_arg(par_types, default_arg_count, is_vararg, p_call);
+ 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) {
// Enum type is treated as a dictionary value for function calls.
base_type.is_meta_type = false;
}
- if (is_self && static_context && !is_static) {
- if (parser->current_function) {
- // Get the parent function above any lambda.
- GDScriptParser::FunctionNode *parent_function = parser->current_function;
- while (parent_function->source_lambda) {
- parent_function = parent_function->source_lambda->parent_function;
- }
+ if (is_self && static_context && !method_flags.has_flag(METHOD_FLAG_STATIC)) {
+ // Get the parent function above any lambda.
+ GDScriptParser::FunctionNode *parent_function = parser->current_function;
+ while (parent_function && parent_function->source_lambda) {
+ parent_function = parent_function->source_lambda->parent_function;
+ }
+
+ if (parent_function) {
push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call);
} else {
- push_error(vformat(R"*(Cannot call non-static function "%s()" for static variable initializer.)*", p_call->function_name), p_call);
+ push_error(vformat(R"*(Cannot call non-static function "%s()" from a static variable initializer.)*", p_call->function_name), p_call);
}
- } else if (!is_self && base_type.is_meta_type && !is_static) {
+ } else if (!is_self && base_type.is_meta_type && !method_flags.has_flag(METHOD_FLAG_STATIC)) {
base_type.is_meta_type = false; // For `to_string()`.
push_error(vformat(R"*(Cannot call non-static function "%s()" on the class "%s" directly. Make an instance instead.)*", p_call->function_name, base_type.to_string()), p_call);
- } else if (is_self && !is_static) {
+ } else if (is_self && !method_flags.has_flag(METHOD_FLAG_STATIC)) {
mark_lambda_use_self();
}
@@ -3122,11 +3346,12 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
}
#ifdef DEBUG_ENABLED
- if (p_is_root && return_type.kind != GDScriptParser::DataType::UNRESOLVED && return_type.builtin_type != Variant::NIL) {
+ if (p_is_root && return_type.kind != GDScriptParser::DataType::UNRESOLVED && return_type.builtin_type != Variant::NIL &&
+ !(p_call->is_super && p_call->function_name == GDScriptLanguage::get_singleton()->strings._init)) {
parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name);
}
- if (is_static && !is_constructor && !base_type.is_meta_type && !(is_self && static_context)) {
+ if (method_flags.has_flag(METHOD_FLAG_STATIC) && !is_constructor && !base_type.is_meta_type && !(is_self && static_context)) {
String caller_type = String(base_type.native_type);
if (caller_type.is_empty()) {
@@ -3188,8 +3413,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
#else
push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
#endif // SUGGEST_GODOT4_RENAMES
- } else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) {
- push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type), p_call);
+ } else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.is_meta_type)) {
+ push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.to_string()), p_call);
}
}
@@ -3281,17 +3506,26 @@ void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dicti
void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node) {
GDScriptParser::DataType result;
- result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- result.kind = GDScriptParser::DataType::NATIVE;
- result.native_type = SNAME("Node");
- result.builtin_type = Variant::OBJECT;
+ result.kind = GDScriptParser::DataType::VARIANT;
+
+ if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, SNAME("Node"))) {
+ push_error(vformat(R"*(Cannot use shorthand "get_node()" notation ("%c") on a class that isn't a node.)*", p_get_node->use_dollar ? '$' : '%'), p_get_node);
+ p_get_node->set_datatype(result);
+ return;
+ }
- if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, result.native_type)) {
- push_error(R"*(Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.)*", p_get_node);
+ if (static_context) {
+ push_error(vformat(R"*(Cannot use shorthand "get_node()" notation ("%c") in a static function.)*", p_get_node->use_dollar ? '$' : '%'), p_get_node);
+ p_get_node->set_datatype(result);
+ return;
}
mark_lambda_use_self();
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ result.kind = GDScriptParser::DataType::NATIVE;
+ result.builtin_type = Variant::OBJECT;
+ result.native_type = SNAME("Node");
p_get_node->set_datatype(result);
}
@@ -3328,7 +3562,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base_set_class(GDScriptParser::Ide
p_identifier->set_datatype(p_identifier_datatype);
Error err = OK;
- Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err);
+ Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err, parser->script_path);
if (err) {
push_error(vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path), p_identifier);
return;
@@ -3349,7 +3583,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
base = *p_base;
}
- const StringName &name = p_identifier->name;
+ StringName name = p_identifier->name;
if (base.kind == GDScriptParser::DataType::ENUM) {
if (base.is_meta_type) {
@@ -3444,12 +3678,21 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
get_class_node_current_scope_classes(base_class, &script_classes);
}
+ bool is_constructor = base.is_meta_type && p_identifier->name == SNAME("new");
+
for (GDScriptParser::ClassNode *script_class : script_classes) {
if (p_base == nullptr && script_class->identifier && script_class->identifier->name == name) {
reduce_identifier_from_base_set_class(p_identifier, script_class->get_datatype());
+ if (script_class->outer != nullptr) {
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CLASS;
+ }
return;
}
+ if (is_constructor) {
+ name = "_init";
+ }
+
if (script_class->has_member(name)) {
resolve_class_member(script_class, name, p_identifier);
@@ -3499,14 +3742,16 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
} break;
case GDScriptParser::ClassNode::Member::FUNCTION: {
- if (is_base && !base.is_meta_type) {
+ if (is_base && (!base.is_meta_type || member.function->is_static)) {
p_identifier->set_datatype(make_callable_type(member.function->info));
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_FUNCTION;
return;
}
} break;
case GDScriptParser::ClassNode::Member::CLASS: {
reduce_identifier_from_base_set_class(p_identifier, member.get_datatype());
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CLASS;
return;
}
@@ -3528,6 +3773,10 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
const StringName &native = base.native_type;
if (class_exists(native)) {
+ if (is_constructor) {
+ name = "_init";
+ }
+
MethodInfo method_info;
if (ClassDB::has_property(native, name)) {
StringName getter_name = ClassDB::get_property_getter(native, name);
@@ -3641,10 +3890,19 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
found_source = true;
} break;
case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE:
+ case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
+ case GDScriptParser::IdentifierNode::MEMBER_CLASS:
break;
}
+#ifdef DEBUG_ENABLED
+ if (!found_source && p_identifier->suite != nullptr && p_identifier->suite->has_local(p_identifier->name)) {
+ parser->push_warning(p_identifier, GDScriptWarning::CONFUSABLE_LOCAL_USAGE, p_identifier->name);
+ }
+#endif
+
// Not a local, so check members.
+
if (!found_source) {
reduce_identifier_from_base(p_identifier);
if (p_identifier->source != GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->get_datatype().is_set()) {
@@ -3657,19 +3915,20 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
bool source_is_variable = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
bool source_is_signal = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
if ((source_is_variable || source_is_signal) && static_context) {
- if (parser->current_function) {
- // Get the parent function above any lambda.
- GDScriptParser::FunctionNode *parent_function = parser->current_function;
- while (parent_function->source_lambda) {
- parent_function = parent_function->source_lambda->parent_function;
- }
+ // Get the parent function above any lambda.
+ GDScriptParser::FunctionNode *parent_function = parser->current_function;
+ while (parent_function && parent_function->source_lambda) {
+ parent_function = parent_function->source_lambda->parent_function;
+ }
+
+ if (parent_function) {
push_error(vformat(R"*(Cannot access %s "%s" from the static function "%s()".)*", source_is_signal ? "signal" : "instance variable", p_identifier->name, parent_function->identifier->name), p_identifier);
} else {
- push_error(vformat(R"*(Cannot access %s "%s" for a static variable initializer.)*", source_is_signal ? "signal" : "instance variable", p_identifier->name), p_identifier);
+ push_error(vformat(R"*(Cannot access %s "%s" from a static variable initializer.)*", source_is_signal ? "signal" : "instance variable", p_identifier->name), p_identifier);
}
}
- if (!lambda_stack.is_empty()) {
+ if (current_lambda != nullptr) {
// If the identifier is a member variable (including the native class properties) or a signal, we consider the lambda to be using `self`, so we keep a reference to the current instance.
if (source_is_variable || source_is_signal) {
mark_lambda_use_self();
@@ -3681,7 +3940,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
return;
}
- GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->function;
+ GDScriptParser::FunctionNode *function_test = current_lambda->function;
// Make sure we aren't capturing variable in the same lambda.
// This also add captures for nested lambdas.
while (function_test != nullptr && function_test != p_identifier->source_function && function_test->source_lambda != nullptr && !function_test->source_lambda->captures_indices.has(p_identifier->name)) {
@@ -3697,10 +3956,10 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
StringName name = p_identifier->name;
p_identifier->source = GDScriptParser::IdentifierNode::UNDEFINED_SOURCE;
- // Check globals. We make an exception for Variant::OBJECT because it's the base class for
- // non-builtin types so we allow doing e.g. Object.new()
+ // Not a local or a member, so check globals.
+
Variant::Type builtin_type = GDScriptParser::get_builtin_type(name);
- if (builtin_type != Variant::OBJECT && builtin_type < Variant::VARIANT_MAX) {
+ if (builtin_type < Variant::VARIANT_MAX) {
if (can_be_builtin) {
p_identifier->set_datatype(make_builtin_meta_type(builtin_type));
return;
@@ -3726,8 +3985,10 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
if (autoload.is_singleton) {
// Singleton exists, so it's at least a Node.
GDScriptParser::DataType result;
- result.kind = GDScriptParser::DataType::NATIVE;
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ result.kind = GDScriptParser::DataType::NATIVE;
+ result.builtin_type = Variant::OBJECT;
+ result.native_type = SNAME("Node");
if (ResourceLoader::get_resource_type(autoload.path) == "GDScript") {
Ref<GDScriptParserRef> singl_parser = get_parser_for(autoload.path);
if (singl_parser.is_valid()) {
@@ -3836,34 +4097,12 @@ void GDScriptAnalyzer::reduce_lambda(GDScriptParser::LambdaNode *p_lambda) {
return;
}
- lambda_stack.push_back(p_lambda);
+ GDScriptParser::LambdaNode *previous_lambda = current_lambda;
+ current_lambda = p_lambda;
resolve_function_signature(p_lambda->function, p_lambda, true);
- resolve_function_body(p_lambda->function, true);
- lambda_stack.pop_back();
-
- int captures_amount = p_lambda->captures.size();
- if (captures_amount > 0) {
- // Create space for lambda parameters.
- // At the beginning to not mess with optional parameters.
- int param_count = p_lambda->function->parameters.size();
- p_lambda->function->parameters.resize(param_count + captures_amount);
- for (int i = param_count - 1; i >= 0; i--) {
- p_lambda->function->parameters.write[i + captures_amount] = p_lambda->function->parameters[i];
- p_lambda->function->parameters_indices[p_lambda->function->parameters[i]->identifier->name] = i + captures_amount;
- }
+ current_lambda = previous_lambda;
- // Add captures as extra parameters at the beginning.
- for (int i = 0; i < p_lambda->captures.size(); i++) {
- GDScriptParser::IdentifierNode *capture = p_lambda->captures[i];
- GDScriptParser::ParameterNode *capture_param = parser->alloc_node<GDScriptParser::ParameterNode>();
- capture_param->identifier = capture;
- capture_param->usages = capture->usages;
- capture_param->set_datatype(capture->get_datatype());
-
- p_lambda->function->parameters.write[i] = capture_param;
- p_lambda->function->parameters_indices[capture->name] = i;
- }
- }
+ pending_body_resolution_lambdas.push_back(p_lambda);
}
void GDScriptAnalyzer::reduce_literal(GDScriptParser::LiteralNode *p_literal) {
@@ -3963,7 +4202,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
GDScriptParser::DataType base_type = p_subscript->base->get_datatype();
bool valid = false;
// If the base is a metatype, use the analyzer instead.
- if (p_subscript->base->is_constant && !base_type.is_meta_type) {
+ if (p_subscript->base->is_constant && !base_type.is_meta_type && base_type.kind != GDScriptParser::DataType::CLASS) {
// Just try to get it.
Variant value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, valid);
if (valid) {
@@ -4112,7 +4351,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
}
} else if (base_type.kind != GDScriptParser::DataType::BUILTIN && !index_type.is_variant()) {
if (index_type.builtin_type != Variant::STRING && index_type.builtin_type != Variant::STRING_NAME) {
- push_error(vformat(R"(Only String or StringName can be used as index for type "%s", but received a "%s".)", base_type.to_string(), index_type.to_string()), p_subscript->index);
+ push_error(vformat(R"(Only "String" or "StringName" can be used as index for type "%s", but received "%s".)", base_type.to_string(), index_type.to_string()), p_subscript->index);
}
}
@@ -4451,7 +4690,7 @@ Array GDScriptAnalyzer::make_array_from_element_datatype(const GDScriptParser::D
Ref<Script> script_type = p_element_datatype.script_type;
if (p_element_datatype.kind == GDScriptParser::DataType::CLASS && script_type.is_null()) {
Error err = OK;
- Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_element_datatype.script_path, err);
+ Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_element_datatype.script_path, err, parser->script_path);
if (err) {
push_error(vformat(R"(Error while getting cache for script "%s".)", p_element_datatype.script_path), p_source_node);
return array;
@@ -4494,6 +4733,10 @@ Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNo
return result;
}
+const HashMap<String, Ref<GDScriptParserRef>> &GDScriptAnalyzer::get_depended_parsers() {
+ return depended_parsers;
+}
+
GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) {
GDScriptParser::DataType result;
result.is_constant = true;
@@ -4501,7 +4744,16 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
result.builtin_type = p_value.get_type();
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; // Constant has explicit type.
- if (p_value.get_type() == Variant::OBJECT) {
+ if (p_value.get_type() == Variant::ARRAY) {
+ const Array &array = p_value;
+ if (array.get_typed_script()) {
+ result.set_container_element_type(type_from_metatype(make_script_meta_type(array.get_typed_script())));
+ } else if (array.get_typed_class_name()) {
+ result.set_container_element_type(type_from_metatype(make_native_meta_type(array.get_typed_class_name())));
+ } else if (array.get_typed_builtin() != Variant::NIL) {
+ result.set_container_element_type(type_from_metatype(make_builtin_meta_type((Variant::Type)array.get_typed_builtin())));
+ }
+ } else if (p_value.get_type() == Variant::OBJECT) {
// Object is treated as a native type, not a builtin type.
result.kind = GDScriptParser::DataType::NATIVE;
@@ -4609,7 +4861,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
} else if (class_exists(elem_type_name)) {
elem_type.kind = GDScriptParser::DataType::NATIVE;
elem_type.builtin_type = Variant::OBJECT;
- elem_type.native_type = p_property.hint_string;
+ elem_type.native_type = elem_type_name;
} else if (ScriptServer::is_global_class(elem_type_name)) {
// Just load this as it shouldn't be a GDScript.
Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(elem_type_name));
@@ -4642,9 +4894,8 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
return result;
}
-bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType p_base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg, StringName *r_native_class) {
- r_static = false;
- r_vararg = false;
+bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType p_base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, BitField<MethodFlags> &r_method_flags, StringName *r_native_class) {
+ r_method_flags = METHOD_FLAGS_DEFAULT;
r_default_arg_count = 0;
if (r_native_class) {
*r_native_class = StringName();
@@ -4677,10 +4928,9 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
for (const MethodInfo &E : methods) {
if (E.name == p_function) {
- function_signature_from_info(E, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
- r_static = Variant::is_builtin_method_static(p_base_type.builtin_type, function_name);
+ function_signature_from_info(E, r_return_type, r_par_types, r_default_arg_count, r_method_flags);
// Cannot use non-const methods on enums.
- if (!r_static && was_enum && !(E.flags & METHOD_FLAG_CONST)) {
+ if (!r_method_flags.has_flag(METHOD_FLAG_STATIC) && was_enum && !(E.flags & METHOD_FLAG_CONST)) {
push_error(vformat(R"*(Cannot call non-const Dictionary function "%s()" on enum "%s".)*", p_function, p_base_type.enum_type), p_source);
}
return true;
@@ -4710,8 +4960,8 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
}
if (p_is_constructor) {
- function_name = "_init";
- r_static = true;
+ function_name = GDScriptLanguage::get_singleton()->strings._init;
+ r_method_flags.set_flag(METHOD_FLAG_STATIC);
}
GDScriptParser::ClassNode *base_class = p_base_type.class_type;
@@ -4734,7 +4984,9 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
}
if (found_function != nullptr) {
- r_static = p_is_constructor || found_function->is_static;
+ if (p_is_constructor || found_function->is_static) {
+ r_method_flags.set_flag(METHOD_FLAG_STATIC);
+ }
for (int i = 0; i < found_function->parameters.size(); i++) {
r_par_types.push_back(found_function->parameters[i]->get_datatype());
if (found_function->parameters[i]->initializer != nullptr) {
@@ -4754,7 +5006,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
MethodInfo info = base_script->get_method_info(function_name);
if (!(info == MethodInfo())) {
- return function_signature_from_info(info, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
+ return function_signature_from_info(info, r_return_type, r_par_types, r_default_arg_count, r_method_flags);
}
base_script = base_script->get_base_script();
}
@@ -4765,7 +5017,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
StringName script_class = p_base_type.kind == GDScriptParser::DataType::SCRIPT ? p_base_type.script_type->get_class_name() : StringName(GDScript::get_class_static());
if (ClassDB::get_method_info(script_class, function_name, &info)) {
- return function_signature_from_info(info, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
+ return function_signature_from_info(info, r_return_type, r_par_types, r_default_arg_count, r_method_flags);
}
}
@@ -4779,9 +5031,9 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
MethodInfo info;
if (ClassDB::get_method_info(base_native, function_name, &info)) {
- bool valid = function_signature_from_info(info, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
+ bool valid = function_signature_from_info(info, r_return_type, r_par_types, r_default_arg_count, r_method_flags);
if (valid && Engine::get_singleton()->has_singleton(base_native)) {
- r_static = true;
+ r_method_flags.set_flag(METHOD_FLAG_STATIC);
}
#ifdef DEBUG_ENABLED
MethodBind *native_method = ClassDB::get_method(base_native, function_name);
@@ -4795,11 +5047,10 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
return false;
}
-bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) {
+bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, BitField<MethodFlags> &r_method_flags) {
r_return_type = type_from_property(p_info.return_val);
r_default_arg_count = p_info.default_arguments.size();
- r_vararg = (p_info.flags & METHOD_FLAG_VARARG) != 0;
- r_static = (p_info.flags & METHOD_FLAG_STATIC) != 0;
+ r_method_flags = p_info.flags;
for (const PropertyInfo &E : p_info.arguments) {
r_par_types.push_back(type_from_property(E, true));
@@ -4838,29 +5089,36 @@ void GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p
GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype();
if (arg_type.is_variant() || !arg_type.is_hard_type()) {
+#ifdef DEBUG_ENABLED
// Argument can be anything, so this is unsafe (unless the parameter is a hard variant).
if (!(par_type.is_hard_type() && par_type.is_variant())) {
mark_node_unsafe(p_call->arguments[i]);
+ parser->push_warning(p_call->arguments[i], GDScriptWarning::UNSAFE_CALL_ARGUMENT, itos(i + 1), "function", p_call->function_name, par_type.to_string(), arg_type.to_string_strict());
}
+#endif
} else if (par_type.is_hard_type() && !is_type_compatible(par_type, arg_type, true)) {
- // Supertypes are acceptable for dynamic compliance, but it's unsafe.
- mark_node_unsafe(p_call);
if (!is_type_compatible(arg_type, par_type)) {
push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*",
p_call->function_name, i + 1, par_type.to_string(), arg_type.to_string()),
p_call->arguments[i]);
+#ifdef DEBUG_ENABLED
+ } else {
+ // Supertypes are acceptable for dynamic compliance, but it's unsafe.
+ mark_node_unsafe(p_call);
+ parser->push_warning(p_call->arguments[i], GDScriptWarning::UNSAFE_CALL_ARGUMENT, itos(i + 1), "function", p_call->function_name, par_type.to_string(), arg_type.to_string_strict());
+#endif
}
#ifdef DEBUG_ENABLED
} else if (par_type.kind == GDScriptParser::DataType::BUILTIN && par_type.builtin_type == Variant::INT && arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == Variant::FLOAT) {
- parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name);
+ parser->push_warning(p_call->arguments[i], GDScriptWarning::NARROWING_CONVERSION, p_call->function_name);
#endif
}
}
}
#ifdef DEBUG_ENABLED
-void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context) {
- const StringName &name = p_local->name;
+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;
@@ -4870,29 +5128,34 @@ void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con
for (MethodInfo &info : gdscript_funcs) {
if (info.name == name) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in function");
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in function");
return;
}
}
-
if (Variant::has_utility_function(name)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in function");
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in function");
return;
} else if (ClassDB::class_exists(name)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "global class");
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "native class");
+ return;
+ } else if (ScriptServer::is_global_class(name)) {
+ String class_path = ScriptServer::get_global_class_path(name).get_file();
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, vformat(R"(global class defined in "%s")", class_path));
return;
- } else if (GDScriptParser::get_builtin_type(name) != Variant::VARIANT_MAX) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in type");
+ } else if (GDScriptParser::get_builtin_type(name) < Variant::VARIANT_MAX) {
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in type");
return;
}
}
- while (base_class != nullptr) {
- if (base_class->has_member(name)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE, p_context, p_local->name, base_class->get_member(name).get_type_name(), itos(base_class->get_member(name).get_line()));
- return;
+ if (p_in_local_scope) {
+ while (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;
}
- base_class = base_class->base_type.class_type;
}
StringName parent = base.native_type;
@@ -4900,19 +5163,19 @@ void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con
ERR_FAIL_COND_MSG(!class_exists(parent), "Non-existent native base class.");
if (ClassDB::has_method(parent, name, true)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "method", parent);
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "method", parent);
return;
} else if (ClassDB::has_signal(parent, name, true)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "signal", parent);
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "signal", parent);
return;
} else if (ClassDB::has_property(parent, name, true)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "property", parent);
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "property", parent);
return;
} else if (ClassDB::has_integer_constant(parent, name, true)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "constant", parent);
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "constant", parent);
return;
} else if (ClassDB::has_enum(parent, name, true)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "enum", parent);
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "enum", parent);
return;
}
parent = ClassDB::get_parent_class(parent);
@@ -4929,6 +5192,17 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
}
GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source) {
+ if (p_operation == Variant::OP_AND || p_operation == Variant::OP_OR) {
+ // Those work for any type of argument and always return a boolean.
+ // They don't use the Variant operator since they have short-circuit semantics.
+ r_valid = true;
+ GDScriptParser::DataType result;
+ result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ result.kind = GDScriptParser::DataType::BUILTIN;
+ result.builtin_type = Variant::BOOL;
+ return result;
+ }
+
Variant::Type a_type = p_a.builtin_type;
Variant::Type b_type = p_b.builtin_type;
@@ -4947,11 +5221,21 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
}
}
- Variant::ValidatedOperatorEvaluator op_eval = Variant::get_validated_operator_evaluator(p_operation, a_type, b_type);
+ GDScriptParser::DataType result;
bool hard_operation = p_a.is_hard_type() && p_b.is_hard_type();
+
+ if (p_operation == Variant::OP_ADD && a_type == Variant::ARRAY && b_type == Variant::ARRAY) {
+ if (p_a.has_container_element_type() && p_b.has_container_element_type() && p_a.get_container_element_type() == p_b.get_container_element_type()) {
+ r_valid = true;
+ result = p_a;
+ result.type_source = hard_operation ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED;
+ return result;
+ }
+ }
+
+ Variant::ValidatedOperatorEvaluator op_eval = Variant::get_validated_operator_evaluator(p_operation, a_type, b_type);
bool validated = op_eval != nullptr;
- GDScriptParser::DataType result;
if (validated) {
r_valid = true;
result.type_source = hard_operation ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED;
@@ -5170,11 +5454,59 @@ void GDScriptAnalyzer::downgrade_node_type_source(GDScriptParser::Node *p_node)
}
void GDScriptAnalyzer::mark_lambda_use_self() {
- for (GDScriptParser::LambdaNode *lambda : lambda_stack) {
+ GDScriptParser::LambdaNode *lambda = current_lambda;
+ while (lambda != nullptr) {
lambda->use_self = true;
+ lambda = lambda->parent_lambda;
}
}
+void GDScriptAnalyzer::resolve_pending_lambda_bodies() {
+ if (pending_body_resolution_lambdas.is_empty()) {
+ return;
+ }
+
+ GDScriptParser::LambdaNode *previous_lambda = current_lambda;
+ bool previous_static_context = static_context;
+
+ List<GDScriptParser::LambdaNode *> lambdas = pending_body_resolution_lambdas;
+ pending_body_resolution_lambdas.clear();
+
+ for (GDScriptParser::LambdaNode *lambda : lambdas) {
+ current_lambda = lambda;
+ static_context = lambda->function->is_static;
+
+ resolve_function_body(lambda->function, true);
+
+ int captures_amount = lambda->captures.size();
+ if (captures_amount > 0) {
+ // Create space for lambda parameters.
+ // At the beginning to not mess with optional parameters.
+ int param_count = lambda->function->parameters.size();
+ lambda->function->parameters.resize(param_count + captures_amount);
+ for (int i = param_count - 1; i >= 0; i--) {
+ lambda->function->parameters.write[i + captures_amount] = lambda->function->parameters[i];
+ lambda->function->parameters_indices[lambda->function->parameters[i]->identifier->name] = i + captures_amount;
+ }
+
+ // Add captures as extra parameters at the beginning.
+ for (int i = 0; i < lambda->captures.size(); i++) {
+ GDScriptParser::IdentifierNode *capture = lambda->captures[i];
+ GDScriptParser::ParameterNode *capture_param = parser->alloc_node<GDScriptParser::ParameterNode>();
+ capture_param->identifier = capture;
+ capture_param->usages = capture->usages;
+ capture_param->set_datatype(capture->get_datatype());
+
+ lambda->function->parameters.write[i] = capture_param;
+ lambda->function->parameters_indices[capture->name] = i;
+ }
+ }
+ }
+
+ current_lambda = previous_lambda;
+ static_context = previous_static_context;
+}
+
bool GDScriptAnalyzer::class_exists(const StringName &p_class) const {
return ClassDB::class_exists(p_class) && ClassDB::is_class_exposed(p_class);
}
@@ -5231,7 +5563,7 @@ Error GDScriptAnalyzer::analyze() {
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : parser->head->annotations) {
resolve_annotation(E);
- E->apply(parser, parser->head);
+ E->apply(parser, parser->head, nullptr);
}
resolve_interface();