Skip to content

Commit 4fafe9c

Browse files
init
1 parent b5bdb88 commit 4fafe9c

File tree

5 files changed

+77
-0
lines changed

5 files changed

+77
-0
lines changed

doc/classes/ProjectSettings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,9 @@
471471
Specifies the maximum number of log files allowed (used for rotation). Set to [code]1[/code] to disable log file rotation.
472472
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.
473473
</member>
474+
<member name="debug/gdscript/warnings/access_private_member" type="int" setter="" getter="" default="1">
475+
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a class member is accessed from external places, such as accessing a private member from other classes that are not derived from the class where the member is defined.
476+
</member>
474477
<member name="debug/gdscript/warnings/assert_always_false" type="int" setter="" getter="" default="1">
475478
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].
476479
</member>

modules/gdscript/gdscript_analyzer.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1946,6 +1946,44 @@ void GDScriptAnalyzer::decide_suite_type(GDScriptParser::Node *p_suite, GDScript
19461946
}
19471947
}
19481948

1949+
#ifdef DEBUG_ENABLED
1950+
void GDScriptAnalyzer::check_access_private_member(GDScriptParser::IdentifierNode *p_identifier, const bool p_is_call) {
1951+
if (p_identifier == nullptr) {
1952+
return;
1953+
}
1954+
if (!String(p_identifier->name).begins_with("_")) {
1955+
return;
1956+
}
1957+
if (parser->current_class->get_datatype().kind != GDScriptParser::DataType::CLASS && parser->current_class->get_datatype().kind != GDScriptParser::DataType::SCRIPT) {
1958+
return;
1959+
}
1960+
if (parser->current_function && parser->current_function->body && parser->current_function->body->has_local(p_identifier->name)) {
1961+
return;
1962+
}
1963+
1964+
if (parser->current_class->has_member(p_identifier->name)) {
1965+
if (!p_is_call) {
1966+
return;
1967+
}
1968+
if (ClassDB::has_method(parser->current_class->get_datatype().native_type, p_identifier->name)) {
1969+
// Ready to implement CALLING_NATIVE_VIRTUAL_METHOD, but here as a comment temporarily
1970+
//parser->push_warning(p_identifier, GDScriptWarning::CALLING_NATIVE_VIRTUAL_METHOD, p_identifier->name);
1971+
}
1972+
return;
1973+
}
1974+
1975+
GDScriptParser::ClassNode *base_class = parser->current_class->base_type.class_type;
1976+
while (base_class != nullptr) {
1977+
if (base_class->has_member(p_identifier->name)) {
1978+
return;
1979+
}
1980+
base_class = base_class->base_type.class_type;
1981+
}
1982+
1983+
parser->push_warning(p_identifier, p_is_call ? GDScriptWarning::CALLING_PRIVATE_METHOD : GDScriptWarning::ACCESS_PRIVATE_MEMBER, p_identifier->name);
1984+
}
1985+
#endif // DEBUG_ENABLED
1986+
19491987
void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
19501988
for (int i = 0; i < p_suite->statements.size(); i++) {
19511989
GDScriptParser::Node *stmt = p_suite->statements[i];
@@ -3515,10 +3553,19 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
35153553
if (p_call->callee == nullptr && current_lambda != nullptr) {
35163554
push_error("Cannot use `super()` inside a lambda.", p_call);
35173555
}
3556+
3557+
#ifdef DEBUG_ENABLED
3558+
GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(p_call->callee);
3559+
check_access_private_member(identifier, true);
3560+
#endif
35183561
} else if (callee_type == GDScriptParser::Node::IDENTIFIER) {
35193562
base_type = parser->current_class->get_datatype();
35203563
base_type.is_meta_type = false;
35213564
is_self = true;
3565+
#ifdef DEBUG_ENABLED
3566+
GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(p_call->callee);
3567+
check_access_private_member(identifier, true);
3568+
#endif
35223569
} else if (callee_type == GDScriptParser::Node::SUBSCRIPT) {
35233570
GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(p_call->callee);
35243571
if (subscript->base == nullptr) {
@@ -3552,6 +3599,9 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
35523599
base_type = subscript->base->get_datatype();
35533600
is_self = subscript->base->type == GDScriptParser::Node::SELF;
35543601
}
3602+
#ifdef DEBUG_ENABLED
3603+
check_access_private_member(subscript->attribute, true);
3604+
#endif
35553605
} else {
35563606
// Invalid call. Error already sent in parser.
35573607
// TODO: Could check if Callable here too.
@@ -4474,6 +4524,10 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
44744524
}
44754525
}
44764526

4527+
#ifdef DEBUG_ENABLED
4528+
check_access_private_member(p_identifier);
4529+
#endif
4530+
44774531
return;
44784532
}
44794533

@@ -4835,6 +4889,10 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
48354889
}
48364890
result_type.kind = GDScriptParser::DataType::VARIANT;
48374891
}
4892+
4893+
#ifdef DEBUG_ENABLED
4894+
check_access_private_member(p_subscript->attribute);
4895+
#endif
48384896
} else {
48394897
if (p_subscript->index == nullptr) {
48404898
return;

modules/gdscript/gdscript_analyzer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ class GDScriptAnalyzer {
7171

7272
void decide_suite_type(GDScriptParser::Node *p_suite, GDScriptParser::Node *p_statement);
7373

74+
#ifdef DEBUG_ENABLED
75+
void check_access_private_member(GDScriptParser::IdentifierNode *p_identifier, const bool p_is_call = false);
76+
#endif
77+
7478
void resolve_annotation(GDScriptParser::AnnotationNode *p_annotation);
7579
void resolve_class_member(GDScriptParser::ClassNode *p_class, const StringName &p_name, const GDScriptParser::Node *p_source = nullptr);
7680
void resolve_class_member(GDScriptParser::ClassNode *p_class, int p_index, const GDScriptParser::Node *p_source = nullptr);

modules/gdscript/gdscript_warning.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ String GDScriptWarning::get_message() const {
162162
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]);
163163
case ONREADY_WITH_EXPORT:
164164
return R"("@onready" will set the default value after "@export" takes effect and will override it.)";
165+
case ACCESS_PRIVATE_MEMBER:
166+
CHECK_SYMBOLS(1);
167+
return vformat(R"(Trying to access a private member "%s" from an external place, which would cause problems during runtime.)", symbols[0]);
168+
case CALLING_PRIVATE_METHOD:
169+
CHECK_SYMBOLS(1);
170+
return vformat(R"*(Trying to call a private method "%s()" from an external place, which would cause problems during runtime.)*", symbols[0]);
165171
#ifndef DISABLE_DEPRECATED
166172
// Never produced. These warnings migrated from 3.x by mistake.
167173
case PROPERTY_USED_AS_FUNCTION: // There is already an error.
@@ -238,6 +244,8 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
238244
"NATIVE_METHOD_OVERRIDE",
239245
"GET_NODE_DEFAULT_WITHOUT_ONREADY",
240246
"ONREADY_WITH_EXPORT",
247+
"ACCESS_PRIVATE_MEMBER",
248+
"CALLING_PRIVATE_METHOD",
241249
#ifndef DISABLE_DEPRECATED
242250
"PROPERTY_USED_AS_FUNCTION",
243251
"CONSTANT_USED_AS_FUNCTION",

modules/gdscript/gdscript_warning.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ class GDScriptWarning {
8989
NATIVE_METHOD_OVERRIDE, // The script method overrides a native one, this may not work as intended.
9090
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.
9191
ONREADY_WITH_EXPORT, // The `@onready` annotation will set the value after `@export` which is likely not intended.
92+
ACCESS_PRIVATE_MEMBER, // Accessing a private member from external places. E.g. accessing an `_`-prefixed member from other classes that are not derived from the class where the member is defined.
93+
CALLING_PRIVATE_METHOD, // Calling a private method from external places. E.g. calling an `_`-prefixed method from other classes that are not derived from the class where the method is defined.
9294
#ifndef DISABLE_DEPRECATED
9395
PROPERTY_USED_AS_FUNCTION, // Function not found, but there's a property with the same name.
9496
CONSTANT_USED_AS_FUNCTION, // Function not found, but there's a constant with the same name.
@@ -146,6 +148,8 @@ class GDScriptWarning {
146148
ERROR, // NATIVE_METHOD_OVERRIDE // May not work as expected.
147149
ERROR, // GET_NODE_DEFAULT_WITHOUT_ONREADY // May not work as expected.
148150
ERROR, // ONREADY_WITH_EXPORT // May not work as expected.
151+
WARN, // ACCESS_PRIVATE_MEMBER
152+
WARN, // CALLING_PRIVATE_METHOD
149153
#ifndef DISABLE_DEPRECATED
150154
WARN, // PROPERTY_USED_AS_FUNCTION
151155
WARN, // CONSTANT_USED_AS_FUNCTION

0 commit comments

Comments
 (0)