GDScript: Add warning if non-`@tool` class extends `@tool` class

This commit is contained in:
Danil Alexeev 2024-07-04 10:23:44 +03:00
parent 6a13fdcae3
commit 3f52871f70
No known key found for this signature in database
GPG Key ID: 124453E157DA8DC7
9 changed files with 55 additions and 0 deletions

View File

@ -533,6 +533,9 @@
<member name="debug/gdscript/warnings/integer_division" type="int" setter="" getter="" default="1"> <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). 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>
<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>
<member name="debug/gdscript/warnings/narrowing_conversion" type="int" setter="" getter="" default="1"> <member name="debug/gdscript/warnings/narrowing_conversion" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when passing a floating-point value to a function that expects an integer (it will be converted and lose precision). When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when passing a floating-point value to a function that expects an integer (it will be converted and lose precision).
</member> </member>

View File

@ -411,6 +411,12 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
return err; return err;
} }
#ifdef DEBUG_ENABLED
if (!parser->_is_tool && ext_parser->get_parser()->_is_tool) {
parser->push_warning(p_class, GDScriptWarning::MISSING_TOOL);
}
#endif
base = ext_parser->get_parser()->head->get_datatype(); base = ext_parser->get_parser()->head->get_datatype();
} else { } else {
if (p_class->extends.is_empty()) { if (p_class->extends.is_empty()) {
@ -438,6 +444,13 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), id); push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), id);
return err; return err;
} }
#ifdef DEBUG_ENABLED
if (!parser->_is_tool && base_parser->get_parser()->_is_tool) {
parser->push_warning(p_class, GDScriptWarning::MISSING_TOOL);
}
#endif
base = base_parser->get_parser()->head->get_datatype(); base = base_parser->get_parser()->head->get_datatype();
} }
} else if (ProjectSettings::get_singleton()->has_autoload(name) && ProjectSettings::get_singleton()->get_autoload(name).is_singleton) { } else if (ProjectSettings::get_singleton()->has_autoload(name) && ProjectSettings::get_singleton()->get_autoload(name).is_singleton) {
@ -458,6 +471,13 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), id); push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), id);
return err; return err;
} }
#ifdef DEBUG_ENABLED
if (!parser->_is_tool && info_parser->get_parser()->_is_tool) {
parser->push_warning(p_class, GDScriptWarning::MISSING_TOOL);
}
#endif
base = info_parser->get_parser()->head->get_datatype(); base = info_parser->get_parser()->head->get_datatype();
} else if (class_exists(name)) { } else if (class_exists(name)) {
if (Engine::get_singleton()->has_singleton(name)) { if (Engine::get_singleton()->has_singleton(name)) {

View File

@ -109,6 +109,8 @@ String GDScriptWarning::get_message() const {
case STATIC_CALLED_ON_INSTANCE: case STATIC_CALLED_ON_INSTANCE:
CHECK_SYMBOLS(2); CHECK_SYMBOLS(2);
return vformat(R"*(The function "%s()" is a static function but was called from an instance. Instead, it should be directly called from the type: "%s.%s()".)*", symbols[0], symbols[1], symbols[0]); return vformat(R"*(The function "%s()" is a static function but was called from an instance. Instead, it should be directly called from the type: "%s.%s()".)*", symbols[0], symbols[1], symbols[0]);
case MISSING_TOOL:
return R"(The base class script has the "@tool" annotation, but this script does not have it.)";
case REDUNDANT_STATIC_UNLOAD: case REDUNDANT_STATIC_UNLOAD:
return R"(The "@static_unload" annotation is redundant because the file does not have a class with static variables.)"; return R"(The "@static_unload" annotation is redundant because the file does not have a class with static variables.)";
case REDUNDANT_AWAIT: case REDUNDANT_AWAIT:
@ -219,6 +221,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"UNSAFE_VOID_RETURN", "UNSAFE_VOID_RETURN",
"RETURN_VALUE_DISCARDED", "RETURN_VALUE_DISCARDED",
"STATIC_CALLED_ON_INSTANCE", "STATIC_CALLED_ON_INSTANCE",
"MISSING_TOOL",
"REDUNDANT_STATIC_UNLOAD", "REDUNDANT_STATIC_UNLOAD",
"REDUNDANT_AWAIT", "REDUNDANT_AWAIT",
"ASSERT_ALWAYS_TRUE", "ASSERT_ALWAYS_TRUE",

View File

@ -70,6 +70,7 @@ public:
UNSAFE_VOID_RETURN, // Function returns void but returned a call to a function that can't be type checked. UNSAFE_VOID_RETURN, // Function returns void but returned a call to a function that can't be type checked.
RETURN_VALUE_DISCARDED, // Function call returns something but the value isn't used. RETURN_VALUE_DISCARDED, // Function call returns something but the value isn't used.
STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself. STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself.
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_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). REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
ASSERT_ALWAYS_TRUE, // Expression for assert argument is always true. ASSERT_ALWAYS_TRUE, // Expression for assert argument is always true.
@ -123,6 +124,7 @@ public:
WARN, // UNSAFE_VOID_RETURN WARN, // UNSAFE_VOID_RETURN
IGNORE, // RETURN_VALUE_DISCARDED // Too spammy by default on common cases (connect, Tween, etc.). IGNORE, // RETURN_VALUE_DISCARDED // Too spammy by default on common cases (connect, Tween, etc.).
WARN, // STATIC_CALLED_ON_INSTANCE WARN, // STATIC_CALLED_ON_INSTANCE
WARN, // MISSING_TOOL
WARN, // REDUNDANT_STATIC_UNLOAD WARN, // REDUNDANT_STATIC_UNLOAD
WARN, // REDUNDANT_AWAIT WARN, // REDUNDANT_AWAIT
WARN, // ASSERT_ALWAYS_TRUE WARN, // ASSERT_ALWAYS_TRUE

View File

@ -0,0 +1,7 @@
extends "./non_tool_extends_tool.notest.gd"
class InnerClass extends "./non_tool_extends_tool.notest.gd":
pass
func test():
pass

View File

@ -0,0 +1,9 @@
GDTEST_OK
>> WARNING
>> Line: 1
>> MISSING_TOOL
>> The base class script has the "@tool" annotation, but this script does not have it.
>> WARNING
>> Line: 3
>> MISSING_TOOL
>> The base class script has the "@tool" annotation, but this script does not have it.

View File

@ -0,0 +1,9 @@
@warning_ignore("missing_tool")
extends "./non_tool_extends_tool.notest.gd"
@warning_ignore("missing_tool")
class InnerClass extends "./non_tool_extends_tool.notest.gd":
pass
func test():
pass