diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 28a44357ebc..ec20811385f 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -3384,7 +3384,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a } 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); + push_error(vformat(R"*(Cannot call non-static function "%s()" from the static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call); } else { push_error(vformat(R"*(Cannot call non-static function "%s()" from a static variable initializer.)*", p_call->function_name), p_call); } @@ -3801,6 +3801,8 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod if (is_base && (!base.is_meta_type || member.function->is_static || is_constructor)) { p_identifier->set_datatype(make_callable_type(member.function->info)); p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_FUNCTION; + p_identifier->function_source = member.function; + p_identifier->function_source_is_static = member.function->is_static; return; } } break; @@ -3849,6 +3851,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod if (method_info.name == p_identifier->name) { p_identifier->set_datatype(make_callable_type(method_info)); p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_FUNCTION; + p_identifier->function_source_is_static = method_info.flags & METHOD_FLAG_STATIC; return; } @@ -4029,25 +4032,37 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident } if (found_source) { - 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) { + const bool source_is_instance_variable = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE; + const bool source_is_instance_function = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_FUNCTION && !p_identifier->function_source_is_static; + const bool source_is_signal = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_SIGNAL; + + if (static_context && (source_is_instance_variable || source_is_instance_function || source_is_signal)) { // 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; } + String source_type; + if (source_is_instance_variable) { + source_type = "non-static variable"; + } else if (source_is_instance_function) { + source_type = "non-static function"; + } else { // source_is_signal + source_type = "signal"; + } + 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); + push_error(vformat(R"*(Cannot access %s "%s" from the static function "%s()".)*", source_type, p_identifier->name, parent_function->identifier->name), p_identifier); } else { - push_error(vformat(R"*(Cannot access %s "%s" from 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_type, p_identifier->name), p_identifier); } } 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) { + // If the identifier is a member variable (including the native class properties), member function, or a signal, + // we consider the lambda to be using `self`, so we keep a reference to the current instance. + if (source_is_instance_variable || source_is_instance_function || source_is_signal) { mark_lambda_use_self(); return; // No need to capture. } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 7fb9ffe9a53..1e67e2d496a 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -902,8 +902,11 @@ public: VariableNode *variable_source; ConstantNode *constant_source; SignalNode *signal_source; + FunctionNode *function_source; }; - FunctionNode *source_function = nullptr; + bool function_source_is_static = false; // For non-GDScript scripts. + + FunctionNode *source_function = nullptr; // TODO: Rename to disambiguate `function_source`. int usages = 0; // Useful for binds/iterator variable. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.gd new file mode 100644 index 00000000000..e041aeb914d --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.gd @@ -0,0 +1,10 @@ +# GH-91403 + +static func static_func(): + print(non_static_func) + +func non_static_func(): + pass + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.out b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.out new file mode 100644 index 00000000000..d8d6c8bc1b7 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot access non-static function "non_static_func" from the static function "static_func()". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.gd new file mode 100644 index 00000000000..36bc9dbf15a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.gd @@ -0,0 +1,15 @@ +# GH-91403 + +func non_static_func(): + pass + +static func static_func( + f := func (): + var g := func (): + print(non_static_func) + g.call() +): + f.call() + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.out b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.out new file mode 100644 index 00000000000..d8d6c8bc1b7 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot access non-static function "non_static_func" from the static function "static_func()". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static.out b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static.out index b78f1313453..c094c08cd8a 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot call non-static function "non_static_func()" from static function "static_func()". +Cannot call non-static function "non_static_func()" from the static function "static_func()". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda.out b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda.out index b78f1313453..c094c08cd8a 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot call non-static function "non_static_func()" from static function "static_func()". +Cannot call non-static function "non_static_func()" from the static function "static_func()". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda_param.out b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda_param.out index b78f1313453..c094c08cd8a 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda_param.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda_param.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot call non-static function "non_static_func()" from static function "static_func()". +Cannot call non-static function "non_static_func()" from the static function "static_func()". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.gd new file mode 100644 index 00000000000..7ae5bea7d77 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.gd @@ -0,0 +1,14 @@ +# GH-91403 + +func non_static_func(): + pass + +static var static_var = func (): + var f := func (): + var g := func (): + print(non_static_func) + g.call() + f.call() + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.out b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.out new file mode 100644 index 00000000000..153e81b4053 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot access non-static function "non_static_func" from a static variable initializer. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.gd new file mode 100644 index 00000000000..7479afc5327 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.gd @@ -0,0 +1,15 @@ +# GH-91403 + +func non_static_func(): + pass + +static var static_var: + set(_value): + var f := func (): + var g := func (): + print(non_static_func) + g.call() + f.call() + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.out b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.out new file mode 100644 index 00000000000..de43f2d3c44 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot access non-static function "non_static_func" from the static function "@static_var_setter()". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_call_non_static_in_lambda_setter.out b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_call_non_static_in_lambda_setter.out index cdf3ab2aebd..a285b800252 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_call_non_static_in_lambda_setter.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_call_non_static_in_lambda_setter.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot call non-static function "non_static_func()" from static function "@static_var_setter()". +Cannot call non-static function "non_static_func()" from the static function "@static_var_setter()". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.gd new file mode 100644 index 00000000000..a21b2c4470b --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.gd @@ -0,0 +1,11 @@ +# GH-91403 + +@static_unload + +func non_static(): + return "non static" + +static var static_var = Callable(non_static) + +func test(): + print("does not run") diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.out b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.out new file mode 100644 index 00000000000..a95069dc4f9 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot access non-static function "non_static" from a static variable initializer. diff --git a/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.gd b/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.gd new file mode 100644 index 00000000000..80ceb6d1a97 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.gd @@ -0,0 +1,75 @@ +@static_unload + +static var static_var +var non_static_var + +signal my_signal() + +static func static_func(): + pass + +func non_static_func(): + pass + +static var test_static_var_lambda = func (): + static_func() + print(static_func) + static_var = 1 + print(static_var) + +var test_non_static_var_lambda = func (): + static_func() + print(static_func) + static_var = 1 + print(static_var) + + non_static_func() + print(non_static_func) + non_static_var = 1 + print(non_static_var) + my_signal.emit() + print(my_signal) + +static var test_static_var_setter: + set(_value): + static_func() + print(static_func) + static_var = 1 + print(static_var) + +var test_non_static_var_setter: + set(_value): + static_func() + print(static_func) + static_var = 1 + print(static_var) + + non_static_func() + print(non_static_func) + non_static_var = 1 + print(non_static_var) + my_signal.emit() + print(my_signal) + +static func test_static_func(): + static_func() + print(static_func) + static_var = 1 + print(static_var) + +func test_non_static_func(): + static_func() + print(static_func) + static_var = 1 + print(static_var) + + non_static_func() + print(non_static_func) + non_static_var = 1 + print(non_static_var) + my_signal.emit() + print(my_signal) + +func test(): + test_static_var_lambda = null + test_non_static_var_lambda = null diff --git a/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.out b/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.out new file mode 100644 index 00000000000..d73c5eb7cde --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.out @@ -0,0 +1 @@ +GDTEST_OK