From 0ee69f94756cf95e0533f32508111d30916ad918 Mon Sep 17 00:00:00 2001 From: JustDooooIt <844730658@qq.com> Date: Thu, 29 May 2025 16:59:54 +0800 Subject: [PATCH 01/13] Add the must_call_super flag --- modules/gdscript/gdscript_parser.h | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index a235f0d799da..ff221992a24d 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -856,6 +856,7 @@ class GDScriptParser { SuiteNode *body = nullptr; bool is_static = false; // For lambdas it's determined in the analyzer. bool is_coroutine = false; + bool must_call_super = false; Variant rpc_config; MethodInfo info; LambdaNode *source_lambda = nullptr; From 4cbaf877c473107e68fdda6054388cdbe3b1baa6 Mon Sep 17 00:00:00 2001 From: JustDooooIt <844730658@qq.com> Date: Thu, 29 May 2025 17:01:47 +0800 Subject: [PATCH 02/13] Add must_call_super annotation --- modules/gdscript/gdscript_parser.cpp | 11 +++++++++++ modules/gdscript/gdscript_parser.h | 1 + 2 files changed, 12 insertions(+) diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 0153f47b40a8..521552bc60a5 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -4349,6 +4349,17 @@ bool GDScriptParser::static_unload_annotation(AnnotationNode *p_annotation, Node return true; } +bool GDScriptParser::must_call_super_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) { + ERR_FAIL_COND_V_MSG(p_target->type != Node::FUNCTION, false, R"("@must_call_super" annotation can only be applied to functions.)"); + FunctionNode *func_node = static_cast(p_target); + if (func_node->must_call_super) { + push_error(vformat(R"("%s" annotation can only be used once per function.)", p_annotation->name), p_annotation); + return false; + } + func_node->must_call_super = 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.)"); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index ff221992a24d..e1173e66de2b 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1529,6 +1529,7 @@ class GDScriptParser { 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 must_call_super_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); From d0263ff0679ba80d404b2666a6cec63a49f34507 Mon Sep 17 00:00:00 2001 From: JustDooooIt <844730658@qq.com> Date: Thu, 29 May 2025 17:05:11 +0800 Subject: [PATCH 03/13] Add the must_call_super validation logic --- modules/gdscript/gdscript_analyzer.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 7ddb8fe05bc8..f57a754876b1 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -1925,6 +1925,27 @@ void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_fun } } + if (p_function->must_call_super) { + if (p_is_lambda) { + push_error(R"(Cannot be used in lambda expressions)", p_function); + } else { + if (p_function->body->statements.size() == 0) { + push_error(R"(Must call the base class function)", p_function); + return; + } + GDScriptParser::Node *node = p_function->body->statements[0]; + bool is_super = false; + if (node->type == GDScriptParser::Node::Type::CALL) { + GDScriptParser::CallNode *call_node = static_cast(node); + if (!call_node->is_super) { + push_error(R"(Must call the base class function)", p_function); + } + } else { + push_error(R"(Must call the base class function)", p_function); + } + } + } + parser->current_function = previous_function; static_context = previous_static_context; } From 38fec2ba615169db9c71020e2f07239b0bca44c3 Mon Sep 17 00:00:00 2001 From: JustDooooIt <844730658@qq.com> Date: Thu, 29 May 2025 17:18:59 +0800 Subject: [PATCH 04/13] Add an access_level field --- modules/gdscript/gdscript_parser.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index e1173e66de2b..8fd24bf50a60 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -336,7 +336,15 @@ class GDScriptParser { WHILE, }; + enum AccessLevel { + PRIVATE, + PROTECTED, + FRIEND, + PUBLIC + }; + Type type = NONE; + AccessLevel access_level = PUBLIC; int start_line = 0, end_line = 0; int start_column = 0, end_column = 0; int leftmost_column = 0, rightmost_column = 0; From 0c72b98aab773d1823e6ce511f03e1c5dae4ae7e Mon Sep 17 00:00:00 2001 From: JustDooooIt <844730658@qq.com> Date: Thu, 29 May 2025 17:28:22 +0800 Subject: [PATCH 05/13] Add access_level validation to get_function_signature --- modules/gdscript/gdscript_analyzer.cpp | 10 +++++++--- modules/gdscript/gdscript_analyzer.h | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index f57a754876b1..1b332ee93951 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -1805,6 +1805,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * int default_par_count = 0; BitField method_flags = {}; StringName native_base; + GDScriptParser::Node::AccessLevel acceess_level = GDScriptParser::Node::PUBLIC; if (!p_is_lambda && get_function_signature(p_function, false, base_type, function_name, parent_return_type, parameters_types, default_par_count, method_flags, &native_base)) { bool valid = p_function->is_static == method_flags.has_flag(METHOD_FLAG_STATIC); @@ -2298,7 +2299,8 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { List par_types; int default_arg_count = 0; BitField method_flags = {}; - if (get_function_signature(p_for->list, false, list_type, CoreStringName(_iter_get), return_type, par_types, default_arg_count, method_flags)) { + GDScriptParser::Node::AccessLevel access_level = GDScriptParser::Node::PUBLIC; + if (get_function_signature(p_for->list, false, list_type, CoreStringName(_iter_get), return_type, par_types, default_arg_count, method_flags, access_level)) { variable_type = return_type; variable_type.type_source = list_type.type_source; } else if (!list_type.is_hard_type()) { @@ -3591,6 +3593,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a BitField method_flags = {}; GDScriptParser::DataType return_type; List par_types; + GDScriptParser::Node::AccessLevel access_level = GDScriptParser::Node::PUBLIC; bool is_constructor = (base_type.is_meta_type || (p_call->callee && p_call->callee->type == GDScriptParser::Node::IDENTIFIER)) && p_call->function_name == SNAME("new"); @@ -3605,7 +3608,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a } } - if (get_function_signature(p_call, is_constructor, base_type, p_call->function_name, return_type, par_types, default_arg_count, method_flags)) { + if (get_function_signature(p_call, is_constructor, base_type, p_call->function_name, return_type, par_types, default_arg_count, method_flags, access_level)) { // If the method is implemented in the class hierarchy, the virtual flag will not be set for that MethodInfo and the search stops there. // Virtual check only possible for super() calls because class hierarchy is known. Node/Objects may have scripts attached we don't know of at compile-time. p_call->is_static = method_flags.has_flag(METHOD_FLAG_STATIC); @@ -5730,7 +5733,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo return result; } -bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType p_base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List &r_par_types, int &r_default_arg_count, BitField &r_method_flags, StringName *r_native_class) { +bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType p_base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List &r_par_types, int &r_default_arg_count, BitField &r_method_flags, GDScriptParser::Node::AccessLevel &access_level, StringName *r_native_class) { r_method_flags = METHOD_FLAGS_DEFAULT; r_default_arg_count = 0; if (r_native_class) { @@ -5832,6 +5835,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo r_return_type = p_is_constructor ? p_base_type : found_function->get_datatype(); r_return_type.is_meta_type = false; r_return_type.is_coroutine = found_function->is_coroutine; + access_level = found_function->access_level; return true; } diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 576276471d3a..441588521ac0 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -128,7 +128,7 @@ class GDScriptAnalyzer { GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source); GDScriptParser::DataType type_from_property(const PropertyInfo &p_property, bool p_is_arg = false, bool p_is_readonly = false) const; GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source); - bool get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List &r_par_types, int &r_default_arg_count, BitField &r_method_flags, StringName *r_native_class = nullptr); + bool get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List &r_par_types, int &r_default_arg_count, BitField &r_method_flags, GDScriptParser::Node::AccessLevel &access_level, StringName *r_native_class = nullptr); bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List &r_par_types, int &r_default_arg_count, BitField &r_method_flags); void validate_call_arg(const List &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call); void validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call); From edc684b41657c37867c1969029cefd231d117075 Mon Sep 17 00:00:00 2001 From: JustDooooIt <844730658@qq.com> Date: Thu, 29 May 2025 17:31:24 +0800 Subject: [PATCH 06/13] Add @protected and @private annotations --- modules/gdscript/gdscript_parser.cpp | 22 ++++++++++++++++++++++ modules/gdscript/gdscript_parser.h | 2 ++ 2 files changed, 24 insertions(+) diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 521552bc60a5..33ed15a96dce 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -4360,6 +4360,28 @@ bool GDScriptParser::must_call_super_annotation(AnnotationNode *p_annotation, No return true; } +bool GDScriptParser::protected_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) { + ERR_FAIL_COND_V_MSG(p_target->type != Node::CONSTANT && p_target->type != Node::VARIABLE && p_target->type != Node::FUNCTION && p_target->type != Node::CLASS && p_target->type != Node::SIGNAL, + false, R"(@protected can only be applied to constants, variables, signals, functions, and classes.)"); + if (p_target->access_level != GDScriptParser::Node::AccessLevel::PUBLIC) { + push_error(R"(Only one of @protected/@private is allowed, and it can be used only once.)", p_target); + return false; + } + p_target->access_level = GDScriptParser::Node::AccessLevel::PROTECTED; + return true; +} + +bool GDScriptParser::private_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) { + ERR_FAIL_COND_V_MSG(p_target->type != Node::CONSTANT && p_target->type != Node::VARIABLE && p_target->type != Node::FUNCTION && p_target->type != Node::CLASS && p_target->type != Node::SIGNAL, + false, R"(@private can only be applied to constants, variables, signals, functions, and classes.)"); + if (p_target->access_level != GDScriptParser::Node::AccessLevel::PUBLIC) { + push_error(R"(Only one of @protected/@private is allowed, and it can be used only once.)", p_target); + return false; + } + p_target->access_level = GDScriptParser::Node::AccessLevel::PRIVATE; + 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.)"); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 8fd24bf50a60..1ab068938f2b 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1538,6 +1538,8 @@ 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 must_call_super_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); + bool protected_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); + bool private_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); From 114e94c7e8ccb2095dd310a62d0c0a0785c9b205 Mon Sep 17 00:00:00 2001 From: JustDooooIt <844730658@qq.com> Date: Thu, 29 May 2025 17:45:29 +0800 Subject: [PATCH 07/13] Determine whether a function can be called under the condition of using the super keyword --- modules/gdscript/gdscript_analyzer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 1b332ee93951..67cc3aa7a949 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -3614,6 +3614,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a p_call->is_static = method_flags.has_flag(METHOD_FLAG_STATIC); if (p_call->is_super && method_flags.has_flag(METHOD_FLAG_VIRTUAL)) { push_error(vformat(R"*(Cannot call the parent class' virtual function "%s()" because it hasn't been defined.)*", p_call->function_name), p_call); + } else if (p_call->is_super && access_level < GDScriptParser::Node::AccessLevel::PROTECTED) { + push_error(R"(Access restricted.)", p_call); } // If the function requires typed arrays we must make literals be typed. From 25f58d378bdd7906b5b176d3473ca8809a20a8b4 Mon Sep 17 00:00:00 2001 From: JustDooooIt <844730658@qq.com> Date: Thu, 29 May 2025 17:52:51 +0800 Subject: [PATCH 08/13] Determine whether a function can be called under the condition of using the self keyword --- modules/gdscript/gdscript_analyzer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 67cc3aa7a949..2cd51f10f20a 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -3657,6 +3657,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a push_error(vformat(R"*(Cannot call non-static function "%s()" on the class "%s" directly. Make an instance instead.)*", p_call->function_name, base_type.to_string()), p_call); } else if (is_self && !p_call->is_static) { mark_lambda_use_self(); + } else if (!is_self && access_level < GDScriptParser::Node::AccessLevel::PUBLIC) { + push_error(R"(Access restricted.)", p_call); } if (!p_is_root && !p_is_await && return_type.is_hard_type() && return_type.kind == GDScriptParser::DataType::BUILTIN && return_type.builtin_type == Variant::NIL) { From 05f7995c8ce2563f0ddd7332d04c7b1ec998daf9 Mon Sep 17 00:00:00 2001 From: JustDooooIt <844730658@qq.com> Date: Thu, 29 May 2025 18:01:07 +0800 Subject: [PATCH 09/13] Add an is_base flag to IdentifierNode to determine whether the identifier is from a parent class when it is a member variable. --- modules/gdscript/gdscript_parser.h | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 1ab068938f2b..0c2bf1b0850a 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -921,6 +921,7 @@ class GDScriptParser { FunctionNode *function_source; }; bool function_source_is_static = false; // For non-GDScript scripts. + bool is_base = false; FunctionNode *source_function = nullptr; // TODO: Rename to disambiguate `function_source`. From f167b6f743e5068018b8e2867ca5cee704e69e4b Mon Sep 17 00:00:00 2001 From: JustDooooIt <844730658@qq.com> Date: Thu, 29 May 2025 18:05:58 +0800 Subject: [PATCH 10/13] Determine whether a member variable can be accessed --- modules/gdscript/gdscript_analyzer.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 2cd51f10f20a..243bcc985d0f 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -4475,6 +4475,18 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident } else { push_error(vformat(R"*(Cannot access %s "%s" from a static variable initializer.)*", source_type, p_identifier->name), p_identifier); } + } else if (!static_context && source_is_instance_variable) { + switch (p_identifier->variable_source->access_level) { + case GDScriptParser::Node::AccessLevel::PRIVATE: { + if (p_identifier->is_base || p_identifier->source != GDScriptParser::IdentifierNode::MEMBER_VARIABLE) { + push_error(R"(Access restricted.)", p_identifier); + } + } break; + case GDScriptParser::Node::AccessLevel::PROTECTED: { + } break; + default: + break; + } } if (current_lambda != nullptr) { From 1b2df14c27af1f4ef0bc535649733ad316cdb661 Mon Sep 17 00:00:00 2001 From: JustDooooIt <844730658@qq.com> Date: Thu, 29 May 2025 18:11:34 +0800 Subject: [PATCH 11/13] Set the is_base flag --- modules/gdscript/gdscript_analyzer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 243bcc985d0f..8dfac211c254 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -4199,6 +4199,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod if (is_base && (!base.is_meta_type || member.variable->is_static)) { p_identifier->set_datatype(member.get_datatype()); p_identifier->source = member.variable->is_static ? GDScriptParser::IdentifierNode::STATIC_VARIABLE : GDScriptParser::IdentifierNode::MEMBER_VARIABLE; + p_identifier->is_base = is_base; p_identifier->variable_source = member.variable; member.variable->usages += 1; return; From ed244df0bcdea5d9cbc4ec344ec8873205497152 Mon Sep 17 00:00:00 2001 From: JustDooooIt <844730658@qq.com> Date: Thu, 29 May 2025 18:27:40 +0800 Subject: [PATCH 12/13] Register annotations.And fix the parameter list of get_function_signature. --- modules/gdscript/gdscript_analyzer.cpp | 14 +++++++++++++- modules/gdscript/gdscript_parser.cpp | 3 +++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 8dfac211c254..949145b896fb 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -1806,7 +1806,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * BitField method_flags = {}; StringName native_base; GDScriptParser::Node::AccessLevel acceess_level = GDScriptParser::Node::PUBLIC; - if (!p_is_lambda && get_function_signature(p_function, false, base_type, function_name, parent_return_type, parameters_types, default_par_count, method_flags, &native_base)) { + if (!p_is_lambda && get_function_signature(p_function, false, base_type, function_name, parent_return_type, parameters_types, default_par_count, method_flags, acceess_level, &native_base)) { bool valid = p_function->is_static == method_flags.has_flag(METHOD_FLAG_STATIC); if (p_function->return_type != nullptr) { @@ -4825,6 +4825,18 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri } } + for (GDScriptParser::ClassNode::Member member : base_type.class_type->members) { + if (member.get_name() == p_subscript->attribute->name) { + for (GDScriptParser::AnnotationNode *annotation : member.get_source_node()->annotations) { + if (annotation->name == "@protected" || annotation->name == "@protected") { + push_error(R"(Access restricted.)", p_subscript); + break; + } + } + break; + } + } + if (valid) { // Do nothing. } else if (base_type.is_variant() || !base_type.is_hard_type()) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 33ed15a96dce..411fc9a1901f 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -96,6 +96,9 @@ GDScriptParser::GDScriptParser() { 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); + register_annotation(MethodInfo("@must_call_super"), AnnotationInfo::FUNCTION, &GDScriptParser::must_call_super_annotation); + register_annotation(MethodInfo("@protected"), AnnotationInfo::CLASS_LEVEL, &GDScriptParser::protected_annotation); + register_annotation(MethodInfo("@private"), AnnotationInfo::CLASS_LEVEL, &GDScriptParser::private_annotation); // Onready annotation. register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation); // Export annotations. From a52cc6a097df33305e7f500af938971d2a4cadf9 Mon Sep 17 00:00:00 2001 From: JustDooooIt <844730658@qq.com> Date: Thu, 29 May 2025 18:52:41 +0800 Subject: [PATCH 13/13] Add access level check under self condition --- modules/gdscript/gdscript_analyzer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 949145b896fb..fbd91b36099d 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -3656,6 +3656,9 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a base_type.is_meta_type = false; // For `to_string()`. push_error(vformat(R"*(Cannot call non-static function "%s()" on the class "%s" directly. Make an instance instead.)*", p_call->function_name, base_type.to_string()), p_call); } else if (is_self && !p_call->is_static) { + if (access_level < GDScriptParser::Node::AccessLevel::PROTECTED) { + push_error(R"(Access restricted.)", p_call); + } mark_lambda_use_self(); } else if (!is_self && access_level < GDScriptParser::Node::AccessLevel::PUBLIC) { push_error(R"(Access restricted.)", p_call);