Преглед изворни кода

Add opt-in GDScript warning for when calling coroutine without `await`

Mikael Hermansson пре 3 месеци
родитељ
комит
a3e58a385f

+ 3 - 0
doc/classes/ProjectSettings.xml

@@ -565,6 +565,9 @@
 		<member name="debug/gdscript/warnings/integer_division" type="int" setter="" getter="" default="1">
 			When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when dividing an integer by another integer (the decimal part will be discarded).
 		</member>
+		<member name="debug/gdscript/warnings/missing_await" type="int" setter="" getter="" default="0">
+			When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a coroutine without [code]await[/code].
+		</member>
 		<member name="debug/gdscript/warnings/missing_tool" type="int" setter="" getter="" default="1">
 			When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when the base class script has the [code]@tool[/code] annotation, but the current class script does not have it.
 		</member>

+ 8 - 2
modules/gdscript/gdscript_analyzer.cpp

@@ -3756,8 +3756,14 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
 		}
 	}
 
-	if (call_type.is_coroutine && !p_is_await && !p_is_root) {
-		push_error(vformat(R"*(Function "%s()" is a coroutine, so it must be called with "await".)*", p_call->function_name), p_call);
+	if (call_type.is_coroutine && !p_is_await) {
+		if (p_is_root) {
+#ifdef DEBUG_ENABLED
+			parser->push_warning(p_call, GDScriptWarning::MISSING_AWAIT);
+#endif // DEBUG_ENABLED
+		} else {
+			push_error(vformat(R"*(Function "%s()" is a coroutine, so it must be called with "await".)*", p_call->function_name), p_call);
+		}
 	}
 
 	p_call->set_datatype(call_type);

+ 3 - 0
modules/gdscript/gdscript_warning.cpp

@@ -118,6 +118,8 @@ String GDScriptWarning::get_message() const {
 			return R"(The "@static_unload" annotation is redundant because the file does not have a class with static variables.)";
 		case REDUNDANT_AWAIT:
 			return R"("await" keyword is unnecessary because the expression isn't a coroutine nor a signal.)";
+		case MISSING_AWAIT:
+			return R"("await" keyword might be desired because the expression is a coroutine.)";
 		case ASSERT_ALWAYS_TRUE:
 			return "Assert statement is redundant because the expression is always true.";
 		case ASSERT_ALWAYS_FALSE:
@@ -221,6 +223,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
 		PNAME("MISSING_TOOL"),
 		PNAME("REDUNDANT_STATIC_UNLOAD"),
 		PNAME("REDUNDANT_AWAIT"),
+		PNAME("MISSING_AWAIT"),
 		PNAME("ASSERT_ALWAYS_TRUE"),
 		PNAME("ASSERT_ALWAYS_FALSE"),
 		PNAME("INTEGER_DIVISION"),

+ 2 - 0
modules/gdscript/gdscript_warning.h

@@ -72,6 +72,7 @@ public:
 		MISSING_TOOL, // The base class script has the "@tool" annotation, but this script does not have it.
 		REDUNDANT_STATIC_UNLOAD, // The `@static_unload` annotation is used but the class does not have static data.
 		REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
+		MISSING_AWAIT, // await is not used but expression is a coroutine.
 		ASSERT_ALWAYS_TRUE, // Expression for assert argument is always true.
 		ASSERT_ALWAYS_FALSE, // Expression for assert argument is always false.
 		INTEGER_DIVISION, // Integer divide by integer, decimal part is discarded.
@@ -129,6 +130,7 @@ public:
 		WARN, // MISSING_TOOL
 		WARN, // REDUNDANT_STATIC_UNLOAD
 		WARN, // REDUNDANT_AWAIT
+		IGNORE, // MISSING_AWAIT
 		WARN, // ASSERT_ALWAYS_TRUE
 		WARN, // ASSERT_ALWAYS_FALSE
 		WARN, // INTEGER_DIVISION

+ 7 - 0
modules/gdscript/tests/scripts/analyzer/warnings/missing_await.gd

@@ -0,0 +1,7 @@
+func coroutine() -> void:
+	@warning_ignore("redundant_await")
+	await 0
+
+func test():
+	await coroutine()
+	coroutine()

+ 2 - 0
modules/gdscript/tests/scripts/analyzer/warnings/missing_await.out

@@ -0,0 +1,2 @@
+GDTEST_OK
+~~ WARNING at line 7: (MISSING_AWAIT) "await" keyword might be desired because the expression is a coroutine.

+ 3 - 0
modules/gdscript/tests/scripts/runtime/features/await_signal_with_parameters.gd

@@ -15,11 +15,14 @@ func await_two_parameters():
 	print(result)
 
 func test():
+	@warning_ignore("missing_await")
 	await_no_parameters()
 	no_parameters.emit()
 
+	@warning_ignore("missing_await")
 	await_one_parameter()
 	one_parameter.emit(1)
 
+	@warning_ignore("missing_await")
 	await_two_parameters()
 	two_parameters.emit(1, 2)

+ 1 - 0
modules/gdscript/tests/scripts/runtime/features/emit_after_await.gd

@@ -8,5 +8,6 @@ func async_func():
 	my_signal.emit()
 
 func test():
+	@warning_ignore("missing_await")
 	async_func()
 	my_signal.emit()