diff options
Diffstat (limited to 'modules/gdscript/gdscript_analyzer.cpp')
-rw-r--r-- | modules/gdscript/gdscript_analyzer.cpp | 424 |
1 files changed, 261 insertions, 163 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 214b484b12..7b3d31f4a8 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -613,144 +613,183 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type return result; } - StringName first = p_type->type_chain[0]->name; - - 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; + const GDScriptParser::IdentifierNode *first_id = p_type->type_chain[0]; + StringName first = first_id->name; + bool type_found = false; + + 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 (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]); + 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")); + } + } + } 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 (script_class->members_indices.has(first)) { - resolve_class_member(script_class, first, p_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; @@ -882,9 +921,12 @@ 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) { @@ -893,7 +935,9 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, E->apply(parser, member.variable); } } + static_context = previous_static_context; + #ifdef DEBUG_ENABLED if (member.variable->exported && member.variable->onready) { parser->push_warning(member.variable, GDScriptWarning::ONREADY_WITH_EXPORT); @@ -956,10 +1000,16 @@ 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) { @@ -1234,17 +1284,15 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co } 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); } @@ -1345,6 +1393,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; } @@ -1543,21 +1596,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", 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); @@ -1565,7 +1623,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) { @@ -1665,6 +1722,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); } @@ -1757,6 +1820,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; @@ -1867,6 +1931,13 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi } } +#ifdef DEBUG_ENABLED + if (!has_specified_type && !p_assignable->infer_datatype && !is_constant) { + const bool is_parameter = p_assignable->type == GDScriptParser::Node::PARAMETER; + parser->push_warning(p_assignable, GDScriptWarning::UNTYPED_DECLARATION, is_parameter ? "Parameter" : "Variable", p_assignable->identifier->name); + } +#endif + type.is_constant = is_constant; type.is_read_only = false; p_assignable->set_datatype(type); @@ -2077,13 +2148,18 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { #endif } #ifdef DEBUG_ENABLED - } else { + } else if (variable_type.is_hard_type()) { parser->push_warning(p_for->datatype_specifier, GDScriptWarning::REDUNDANT_FOR_VARIABLE_TYPE, p_for->variable->name, variable_type.to_string(), specified_type.to_string()); #endif } 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::UNTYPED_DECLARATION, R"("for" iterator variable)", p_for->variable->name); + } +#endif } } @@ -3080,7 +3156,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) { @@ -3753,7 +3829,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident } } - 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(); @@ -3765,7 +3841,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)) { @@ -3920,34 +3996,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; - } - - // 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()); + current_lambda = previous_lambda; - 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) { @@ -5288,11 +5342,55 @@ 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; + + List<GDScriptParser::LambdaNode *> lambdas = pending_body_resolution_lambdas; + pending_body_resolution_lambdas.clear(); + + for (GDScriptParser::LambdaNode *lambda : lambdas) { + current_lambda = lambda; + 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; +} + bool GDScriptAnalyzer::class_exists(const StringName &p_class) const { return ClassDB::class_exists(p_class) && ClassDB::is_class_exposed(p_class); } |