diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 131ac6ea9308..79d5a44e20db 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -469,12 +469,24 @@ Specifies the maximum number of log files allowed (used for rotation). Set to [code]1[/code] to disable log file rotation. 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 a member beginning with [code]__[/code] is accessed from outside the class. + + + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a member beginning with [code]_[/code] is accessed from outside the class or the super class where it is defined. + 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 [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 [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a method beginning with [code]__[/code] is called from outside the class. + + + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a method beginning with [code]_[/code] is called from outside the class or the super class where it is defined. + 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. @@ -616,6 +628,9 @@ 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 [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a protected 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. diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 60109efc4d5b..4ed2be7730ad 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -341,6 +341,54 @@ void GDScriptAnalyzer::get_class_node_current_scope_classes(GDScriptParser::Clas } } +#ifdef DEBUG_ENABLED +void GDScriptAnalyzer::check_access_private_member(GDScriptParser::IdentifierNode *p_identifier, const bool p_is_call) { + if (p_identifier == nullptr) { + return; + } + GDScriptParser::IdentifierNode::IdentifierAccess identifier_access = p_identifier->get_access(); + if (identifier_access == GDScriptParser::IdentifierNode::IdentifierAccess::ACCESS_PUBLIC) { + return; + } + if (parser->current_class->get_datatype().kind != GDScriptParser::DataType::CLASS && parser->current_class->get_datatype().kind != GDScriptParser::DataType::SCRIPT) { + return; + } + if (parser->current_function && parser->current_function->body && parser->current_function->body->has_local(p_identifier->name)) { + return; + } + + bool has_member = parser->current_class->has_member(p_identifier->name); + if (has_member) { + return; + } + if (identifier_access == GDScriptParser::IdentifierNode::IdentifierAccess::ACCESS_PRIVATE) { + parser->push_warning(p_identifier, p_is_call ? GDScriptWarning::CALLING_PRIVATE_METHOD : GDScriptWarning::ACCESSING_PRIVATE_MEMBER, p_identifier->name); + return; + } + + bool access_valid_protected_member = false; + GDScriptParser::ClassNode *base_class = parser->current_class->base_type.class_type; + while (base_class != nullptr) { + if (base_class->has_member(p_identifier->name)) { + access_valid_protected_member = true; + break; + } + base_class = base_class->base_type.class_type; + } + if (!access_valid_protected_member && identifier_access == GDScriptParser::IdentifierNode::IdentifierAccess::ACCESS_PROTECTED) { + parser->push_warning(p_identifier, p_is_call ? GDScriptWarning::CALLING_PROTECTED_METHOD : GDScriptWarning::ACCESSING_PROTECTED_MEMBER, p_identifier->name); + } +} + +void GDScriptAnalyzer::warn_unused_private_protected_class_variable(const GDScriptParser::Node *p_node, const GDScriptParser::IdentifierNode *p_identifier) { + GDScriptParser::IdentifierNode::IdentifierAccess identifier_access = p_identifier->get_access(); + if (identifier_access == GDScriptParser::IdentifierNode::IdentifierAccess::ACCESS_PUBLIC) { + return; + } + parser->push_warning(p_node, identifier_access == GDScriptParser::IdentifierNode::IdentifierAccess::ACCESS_PRIVATE ? GDScriptWarning::UNUSED_PRIVATE_CLASS_VARIABLE : GDScriptWarning::UNUSED_PROTECTED_CLASS_VARIABLE, p_identifier->name); +} +#endif // DEBUG_ENABLED + Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source) { if (p_source == nullptr && parser->has_class(p_class)) { p_source = p_class; @@ -1435,8 +1483,8 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co GDScriptParser::ClassNode::Member member = p_class->members[i]; if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) { #ifdef DEBUG_ENABLED - if (member.variable->usages == 0 && String(member.variable->identifier->name).begins_with("_")) { - parser->push_warning(member.variable->identifier, GDScriptWarning::UNUSED_PRIVATE_CLASS_VARIABLE, member.variable->identifier->name); + if (member.variable->usages == 0) { + warn_unused_private_protected_class_variable(member.variable->identifier, member.variable->identifier); } #endif @@ -3510,10 +3558,19 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a if (p_call->callee == nullptr && current_lambda != nullptr) { push_error("Cannot use `super()` inside a lambda.", p_call); } + +#ifdef DEBUG_ENABLED + GDScriptParser::IdentifierNode *identifier = static_cast(p_call->callee); + check_access_private_member(identifier, true); +#endif } else if (callee_type == GDScriptParser::Node::IDENTIFIER) { base_type = parser->current_class->get_datatype(); base_type.is_meta_type = false; is_self = true; +#ifdef DEBUG_ENABLED + GDScriptParser::IdentifierNode *identifier = static_cast(p_call->callee); + check_access_private_member(identifier, true); +#endif } else if (callee_type == GDScriptParser::Node::SUBSCRIPT) { GDScriptParser::SubscriptNode *subscript = static_cast(p_call->callee); if (subscript->base == nullptr) { @@ -3547,6 +3604,9 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a base_type = subscript->base->get_datatype(); is_self = subscript->base->type == GDScriptParser::Node::SELF; } +#ifdef DEBUG_ENABLED + check_access_private_member(subscript->attribute, true); +#endif } else { // Invalid call. Error already sent in parser. // TODO: Could check if Callable here too. @@ -4468,7 +4528,9 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident function_test = function_test->source_lambda->parent_function; } } - +#ifdef DEBUG_ENABLED + check_access_private_member(p_identifier); +#endif return; } @@ -4830,6 +4892,9 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri } result_type.kind = GDScriptParser::DataType::VARIANT; } +#ifdef DEBUG_ENABLED + check_access_private_member(p_subscript->attribute); +#endif } else { if (p_subscript->index == nullptr) { return; diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 6acc34101f24..7fb1200f5ff1 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -66,6 +66,11 @@ class GDScriptAnalyzer { void get_class_node_current_scope_classes(GDScriptParser::ClassNode *p_node, List *p_list, GDScriptParser::Node *p_source); +#ifdef DEBUG_ENABLED + void check_access_private_member(GDScriptParser::IdentifierNode *p_identifier, const bool p_is_call = false); + void warn_unused_private_protected_class_variable(const GDScriptParser::Node *p_node, const GDScriptParser::IdentifierNode *p_identifier); +#endif + Error resolve_class_inheritance(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr); Error resolve_class_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive); GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 646391787173..77e25a15f230 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -98,6 +98,8 @@ GDScriptParser::GDScriptParser() { register_annotation(MethodInfo("@static_unload"), AnnotationInfo::SCRIPT, &GDScriptParser::static_unload_annotation); // Onready annotation. register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation); + + // register_annotation(MethodInfo("@virtual"), AnnotationInfo::FUNCTION, &GDScriptParser::virtual_annotation); // Export annotations. register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations); register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations, varray(), true); @@ -4282,6 +4284,16 @@ bool GDScriptParser::onready_annotation(AnnotationNode *p_annotation, Node *p_ta current_class->onready_used = true; return true; } +// bool GDScriptParser::virtual_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) { +// ERR_FAIL_COND_V_MSG(p_target->type != Node::FUNCTION, false, R"("@virtual" annotation can only be applied to class methods.)"); +// FunctionNode *method = static_cast(p_target); +// if (method->is_virtual) { +// push_error(R"("@virtual" annotation can only be applied to a method once.)", p_annotation); +// return false; +// } +// method->is_virtual = true; +// return true; +// } static String _get_annotation_error_string(const StringName &p_annotation_name, const Vector &p_expected_types, const GDScriptParser::DataType &p_provided_type) { Vector types; diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 22b14e8bb5c1..f3abd7a7576c 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -336,8 +336,8 @@ class GDScriptParser { VARIABLE, WHILE, }; - Type type = NONE; + int start_line = 0, end_line = 0; int start_column = 0, end_column = 0; int leftmost_column = 0, rightmost_column = 0; @@ -796,6 +796,20 @@ class GDScriptParser { members.push_back(Member(p_annotation_node)); } + Vector get_super_class_fqcns() const { + Vector ret; + + const ClassNode *iterated_class = base_type.class_type; + while (iterated_class) { + if (iterated_class->datatype.kind == DataType::SCRIPT || iterated_class->datatype.kind == DataType::CLASS) { + ret.append(iterated_class->fqcn); + } + iterated_class = iterated_class->base_type.class_type; + } + + return ret; + } + ClassNode() { type = CLASS; } @@ -855,6 +869,7 @@ class GDScriptParser { SuiteNode *body = nullptr; bool is_static = false; // For lambdas it's determined in the analyzer. bool is_coroutine = false; + // bool is_virtual = false; Variant rpc_config; MethodInfo info; LambdaNode *source_lambda = nullptr; @@ -902,6 +917,12 @@ class GDScriptParser { }; Source source = UNDEFINED_SOURCE; + enum IdentifierAccess { + ACCESS_PUBLIC, + ACCESS_PRIVATE, + ACCESS_PROTECTED, + }; + union { ParameterNode *parameter_source = nullptr; IdentifierNode *bind_source; @@ -916,6 +937,17 @@ class GDScriptParser { int usages = 0; // Useful for binds/iterator variable. + IdentifierAccess get_access() const { + String str_name = String(name); + if (str_name.begins_with("__")) { + return ACCESS_PRIVATE; + } else if (str_name.begins_with("_")) { + return ACCESS_PROTECTED; + } else { + return ACCESS_PUBLIC; + } + } + IdentifierNode() { type = IDENTIFIER; } @@ -1509,6 +1541,7 @@ class GDScriptParser { 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); + // bool virtual_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); template bool export_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); bool export_storage_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index f14b38a1ef24..59bc3820f9d3 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -51,6 +51,7 @@ String GDScriptWarning::get_message() const { CHECK_SYMBOLS(1); return vformat(R"(The local constant "%s" is declared but never used in the block. If this is intended, prefix it with an underscore: "_%s".)", symbols[0], symbols[0]); case UNUSED_PRIVATE_CLASS_VARIABLE: + case UNUSED_PROTECTED_CLASS_VARIABLE: CHECK_SYMBOLS(1); return vformat(R"(The class variable "%s" is declared but never used in the class.)", symbols[0]); case UNUSED_PARAMETER: @@ -162,6 +163,18 @@ String GDScriptWarning::get_message() const { return vformat(R"*(The default value is using "%s" which won't return nodes in the scene tree before "_ready()" is called. Use the "@onready" annotation to solve this.)*", symbols[0]); case ONREADY_WITH_EXPORT: return R"("@onready" will set the default value after "@export" takes effect and will override it.)"; + case ACCESSING_PRIVATE_MEMBER: + CHECK_SYMBOLS(1); + return vformat(R"(Trying accessing the member "%s" prefixed with "__", which would be a private member.)", symbols[0]); + case CALLING_PRIVATE_METHOD: + CHECK_SYMBOLS(1); + return vformat(R"*(Trying calling the method "%s()" prefixed with "__", which would be a private method.)*", symbols[0]); + case ACCESSING_PROTECTED_MEMBER: + CHECK_SYMBOLS(1); + return vformat(R"(Trying accessing the member "%s" prefixed with "_", which would be a protected member.)", symbols[0]); + case CALLING_PROTECTED_METHOD: + CHECK_SYMBOLS(1); + return vformat(R"*(Trying calling the method "%s()" prefixed with "_", which would be a protected method.)*", symbols[0]); #ifndef DISABLE_DEPRECATED // Never produced. These warnings migrated from 3.x by mistake. case PROPERTY_USED_AS_FUNCTION: // There is already an error. @@ -199,6 +212,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "UNUSED_VARIABLE", "UNUSED_LOCAL_CONSTANT", "UNUSED_PRIVATE_CLASS_VARIABLE", + "UNUSED_PROTECTED_CLASS_VARIABLE", "UNUSED_PARAMETER", "UNUSED_SIGNAL", "SHADOWED_VARIABLE", @@ -238,6 +252,10 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "NATIVE_METHOD_OVERRIDE", "GET_NODE_DEFAULT_WITHOUT_ONREADY", "ONREADY_WITH_EXPORT", + "ACCESSING_PRIVATE_MEMBER", + "CALLING_PRIVATE_METHOD", + "ACCESSING_PROTECTED_MEMBER", + "CALLING_PROTECTED_METHOD", #ifndef DISABLE_DEPRECATED "PROPERTY_USED_AS_FUNCTION", "CONSTANT_USED_AS_FUNCTION", diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h index e9ab309cd647..28362de718f8 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -50,7 +50,8 @@ class GDScriptWarning { UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc). UNUSED_VARIABLE, // Local variable is declared but never used. UNUSED_LOCAL_CONSTANT, // Local constant is declared but never used. - UNUSED_PRIVATE_CLASS_VARIABLE, // Class variable is declared private ("_" prefix) but never used in the class. + UNUSED_PRIVATE_CLASS_VARIABLE, // Class variable is declared private ("__" prefix) but never used in the class. + UNUSED_PROTECTED_CLASS_VARIABLE, // Class variable is declared private ("__" prefix) but never used in the class. UNUSED_PARAMETER, // Function parameter is never used. UNUSED_SIGNAL, // Signal is defined but never explicitly used in the class. SHADOWED_VARIABLE, // A local variable/constant shadows a current class member. @@ -90,6 +91,10 @@ class GDScriptWarning { NATIVE_METHOD_OVERRIDE, // The script method overrides a native one, this may not work as intended. GET_NODE_DEFAULT_WITHOUT_ONREADY, // A class variable uses `get_node()` (or the `$` notation) as its default value, but does not use the @onready annotation. ONREADY_WITH_EXPORT, // The `@onready` annotation will set the value after `@export` which is likely not intended. + ACCESSING_PRIVATE_MEMBER, // A member prefixed with `__` is being accessed, whether the member exists or not. + CALLING_PRIVATE_METHOD, // A method prefixed with `__` is being called, whether the method exists or not. + ACCESSING_PROTECTED_MEMBER, // A member prefixed with `_` is being accessed, whether the member exists or not. + CALLING_PROTECTED_METHOD, // A method prefixed with `_` is being called, whether the method exists or not. #ifndef DISABLE_DEPRECATED 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. @@ -108,6 +113,7 @@ class GDScriptWarning { WARN, // UNUSED_VARIABLE WARN, // UNUSED_LOCAL_CONSTANT WARN, // UNUSED_PRIVATE_CLASS_VARIABLE + WARN, // UNUSED_PROTECTED_CLASS_VARIABLE WARN, // UNUSED_PARAMETER WARN, // UNUSED_SIGNAL WARN, // SHADOWED_VARIABLE @@ -147,6 +153,10 @@ class GDScriptWarning { ERROR, // NATIVE_METHOD_OVERRIDE // May not work as expected. ERROR, // GET_NODE_DEFAULT_WITHOUT_ONREADY // May not work as expected. ERROR, // ONREADY_WITH_EXPORT // May not work as expected. + WARN, // ACCESSING_PRIVATE_MEMBER + WARN, // CALLING_PRIVATE_METHOD + WARN, // ACCESSING_PROTECTED_MEMBER + WARN, // CALLING_PROTECTED_METHOD #ifndef DISABLE_DEPRECATED WARN, // PROPERTY_USED_AS_FUNCTION WARN, // CONSTANT_USED_AS_FUNCTION diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd index 3a1863a1f409..88f32d3653c8 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd @@ -26,7 +26,6 @@ class InnerClass: print("Inner") var o := EnumFunctionTypecheckOuterClass.new() - _d = o.outer_outer_no_class(EnumFunctionTypecheckOuterClass.MyEnum.V1) print() _d = o.outer_outer_class(EnumFunctionTypecheckOuterClass.MyEnum.V1) diff --git a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd index a8641e4f3b8c..0b1c4191fd03 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd @@ -5,6 +5,7 @@ class BaseClass: class SuperClassMethodsRecognized extends BaseClass: func _init(): # Recognizes super class methods. + @warning_ignore("calling_protected_method") var _x = _get_property_list() class SuperMethodsRecognized extends BaseClass: @@ -16,6 +17,8 @@ class SuperMethodsRecognized extends BaseClass: func test(): var test1 = SuperClassMethodsRecognized.new() + @warning_ignore("calling_protected_method") print(test1._get_property_list()) # Calls base class's method. var test2 = SuperMethodsRecognized.new() + @warning_ignore("calling_protected_method") print(test2._get_property_list()) diff --git a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.gd b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.gd index 333950d64e98..3a77e12c68be 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.gd @@ -10,7 +10,9 @@ class A extends Node: var get_node_default_without_onready = $Node @warning_ignore("unused_private_class_variable") -var _unused_private_class_variable +var __unused_private_class_variable +@warning_ignore("unused_protected_class_variable") +var _unused_protected_class_variable @warning_ignore("onready_with_export") @onready @export var onready_with_export = 1 @@ -36,7 +38,7 @@ func test_warnings(unused_private_class_variable): print(unassigned_variable) var _unassigned_variable_op_assign - @warning_ignore("unassigned_variable_op_assign") + @warning_ignore("unassigned_variable_op_assign", "accessing_protected_member") _unassigned_variable_op_assign += t @warning_ignore("unused_variable") @@ -151,6 +153,86 @@ func test_unsafe_void_return() -> void: func get_class(): pass +class AccessTestA: + @warning_ignore("unused_protected_class_variable") + static var _static_a = null + @warning_ignore("unused_private_class_variable") + static var __static_b = null + + @warning_ignore("unused_protected_class_variable") + var _a = null + @warning_ignore("unused_private_class_variable") + var __b = null + + func _call_a(): + pass + + func __call_b(): + pass + + func _call_a_ret(): + return null + + func __call_b_ret(): + return null + + static func _static_call_a(): + pass + + static func __static_call_b(): + pass + + static func _static_call_a_ret(): + return null + + static func __static_call_b_ret(): + return null + +class AccessTestB: + func _init(): + var cls_a = AccessTestA.new() + @warning_ignore("accessing_protected_member") + AccessTestA._static_a = null + @warning_ignore("accessing_private_member") + AccessTestA.__static_b = null + @warning_ignore("accessing_protected_member") + cls_a._a = null + @warning_ignore("accessing_private_member") + cls_a.__b = null + @warning_ignore("calling_protected_method") + cls_a._call_a() + @warning_ignore("calling_private_method") + cls_a.__call_b() + + @warning_ignore("unused_variable", "calling_protected_method") + var t1 = cls_a._call_a_ret() + @warning_ignore("unused_variable", "calling_private_method") + var t2 = cls_a.__call_b_ret() + @warning_ignore("calling_protected_method") + AccessTestA._static_call_a() + @warning_ignore("calling_private_method") + AccessTestA.__static_call_b() + @warning_ignore("unused_variable", "calling_protected_method") + var t3 = AccessTestA._static_call_a_ret() + @warning_ignore("unused_variable", "calling_private_method") + var t4 = AccessTestA.__static_call_b_ret() + +class AccessTestC extends AccessTestA: + func _init(): + @warning_ignore("accessing_private_member") + AccessTestA.__static_b = null + @warning_ignore("accessing_private_member") + __b = null + @warning_ignore("calling_private_method") + __call_b() + + @warning_ignore("unused_variable", "calling_private_method") + var t1 = __call_b_ret() + @warning_ignore("calling_private_method") + AccessTestA.__static_call_b() + @warning_ignore("unused_variable", "calling_private_method") + var t2 = AccessTestA.__static_call_b_ret() + # We don't want to execute it because of errors, just analyze. func test(): pass diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/private_protected_members.gd b/modules/gdscript/tests/scripts/analyzer/warnings/private_protected_members.gd new file mode 100644 index 000000000000..bdd1d3b270dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/warnings/private_protected_members.gd @@ -0,0 +1,80 @@ +class A: + @warning_ignore("unused_protected_class_variable") + static var _static_a = null + @warning_ignore("unused_private_class_variable") + static var __static_b = null + + @warning_ignore("unused_protected_class_variable") + var _a = null + @warning_ignore("unused_private_class_variable") + var __b = null + + func _call_a(): + pass + + func __call_b(): + pass + + func _call_a_ret(): + return null + + func __call_b_ret(): + return null + + static func _static_call_a(): + pass + + static func __static_call_b(): + pass + + static func _static_call_a_ret(): + return null + + static func __static_call_b_ret(): + return null + +class B: + func _init(): + var cls_a = A.new() + A._static_a = null + A.__static_b = null + cls_a._a = null + cls_a.__b = null + cls_a._call_a() + cls_a.__call_b() + + @warning_ignore("unused_variable") + var t1 = cls_a._call_a_ret() + @warning_ignore("unused_variable") + var t2 = cls_a.__call_b_ret() + + A._static_call_a() + A.__static_call_b() + @warning_ignore("unused_variable") + var t3 = A._static_call_a_ret() + @warning_ignore("unused_variable") + var t4 = A.__static_call_b_ret() + +class C extends A: + func _init(): + A._static_a = null + A.__static_b = null + _a = null + __b = null + _call_a() + __call_b() + + @warning_ignore("unused_variable") + var t1 = _call_a_ret() + @warning_ignore("unused_variable") + var t2 = __call_b_ret() + + _static_call_a() + __static_call_b() + @warning_ignore("unused_variable") + var t3 = _static_call_a_ret() + @warning_ignore("unused_variable") + var t4 = __static_call_b_ret() + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/private_protected_members.out b/modules/gdscript/tests/scripts/analyzer/warnings/private_protected_members.out new file mode 100644 index 000000000000..77332a726bd6 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/warnings/private_protected_members.out @@ -0,0 +1,19 @@ +GDTEST_OK +~~ WARNING at line 39: (ACCESSING_PROTECTED_MEMBER) Trying accessing the member "_static_a" prefixed with "_", which would be a protected member. +~~ WARNING at line 40: (ACCESSING_PRIVATE_MEMBER) Trying accessing the member "__static_b" prefixed with "__", which would be a private member. +~~ WARNING at line 41: (ACCESSING_PROTECTED_MEMBER) Trying accessing the member "_a" prefixed with "_", which would be a protected member. +~~ WARNING at line 42: (ACCESSING_PRIVATE_MEMBER) Trying accessing the member "__b" prefixed with "__", which would be a private member. +~~ WARNING at line 43: (CALLING_PROTECTED_METHOD) Trying calling the method "_call_a()" prefixed with "_", which would be a protected method. +~~ WARNING at line 44: (CALLING_PRIVATE_METHOD) Trying calling the method "__call_b()" prefixed with "__", which would be a private method. +~~ WARNING at line 47: (CALLING_PROTECTED_METHOD) Trying calling the method "_call_a_ret()" prefixed with "_", which would be a protected method. +~~ WARNING at line 49: (CALLING_PRIVATE_METHOD) Trying calling the method "__call_b_ret()" prefixed with "__", which would be a private method. +~~ WARNING at line 51: (CALLING_PROTECTED_METHOD) Trying calling the method "_static_call_a()" prefixed with "_", which would be a protected method. +~~ WARNING at line 52: (CALLING_PRIVATE_METHOD) Trying calling the method "__static_call_b()" prefixed with "__", which would be a private method. +~~ WARNING at line 54: (CALLING_PROTECTED_METHOD) Trying calling the method "_static_call_a_ret()" prefixed with "_", which would be a protected method. +~~ WARNING at line 56: (CALLING_PRIVATE_METHOD) Trying calling the method "__static_call_b_ret()" prefixed with "__", which would be a private method. +~~ WARNING at line 61: (ACCESSING_PRIVATE_MEMBER) Trying accessing the member "__static_b" prefixed with "__", which would be a private member. +~~ WARNING at line 63: (ACCESSING_PRIVATE_MEMBER) Trying accessing the member "__b" prefixed with "__", which would be a private member. +~~ WARNING at line 65: (CALLING_PRIVATE_METHOD) Trying calling the method "__call_b()" prefixed with "__", which would be a private method. +~~ WARNING at line 70: (CALLING_PRIVATE_METHOD) Trying calling the method "__call_b_ret()" prefixed with "__", which would be a private method. +~~ WARNING at line 73: (CALLING_PRIVATE_METHOD) Trying calling the method "__static_call_b()" prefixed with "__", which would be a private method. +~~ WARNING at line 77: (CALLING_PRIVATE_METHOD) Trying calling the method "__static_call_b_ret()" prefixed with "__", which would be a private method. diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.gd b/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.gd index 5ca8ceffddc5..acff6e2bbb7e 100644 --- a/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.gd +++ b/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.gd @@ -1,10 +1,16 @@ # GH-72135 var _a -@warning_ignore("unused_private_class_variable") +@warning_ignore("unused_protected_class_variable") var _b -@warning_ignore("unused_private_class_variable") var _c +@warning_ignore("unused_protected_class_variable") var _c var _d +var __e +@warning_ignore("unused_private_class_variable") +var __f +@warning_ignore("unused_private_class_variable") var __g +var __h + func test(): pass diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.out b/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.out index 692ec589def8..82d671fa32b4 100644 --- a/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.out +++ b/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.out @@ -1,3 +1,5 @@ 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 7: (UNUSED_PRIVATE_CLASS_VARIABLE) The class variable "_d" is declared but never used in the class. +~~ WARNING at line 3: (UNUSED_PROTECTED_CLASS_VARIABLE) The class variable "_a" is declared but never used in the class. +~~ WARNING at line 7: (UNUSED_PROTECTED_CLASS_VARIABLE) The class variable "_d" is declared but never used in the class. +~~ WARNING at line 9: (UNUSED_PRIVATE_CLASS_VARIABLE) The class variable "__e" is declared but never used in the class. +~~ WARNING at line 13: (UNUSED_PRIVATE_CLASS_VARIABLE) The class variable "__h" is declared but never used in the class. diff --git a/modules/gdscript/tests/scripts/parser/features/warning_ignore_regions.gd b/modules/gdscript/tests/scripts/parser/features/warning_ignore_regions.gd index ffdeae1763ad..50832ed280e1 100644 --- a/modules/gdscript/tests/scripts/parser/features/warning_ignore_regions.gd +++ b/modules/gdscript/tests/scripts/parser/features/warning_ignore_regions.gd @@ -1,12 +1,19 @@ @warning_ignore_start("unreachable_code", "narrowing_conversion") var _a = 1 -@warning_ignore_start("unused_private_class_variable") +@warning_ignore_start("unused_protected_class_variable") var _b = 2 var _c = 3 -@warning_ignore_restore("unused_private_class_variable") +@warning_ignore_restore("unused_protected_class_variable") var _d = 4 +var __e = 5 +@warning_ignore_start("unused_private_class_variable") +var __f = 6 +var __g = 7 +@warning_ignore_restore("unused_private_class_variable") +var __h = 8 + func test(): return diff --git a/modules/gdscript/tests/scripts/parser/features/warning_ignore_regions.out b/modules/gdscript/tests/scripts/parser/features/warning_ignore_regions.out index 11f7263ed8e4..121fe8ea208f 100644 --- a/modules/gdscript/tests/scripts/parser/features/warning_ignore_regions.out +++ b/modules/gdscript/tests/scripts/parser/features/warning_ignore_regions.out @@ -1,6 +1,8 @@ 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). +~~ WARNING at line 3: (UNUSED_PROTECTED_CLASS_VARIABLE) The class variable "_a" is declared but never used in the class. +~~ WARNING at line 8: (UNUSED_PROTECTED_CLASS_VARIABLE) The class variable "_d" is declared but never used in the class. +~~ WARNING at line 10: (UNUSED_PRIVATE_CLASS_VARIABLE) The class variable "__e" is declared but never used in the class. +~~ WARNING at line 15: (UNUSED_PRIVATE_CLASS_VARIABLE) The class variable "__h" is declared but never used in the class. +~~ WARNING at line 20: (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 25: (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 29: (NARROWING_CONVERSION) Narrowing conversion (float is converted to int and loses precision). diff --git a/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.gd b/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.gd index 99156adb2865..668b7f05bcaf 100644 --- a/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.gd +++ b/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.gd @@ -14,5 +14,6 @@ class B extends A: func test(): var node := B.new() + @warning_ignore("calling_protected_method") node._ready() node.free() diff --git a/modules/gdscript/tests/scripts/runtime/features/self_destruction.gd b/modules/gdscript/tests/scripts/runtime/features/self_destruction.gd index 442335faeba0..971031951da2 100644 --- a/modules/gdscript/tests/scripts/runtime/features/self_destruction.gd +++ b/modules/gdscript/tests/scripts/runtime/features/self_destruction.gd @@ -41,7 +41,7 @@ func test(): # Signal handling. obj = MyObj.new(nullify_obj) - @warning_ignore("return_value_discarded") + @warning_ignore("return_value_discarded", "accessing_protected_member") some_signal.connect(obj._on_some_signal) some_signal.emit() print(obj)