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.cpp267
1 files changed, 206 insertions, 61 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 94daba4bf6..d346264933 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -277,6 +277,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
base = parser->get_parser()->head->get_datatype();
} else {
if (p_class->extends.is_empty()) {
+ push_error("Could not resolve an empty super class path.", p_class);
return ERR_PARSE_ERROR;
}
const StringName &name = p_class->extends[extends_index++];
@@ -646,41 +647,51 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
}
- if (member.variable->datatype_specifier != nullptr) {
- datatype = specified_type;
+ // Check if initializer is an unset identifier (ie: a variable within scope, but declared below)
+ if (member.variable->initializer && !member.variable->initializer->get_datatype().is_set()) {
+ if (member.variable->initializer->type == GDScriptParser::Node::IDENTIFIER) {
+ GDScriptParser::IdentifierNode *initializer_identifier = static_cast<GDScriptParser::IdentifierNode *>(member.variable->initializer);
+ push_error(vformat(R"(Identifier "%s" must be declared above current variable.)", initializer_identifier->name), member.variable->initializer);
+ } else {
+ ERR_PRINT("Parser bug (please report): tried to assign unset node without an identifier.");
+ }
+ } else {
+ if (member.variable->datatype_specifier != nullptr) {
+ datatype = specified_type;
- if (member.variable->initializer != nullptr) {
- if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) {
- // Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) {
- push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer);
- } else {
- // TODO: Add warning.
+ if (member.variable->initializer != nullptr) {
+ if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) {
+ // Try reverse test since it can be a masked subtype.
+ if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) {
+ push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer);
+ } else {
+ // TODO: Add warning.
+ mark_node_unsafe(member.variable->initializer);
+ member.variable->use_conversion_assign = true;
+ }
+ } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
+#ifdef DEBUG_ENABLED
+ parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
+#endif
+ }
+ if (member.variable->initializer->get_datatype().is_variant()) {
+ // TODO: Warn unsafe assign.
mark_node_unsafe(member.variable->initializer);
member.variable->use_conversion_assign = true;
}
- } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
-#ifdef DEBUG_ENABLED
- parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
-#endif
}
- if (member.variable->initializer->get_datatype().is_variant()) {
- // TODO: Warn unsafe assign.
- mark_node_unsafe(member.variable->initializer);
- member.variable->use_conversion_assign = true;
+ } else if (member.variable->infer_datatype) {
+ if (member.variable->initializer == nullptr) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier);
+ } else if (!datatype.is_set() || datatype.has_no_type()) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer);
+ } else if (datatype.is_variant()) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer);
+ } else if (datatype.builtin_type == Variant::NIL) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is "null".)", member.variable->identifier->name), member.variable->initializer);
}
+ datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
}
- } else if (member.variable->infer_datatype) {
- if (member.variable->initializer == nullptr) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier);
- } else if (!datatype.is_set() || datatype.has_no_type()) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer);
- } else if (datatype.is_variant()) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer);
- } else if (datatype.builtin_type == Variant::NIL) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is "null".)", member.variable->identifier->name), member.variable->initializer);
- }
- datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
}
datatype.is_constant = false;
@@ -1114,6 +1125,10 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
GDScriptParser::FunctionNode *previous_function = parser->current_function;
parser->current_function = p_function;
+#ifdef TOOLS_ENABLED
+ int default_value_count = 0;
+#endif // TOOLS_ENABLED
+
for (int i = 0; i < p_function->parameters.size(); i++) {
resolve_parameter(p_function->parameters[i]);
#ifdef DEBUG_ENABLED
@@ -1123,13 +1138,17 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
is_shadowing(p_function->parameters[i]->identifier, "function parameter");
#endif // DEBUG_ENABLED
#ifdef TOOLS_ENABLED
- if (p_function->parameters[i]->default_value && p_function->parameters[i]->default_value->is_constant) {
- p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value);
+ if (p_function->parameters[i]->default_value) {
+ default_value_count++;
+
+ if (p_function->parameters[i]->default_value->is_constant) {
+ p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value);
+ }
}
#endif // TOOLS_ENABLED
}
- if (p_function->identifier->name == "_init") {
+ if (p_function->identifier->name == GDScriptLanguage::get_singleton()->strings._init) {
// Constructor.
GDScriptParser::DataType return_type = parser->current_class->get_datatype();
return_type.is_meta_type = false;
@@ -1143,6 +1162,57 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
} else {
GDScriptParser::DataType return_type = resolve_datatype(p_function->return_type);
p_function->set_datatype(return_type);
+
+#ifdef TOOLS_ENABLED
+ // Check if the function signature matches the parent. If not it's an error since it breaks polymorphism.
+ // Not for the constructor which can vary in signature.
+ GDScriptParser::DataType base_type = parser->current_class->base_type;
+ GDScriptParser::DataType parent_return_type;
+ List<GDScriptParser::DataType> parameters_types;
+ int default_par_count = 0;
+ bool is_static = false;
+ bool is_vararg = false;
+ if (get_function_signature(p_function, false, base_type, p_function->identifier->name, parent_return_type, parameters_types, default_par_count, is_static, is_vararg)) {
+ bool valid = p_function->is_static == is_static;
+ valid = valid && parent_return_type == p_function->get_datatype();
+
+ 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) {
+ // Compute parent signature as a string to show in the error message.
+ String parent_signature = parent_return_type.is_hard_type() ? parent_return_type.to_string() : "Variant";
+ if (parent_signature == "null") {
+ parent_signature = "void";
+ }
+ parent_signature += " " + p_function->identifier->name.operator String() + "(";
+ int j = 0;
+ for (const GDScriptParser::DataType &par_type : parameters_types) {
+ if (j > 0) {
+ parent_signature += ", ";
+ }
+ String parameter = par_type.to_string();
+ if (parameter == "null") {
+ parameter = "Variant";
+ }
+ parent_signature += parameter;
+ if (j == parameters_types.size() - default_par_count) {
+ parent_signature += " = default";
+ }
+
+ j++;
+ }
+ parent_signature += ")";
+ push_error(vformat(R"(The function signature doesn't match the parent. Parent signature is "%s".)", parent_signature), p_function);
+ }
+ }
+#endif // TOOLS_ENABLED
}
parser->current_function = previous_function;
@@ -1241,7 +1311,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
bool list_resolved = false;
// Optimize constant range() call to not allocate an array.
- // Use int, Vector2, Vector3 instead, which also can be used as range iterators.
+ // Use int, Vector2i, Vector3i instead, which also can be used as range iterators.
if (p_for->list && p_for->list->type == GDScriptParser::Node::CALL) {
GDScriptParser::CallNode *call = static_cast<GDScriptParser::CallNode *>(p_for->list);
GDScriptParser::Node::Type callee_type = call->get_callee_type();
@@ -1414,7 +1484,7 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
parser->push_warning(p_variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
#endif
}
- if (p_variable->initializer->get_datatype().is_variant()) {
+ if (p_variable->initializer->get_datatype().is_variant() && !type.is_variant()) {
// TODO: Warn unsafe assign.
mark_node_unsafe(p_variable->initializer);
p_variable->use_conversion_assign = true;
@@ -2233,7 +2303,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
break;
#ifdef DEBUG_ENABLED
} else {
- if (par_type.builtin_type == Variant::INT && p_call->arguments[i]->get_datatype().builtin_type == Variant::FLOAT) {
+ 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);
}
#endif
@@ -2356,6 +2426,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
base_type = parser->current_class->base_type;
base_type.is_meta_type = false;
is_self = true;
+
+ if (p_call->callee == nullptr && !lambda_stack.is_empty()) {
+ push_error("Cannot use `super()` inside a lambda.", p_call);
+ }
} else if (callee_type == GDScriptParser::Node::IDENTIFIER) {
base_type = parser->current_class->get_datatype();
base_type.is_meta_type = false;
@@ -2406,7 +2480,9 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
GDScriptParser::DataType return_type;
List<GDScriptParser::DataType> par_types;
- if (get_function_signature(p_call, base_type, p_call->function_name, return_type, par_types, default_arg_count, is_static, is_vararg)) {
+ 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.
for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) {
int index = E.key;
@@ -2422,18 +2498,24 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
}
if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
- push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parser->current_function->identifier->name), p_call->callee);
+ // 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;
+ }
+ 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 if (!is_self && base_type.is_meta_type && !is_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->callee);
- } else if (is_self && !is_static && !lambda_stack.is_empty()) {
- push_error(vformat(R"*(Cannot call non-static function "%s()" from a lambda function.)*", p_call->function_name), p_call->callee);
+ 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) {
+ mark_lambda_use_self();
}
call_type = return_type;
} else {
- // Check if the name exists as something else.
bool found = false;
+
+ // Check if the name exists as something else.
if (!p_call->is_super && callee_type != GDScriptParser::Node::NONE) {
GDScriptParser::IdentifierNode *callee_id;
if (callee_type == GDScriptParser::Node::IDENTIFIER) {
@@ -2463,11 +2545,13 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
if (!found && (is_self || (base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN))) {
String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string();
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);
+ } 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.operator String()), p_call);
}
}
if (call_type.is_coroutine && !p_is_await && !p_is_root) {
- push_error(vformat(R"*(Function "%s()" is a coroutine, so it must be called with "await".)*", p_call->function_name), p_call->callee);
+ push_error(vformat(R"*(Function "%s()" is a coroutine, so it must be called with "await".)*", p_call->function_name), p_call);
}
p_call->set_datatype(call_type);
@@ -2557,26 +2641,32 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node)
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);
- } else if (!lambda_stack.is_empty()) {
- push_error(R"*(Cannot use shorthand "get_node()" notation ("$") inside a lambda. Use a captured variable instead.)*", p_get_node);
}
+ mark_lambda_use_self();
+
p_get_node->set_datatype(result);
}
GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source) {
+ GDScriptParser::DataType type;
+
Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(p_class_name));
- Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ if (ref.is_null()) {
+ push_error(vformat(R"(Could not find script for class "%s".)", p_class_name), p_source);
+ type.type_source = GDScriptParser::DataType::UNDETECTED;
+ type.kind = GDScriptParser::DataType::VARIANT;
+ return type;
+ }
+ Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
if (err) {
push_error(vformat(R"(Could not resolve class "%s", because of a parser error.)", p_class_name), p_source);
- GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::UNDETECTED;
type.kind = GDScriptParser::DataType::VARIANT;
return type;
}
- GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
type.kind = GDScriptParser::DataType::CLASS;
type.builtin_type = Variant::OBJECT;
@@ -2769,21 +2859,25 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
MethodBind *getter = ClassDB::get_method(native, getter_name);
if (getter != nullptr) {
p_identifier->set_datatype(type_from_property(getter->get_return_info()));
+ p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
}
return;
}
if (ClassDB::get_method_info(native, name, &method_info)) {
// Method is callable.
p_identifier->set_datatype(make_callable_type(method_info));
+ p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
return;
}
if (ClassDB::get_signal(native, name, &method_info)) {
// Signal is a type too.
p_identifier->set_datatype(make_signal_type(method_info));
+ p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
return;
}
if (ClassDB::has_enum(native, name)) {
p_identifier->set_datatype(make_native_enum_type(native, name));
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
return;
}
bool valid = false;
@@ -2792,6 +2886,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
p_identifier->is_constant = true;
p_identifier->reduced_value = int_constant;
p_identifier->set_datatype(type_from_variant(int_constant, p_identifier));
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
return;
}
}
@@ -2842,7 +2937,11 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
found_source = true;
break;
+ case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:
+ mark_lambda_use_self();
+ break;
case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
+ mark_lambda_use_self();
p_identifier->variable_source->usages++;
[[fallthrough]];
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
@@ -2873,18 +2972,37 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
if (found_source) {
- // If the identifier is local, check if it's any kind of capture by comparing their source function.
- // Only capture locals and members and enum values. Constants are still accessible from the lambda using the script reference.
- if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT || lambda_stack.is_empty()) {
- return;
+ if ((p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) && parser->current_function && parser->current_function->is_static) {
+ // 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;
+ }
+ push_error(vformat(R"*(Cannot access instance variable "%s" from the static function "%s()".)*", p_identifier->name, parent_function->identifier->name), p_identifier);
}
- GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->function;
- 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)) {
- function_test->source_lambda->captures_indices[p_identifier->name] = function_test->source_lambda->captures.size();
- function_test->source_lambda->captures.push_back(p_identifier);
- function_test = function_test->source_lambda->parent_function;
+ if (!lambda_stack.is_empty()) {
+ // If the identifier is a member variable (including the native class properties), we consider the lambda to be using `self`, so we keep a reference to the current instance.
+ if (p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) {
+ mark_lambda_use_self();
+ return; // No need to capture.
+ }
+ // If the identifier is local, check if it's any kind of capture by comparing their source function.
+ // Only capture locals and enum values. Constants are still accessible from the lambda using the script reference. If not, this method is done.
+ if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT) {
+ return;
+ }
+
+ GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->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)) {
+ function_test->source_lambda->captures_indices[p_identifier->name] = function_test->source_lambda->captures.size();
+ function_test->source_lambda->captures.push_back(p_identifier);
+ function_test = function_test->source_lambda->parent_function;
+ }
}
+
return;
}
@@ -3064,6 +3182,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) {
p_self->is_constant = false;
p_self->set_datatype(type_from_metatype(parser->current_class->get_datatype()));
+ mark_lambda_use_self();
}
void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscript) {
@@ -3074,6 +3193,12 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base), true);
} else {
reduce_expression(p_subscript->base);
+
+ if (p_subscript->base->type == GDScriptParser::Node::ARRAY) {
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_subscript->base));
+ } else if (p_subscript->base->type == GDScriptParser::Node::DICTIONARY) {
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_subscript->base));
+ }
}
GDScriptParser::DataType result_type;
@@ -3391,6 +3516,13 @@ void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array) {
for (int i = 0; i < p_array->elements.size(); i++) {
GDScriptParser::ExpressionNode *element = p_array->elements[i];
+
+ if (element->type == GDScriptParser::Node::ARRAY) {
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element));
+ } else if (element->type == GDScriptParser::Node::DICTIONARY) {
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element));
+ }
+
all_is_constant = all_is_constant && element->is_constant;
if (!all_is_constant) {
return;
@@ -3411,6 +3543,13 @@ void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_d
for (int i = 0; i < p_dictionary->elements.size(); i++) {
const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
+
+ if (element.value->type == GDScriptParser::Node::ARRAY) {
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element.value));
+ } else if (element.value->type == GDScriptParser::Node::DICTIONARY) {
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element.value));
+ }
+
all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant;
if (!all_is_constant) {
return;
@@ -3560,7 +3699,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
return result;
}
-bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source, 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) {
+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) {
r_static = false;
r_vararg = false;
r_default_arg_count = 0;
@@ -3600,8 +3739,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source
return false;
}
- bool is_constructor = (p_base_type.is_meta_type || (p_source->callee && p_source->callee->type == GDScriptParser::Node::IDENTIFIER)) && p_function == StaticCString::create("new");
- if (is_constructor) {
+ if (p_is_constructor) {
function_name = "_init";
r_static = true;
}
@@ -3622,7 +3760,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source
}
if (found_function != nullptr) {
- r_static = is_constructor || found_function->is_static;
+ r_static = p_is_constructor || found_function->is_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]->default_value != nullptr) {
@@ -3648,7 +3786,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source
}
// If the base is a script, it might be trying to access members of the Script class itself.
- if (p_base_type.is_meta_type && !is_constructor && (p_base_type.kind == GDScriptParser::DataType::SCRIPT || p_base_type.kind == GDScriptParser::DataType::CLASS)) {
+ if (p_base_type.is_meta_type && !p_is_constructor && (p_base_type.kind == GDScriptParser::DataType::SCRIPT || p_base_type.kind == GDScriptParser::DataType::CLASS)) {
MethodInfo info;
StringName script_class = p_base_type.kind == GDScriptParser::DataType::SCRIPT ? p_base_type.script_type->get_class_name() : StringName(GDScript::get_class_static());
@@ -3668,7 +3806,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source
}
#endif
- if (is_constructor) {
+ if (p_is_constructor) {
// Native types always have a default constructor.
r_return_type = p_base_type;
r_return_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@@ -3692,6 +3830,7 @@ bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GD
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;
for (const PropertyInfo &E : p_info.arguments) {
r_par_types.push_back(type_from_property(E));
@@ -4036,6 +4175,12 @@ void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) {
#endif
}
+void GDScriptAnalyzer::mark_lambda_use_self() {
+ for (GDScriptParser::LambdaNode *lambda : lambda_stack) {
+ lambda->use_self = true;
+ }
+}
+
bool GDScriptAnalyzer::class_exists(const StringName &p_class) const {
return ClassDB::class_exists(p_class) && ClassDB::is_class_exposed(p_class);
}