Skip to content

Commit b651fc3

Browse files
committed
Merge pull request #93889 from dalexeev/gds-exclude-addons-exceptions-setting
GDScript: Add `debug/gdscript/warnings/directory_rules` project setting
2 parents dd3df2a + 1bd7b99 commit b651fc3

File tree

10 files changed

+222
-96
lines changed

10 files changed

+222
-96
lines changed

doc/classes/ProjectSettings.xml

Lines changed: 51 additions & 48 deletions
Large diffs are not rendered by default.

editor/plugins/plugin_config_dialog.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ void PluginConfigDialog::_on_required_text_changed() {
131131
if ((!script_edit->get_text().get_extension().is_empty() && script_edit->get_text().get_extension() != ext) || script_edit->get_text().ends_with(".")) {
132132
validation_panel->set_message(MSG_ID_SCRIPT, vformat(TTR("Script extension must match chosen language extension (.%s)."), ext), EditorValidationPanel::MSG_ERROR);
133133
}
134+
if (language->get_name() == "GDScript") {
135+
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);
136+
}
134137
}
135138

136139
String PluginConfigDialog::_get_subfolder() {
@@ -317,6 +320,7 @@ PluginConfigDialog::PluginConfigDialog() {
317320
validation_panel->add_line(MSG_ID_SCRIPT, TTR("Script extension is valid."));
318321
validation_panel->add_line(MSG_ID_SUBFOLDER, TTR("Subfolder name is valid."));
319322
validation_panel->add_line(MSG_ID_ACTIVE, "");
323+
validation_panel->add_line(MSG_ID_ENABLE_WARNINGS, "");
320324
validation_panel->set_update_callback(callable_mp(this, &PluginConfigDialog::_on_required_text_changed));
321325
validation_panel->set_accept_button(get_ok_button());
322326

editor/plugins/plugin_config_dialog.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class PluginConfigDialog : public ConfirmationDialog {
4747
MSG_ID_SUBFOLDER,
4848
MSG_ID_SCRIPT,
4949
MSG_ID_ACTIVE,
50+
MSG_ID_ENABLE_WARNINGS,
5051
};
5152

5253
LineEdit *name_edit = nullptr;

modules/gdscript/gdscript.cpp

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2282,11 +2282,18 @@ void GDScriptLanguage::init() {
22822282
GDExtensionManager::get_singleton()->connect("extension_loaded", callable_mp(this, &GDScriptLanguage::_extension_loaded));
22832283
GDExtensionManager::get_singleton()->connect("extension_unloading", callable_mp(this, &GDScriptLanguage::_extension_unloading));
22842284
}
2285-
#endif
2285+
#endif // TOOLS_ENABLED
2286+
2287+
#ifdef DEBUG_ENABLED
2288+
GDScriptParser::update_project_settings();
2289+
if (!ProjectSettings::get_singleton()->is_connected("settings_changed", callable_mp_static(&GDScriptParser::update_project_settings))) {
2290+
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp_static(&GDScriptParser::update_project_settings));
2291+
}
2292+
#endif // DEBUG_ENABLED
22862293

22872294
#ifdef TESTS_ENABLED
22882295
GDScriptTests::GDScriptTestRunner::handle_cmdline();
2289-
#endif
2296+
#endif // TESTS_ENABLED
22902297
}
22912298

22922299
#ifdef TOOLS_ENABLED
@@ -2950,7 +2957,7 @@ GDScriptLanguage::GDScriptLanguage() {
29502957
profiling = false;
29512958
profile_native_calls = false;
29522959
script_frame_time = 0;
2953-
#endif
2960+
#endif // DEBUG_ENABLED
29542961

29552962
_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);
29562963
track_call_stack = GLOBAL_DEF_RST("debug/settings/gdscript/always_track_call_stacks", false);
@@ -2961,20 +2968,29 @@ GDScriptLanguage::GDScriptLanguage() {
29612968
track_locals = track_locals || EngineDebugger::is_active();
29622969

29632970
GLOBAL_DEF("debug/gdscript/warnings/enable", true);
2964-
GLOBAL_DEF("debug/gdscript/warnings/exclude_addons", true);
2965-
GLOBAL_DEF("debug/gdscript/warnings/renamed_in_godot_4_hint", true);
2971+
2972+
GLOBAL_DEF(PropertyInfo(Variant::DICTIONARY,
2973+
"debug/gdscript/warnings/directory_rules",
2974+
PROPERTY_HINT_TYPE_STRING,
2975+
vformat("%d/%d:;%d/%d:Exclude,Include", Variant::STRING, PROPERTY_HINT_DIR, Variant::INT, PROPERTY_HINT_ENUM)),
2976+
Dictionary({ { "res://addons", GDScriptParser::WarningDirectoryRule::DECISION_EXCLUDE } }));
2977+
29662978
for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
2967-
GDScriptWarning::Code code = (GDScriptWarning::Code)i;
2968-
Variant default_enabled = GDScriptWarning::get_default_value(code);
2969-
String path = GDScriptWarning::get_settings_path_from_code(code);
2970-
GLOBAL_DEF(GDScriptWarning::get_property_info(code), default_enabled);
2971-
}
2979+
const GDScriptWarning::Code code = (GDScriptWarning::Code)i;
2980+
const Variant default_value = GDScriptWarning::get_default_value(code);
2981+
GLOBAL_DEF(GDScriptWarning::get_property_info(code), default_value);
29722982

29732983
#ifndef DISABLE_DEPRECATED
2974-
ProjectSettings::get_singleton()->set_as_internal("debug/gdscript/warnings/property_used_as_function", true);
2975-
ProjectSettings::get_singleton()->set_as_internal("debug/gdscript/warnings/constant_used_as_function", true);
2976-
ProjectSettings::get_singleton()->set_as_internal("debug/gdscript/warnings/function_used_as_property", true);
2977-
#endif
2984+
if (i >= GDScriptWarning::FIRST_DEPRECATED_WARNING) {
2985+
const String setting_path = GDScriptWarning::get_setting_path_from_code(code);
2986+
ProjectSettings::get_singleton()->set_as_internal(setting_path, true);
2987+
}
2988+
#endif // DISABLE_DEPRECATED
2989+
}
2990+
2991+
// TODO: This setting has nothing to do with warnings. It should be moved at the next compatibility breakage,
2992+
// if the setting is still relevant at that time.
2993+
GLOBAL_DEF("debug/gdscript/warnings/renamed_in_godot_4_hint", true);
29782994
#endif // DEBUG_ENABLED
29792995
}
29802996

modules/gdscript/gdscript_editor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1010,7 +1010,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
10101010
if (warning_code >= GDScriptWarning::FIRST_DEPRECATED_WARNING) {
10111011
break; // Don't suggest deprecated warnings as they are never produced.
10121012
}
1013-
#endif
1013+
#endif // DISABLE_DEPRECATED
10141014
ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
10151015
warning.insert_text = warning.display.quote(p_quote_style);
10161016
r_result.insert(warning.display, warning);

modules/gdscript/gdscript_parser.cpp

Lines changed: 92 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,15 @@ Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
6868
return Variant::VARIANT_MAX;
6969
}
7070

71+
#ifdef DEBUG_ENABLED
72+
bool GDScriptParser::is_project_ignoring_warnings = false;
73+
GDScriptWarning::WarnLevel GDScriptParser::warning_levels[GDScriptWarning::WARNING_MAX];
74+
LocalVector<GDScriptParser::WarningDirectoryRule> GDScriptParser::warning_directory_rules;
75+
#endif // DEBUG_ENABLED
76+
7177
#ifdef TOOLS_ENABLED
7278
HashMap<String, String> GDScriptParser::theme_color_names;
73-
#endif
79+
#endif // TOOLS_ENABLED
7480

7581
HashMap<StringName, GDScriptParser::AnnotationInfo> GDScriptParser::valid_annotations;
7682

@@ -89,6 +95,53 @@ bool GDScriptParser::annotation_exists(const String &p_annotation_name) const {
8995
return valid_annotations.has(p_annotation_name);
9096
}
9197

98+
#ifdef DEBUG_ENABLED
99+
void GDScriptParser::update_project_settings() {
100+
is_project_ignoring_warnings = !GLOBAL_GET("debug/gdscript/warnings/enable").booleanize();
101+
102+
for (int i = 0; i < GDScriptWarning::WARNING_MAX; i++) {
103+
const String setting_path = GDScriptWarning::get_setting_path_from_code((GDScriptWarning::Code)i);
104+
warning_levels[i] = (GDScriptWarning::WarnLevel)(int)GLOBAL_GET(setting_path);
105+
}
106+
107+
#ifndef DISABLE_DEPRECATED
108+
// 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.
109+
if (unlikely(ProjectSettings::get_singleton()->has_setting("debug/gdscript/warnings/exclude_addons"))) {
110+
const bool is_excluding_addons = ProjectSettings::get_singleton()->get_setting("debug/gdscript/warnings/exclude_addons", true).booleanize();
111+
ProjectSettings::get_singleton()->clear("debug/gdscript/warnings/exclude_addons");
112+
113+
Dictionary rules = ProjectSettings::get_singleton()->get_setting("debug/gdscript/warnings/directory_rules");
114+
rules["res://addons"] = is_excluding_addons ? WarningDirectoryRule::DECISION_EXCLUDE : WarningDirectoryRule::DECISION_INCLUDE;
115+
ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/directory_rules", rules);
116+
}
117+
#endif // DISABLE_DEPRECATED
118+
119+
warning_directory_rules.clear();
120+
121+
const Dictionary rules = GLOBAL_GET("debug/gdscript/warnings/directory_rules");
122+
for (const KeyValue<Variant, Variant> &kv : rules) {
123+
String dir = kv.key.operator String().simplify_path();
124+
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.)");
125+
if (!dir.ends_with("/")) {
126+
dir += '/';
127+
}
128+
129+
const int decision = kv.value;
130+
ERR_CONTINUE(decision < 0 || decision >= WarningDirectoryRule::DECISION_MAX);
131+
132+
warning_directory_rules.push_back({ dir, (WarningDirectoryRule::Decision)decision });
133+
}
134+
135+
struct RuleSort {
136+
bool operator()(const WarningDirectoryRule &p_a, const WarningDirectoryRule &p_b) const {
137+
return p_a.directory_path.count("/") > p_b.directory_path.count("/");
138+
}
139+
};
140+
141+
warning_directory_rules.sort_custom<RuleSort>();
142+
}
143+
#endif // DEBUG_ENABLED
144+
92145
GDScriptParser::GDScriptParser() {
93146
// Register valid annotations.
94147
if (unlikely(valid_annotations.is_empty())) {
@@ -137,11 +190,10 @@ GDScriptParser::GDScriptParser() {
137190
}
138191

139192
#ifdef DEBUG_ENABLED
140-
is_ignoring_warnings = !(bool)GLOBAL_GET("debug/gdscript/warnings/enable");
141193
for (int i = 0; i < GDScriptWarning::WARNING_MAX; i++) {
142194
warning_ignore_start_lines[i] = INT_MAX;
143195
}
144-
#endif
196+
#endif // DEBUG_ENABLED
145197

146198
#ifdef TOOLS_ENABLED
147199
if (unlikely(theme_color_names.is_empty())) {
@@ -161,7 +213,7 @@ GDScriptParser::GDScriptParser() {
161213
theme_color_names.insert("a", "axis_w_color");
162214
theme_color_names.insert("a8", "axis_w_color");
163215
}
164-
#endif
216+
#endif // TOOLS_ENABLED
165217
}
166218

167219
GDScriptParser::~GDScriptParser() {
@@ -195,13 +247,11 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_
195247
ERR_FAIL_NULL(p_source);
196248
ERR_FAIL_INDEX(p_code, GDScriptWarning::WARNING_MAX);
197249

198-
if (is_ignoring_warnings) {
199-
return;
200-
}
201-
if (GLOBAL_GET_CACHED(bool, "debug/gdscript/warnings/exclude_addons") && script_path.begins_with("res://addons/")) {
250+
if (is_project_ignoring_warnings || is_script_ignoring_warnings) {
202251
return;
203252
}
204-
GDScriptWarning::WarnLevel warn_level = (GDScriptWarning::WarnLevel)(int)GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(p_code));
253+
254+
const GDScriptWarning::WarnLevel warn_level = warning_levels[p_code];
205255
if (warn_level == GDScriptWarning::IGNORE) {
206256
return;
207257
}
@@ -251,6 +301,24 @@ void GDScriptParser::apply_pending_warnings() {
251301

252302
pending_warnings.clear();
253303
}
304+
305+
void GDScriptParser::evaluate_warning_directory_rules_for_script_path() {
306+
is_script_ignoring_warnings = false;
307+
for (const WarningDirectoryRule &rule : warning_directory_rules) {
308+
if (script_path.begins_with(rule.directory_path)) {
309+
switch (rule.decision) {
310+
case WarningDirectoryRule::DECISION_EXCLUDE:
311+
is_script_ignoring_warnings = true;
312+
return; // Stop checking rules.
313+
case WarningDirectoryRule::DECISION_INCLUDE:
314+
is_script_ignoring_warnings = false;
315+
return; // Stop checking rules.
316+
case WarningDirectoryRule::DECISION_MAX:
317+
return; // Unreachable.
318+
}
319+
}
320+
}
321+
}
254322
#endif // DEBUG_ENABLED
255323

256324
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_
391459
text_tokenizer->set_source_code(source);
392460

393461
tokenizer = text_tokenizer;
394-
395462
tokenizer->set_cursor_position(cursor_line, cursor_column);
463+
396464
script_path = p_script_path.simplify_path();
465+
466+
#ifdef DEBUG_ENABLED
467+
evaluate_warning_directory_rules_for_script_path();
468+
#endif // DEBUG_ENABLED
469+
397470
current = tokenizer->scan();
398471
// Avoid error or newline as the first token.
399472
// The latter can mess with the parser when opening files filled exclusively with comments and newlines.
@@ -414,15 +487,15 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
414487
nd->end_line = 1;
415488
push_warning(nd, GDScriptWarning::EMPTY_FILE);
416489
}
417-
#endif
490+
#endif // DEBUG_ENABLED
418491

419492
push_multiline(false); // Keep one for the whole parsing.
420493
parse_program();
421494
pop_multiline();
422495

423496
#ifdef TOOLS_ENABLED
424497
comment_data = tokenizer->get_comments();
425-
#endif
498+
#endif // TOOLS_ENABLED
426499

427500
memdelete(text_tokenizer);
428501
tokenizer = nullptr;
@@ -431,7 +504,7 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
431504
if (multiline_stack.size() > 0) {
432505
ERR_PRINT("Parser bug: Imbalanced multiline stack.");
433506
}
434-
#endif
507+
#endif // DEBUG_ENABLED
435508

436509
if (errors.is_empty()) {
437510
return OK;
@@ -450,7 +523,13 @@ Error GDScriptParser::parse_binary(const Vector<uint8_t> &p_binary, const String
450523
}
451524

452525
tokenizer = buffer_tokenizer;
526+
453527
script_path = p_script_path.simplify_path();
528+
529+
#ifdef DEBUG_ENABLED
530+
evaluate_warning_directory_rules_for_script_path();
531+
#endif // DEBUG_ENABLED
532+
454533
current = tokenizer->scan();
455534
// Avoid error or newline as the first token.
456535
// 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
49975076

49985077
bool GDScriptParser::warning_ignore_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
49995078
#ifdef DEBUG_ENABLED
5000-
if (is_ignoring_warnings) {
5001-
return true; // We already ignore all warnings, let's optimize it.
5002-
}
5003-
50045079
bool has_error = false;
50055080
for (const Variant &warning_name : p_annotation->resolved_arguments) {
50065081
GDScriptWarning::Code warning_code = GDScriptWarning::get_code_from_name(String(warning_name).to_upper());

modules/gdscript/gdscript_parser.h

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,20 +1349,37 @@ class GDScriptParser {
13491349
List<ParserError> errors;
13501350

13511351
#ifdef DEBUG_ENABLED
1352+
public:
1353+
struct WarningDirectoryRule {
1354+
enum Decision {
1355+
DECISION_EXCLUDE,
1356+
DECISION_INCLUDE,
1357+
DECISION_MAX,
1358+
};
1359+
1360+
String directory_path; // With a trailing slash.
1361+
Decision decision = DECISION_EXCLUDE;
1362+
};
1363+
1364+
private:
13521365
struct PendingWarning {
13531366
const Node *source = nullptr;
13541367
GDScriptWarning::Code code = GDScriptWarning::WARNING_MAX;
13551368
bool treated_as_error = false;
13561369
Vector<String> symbols;
13571370
};
13581371

1359-
bool is_ignoring_warnings = false;
1372+
static bool is_project_ignoring_warnings;
1373+
static GDScriptWarning::WarnLevel warning_levels[GDScriptWarning::WARNING_MAX];
1374+
static LocalVector<WarningDirectoryRule> warning_directory_rules;
1375+
13601376
List<GDScriptWarning> warnings;
13611377
List<PendingWarning> pending_warnings;
1378+
bool is_script_ignoring_warnings = false;
13621379
HashSet<int> warning_ignored_lines[GDScriptWarning::WARNING_MAX];
13631380
int warning_ignore_start_lines[GDScriptWarning::WARNING_MAX];
13641381
HashSet<int> unsafe_lines;
1365-
#endif
1382+
#endif // DEBUG_ENABLED
13661383

13671384
GDScriptTokenizer *tokenizer = nullptr;
13681385
GDScriptTokenizer::Token previous;
@@ -1473,6 +1490,7 @@ class GDScriptParser {
14731490
}
14741491

14751492
void clear();
1493+
14761494
void push_error(const String &p_message, const Node *p_origin = nullptr);
14771495
#ifdef DEBUG_ENABLED
14781496
void push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Vector<String> &p_symbols);
@@ -1481,7 +1499,9 @@ class GDScriptParser {
14811499
push_warning(p_source, p_code, Vector<String>{ p_symbols... });
14821500
}
14831501
void apply_pending_warnings();
1484-
#endif
1502+
void evaluate_warning_directory_rules_for_script_path();
1503+
#endif // DEBUG_ENABLED
1504+
14851505
// Setting p_force to false will prevent the completion context from being update if a context was already set before.
14861506
// This should only be done when we push context before we consumed any tokens for the corresponding structure.
14871507
// See parse_precedence for an example.
@@ -1615,11 +1635,13 @@ class GDScriptParser {
16151635
// TODO: Keep track of deps.
16161636
return List<String>();
16171637
}
1638+
16181639
#ifdef DEBUG_ENABLED
1640+
static void update_project_settings();
16191641
const List<GDScriptWarning> &get_warnings() const { return warnings; }
16201642
const HashSet<int> &get_unsafe_lines() const { return unsafe_lines; }
16211643
int get_last_line_number() const { return current.end_line; }
1622-
#endif
1644+
#endif // DEBUG_ENABLED
16231645

16241646
#ifdef TOOLS_ENABLED
16251647
static HashMap<String, String> theme_color_names;

0 commit comments

Comments
 (0)