diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 0355119442ad..b9b35e5d1972 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -726,7 +726,7 @@ [/codeblock] [b]Note:[/b] Only the script can have a custom icon. Inner classes are not supported. [b]Note:[/b] As annotations describe their subject, the [annotation @icon] annotation must be placed before the class definition and inheritance. - [b]Note:[/b] Unlike other annotations, the argument of the [annotation @icon] annotation must be a string literal (constant expressions are not supported). + [b]Note:[/b] Unlike most other annotations, the argument of the [annotation @icon] annotation must be a string literal (constant expressions are not supported). @@ -794,6 +794,33 @@ @warning_ignore("unreachable_code") print("unreachable") [/codeblock] + See also [annotation @warning_ignore_start] and [annotation @warning_ignore_restore]. + + + + + + + Stops ignoring the listed warning types after [annotation @warning_ignore_start]. Ignoring the specified warning types will be reset to Project Settings. This annotation can be omitted to ignore the warning types until the end of the file. + [b]Note:[/b] Unlike most other annotations, arguments of the [annotation @warning_ignore_restore] annotation must be string literals (constant expressions are not supported). + + + + + + + Starts ignoring the listed warning types until the end of the file or the [annotation @warning_ignore_restore] annotation with the given warning type. + [codeblock] + func test(): + var a = 1 # Warning (if enabled in the Project Settings). + @warning_ignore_start("unused_variable") + var b = 2 # No warning. + var c = 3 # No warning. + @warning_ignore_restore("unused_variable") + var d = 4 # Warning (if enabled in the Project Settings). + [/codeblock] + [b]Note:[/b] To suppress a single warning, use [annotation @warning_ignore] instead. + [b]Note:[/b] Unlike most other annotations, arguments of the [annotation @warning_ignore_start] annotation must be string literals (constant expressions are not supported). diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index aceae59078c7..8bbf13dfc391 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -962,8 +962,11 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a } } break; } - } else if (p_annotation->name == SNAME("@warning_ignore")) { + } else if (p_annotation->name == SNAME("@warning_ignore") || p_annotation->name == SNAME("@warning_ignore_start") || p_annotation->name == SNAME("@warning_ignore_restore")) { for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) { + if (warning_code == GDScriptWarning::RENAMED_IN_GODOT_4_HINT) { + continue; + } 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 2f98b511963a..2e7924d3fbf5 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -94,10 +94,11 @@ bool GDScriptParser::annotation_exists(const String &p_annotation_name) const { GDScriptParser::GDScriptParser() { // Register valid annotations. if (unlikely(valid_annotations.is_empty())) { + // Script annotations. register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_annotation); register_annotation(MethodInfo("@icon", PropertyInfo(Variant::STRING, "icon_path")), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation); register_annotation(MethodInfo("@static_unload"), AnnotationInfo::SCRIPT, &GDScriptParser::static_unload_annotation); - + // Onready annotation. register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation); // Export annotations. register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations); @@ -128,13 +129,18 @@ GDScriptParser::GDScriptParser() { register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations, varray("")); register_annotation(MethodInfo("@export_subgroup", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations, varray("")); // Warning annotations. - register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS_LEVEL | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, varray(), true); + register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS_LEVEL | AnnotationInfo::STATEMENT, &GDScriptParser::warning_ignore_annotation, varray(), true); + register_annotation(MethodInfo("@warning_ignore_start", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::STANDALONE, &GDScriptParser::warning_ignore_region_annotations, varray(), true); + register_annotation(MethodInfo("@warning_ignore_restore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::STANDALONE, &GDScriptParser::warning_ignore_region_annotations, varray(), true); // Networking. register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::rpc_annotation, varray("authority", "call_remote", "unreliable", 0)); } #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 #ifdef TOOLS_ENABLED @@ -214,6 +220,9 @@ void GDScriptParser::apply_pending_warnings() { if (warning_ignored_lines[pw.code].has(pw.source->start_line)) { continue; } + if (warning_ignore_start_lines[pw.code] <= pw.source->start_line) { + continue; + } GDScriptWarning warning; warning.code = pw.code; @@ -625,7 +634,7 @@ void GDScriptParser::parse_program() { } else if (annotation->applies_to(AnnotationInfo::SCRIPT)) { PUSH_PENDING_ANNOTATIONS_TO_HEAD; if (annotation->name == SNAME("@tool") || annotation->name == SNAME("@icon")) { - // Some annotations need to be resolved in the parser. + // Some annotations need to be resolved and applied in the parser. annotation->apply(this, head, nullptr); // `head->outer == nullptr`. } else { head->annotations.push_back(annotation); @@ -640,8 +649,10 @@ void GDScriptParser::parse_program() { // so we stop looking for script-level stuff. can_have_class_or_extends = false; break; + } else if (annotation->name == SNAME("@warning_ignore_start") || annotation->name == SNAME("@warning_ignore_restore")) { + // Some annotations need to be resolved and applied in the parser. + annotation->apply(this, nullptr, nullptr); } else { - // For potential non-group standalone annotations. push_error(R"(Unexpected standalone annotation.)"); } } else { @@ -1030,8 +1041,10 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) { } if (annotation->name == SNAME("@export_category") || annotation->name == SNAME("@export_group") || annotation->name == SNAME("@export_subgroup")) { current_class->add_member_group(annotation); + } else if (annotation->name == SNAME("@warning_ignore_start") || annotation->name == SNAME("@warning_ignore_restore")) { + // Some annotations need to be resolved and applied in the parser. + annotation->apply(this, nullptr, nullptr); } else { - // For potential non-group standalone annotations. push_error(R"(Unexpected standalone annotation.)"); } } else { // `AnnotationInfo::CLASS_LEVEL`. @@ -1896,9 +1909,21 @@ GDScriptParser::Node *GDScriptParser::parse_statement() { break; case GDScriptTokenizer::Token::ANNOTATION: { advance(); - AnnotationNode *annotation = parse_annotation(AnnotationInfo::STATEMENT); + AnnotationNode *annotation = parse_annotation(AnnotationInfo::STATEMENT | AnnotationInfo::STANDALONE); if (annotation != nullptr) { - annotation_stack.push_back(annotation); + if (annotation->applies_to(AnnotationInfo::STANDALONE)) { + if (previous.type != GDScriptTokenizer::Token::NEWLINE) { + push_error(R"(Expected newline after a standalone annotation.)"); + } + if (annotation->name == SNAME("@warning_ignore_start") || annotation->name == SNAME("@warning_ignore_restore")) { + // Some annotations need to be resolved and applied in the parser. + annotation->apply(this, nullptr, nullptr); + } else { + push_error(R"(Unexpected standalone annotation.)"); + } + } else { + annotation_stack.push_back(annotation); + } } break; } @@ -4096,23 +4121,25 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation) return false; } - // Some annotations need to be resolved in the parser. - if (p_annotation->name == SNAME("@icon")) { - ExpressionNode *argument = p_annotation->arguments[0]; + // Some annotations need to be resolved and applied in the parser. + if (p_annotation->name == SNAME("@icon") || p_annotation->name == SNAME("@warning_ignore_start") || p_annotation->name == SNAME("@warning_ignore_restore")) { + for (int i = 0; i < p_annotation->arguments.size(); i++) { + ExpressionNode *argument = p_annotation->arguments[i]; - if (argument->type != Node::LITERAL) { - push_error(R"(Argument 1 of annotation "@icon" must be a string literal.)", argument); - return false; - } + if (argument->type != Node::LITERAL) { + push_error(vformat(R"(Argument %d of annotation "%s" must be a string literal.)", i + 1, p_annotation->name), argument); + return false; + } - Variant value = static_cast(argument)->value; + Variant value = static_cast(argument)->value; - if (value.get_type() != Variant::STRING) { - push_error(R"(Argument 1 of annotation "@icon" must be a string literal.)", argument); - return false; - } + if (value.get_type() != Variant::STRING) { + push_error(vformat(R"(Argument %d of annotation "%s" must be a string literal.)", i + 1, p_annotation->name), argument); + return false; + } - p_annotation->resolved_arguments.push_back(value); + p_annotation->resolved_arguments.push_back(value); + } } // For other annotations, see `GDScriptAnalyzer::resolve_annotation()`. @@ -4162,6 +4189,17 @@ bool GDScriptParser::icon_annotation(AnnotationNode *p_annotation, Node *p_targe return true; } +bool GDScriptParser::static_unload_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) { + ERR_FAIL_COND_V_MSG(p_target->type != Node::CLASS, false, vformat(R"("%s" annotation can only be applied to classes.)", p_annotation->name)); + ClassNode *class_node = static_cast(p_target); + if (class_node->annotated_static_unload) { + push_error(vformat(R"("%s" annotation can only be used once per script.)", p_annotation->name), p_annotation); + return false; + } + class_node->annotated_static_unload = true; + return true; +} + bool GDScriptParser::onready_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) { ERR_FAIL_COND_V_MSG(p_target->type != Node::VARIABLE, false, R"("@onready" annotation can only be applied to class variables.)"); @@ -4756,11 +4794,8 @@ bool GDScriptParser::export_group_annotations(AnnotationNode *p_annotation, Node return true; } -bool GDScriptParser::warning_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) { -#ifndef DEBUG_ENABLED - // Only available in debug builds. - return true; -#else // DEBUG_ENABLED +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. } @@ -4805,8 +4840,14 @@ bool GDScriptParser::warning_annotations(AnnotationNode *p_annotation, Node *p_t } break; case Node::FUNCTION: { - // `@warning_ignore` on function has a controversial feature that is used in tests. - // It's better not to remove it for now, while there is no way to mass-ignore warnings. + FunctionNode *function = static_cast(p_target); + end_line = function->start_line; + for (int i = 0; i < function->parameters.size(); i++) { + end_line = MAX(end_line, function->parameters[i]->end_line); + if (function->parameters[i]->initializer != nullptr) { + end_line = MAX(end_line, function->parameters[i]->initializer->end_line); + } + } } break; case Node::MATCH_BRANCH: { @@ -4828,6 +4869,48 @@ bool GDScriptParser::warning_annotations(AnnotationNode *p_annotation, Node *p_t } } return !has_error; +#else // !DEBUG_ENABLED + // Only available in debug builds. + return true; +#endif // DEBUG_ENABLED +} + +bool GDScriptParser::warning_ignore_region_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) { +#ifdef DEBUG_ENABLED + bool has_error = false; + const bool is_start = p_annotation->name == SNAME("@warning_ignore_start"); + for (const Variant &warning_name : p_annotation->resolved_arguments) { + GDScriptWarning::Code warning_code = GDScriptWarning::get_code_from_name(String(warning_name).to_upper()); + if (warning_code == GDScriptWarning::WARNING_MAX) { + push_error(vformat(R"(Invalid warning name: "%s".)", warning_name), p_annotation); + has_error = true; + continue; + } + if (is_start) { + if (warning_ignore_start_lines[warning_code] != INT_MAX) { + push_error(vformat(R"(Warning "%s" is already being ignored by "@warning_ignore_start" at line %d.)", String(warning_name).to_upper(), warning_ignore_start_lines[warning_code]), p_annotation); + has_error = true; + continue; + } + warning_ignore_start_lines[warning_code] = p_annotation->start_line; + } else { + if (warning_ignore_start_lines[warning_code] == INT_MAX) { + push_error(vformat(R"(Warning "%s" is not being ignored by "@warning_ignore_start".)", String(warning_name).to_upper()), p_annotation); + has_error = true; + continue; + } + const int start_line = warning_ignore_start_lines[warning_code]; + const int end_line = MAX(start_line, p_annotation->start_line); // Prevent infinite loop. + for (int i = start_line; i <= end_line; i++) { + warning_ignored_lines[warning_code].insert(i); + } + warning_ignore_start_lines[warning_code] = INT_MAX; + } + } + return !has_error; +#else // !DEBUG_ENABLED + // Only available in debug builds. + return true; #endif // DEBUG_ENABLED } @@ -4892,17 +4975,6 @@ bool GDScriptParser::rpc_annotation(AnnotationNode *p_annotation, Node *p_target return true; } -bool GDScriptParser::static_unload_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) { - ERR_FAIL_COND_V_MSG(p_target->type != Node::CLASS, false, vformat(R"("%s" annotation can only be applied to classes.)", p_annotation->name)); - ClassNode *class_node = static_cast(p_target); - if (class_node->annotated_static_unload) { - push_error(vformat(R"("%s" annotation can only be used once per script.)", p_annotation->name), p_annotation); - return false; - } - class_node->annotated_static_unload = true; - return true; -} - GDScriptParser::DataType GDScriptParser::SuiteNode::Local::get_datatype() const { switch (type) { case CONSTANT: diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index d40ba217c114..902a17722867 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1358,6 +1358,7 @@ class GDScriptParser { List warnings; List pending_warnings; HashSet warning_ignored_lines[GDScriptWarning::WARNING_MAX]; + int warning_ignore_start_lines[GDScriptWarning::WARNING_MAX]; HashSet unsafe_lines; #endif @@ -1506,6 +1507,7 @@ class GDScriptParser { void clear_unused_annotations(); bool tool_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); bool icon_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); + bool static_unload_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); bool onready_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); template bool export_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); @@ -1514,9 +1516,9 @@ class GDScriptParser { bool export_tool_button_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); template bool export_group_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); - bool warning_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); + bool warning_ignore_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); + bool warning_ignore_region_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); bool rpc_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); - bool static_unload_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); // Statements. Node *parse_statement(); VariableNode *parse_variable(bool p_is_static); diff --git a/modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd b/modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd index d0f895d78464..eb4ec1c38ac7 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd @@ -13,7 +13,7 @@ func param_inferred(param := variant()) -> void: print(param) func return_untyped(): return variant() func return_typed() -> Variant: return variant() -@warning_ignore("unused_variable", "inference_on_variant") +@warning_ignore_start("unused_variable", "inference_on_variant") func test() -> void: var weak = variant() var typed: Variant = variant() @@ -32,4 +32,4 @@ func test() -> void: if typed != null: pass if typed is Node: pass - print('ok') + print("ok") diff --git a/modules/gdscript/tests/scripts/analyzer/features/type_test_usage.gd b/modules/gdscript/tests/scripts/analyzer/features/type_test_usage.gd index ee30f01dfb87..6999a1612ac8 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/type_test_usage.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/type_test_usage.gd @@ -123,4 +123,4 @@ func test(): Utils.check((const_null is A) == false) Utils.check(is_instance_of(const_null, A) == false) - print('ok') + print("ok") diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd index eb53d0a70042..f69698112cbe 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd @@ -20,9 +20,7 @@ class Members: Utils.check(str(two) == '[486]') return true - -@warning_ignore("unsafe_method_access") -@warning_ignore("return_value_discarded") +@warning_ignore_start('unsafe_method_access', 'return_value_discarded') func test(): var untyped_basic = [459] Utils.check(str(untyped_basic) == '[459]') @@ -207,7 +205,7 @@ func test(): var a := A.new() var typed_natives: Array[RefCounted] = [a] - var typed_scripts = Array(typed_natives, TYPE_OBJECT, "RefCounted", A) + var typed_scripts = Array(typed_natives, TYPE_OBJECT, 'RefCounted', A) Utils.check(typed_scripts[0] == a) diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_dictionary_usage.gd b/modules/gdscript/tests/scripts/analyzer/features/typed_dictionary_usage.gd index c9ab368f45a3..9bb077aacc59 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/typed_dictionary_usage.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/typed_dictionary_usage.gd @@ -21,9 +21,7 @@ class Members: return true -@warning_ignore("unsafe_method_access") -@warning_ignore("assert_always_true") -@warning_ignore("return_value_discarded") +@warning_ignore_start("unsafe_method_access", "return_value_discarded") func test(): var untyped_basic = { 459: 954 } Utils.check(str(untyped_basic) == '{ 459: 954 }') diff --git a/modules/gdscript/tests/scripts/analyzer/features/vararg_call.gd b/modules/gdscript/tests/scripts/analyzer/features/vararg_call.gd index d444250f1ea9..b77bb35405bd 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/vararg_call.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/vararg_call.gd @@ -1,6 +1,6 @@ signal ok() -@warning_ignore("return_value_discarded") +@warning_ignore_start("return_value_discarded") func test(): - ok.connect(func(): print('ok')) - emit_signal(&'ok') + ok.connect(func(): print("ok")) + emit_signal(&"ok") diff --git a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_targets.out b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_targets.out index bf8704081119..bf92bd6ede71 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_targets.out +++ b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_targets.out @@ -1,6 +1,7 @@ GDTEST_OK ~~ WARNING at line 3: (CONFUSABLE_IDENTIFIER) The identifier "my_vАr" has misleading characters and might be confused with something else. ~~ WARNING at line 8: (NARROWING_CONVERSION) Narrowing conversion (float is converted to int and loses precision). +~~ WARNING at line 14: (NARROWING_CONVERSION) Narrowing conversion (float is converted to int and loses precision). ~~ WARNING at line 19: (NARROWING_CONVERSION) Narrowing conversion (float is converted to int and loses precision). ~~ WARNING at line 24: (NARROWING_CONVERSION) Narrowing conversion (float is converted to int and loses precision). ~~ WARNING at line 27: (CONFUSABLE_IDENTIFIER) The identifier "_my_vАr" has misleading characters and might be confused with something else. diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.gd b/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.gd index 6a16ae6bcc0b..0624f3fd379e 100644 --- a/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.gd +++ b/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.gd @@ -4,11 +4,10 @@ extends ShadowingBase var member: int = 0 var print_debug := 'print_debug' -@warning_ignore("shadowed_global_identifier") +@warning_ignore('shadowed_global_identifier') var print := 'print' -@warning_ignore("unused_variable") -@warning_ignore("unused_local_constant") +@warning_ignore_start('unused_variable', 'unused_local_constant') func test(): var Array := 'Array' var Node := 'Node' diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out b/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out index 0b11103a8013..702361f8a391 100644 --- a/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out +++ b/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out @@ -1,13 +1,13 @@ GDTEST_OK ~~ WARNING at line 6: (SHADOWED_GLOBAL_IDENTIFIER) The variable "print_debug" has the same name as a built-in function. -~~ WARNING at line 13: (SHADOWED_GLOBAL_IDENTIFIER) The variable "Array" has the same name as a built-in type. -~~ WARNING at line 14: (SHADOWED_GLOBAL_IDENTIFIER) The variable "Node" has the same name as a native class. -~~ WARNING at line 15: (SHADOWED_GLOBAL_IDENTIFIER) The variable "is_same" has the same name as a built-in function. -~~ WARNING at line 16: (SHADOWED_GLOBAL_IDENTIFIER) The variable "sqrt" has the same name as a built-in function. -~~ WARNING at line 17: (SHADOWED_VARIABLE) The local variable "member" is shadowing an already-declared variable at line 4 in the current class. -~~ WARNING at line 18: (SHADOWED_VARIABLE_BASE_CLASS) The local variable "reference" is shadowing an already-declared method in the base class "RefCounted". -~~ WARNING at line 19: (SHADOWED_GLOBAL_IDENTIFIER) The variable "ShadowedClass" has the same name as a global class defined in "shadowning.gd". -~~ WARNING at line 20: (SHADOWED_VARIABLE_BASE_CLASS) The local variable "base_variable_member" is shadowing an already-declared variable at line 4 in the base class "ShadowingBase". -~~ WARNING at line 21: (SHADOWED_VARIABLE_BASE_CLASS) The local constant "base_function_member" is shadowing an already-declared function at line 6 in the base class "ShadowingBase". -~~ WARNING at line 22: (SHADOWED_VARIABLE_BASE_CLASS) The local variable "base_const_member" is shadowing an already-declared constant at line 3 in the base class "ShadowingBase". +~~ WARNING at line 12: (SHADOWED_GLOBAL_IDENTIFIER) The variable "Array" has the same name as a built-in type. +~~ WARNING at line 13: (SHADOWED_GLOBAL_IDENTIFIER) The variable "Node" has the same name as a native class. +~~ WARNING at line 14: (SHADOWED_GLOBAL_IDENTIFIER) The variable "is_same" has the same name as a built-in function. +~~ WARNING at line 15: (SHADOWED_GLOBAL_IDENTIFIER) The variable "sqrt" has the same name as a built-in function. +~~ WARNING at line 16: (SHADOWED_VARIABLE) The local variable "member" is shadowing an already-declared variable at line 4 in the current class. +~~ WARNING at line 17: (SHADOWED_VARIABLE_BASE_CLASS) The local variable "reference" is shadowing an already-declared method in the base class "RefCounted". +~~ WARNING at line 18: (SHADOWED_GLOBAL_IDENTIFIER) The variable "ShadowedClass" has the same name as a global class defined in "shadowning.gd". +~~ WARNING at line 19: (SHADOWED_VARIABLE_BASE_CLASS) The local variable "base_variable_member" is shadowing an already-declared variable at line 4 in the base class "ShadowingBase". +~~ WARNING at line 20: (SHADOWED_VARIABLE_BASE_CLASS) The local constant "base_function_member" is shadowing an already-declared function at line 6 in the base class "ShadowingBase". +~~ WARNING at line 21: (SHADOWED_VARIABLE_BASE_CLASS) The local variable "base_const_member" is shadowing an already-declared constant at line 3 in the base class "ShadowingBase". warn diff --git a/modules/gdscript/tests/scripts/parser/errors/warning_ignore_extra_start.gd b/modules/gdscript/tests/scripts/parser/errors/warning_ignore_extra_start.gd new file mode 100644 index 000000000000..788fbc53d71a --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/warning_ignore_extra_start.gd @@ -0,0 +1,5 @@ +@warning_ignore_start("unreachable_code") +@warning_ignore_start("unreachable_code") + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/parser/errors/warning_ignore_extra_start.out b/modules/gdscript/tests/scripts/parser/errors/warning_ignore_extra_start.out new file mode 100644 index 000000000000..1d02f0eaa014 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/warning_ignore_extra_start.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Warning "UNREACHABLE_CODE" is already being ignored by "@warning_ignore_start" at line 1. diff --git a/modules/gdscript/tests/scripts/parser/errors/warning_ignore_restore_without_start.gd b/modules/gdscript/tests/scripts/parser/errors/warning_ignore_restore_without_start.gd new file mode 100644 index 000000000000..3580a1bf90ba --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/warning_ignore_restore_without_start.gd @@ -0,0 +1,4 @@ +@warning_ignore_restore("unreachable_code") + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/parser/errors/warning_ignore_restore_without_start.out b/modules/gdscript/tests/scripts/parser/errors/warning_ignore_restore_without_start.out new file mode 100644 index 000000000000..8b0fc7d43ebc --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/warning_ignore_restore_without_start.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Warning "UNREACHABLE_CODE" is not being ignored by "@warning_ignore_start". diff --git a/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd index a34cc26e67a5..05988377a2a8 100644 --- a/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd +++ b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd @@ -1,6 +1,6 @@ # https://github.com/godotengine/godot/issues/50285 -@warning_ignore("unused_local_constant") +@warning_ignore_start("unused_local_constant") func test(): const CONST_INNER_DICTIONARY = { "key": true } const CONST_NESTED_DICTIONARY_OLD_WORKAROUND = { diff --git a/modules/gdscript/tests/scripts/parser/features/signal_declaration.gd b/modules/gdscript/tests/scripts/parser/features/signal_declaration.gd index d02f82d41744..23f556201714 100644 --- a/modules/gdscript/tests/scripts/parser/features/signal_declaration.gd +++ b/modules/gdscript/tests/scripts/parser/features/signal_declaration.gd @@ -1,3 +1,5 @@ +@warning_ignore_start("unused_signal") + # No parentheses. signal a @@ -17,12 +19,5 @@ signal d( # With type hints. signal e(a: int, b: Variant, c: Node) -func no_exec(): - a.emit() - b.emit() - c.emit() - d.emit() - e.emit() - func test(): print("Ok") diff --git a/modules/gdscript/tests/scripts/parser/features/warning_ignore_regions.gd b/modules/gdscript/tests/scripts/parser/features/warning_ignore_regions.gd new file mode 100644 index 000000000000..ffdeae1763ad --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/warning_ignore_regions.gd @@ -0,0 +1,26 @@ +@warning_ignore_start("unreachable_code", "narrowing_conversion") + +var _a = 1 +@warning_ignore_start("unused_private_class_variable") +var _b = 2 +var _c = 3 +@warning_ignore_restore("unused_private_class_variable") +var _d = 4 + +func test(): + return + + var a = 1 + @warning_ignore_start("unused_variable") + var b = 2 + var c = 3 + @warning_ignore_restore("unused_variable") + var d = 4 + + var _x: int = 1.0 + @warning_ignore_restore("narrowing_conversion") + var _y: int = 1.0 + +func test_2(): + return + print(42) diff --git a/modules/gdscript/tests/scripts/parser/features/warning_ignore_regions.out b/modules/gdscript/tests/scripts/parser/features/warning_ignore_regions.out new file mode 100644 index 000000000000..11f7263ed8e4 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/warning_ignore_regions.out @@ -0,0 +1,6 @@ +GDTEST_OK +~~ WARNING at line 3: (UNUSED_PRIVATE_CLASS_VARIABLE) The class variable "_a" is declared but never used in the class. +~~ WARNING at line 8: (UNUSED_PRIVATE_CLASS_VARIABLE) The class variable "_d" is declared but never used in the class. +~~ WARNING at line 13: (UNUSED_VARIABLE) The local variable "a" is declared but never used in the block. If this is intended, prefix it with an underscore: "_a". +~~ WARNING at line 18: (UNUSED_VARIABLE) The local variable "d" is declared but never used in the block. If this is intended, prefix it with an underscore: "_d". +~~ WARNING at line 22: (NARROWING_CONVERSION) Narrowing conversion (float is converted to int and loses precision). diff --git a/modules/gdscript/tests/scripts/runtime/features/assign_operator.gd b/modules/gdscript/tests/scripts/runtime/features/assign_operator.gd index 2a9fe851ef01..0baa16f6b7c1 100644 --- a/modules/gdscript/tests/scripts/runtime/features/assign_operator.gd +++ b/modules/gdscript/tests/scripts/runtime/features/assign_operator.gd @@ -1,6 +1,6 @@ # https://github.com/godotengine/godot/issues/75832 -@warning_ignore("narrowing_conversion") +@warning_ignore_start("narrowing_conversion") func test(): var hf := 2.0 var sf = 2.0 diff --git a/modules/gdscript/tests/scripts/runtime/features/member_info.gd b/modules/gdscript/tests/scripts/runtime/features/member_info.gd index 4ce53aa395e3..fb99cf5de6a1 100644 --- a/modules/gdscript/tests/scripts/runtime/features/member_info.gd +++ b/modules/gdscript/tests/scripts/runtime/features/member_info.gd @@ -56,6 +56,7 @@ func test_func_hard_int() -> int: return 1 func test_func_args_1(_a: int, _b: Array[int], _c: Dictionary[int, int], _d: int = 1, _e = 2): pass func test_func_args_2(_a = 1, _b = _a, _c = [2], _d = 3): pass +@warning_ignore_start("unused_signal") signal test_signal_1() signal test_signal_2(a: Variant, b) signal test_signal_3(a: int, b: Array[int], c: Dictionary[int, int]) @@ -64,16 +65,7 @@ signal test_signal_5(a: MyEnum, b: Array[MyEnum], c: Dictionary[MyEnum, MyEnum]) signal test_signal_6(a: Resource, b: Array[Resource], c: Dictionary[Resource, Resource]) signal test_signal_7(a: TestMemberInfo, b: Array[TestMemberInfo], c: Dictionary[TestMemberInfo, TestMemberInfo]) signal test_signal_8(a: MyClass, b: Array[MyClass], c: Dictionary[MyClass, MyClass]) - -func no_exec(): - test_signal_1.emit() - test_signal_2.emit() - test_signal_3.emit() - test_signal_4.emit() - test_signal_5.emit() - test_signal_6.emit() - test_signal_7.emit() - test_signal_8.emit() +@warning_ignore_restore("unused_signal") func test(): var script: Script = get_script()