Forráskód Böngészése

GDScript: Fix access non-static members in static context

Danil Alexeev 1 éve
szülő
commit
8122a27eac
18 módosított fájl, 182 hozzáadás és 13 törlés
  1. 23 8
      modules/gdscript/gdscript_analyzer.cpp
  2. 4 1
      modules/gdscript/gdscript_parser.h
  3. 10 0
      modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.gd
  4. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.out
  5. 15 0
      modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.gd
  6. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.out
  7. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static.out
  8. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda.out
  9. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda_param.out
  10. 14 0
      modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.gd
  11. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.out
  12. 15 0
      modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.gd
  13. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.out
  14. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/static_var_init_call_non_static_in_lambda_setter.out
  15. 11 0
      modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.gd
  16. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.out
  17. 75 0
      modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.gd
  18. 1 0
      modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.out

+ 23 - 8
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.
 			}

+ 4 - 1
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.
 

+ 10 - 0
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

+ 2 - 0
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()".

+ 15 - 0
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

+ 2 - 0
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()".

+ 1 - 1
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()".

+ 1 - 1
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()".

+ 1 - 1
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()".

+ 14 - 0
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

+ 2 - 0
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.

+ 15 - 0
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

+ 2 - 0
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()".

+ 1 - 1
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()".

+ 11 - 0
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")

+ 2 - 0
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.

+ 75 - 0
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

+ 1 - 0
modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.out

@@ -0,0 +1 @@
+GDTEST_OK