diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index e37f099cf38..f8f43411436 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -530,151 +530,154 @@
If the [code]--log-file <file>[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url] is used, log rotation is always disabled.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an [code]assert[/code] call always evaluates to [code]false[/code].
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when an [code]assert[/code] call always evaluates to [code]false[/code].
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an [code]assert[/code] call always evaluates to [code]true[/code].
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when an [code]assert[/code] call always evaluates to [code]true[/code].
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local variable captured by a lambda is reassigned, since this does not modify the outer local variable.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a local variable captured by a lambda is reassigned, since this does not modify the outer local variable.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier contains characters that can be confused with something else, like when mixing different alphabets.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when an identifier contains characters that can be confused with something else, like when mixing different alphabets.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier declared in the nested block has the same name as an identifier declared below in the parent block.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when an identifier declared in the nested block has the same name as an identifier declared below in the parent block.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier that will be shadowed below in the block is used.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when an identifier that will be shadowed below in the block is used.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when deprecated keywords are used.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when deprecated keywords are used.
[b]Note:[/b] There are currently no deprecated keywords, so this warning is never produced.
+
+ The rules for including or excluding scripts when generating warnings, as a dictionary. Each rule is an entry consisting of a directory path (key) and a decision (value). When trying to generate a warning, the GDScript parser chooses the most specific rule, i.e. the most nested directory containing the script. If the decision is [b]Exclude[/b], warnings are not generated for this script. If the decision is [b]Include[/b] or the script doesn't satisfy any of the rules, the warning configuration specified in the Project Settings is applied.
+ It is recommended to include your own addons/libraries, either project-specific or actively being developed at the moment. Third-party or project-agnostic addons/libraries should be excluded, as they may be incompatible with the project's warning configuration.
+ [b]Note:[/b] It is not recommended to remove or change the rule for [code]"res://addons"[/code] as the project's warning configuration may break third-party addons. Instead, consider including individual addons, if necessary.
+ [b]Note:[/b] The editor does not check whether the specified paths are existing directories. It also does not automatically update these paths when directories are moved.
+
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an empty file is parsed.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when an empty file is parsed.
If [code]true[/code], enables specific GDScript warnings (see [code]debug/gdscript/warnings/*[/code] settings). If [code]false[/code], disables all GDScript warnings.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a variable has an enum type but no explicit default value, but only if the enum does not contain [code]0[/code] as a valid value.
-
-
- If [code]true[/code], scripts in the [code]res://addons[/code] folder will not generate warnings.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a variable has an enum type but no explicit default value, but only if the enum does not contain [code]0[/code] as a valid value.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when [method Node.get_node] (or the shorthand [code]$[/code]) is used as default value of a class variable without the [code]@onready[/code] annotation.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when [method Node.get_node] (or the shorthand [code]$[/code]) is used as default value of a class variable without the [code]@onready[/code] annotation.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a ternary operator may emit values with incompatible types.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a ternary operator may emit values with incompatible types.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a static inferred type uses a [Variant] as initial value, which makes the static type to also be Variant.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a static inferred type uses a [Variant] as initial value, which makes the static type to also be Variant.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a variable, constant, or parameter has an implicitly inferred static type. In GDScript, type inference is performed by declaring a variable with [code]:=[/code] instead of [code]=[/code] and leaving out the type specifier. For example, [code]var x := 1[/code] will [i]infer[/i] the [int] type, while [code]var x: int = 1[/code] explicitly declares the variable as [int].
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a variable, constant, or parameter has an implicitly inferred static type. In GDScript, type inference is performed by declaring a variable with [code]:=[/code] instead of [code]=[/code] and leaving out the type specifier. For example, [code]var x := 1[/code] will [i]infer[/i] the [int] type, while [code]var x: int = 1[/code] explicitly declares the variable as [int].
[b]Note:[/b] This warning is recommended [i]in addition[/i] to [member debug/gdscript/warnings/untyped_declaration] if you want to always specify the type explicitly. Having [code]INFERRED_DECLARATION[/code] warning level higher than [code]UNTYPED_DECLARATION[/code] warning level makes little sense and is not recommended.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when trying to use an integer as an enum without an explicit cast.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when trying to use an integer as an enum without an explicit cast.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when trying to use an integer as an enum when there is no matching enum member for that numeric value.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when trying to use an integer as an enum when there is no matching enum member for that numeric value.
- 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 [b]Warn[/b] or [b]Error[/b], 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 [b]Warn[/b] or [b]Error[/b], 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.
+ When set to [b]Warn[/b] or [b]Error[/b], 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.
- 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 [b]Warn[/b] or [b]Error[/b], 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 a method in the script overrides a native method, because it may not behave as expected.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a method in the script overrides a native method, because it may not behave as expected.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when the [code]@onready[/code] annotation is used together with the [code]@export[/code] annotation, since it may not behave as expected.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when the [code]@onready[/code] annotation is used together with the [code]@export[/code] annotation, since it may not behave as expected.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a function that is not a coroutine is called with await.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a function that is not a coroutine is called with await.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when the [code]@static_unload[/code] annotation is used in a script without any static variables.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when the [code]@static_unload[/code] annotation is used in a script without any static variables.
When enabled, using a property, enum, or function that was renamed since Godot 3 will produce a hint if an error occurs.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a function without using its return value (by assigning it to a variable or using it as a function argument). These return values are sometimes used to indicate possible errors using the [enum Error] enum.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when calling a function without using its return value (by assigning it to a variable or using it as a function argument). These return values are sometimes used to indicate possible errors using the [enum Error] enum.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when defining a local or member variable, signal, or enum that would have the same name as a built-in function or global class name, thus shadowing it.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when defining a local or member variable, signal, or enum that would have the same name as a built-in function or global class name, thus shadowing it.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local variable or local constant shadows a member declared in the current class.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a local variable or local constant shadows a member declared in the current class.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local variable or local constant shadows a member declared in a base class.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a local variable or local constant shadows a member declared in a base class.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling an expression that may have no effect on the surrounding code, such as writing [code]2 + 2[/code] as a statement.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when calling an expression that may have no effect on the surrounding code, such as writing [code]2 + 2[/code] as a statement.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a ternary expression that may have no effect on the surrounding code, such as writing [code]42 if active else 0[/code] as a statement.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when calling a ternary expression that may have no effect on the surrounding code, such as writing [code]42 if active else 0[/code] as a statement.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a static method from an instance of a class instead of from the class directly.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when calling a static method from an instance of a class instead of from the class directly.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when using a variable that wasn't previously assigned.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when using a variable that wasn't previously assigned.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when assigning a variable using an assignment operator like [code]+=[/code] if the variable wasn't previously assigned.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when assigning a variable using an assignment operator like [code]+=[/code] if the variable wasn't previously assigned.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when unreachable code is detected (such as after a [code]return[/code] statement that will always be executed).
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when unreachable code is detected (such as after a [code]return[/code] statement that will always be executed).
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an unreachable [code]match[/code] pattern is detected.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when an unreachable [code]match[/code] pattern is detected.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when using an expression whose type may not be compatible with the function parameter expected.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when using an expression whose type may not be compatible with the function parameter expected.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a [Variant] value is cast to a non-Variant.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a [Variant] value is cast to a non-Variant.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a method whose presence is not guaranteed at compile-time in the class.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when calling a method whose presence is not guaranteed at compile-time in the class.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when accessing a property whose presence is not guaranteed at compile-time in the class.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when accessing a property whose presence is not guaranteed at compile-time in the class.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when returning a call from a [code]void[/code] function when such call cannot be guaranteed to be also [code]void[/code].
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when returning a call from a [code]void[/code] function when such call cannot be guaranteed to be also [code]void[/code].
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a variable or parameter has no static type, or if a function has no static return type.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a variable or parameter has no static type, or if a function has no static return type.
[b]Note:[/b] This warning is recommended together with [member EditorSettings.text_editor/completion/add_type_hints] to help achieve type safety.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local constant is never used.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a local constant is never used.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a function parameter is never used.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a function parameter is never used.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a private member variable is never used.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a private member variable is never used.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a signal is declared but never explicitly used in the class.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a signal is declared but never explicitly used in the class.
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local variable is unused.
+ When set to [b]Warn[/b] or [b]Error[/b], produces a warning or an error respectively when a local variable is unused.
Message to be displayed before the backtrace when the engine crashes. By default, this message is only used in exported projects due to the editor-only override applied to this setting.
diff --git a/editor/plugins/plugin_config_dialog.cpp b/editor/plugins/plugin_config_dialog.cpp
index c16225b2126..7aefbd974f5 100644
--- a/editor/plugins/plugin_config_dialog.cpp
+++ b/editor/plugins/plugin_config_dialog.cpp
@@ -131,6 +131,9 @@ void PluginConfigDialog::_on_required_text_changed() {
if ((!script_edit->get_text().get_extension().is_empty() && script_edit->get_text().get_extension() != ext) || script_edit->get_text().ends_with(".")) {
validation_panel->set_message(MSG_ID_SCRIPT, vformat(TTR("Script extension must match chosen language extension (.%s)."), ext), EditorValidationPanel::MSG_ERROR);
}
+ if (language->get_name() == "GDScript") {
+ validation_panel->set_message(MSG_ID_ENABLE_WARNINGS, TTR("Consider enabling GDScript warnings for this plugin by adding an entry for it to the project setting Debug > GDScript > Warnings > Directory Rules."), EditorValidationPanel::MSG_INFO);
+ }
}
String PluginConfigDialog::_get_subfolder() {
@@ -317,6 +320,7 @@ PluginConfigDialog::PluginConfigDialog() {
validation_panel->add_line(MSG_ID_SCRIPT, TTR("Script extension is valid."));
validation_panel->add_line(MSG_ID_SUBFOLDER, TTR("Subfolder name is valid."));
validation_panel->add_line(MSG_ID_ACTIVE, "");
+ validation_panel->add_line(MSG_ID_ENABLE_WARNINGS, "");
validation_panel->set_update_callback(callable_mp(this, &PluginConfigDialog::_on_required_text_changed));
validation_panel->set_accept_button(get_ok_button());
diff --git a/editor/plugins/plugin_config_dialog.h b/editor/plugins/plugin_config_dialog.h
index fed7293cb69..1e1218fe445 100644
--- a/editor/plugins/plugin_config_dialog.h
+++ b/editor/plugins/plugin_config_dialog.h
@@ -47,6 +47,7 @@ class PluginConfigDialog : public ConfirmationDialog {
MSG_ID_SUBFOLDER,
MSG_ID_SCRIPT,
MSG_ID_ACTIVE,
+ MSG_ID_ENABLE_WARNINGS,
};
LineEdit *name_edit = nullptr;
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index d8664bb7966..9ebcb2b9602 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -2282,11 +2282,18 @@ void GDScriptLanguage::init() {
GDExtensionManager::get_singleton()->connect("extension_loaded", callable_mp(this, &GDScriptLanguage::_extension_loaded));
GDExtensionManager::get_singleton()->connect("extension_unloading", callable_mp(this, &GDScriptLanguage::_extension_unloading));
}
-#endif
+#endif // TOOLS_ENABLED
+
+#ifdef DEBUG_ENABLED
+ GDScriptParser::update_project_settings();
+ if (!ProjectSettings::get_singleton()->is_connected("settings_changed", callable_mp_static(&GDScriptParser::update_project_settings))) {
+ ProjectSettings::get_singleton()->connect("settings_changed", callable_mp_static(&GDScriptParser::update_project_settings));
+ }
+#endif // DEBUG_ENABLED
#ifdef TESTS_ENABLED
GDScriptTests::GDScriptTestRunner::handle_cmdline();
-#endif
+#endif // TESTS_ENABLED
}
#ifdef TOOLS_ENABLED
@@ -2950,7 +2957,7 @@ GDScriptLanguage::GDScriptLanguage() {
profiling = false;
profile_native_calls = false;
script_frame_time = 0;
-#endif
+#endif // DEBUG_ENABLED
_debug_max_call_stack = GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "512," + itos(GDScriptFunction::MAX_CALL_DEPTH - 1) + ",1"), 1024);
track_call_stack = GLOBAL_DEF_RST("debug/settings/gdscript/always_track_call_stacks", false);
@@ -2961,20 +2968,29 @@ GDScriptLanguage::GDScriptLanguage() {
track_locals = track_locals || EngineDebugger::is_active();
GLOBAL_DEF("debug/gdscript/warnings/enable", true);
- GLOBAL_DEF("debug/gdscript/warnings/exclude_addons", true);
- GLOBAL_DEF("debug/gdscript/warnings/renamed_in_godot_4_hint", true);
+
+ GLOBAL_DEF(PropertyInfo(Variant::DICTIONARY,
+ "debug/gdscript/warnings/directory_rules",
+ PROPERTY_HINT_TYPE_STRING,
+ vformat("%d/%d:;%d/%d:Exclude,Include", Variant::STRING, PROPERTY_HINT_DIR, Variant::INT, PROPERTY_HINT_ENUM)),
+ Dictionary({ { "res://addons", GDScriptParser::WarningDirectoryRule::DECISION_EXCLUDE } }));
+
for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
- GDScriptWarning::Code code = (GDScriptWarning::Code)i;
- Variant default_enabled = GDScriptWarning::get_default_value(code);
- String path = GDScriptWarning::get_settings_path_from_code(code);
- GLOBAL_DEF(GDScriptWarning::get_property_info(code), default_enabled);
- }
+ const GDScriptWarning::Code code = (GDScriptWarning::Code)i;
+ const Variant default_value = GDScriptWarning::get_default_value(code);
+ GLOBAL_DEF(GDScriptWarning::get_property_info(code), default_value);
#ifndef DISABLE_DEPRECATED
- ProjectSettings::get_singleton()->set_as_internal("debug/gdscript/warnings/property_used_as_function", true);
- ProjectSettings::get_singleton()->set_as_internal("debug/gdscript/warnings/constant_used_as_function", true);
- ProjectSettings::get_singleton()->set_as_internal("debug/gdscript/warnings/function_used_as_property", true);
-#endif
+ if (i >= GDScriptWarning::FIRST_DEPRECATED_WARNING) {
+ const String setting_path = GDScriptWarning::get_setting_path_from_code(code);
+ ProjectSettings::get_singleton()->set_as_internal(setting_path, true);
+ }
+#endif // DISABLE_DEPRECATED
+ }
+
+ // TODO: This setting has nothing to do with warnings. It should be moved at the next compatibility breakage,
+ // if the setting is still relevant at that time.
+ GLOBAL_DEF("debug/gdscript/warnings/renamed_in_godot_4_hint", true);
#endif // DEBUG_ENABLED
}
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 38f710ae1a8..8b4ae0b65f1 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -1010,7 +1010,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
if (warning_code >= GDScriptWarning::FIRST_DEPRECATED_WARNING) {
break; // Don't suggest deprecated warnings as they are never produced.
}
-#endif
+#endif // DISABLE_DEPRECATED
ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
warning.insert_text = warning.display.quote(p_quote_style);
r_result.insert(warning.display, warning);
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index ca64ba73862..06056ca940d 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -68,9 +68,15 @@ Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
return Variant::VARIANT_MAX;
}
+#ifdef DEBUG_ENABLED
+bool GDScriptParser::is_project_ignoring_warnings = false;
+GDScriptWarning::WarnLevel GDScriptParser::warning_levels[GDScriptWarning::WARNING_MAX];
+LocalVector GDScriptParser::warning_directory_rules;
+#endif // DEBUG_ENABLED
+
#ifdef TOOLS_ENABLED
HashMap GDScriptParser::theme_color_names;
-#endif
+#endif // TOOLS_ENABLED
HashMap GDScriptParser::valid_annotations;
@@ -89,6 +95,53 @@ bool GDScriptParser::annotation_exists(const String &p_annotation_name) const {
return valid_annotations.has(p_annotation_name);
}
+#ifdef DEBUG_ENABLED
+void GDScriptParser::update_project_settings() {
+ is_project_ignoring_warnings = !GLOBAL_GET("debug/gdscript/warnings/enable").booleanize();
+
+ for (int i = 0; i < GDScriptWarning::WARNING_MAX; i++) {
+ const String setting_path = GDScriptWarning::get_setting_path_from_code((GDScriptWarning::Code)i);
+ warning_levels[i] = (GDScriptWarning::WarnLevel)(int)GLOBAL_GET(setting_path);
+ }
+
+#ifndef DISABLE_DEPRECATED
+ // We do not use `GLOBAL_GET`, since we check without taking overrides into account. We leave the migration of non-trivial configurations to the user.
+ if (unlikely(ProjectSettings::get_singleton()->has_setting("debug/gdscript/warnings/exclude_addons"))) {
+ const bool is_excluding_addons = ProjectSettings::get_singleton()->get_setting("debug/gdscript/warnings/exclude_addons", true).booleanize();
+ ProjectSettings::get_singleton()->clear("debug/gdscript/warnings/exclude_addons");
+
+ Dictionary rules = ProjectSettings::get_singleton()->get_setting("debug/gdscript/warnings/directory_rules");
+ rules["res://addons"] = is_excluding_addons ? WarningDirectoryRule::DECISION_EXCLUDE : WarningDirectoryRule::DECISION_INCLUDE;
+ ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/directory_rules", rules);
+ }
+#endif // DISABLE_DEPRECATED
+
+ warning_directory_rules.clear();
+
+ const Dictionary rules = GLOBAL_GET("debug/gdscript/warnings/directory_rules");
+ for (const KeyValue &kv : rules) {
+ String dir = kv.key.operator String().simplify_path();
+ ERR_CONTINUE_MSG(!dir.begins_with("res://"), R"(Paths in the project setting "debug/gdscript/warnings/directory_rules" keys must start with the "res://" prefix.)");
+ if (!dir.ends_with("/")) {
+ dir += '/';
+ }
+
+ const int decision = kv.value;
+ ERR_CONTINUE(decision < 0 || decision >= WarningDirectoryRule::DECISION_MAX);
+
+ warning_directory_rules.push_back({ dir, (WarningDirectoryRule::Decision)decision });
+ }
+
+ struct RuleSort {
+ bool operator()(const WarningDirectoryRule &p_a, const WarningDirectoryRule &p_b) const {
+ return p_a.directory_path.count("/") > p_b.directory_path.count("/");
+ }
+ };
+
+ warning_directory_rules.sort_custom();
+}
+#endif // DEBUG_ENABLED
+
GDScriptParser::GDScriptParser() {
// Register valid annotations.
if (unlikely(valid_annotations.is_empty())) {
@@ -137,11 +190,10 @@ GDScriptParser::GDScriptParser() {
}
#ifdef DEBUG_ENABLED
- is_ignoring_warnings = !(bool)GLOBAL_GET("debug/gdscript/warnings/enable");
for (int i = 0; i < GDScriptWarning::WARNING_MAX; i++) {
warning_ignore_start_lines[i] = INT_MAX;
}
-#endif
+#endif // DEBUG_ENABLED
#ifdef TOOLS_ENABLED
if (unlikely(theme_color_names.is_empty())) {
@@ -161,7 +213,7 @@ GDScriptParser::GDScriptParser() {
theme_color_names.insert("a", "axis_w_color");
theme_color_names.insert("a8", "axis_w_color");
}
-#endif
+#endif // TOOLS_ENABLED
}
GDScriptParser::~GDScriptParser() {
@@ -195,13 +247,11 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_
ERR_FAIL_NULL(p_source);
ERR_FAIL_INDEX(p_code, GDScriptWarning::WARNING_MAX);
- if (is_ignoring_warnings) {
+ if (is_project_ignoring_warnings || is_script_ignoring_warnings) {
return;
}
- if (GLOBAL_GET_CACHED(bool, "debug/gdscript/warnings/exclude_addons") && script_path.begins_with("res://addons/")) {
- return;
- }
- GDScriptWarning::WarnLevel warn_level = (GDScriptWarning::WarnLevel)(int)GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(p_code));
+
+ const GDScriptWarning::WarnLevel warn_level = warning_levels[p_code];
if (warn_level == GDScriptWarning::IGNORE) {
return;
}
@@ -251,6 +301,24 @@ void GDScriptParser::apply_pending_warnings() {
pending_warnings.clear();
}
+
+void GDScriptParser::evaluate_warning_directory_rules_for_script_path() {
+ is_script_ignoring_warnings = false;
+ for (const WarningDirectoryRule &rule : warning_directory_rules) {
+ if (script_path.begins_with(rule.directory_path)) {
+ switch (rule.decision) {
+ case WarningDirectoryRule::DECISION_EXCLUDE:
+ is_script_ignoring_warnings = true;
+ return; // Stop checking rules.
+ case WarningDirectoryRule::DECISION_INCLUDE:
+ is_script_ignoring_warnings = false;
+ return; // Stop checking rules.
+ case WarningDirectoryRule::DECISION_MAX:
+ return; // Unreachable.
+ }
+ }
+ }
+}
#endif // DEBUG_ENABLED
void GDScriptParser::override_completion_context(const Node *p_for_node, CompletionType p_type, Node *p_node, int p_argument) {
@@ -391,9 +459,14 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
text_tokenizer->set_source_code(source);
tokenizer = text_tokenizer;
-
tokenizer->set_cursor_position(cursor_line, cursor_column);
+
script_path = p_script_path.simplify_path();
+
+#ifdef DEBUG_ENABLED
+ evaluate_warning_directory_rules_for_script_path();
+#endif // DEBUG_ENABLED
+
current = tokenizer->scan();
// Avoid error or newline as the first token.
// The latter can mess with the parser when opening files filled exclusively with comments and newlines.
@@ -414,7 +487,7 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
nd->end_line = 1;
push_warning(nd, GDScriptWarning::EMPTY_FILE);
}
-#endif
+#endif // DEBUG_ENABLED
push_multiline(false); // Keep one for the whole parsing.
parse_program();
@@ -422,7 +495,7 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
#ifdef TOOLS_ENABLED
comment_data = tokenizer->get_comments();
-#endif
+#endif // TOOLS_ENABLED
memdelete(text_tokenizer);
tokenizer = nullptr;
@@ -431,7 +504,7 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
if (multiline_stack.size() > 0) {
ERR_PRINT("Parser bug: Imbalanced multiline stack.");
}
-#endif
+#endif // DEBUG_ENABLED
if (errors.is_empty()) {
return OK;
@@ -450,7 +523,13 @@ Error GDScriptParser::parse_binary(const Vector &p_binary, const String
}
tokenizer = buffer_tokenizer;
+
script_path = p_script_path.simplify_path();
+
+#ifdef DEBUG_ENABLED
+ evaluate_warning_directory_rules_for_script_path();
+#endif // DEBUG_ENABLED
+
current = tokenizer->scan();
// Avoid error or newline as the first token.
// The latter can mess with the parser when opening files filled exclusively with comments and newlines.
@@ -4997,10 +5076,6 @@ bool GDScriptParser::export_group_annotations(AnnotationNode *p_annotation, Node
bool GDScriptParser::warning_ignore_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
#ifdef DEBUG_ENABLED
- if (is_ignoring_warnings) {
- return true; // We already ignore all warnings, let's optimize it.
- }
-
bool has_error = false;
for (const Variant &warning_name : p_annotation->resolved_arguments) {
GDScriptWarning::Code warning_code = GDScriptWarning::get_code_from_name(String(warning_name).to_upper());
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 8a8ab5af458..2427481a151 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -1349,6 +1349,19 @@ private:
List errors;
#ifdef DEBUG_ENABLED
+public:
+ struct WarningDirectoryRule {
+ enum Decision {
+ DECISION_EXCLUDE,
+ DECISION_INCLUDE,
+ DECISION_MAX,
+ };
+
+ String directory_path; // With a trailing slash.
+ Decision decision = DECISION_EXCLUDE;
+ };
+
+private:
struct PendingWarning {
const Node *source = nullptr;
GDScriptWarning::Code code = GDScriptWarning::WARNING_MAX;
@@ -1356,13 +1369,17 @@ private:
Vector symbols;
};
- bool is_ignoring_warnings = false;
+ static bool is_project_ignoring_warnings;
+ static GDScriptWarning::WarnLevel warning_levels[GDScriptWarning::WARNING_MAX];
+ static LocalVector warning_directory_rules;
+
List warnings;
List pending_warnings;
+ bool is_script_ignoring_warnings = false;
HashSet warning_ignored_lines[GDScriptWarning::WARNING_MAX];
int warning_ignore_start_lines[GDScriptWarning::WARNING_MAX];
HashSet unsafe_lines;
-#endif
+#endif // DEBUG_ENABLED
GDScriptTokenizer *tokenizer = nullptr;
GDScriptTokenizer::Token previous;
@@ -1473,6 +1490,7 @@ private:
}
void clear();
+
void push_error(const String &p_message, const Node *p_origin = nullptr);
#ifdef DEBUG_ENABLED
void push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Vector &p_symbols);
@@ -1481,7 +1499,9 @@ private:
push_warning(p_source, p_code, Vector{ p_symbols... });
}
void apply_pending_warnings();
-#endif
+ void evaluate_warning_directory_rules_for_script_path();
+#endif // DEBUG_ENABLED
+
// Setting p_force to false will prevent the completion context from being update if a context was already set before.
// This should only be done when we push context before we consumed any tokens for the corresponding structure.
// See parse_precedence for an example.
@@ -1615,11 +1635,13 @@ public:
// TODO: Keep track of deps.
return List();
}
+
#ifdef DEBUG_ENABLED
+ static void update_project_settings();
const List &get_warnings() const { return warnings; }
const HashSet &get_unsafe_lines() const { return unsafe_lines; }
int get_last_line_number() const { return current.end_line; }
-#endif
+#endif // DEBUG_ENABLED
#ifdef TOOLS_ENABLED
static HashMap theme_color_names;
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 6e21cac2baa..9761f4c25db 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -170,7 +170,7 @@ String GDScriptWarning::get_message() const {
case CONSTANT_USED_AS_FUNCTION: // There is already an error.
case FUNCTION_USED_AS_PROPERTY: // This is valid, returns `Callable`.
break;
-#endif
+#endif // DISABLE_DEPRECATED
case WARNING_MAX:
break; // Can't happen, but silences warning.
}
@@ -185,7 +185,7 @@ int GDScriptWarning::get_default_value(Code p_code) {
}
PropertyInfo GDScriptWarning::get_property_info(Code p_code) {
- return PropertyInfo(Variant::INT, get_settings_path_from_code(p_code), PROPERTY_HINT_ENUM, "Ignore,Warn,Error");
+ return PropertyInfo(Variant::INT, get_setting_path_from_code(p_code), PROPERTY_HINT_ENUM, "Ignore,Warn,Error");
}
String GDScriptWarning::get_name() const {
@@ -245,7 +245,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"PROPERTY_USED_AS_FUNCTION",
"CONSTANT_USED_AS_FUNCTION",
"FUNCTION_USED_AS_PROPERTY",
-#endif
+#endif // DISABLE_DEPRECATED
};
static_assert(std_size(names) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
@@ -253,7 +253,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
return names[(int)p_code];
}
-String GDScriptWarning::get_settings_path_from_code(Code p_code) {
+String GDScriptWarning::get_setting_path_from_code(Code p_code) {
return "debug/gdscript/warnings/" + get_name_from_code(p_code).to_lower();
}
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index 5ce1b6fff4e..e4f22c85ba6 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -94,13 +94,13 @@ public:
PROPERTY_USED_AS_FUNCTION, // Function not found, but there's a property with the same name.
CONSTANT_USED_AS_FUNCTION, // Function not found, but there's a constant with the same name.
FUNCTION_USED_AS_PROPERTY, // Property not found, but there's a function with the same name.
-#endif
+#endif // DISABLE_DEPRECATED
WARNING_MAX,
};
#ifndef DISABLE_DEPRECATED
static constexpr int FIRST_DEPRECATED_WARNING = PROPERTY_USED_AS_FUNCTION;
-#endif
+#endif // DISABLE_DEPRECATED
constexpr static WarnLevel default_warning_levels[] = {
WARN, // UNASSIGNED_VARIABLE
@@ -152,7 +152,7 @@ public:
WARN, // PROPERTY_USED_AS_FUNCTION
WARN, // CONSTANT_USED_AS_FUNCTION
WARN, // FUNCTION_USED_AS_PROPERTY
-#endif
+#endif // DISABLE_DEPRECATED
};
static_assert(std_size(default_warning_levels) == WARNING_MAX, "Amount of default levels does not match the amount of warnings.");
@@ -166,7 +166,7 @@ public:
static int get_default_value(Code p_code);
static PropertyInfo get_property_info(Code p_code);
static String get_name_from_code(Code p_code);
- static String get_settings_path_from_code(Code p_code);
+ static String get_setting_path_from_code(Code p_code);
static Code get_code_from_name(const String &p_name);
};
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index 59faf819aea..07861790f25 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -145,6 +145,7 @@ GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_l
if (do_init_languages) {
init_language(p_source_dir);
}
+
#ifdef DEBUG_ENABLED
// Set all warning levels to "Warn" in order to test them properly, even the ones that default to error.
ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/enable", true);
@@ -153,12 +154,16 @@ GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_l
// TODO: Add ability for test scripts to specify which warnings to enable/disable for testing.
continue;
}
- String warning_setting = GDScriptWarning::get_settings_path_from_code((GDScriptWarning::Code)i);
- ProjectSettings::get_singleton()->set_setting(warning_setting, (int)GDScriptWarning::WARN);
+ const String setting_path = GDScriptWarning::get_setting_path_from_code((GDScriptWarning::Code)i);
+ ProjectSettings::get_singleton()->set_setting(setting_path, (int)GDScriptWarning::WARN);
}
-#endif
- // Enable printing to show results
+ // Force the call, since the language is initialized **before** applying project settings
+ // and the `settings_changed` signal is emitted with `call_deferred()`.
+ GDScriptParser::update_project_settings();
+#endif // DEBUG_ENABLED
+
+ // Enable printing to show results.
CoreGlobals::print_line_enabled = true;
CoreGlobals::print_error_enabled = true;
}