GDScript: Fix typing of lambda functions

This commit is contained in:
Dmitrii Maganov 2022-12-28 07:41:03 +02:00
parent b14f7aa9f9
commit 532ffc30bd
12 changed files with 65 additions and 26 deletions

View file

@ -1195,10 +1195,6 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root
case GDScriptParser::Node::FOR:
resolve_for(static_cast<GDScriptParser::ForNode *>(p_node));
break;
case GDScriptParser::Node::FUNCTION:
resolve_function_signature(static_cast<GDScriptParser::FunctionNode *>(p_node));
resolve_function_body(static_cast<GDScriptParser::FunctionNode *>(p_node));
break;
case GDScriptParser::Node::IF:
resolve_if(static_cast<GDScriptParser::IfNode *>(p_node));
break;
@ -1258,6 +1254,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root
case GDScriptParser::Node::BREAKPOINT:
case GDScriptParser::Node::CONTINUE:
case GDScriptParser::Node::ENUM:
case GDScriptParser::Node::FUNCTION:
case GDScriptParser::Node::PASS:
case GDScriptParser::Node::SIGNAL:
// Nothing to do.
@ -1269,13 +1266,15 @@ void GDScriptAnalyzer::resolve_annotation(GDScriptParser::AnnotationNode *p_anno
// TODO: Add second validation function for annotations, so they can use checked types.
}
void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source) {
void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source, bool p_is_lambda) {
if (p_source == nullptr) {
p_source = p_function;
}
StringName function_name = p_function->identifier != nullptr ? p_function->identifier->name : StringName();
if (p_function->get_datatype().is_resolving()) {
push_error(vformat(R"(Could not resolve function "%s": Cyclic reference.)", p_function->identifier->name), p_source);
push_error(vformat(R"(Could not resolve function "%s": Cyclic reference.)", function_name), p_source);
return;
}
@ -1301,7 +1300,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
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("_")) {
parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, p_function->identifier->name, p_function->parameters[i]->identifier->name);
parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, function_name, p_function->parameters[i]->identifier->name);
}
is_shadowing(p_function->parameters[i]->identifier, "function parameter");
#endif // DEBUG_ENABLED
@ -1318,7 +1317,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
#endif // TOOLS_ENABLED
}
if (p_function->identifier->name == GDScriptLanguage::get_singleton()->strings._init) {
if (!p_is_lambda && function_name == GDScriptLanguage::get_singleton()->strings._init) {
// Constructor.
GDScriptParser::DataType return_type = parser->current_class->get_datatype();
return_type.is_meta_type = false;
@ -1350,7 +1349,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
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)) {
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)) {
bool valid = p_function->is_static == is_static;
valid = valid && parent_return_type == p_function->get_datatype();
@ -1365,7 +1364,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
if (!valid) {
// Compute parent signature as a string to show in the error message.
String parent_signature = p_function->identifier->name.operator String() + "(";
String parent_signature = function_name.operator String() + "(";
int j = 0;
for (const GDScriptParser::DataType &par_type : parameters_types) {
if (j > 0) {
@ -1404,7 +1403,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
parser->current_function = previous_function;
}
void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_function) {
void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_function, bool p_is_lambda) {
if (p_function->resolved_body) {
return;
}
@ -1422,7 +1421,7 @@ void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_fun
return_type.type_source = GDScriptParser::DataType::INFERRED;
p_function->set_datatype(p_function->body->get_datatype());
} else if (p_function->get_datatype().is_hard_type() && (p_function->get_datatype().kind != GDScriptParser::DataType::BUILTIN || p_function->get_datatype().builtin_type != Variant::NIL)) {
if (!p_function->body->has_return && p_function->identifier->name != GDScriptLanguage::get_singleton()->strings._init) {
if (!p_function->body->has_return && (p_is_lambda || p_function->identifier->name != GDScriptLanguage::get_singleton()->strings._init)) {
push_error(R"(Not all code paths return a value.)", p_function);
}
}
@ -3297,16 +3296,10 @@ void GDScriptAnalyzer::reduce_lambda(GDScriptParser::LambdaNode *p_lambda) {
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);
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) {
@ -3331,9 +3324,6 @@ void GDScriptAnalyzer::reduce_lambda(GDScriptParser::LambdaNode *p_lambda) {
p_lambda->function->parameters_indices[capture->name] = i;
}
}
lambda_stack.pop_back();
parser->current_function = previous_function;
}
void GDScriptAnalyzer::reduce_literal(GDScriptParser::LiteralNode *p_literal) {

View file

@ -65,8 +65,8 @@ class GDScriptAnalyzer {
void resolve_class_interface(GDScriptParser::ClassNode *p_class, bool p_recursive);
void resolve_class_body(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr);
void resolve_class_body(GDScriptParser::ClassNode *p_class, bool p_recursive);
void resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source = nullptr);
void resolve_function_body(GDScriptParser::FunctionNode *p_function);
void resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source = nullptr, bool p_is_lambda = false);
void resolve_function_body(GDScriptParser::FunctionNode *p_function, bool p_is_lambda = false);
void resolve_node(GDScriptParser::Node *p_node, bool p_is_root = true);
void resolve_suite(GDScriptParser::SuiteNode *p_suite);
void resolve_assignable(GDScriptParser::AssignableNode *p_assignable, const char *p_kind);

View file

@ -0,0 +1,4 @@
func test():
var lambda := func() -> int:
print('no return')
lambda.call()

View file

@ -0,0 +1,2 @@
GDTEST_ANALYZER_ERROR
Not all code paths return a value.

View file

@ -0,0 +1,4 @@
func test():
var lambda := func() -> int:
return 'string'
print(lambda.call())

View file

@ -0,0 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot return value of type "String" because the function return type is "int".

View file

@ -0,0 +1,12 @@
func test():
var lambda_0 := func() -> void:
print(0)
lambda_0.call()
var lambda_1 := func(printed: int) -> void:
print(printed)
lambda_1.call(1)
var lambda_2 := func(identity: int) -> int:
return identity
print(lambda_2.call(2))

View file

@ -0,0 +1,4 @@
GDTEST_OK
0
1
2

View file

@ -0,0 +1,6 @@
var shadow: int
func test():
var lambda := func(shadow: String) -> void:
print(shadow)
lambda.call('shadow')

View file

@ -0,0 +1,6 @@
GDTEST_OK
>> WARNING
>> Line: 4
>> SHADOWED_VARIABLE
>> The local function parameter "shadow" is shadowing an already-declared variable at line 1.
shadow

View file

@ -0,0 +1,4 @@
func test():
var lambda := func(unused: Variant) -> void:
pass
lambda.call()

View file

@ -0,0 +1,5 @@
GDTEST_OK
>> WARNING
>> Line: 2
>> UNUSED_PARAMETER
>>