|
@@ -856,6 +856,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
|
|
case GDScriptParser::Node::DICTIONARY:
|
|
case GDScriptParser::Node::DICTIONARY:
|
|
case GDScriptParser::Node::GET_NODE:
|
|
case GDScriptParser::Node::GET_NODE:
|
|
case GDScriptParser::Node::IDENTIFIER:
|
|
case GDScriptParser::Node::IDENTIFIER:
|
|
|
|
+ case GDScriptParser::Node::LAMBDA:
|
|
case GDScriptParser::Node::LITERAL:
|
|
case GDScriptParser::Node::LITERAL:
|
|
case GDScriptParser::Node::PRELOAD:
|
|
case GDScriptParser::Node::PRELOAD:
|
|
case GDScriptParser::Node::SELF:
|
|
case GDScriptParser::Node::SELF:
|
|
@@ -872,9 +873,6 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
|
|
case GDScriptParser::Node::SIGNAL:
|
|
case GDScriptParser::Node::SIGNAL:
|
|
// Nothing to do.
|
|
// Nothing to do.
|
|
break;
|
|
break;
|
|
- case GDScriptParser::Node::LAMBDA:
|
|
|
|
- // FIXME: Recurse into lambda.
|
|
|
|
- break;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1461,6 +1459,9 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre
|
|
case GDScriptParser::Node::IDENTIFIER:
|
|
case GDScriptParser::Node::IDENTIFIER:
|
|
reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_expression));
|
|
reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_expression));
|
|
break;
|
|
break;
|
|
|
|
+ case GDScriptParser::Node::LAMBDA:
|
|
|
|
+ reduce_lambda(static_cast<GDScriptParser::LambdaNode *>(p_expression));
|
|
|
|
+ break;
|
|
case GDScriptParser::Node::LITERAL:
|
|
case GDScriptParser::Node::LITERAL:
|
|
reduce_literal(static_cast<GDScriptParser::LiteralNode *>(p_expression));
|
|
reduce_literal(static_cast<GDScriptParser::LiteralNode *>(p_expression));
|
|
break;
|
|
break;
|
|
@@ -1492,7 +1493,6 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre
|
|
case GDScriptParser::Node::FOR:
|
|
case GDScriptParser::Node::FOR:
|
|
case GDScriptParser::Node::FUNCTION:
|
|
case GDScriptParser::Node::FUNCTION:
|
|
case GDScriptParser::Node::IF:
|
|
case GDScriptParser::Node::IF:
|
|
- case GDScriptParser::Node::LAMBDA:
|
|
|
|
case GDScriptParser::Node::MATCH:
|
|
case GDScriptParser::Node::MATCH:
|
|
case GDScriptParser::Node::MATCH_BRANCH:
|
|
case GDScriptParser::Node::MATCH_BRANCH:
|
|
case GDScriptParser::Node::PARAMETER:
|
|
case GDScriptParser::Node::PARAMETER:
|
|
@@ -2350,6 +2350,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
|
|
case GDScriptParser::ClassNode::Member::ENUM_VALUE:
|
|
case GDScriptParser::ClassNode::Member::ENUM_VALUE:
|
|
p_identifier->is_constant = true;
|
|
p_identifier->is_constant = true;
|
|
p_identifier->reduced_value = member.enum_value.value;
|
|
p_identifier->reduced_value = member.enum_value.value;
|
|
|
|
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
|
|
break;
|
|
break;
|
|
case GDScriptParser::ClassNode::Member::VARIABLE:
|
|
case GDScriptParser::ClassNode::Member::VARIABLE:
|
|
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
|
|
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
|
|
@@ -2450,42 +2451,65 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ bool found_source = false;
|
|
// Check if identifier is local.
|
|
// Check if identifier is local.
|
|
// If that's the case, the declaration already was solved before.
|
|
// If that's the case, the declaration already was solved before.
|
|
switch (p_identifier->source) {
|
|
switch (p_identifier->source) {
|
|
case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER:
|
|
case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER:
|
|
p_identifier->set_datatype(p_identifier->parameter_source->get_datatype());
|
|
p_identifier->set_datatype(p_identifier->parameter_source->get_datatype());
|
|
- return;
|
|
|
|
|
|
+ found_source = true;
|
|
|
|
+ break;
|
|
case GDScriptParser::IdentifierNode::LOCAL_CONSTANT:
|
|
case GDScriptParser::IdentifierNode::LOCAL_CONSTANT:
|
|
case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
|
|
case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
|
|
p_identifier->set_datatype(p_identifier->constant_source->get_datatype());
|
|
p_identifier->set_datatype(p_identifier->constant_source->get_datatype());
|
|
p_identifier->is_constant = true;
|
|
p_identifier->is_constant = true;
|
|
// TODO: Constant should have a value on the node itself.
|
|
// TODO: Constant should have a value on the node itself.
|
|
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
|
|
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
|
|
- return;
|
|
|
|
|
|
+ found_source = true;
|
|
|
|
+ break;
|
|
case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
|
|
case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
|
|
p_identifier->variable_source->usages++;
|
|
p_identifier->variable_source->usages++;
|
|
[[fallthrough]];
|
|
[[fallthrough]];
|
|
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
|
|
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
|
|
p_identifier->set_datatype(p_identifier->variable_source->get_datatype());
|
|
p_identifier->set_datatype(p_identifier->variable_source->get_datatype());
|
|
- return;
|
|
|
|
|
|
+ found_source = true;
|
|
|
|
+ break;
|
|
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
|
|
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
|
|
p_identifier->set_datatype(p_identifier->bind_source->get_datatype());
|
|
p_identifier->set_datatype(p_identifier->bind_source->get_datatype());
|
|
- return;
|
|
|
|
|
|
+ found_source = true;
|
|
|
|
+ break;
|
|
case GDScriptParser::IdentifierNode::LOCAL_BIND: {
|
|
case GDScriptParser::IdentifierNode::LOCAL_BIND: {
|
|
GDScriptParser::DataType result = p_identifier->bind_source->get_datatype();
|
|
GDScriptParser::DataType result = p_identifier->bind_source->get_datatype();
|
|
result.is_constant = true;
|
|
result.is_constant = true;
|
|
p_identifier->set_datatype(result);
|
|
p_identifier->set_datatype(result);
|
|
- return;
|
|
|
|
- }
|
|
|
|
|
|
+ found_source = true;
|
|
|
|
+ } break;
|
|
case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE:
|
|
case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE:
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// Not a local, so check members.
|
|
// Not a local, so check members.
|
|
- reduce_identifier_from_base(p_identifier);
|
|
|
|
- if (p_identifier->get_datatype().is_set()) {
|
|
|
|
- // Found.
|
|
|
|
|
|
+ if (!found_source) {
|
|
|
|
+ reduce_identifier_from_base(p_identifier);
|
|
|
|
+ if (p_identifier->source != GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->get_datatype().is_set()) {
|
|
|
|
+ // Found.
|
|
|
|
+ found_source = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2567,6 +2591,33 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|
p_identifier->set_datatype(dummy); // Just so type is set to something.
|
|
p_identifier->set_datatype(dummy); // Just so type is set to something.
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void GDScriptAnalyzer::reduce_lambda(GDScriptParser::LambdaNode *p_lambda) {
|
|
|
|
+ // Lambda is always a Callable.
|
|
|
|
+ GDScriptParser::DataType lambda_type;
|
|
|
|
+ lambda_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
|
|
|
|
+ lambda_type.kind = GDScriptParser::DataType::BUILTIN;
|
|
|
|
+ lambda_type.builtin_type = Variant::CALLABLE;
|
|
|
|
+ p_lambda->set_datatype(lambda_type);
|
|
|
|
+
|
|
|
|
+ if (p_lambda->function == nullptr) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ GDScriptParser::FunctionNode *previous_function = parser->current_function;
|
|
|
|
+ parser->current_function = p_lambda->function;
|
|
|
|
+
|
|
|
|
+ lambda_stack.push_back(p_lambda);
|
|
|
|
+
|
|
|
|
+ for (int i = 0; i < p_lambda->function->parameters.size(); i++) {
|
|
|
|
+ resolve_parameter(p_lambda->function->parameters[i]);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ resolve_suite(p_lambda->function->body);
|
|
|
|
+
|
|
|
|
+ lambda_stack.pop_back();
|
|
|
|
+ parser->current_function = previous_function;
|
|
|
|
+}
|
|
|
|
+
|
|
void GDScriptAnalyzer::reduce_literal(GDScriptParser::LiteralNode *p_literal) {
|
|
void GDScriptAnalyzer::reduce_literal(GDScriptParser::LiteralNode *p_literal) {
|
|
p_literal->reduced_value = p_literal->value;
|
|
p_literal->reduced_value = p_literal->value;
|
|
p_literal->is_constant = true;
|
|
p_literal->is_constant = true;
|
|
@@ -3526,6 +3577,13 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) {
|
|
return ref;
|
|
return ref;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+const GDScriptParser::LambdaNode *GDScriptAnalyzer::get_current_lambda() const {
|
|
|
|
+ if (lambda_stack.size()) {
|
|
|
|
+ return lambda_stack.back()->get();
|
|
|
|
+ }
|
|
|
|
+ return nullptr;
|
|
|
|
+}
|
|
|
|
+
|
|
Error GDScriptAnalyzer::resolve_inheritance() {
|
|
Error GDScriptAnalyzer::resolve_inheritance() {
|
|
return resolve_inheritance(parser->head);
|
|
return resolve_inheritance(parser->head);
|
|
}
|
|
}
|