diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 78fcb90496c..a1d4ff41c0f 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -565,6 +565,9 @@
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).
+
+ 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].
+
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.
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index d4fa515a702..3d942210f4e 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -3758,8 +3758,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);
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 7ba47f8786f..77b56ca67c9 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/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"),
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index 5f47e120fa8..3567ef454f4 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/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
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/missing_await.gd b/modules/gdscript/tests/scripts/analyzer/warnings/missing_await.gd
new file mode 100644
index 00000000000..14ed99e3959
--- /dev/null
+++ b/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()
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/missing_await.out b/modules/gdscript/tests/scripts/analyzer/warnings/missing_await.out
new file mode 100644
index 00000000000..a1a4ab50f1f
--- /dev/null
+++ b/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.
diff --git a/modules/gdscript/tests/scripts/runtime/features/await_signal_with_parameters.gd b/modules/gdscript/tests/scripts/runtime/features/await_signal_with_parameters.gd
index ff0001676da..6f963afa14d 100644
--- a/modules/gdscript/tests/scripts/runtime/features/await_signal_with_parameters.gd
+++ b/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)
diff --git a/modules/gdscript/tests/scripts/runtime/features/emit_after_await.gd b/modules/gdscript/tests/scripts/runtime/features/emit_after_await.gd
index 21fd526acca..5684d1820be 100644
--- a/modules/gdscript/tests/scripts/runtime/features/emit_after_await.gd
+++ b/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()