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()