Skip to content

Commit c61d609

Browse files
resolved conflict
1 parent 30456ba commit c61d609

File tree

11 files changed

+163
-46
lines changed

11 files changed

+163
-46
lines changed

doc/classes/ProjectSettings.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,12 +509,18 @@
509509
Specifies the maximum number of log files allowed (used for rotation). Set to [code]1[/code] to disable log file rotation.
510510
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.
511511
</member>
512+
<member name="debug/gdscript/warnings/access_private_member" type="int" setter="" getter="" default="1">
513+
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.
514+
</member>
512515
<member name="debug/gdscript/warnings/assert_always_false" type="int" setter="" getter="" default="1">
513516
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].
514517
</member>
515518
<member name="debug/gdscript/warnings/assert_always_true" type="int" setter="" getter="" default="1">
516519
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].
517520
</member>
521+
<member name="debug/gdscript/warnings/call_private_method" type="int" setter="" getter="" default="1">
522+
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a class method is called from external places, such as calling a private method from other classes that are not derived from the class where the method is defined.
523+
</member>
518524
<member name="debug/gdscript/warnings/confusable_capture_reassignment" type="int" setter="" getter="" default="1">
519525
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.
520526
</member>

modules/gdscript/gdscript_analyzer.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2035,6 +2035,33 @@ void GDScriptAnalyzer::decide_suite_type(GDScriptParser::Node *p_suite, GDScript
20352035
}
20362036
}
20372037

2038+
#ifdef DEBUG_ENABLED
2039+
void GDScriptAnalyzer::check_access_private_member(GDScriptParser::IdentifierNode *p_identifier, const GDScriptParser::DataType &p_datatype, const bool p_is_call) {
2040+
if (p_identifier == nullptr) {
2041+
return;
2042+
}
2043+
if (!String(p_identifier->name).begins_with("_")) {
2044+
return;
2045+
}
2046+
if (p_datatype.kind != GDScriptParser::DataType::Kind::CLASS && p_datatype.kind != GDScriptParser::DataType::Kind::SCRIPT) {
2047+
return; // Accessing from self
2048+
}
2049+
if (parser->current_function && parser->current_function->body && parser->current_function->body->has_local(p_identifier->name)) {
2050+
return;
2051+
}
2052+
2053+
GDScriptParser::DataType target_resolved = type_from_metatype(p_datatype);
2054+
if (target_resolved.class_type != nullptr && !target_resolved.class_type->has_member(p_identifier->name)) {
2055+
return; // Accessing an inexisting method
2056+
}
2057+
if (is_type_compatible(target_resolved, type_from_metatype(parser->current_class->get_datatype()))) {
2058+
return;
2059+
}
2060+
2061+
parser->push_warning(p_identifier, p_is_call ? GDScriptWarning::CALL_PRIVATE_METHOD : GDScriptWarning::ACCESS_PRIVATE_MEMBER, p_identifier->name);
2062+
}
2063+
#endif // DEBUG_ENABLED
2064+
20382065
void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
20392066
for (int i = 0; i < p_suite->statements.size(); i++) {
20402067
GDScriptParser::Node *stmt = p_suite->statements[i];
@@ -3538,10 +3565,19 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
35383565
if (p_call->callee == nullptr && current_lambda != nullptr) {
35393566
push_error("Cannot use `super()` inside a lambda.", p_call);
35403567
}
3568+
3569+
#ifdef DEBUG_ENABLED
3570+
GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(p_call->callee);
3571+
check_access_private_member(identifier, p_call->get_datatype(), true);
3572+
#endif
35413573
} else if (callee_type == GDScriptParser::Node::IDENTIFIER) {
35423574
base_type = parser->current_class->get_datatype();
35433575
base_type.is_meta_type = false;
35443576
is_self = true;
3577+
#ifdef DEBUG_ENABLED
3578+
GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(p_call->callee);
3579+
check_access_private_member(identifier, p_call->get_datatype(), true);
3580+
#endif
35453581
} else if (callee_type == GDScriptParser::Node::SUBSCRIPT) {
35463582
GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(p_call->callee);
35473583
if (subscript->base == nullptr) {
@@ -3575,6 +3611,9 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
35753611
base_type = subscript->base->get_datatype();
35763612
is_self = subscript->base->type == GDScriptParser::Node::SELF;
35773613
}
3614+
#ifdef DEBUG_ENABLED
3615+
check_access_private_member(subscript->attribute, subscript->base->get_datatype(), true);
3616+
#endif
35783617
} else {
35793618
// Invalid call. Error already sent in parser.
35803619
// TODO: Could check if Callable here too.
@@ -4506,6 +4545,10 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
45064545
}
45074546
}
45084547

4548+
#ifdef DEBUG_ENABLED
4549+
check_access_private_member(p_identifier, p_identifier->get_datatype());
4550+
#endif
4551+
45094552
return;
45104553
}
45114554

@@ -4867,6 +4910,10 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
48674910
}
48684911
result_type.kind = GDScriptParser::DataType::VARIANT;
48694912
}
4913+
4914+
#ifdef DEBUG_ENABLED
4915+
check_access_private_member(p_subscript->attribute, p_subscript->base->get_datatype());
4916+
#endif
48704917
} else {
48714918
if (p_subscript->index == nullptr) {
48724919
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 GDScriptParser::DataType &p_datatype, 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: 52 additions & 44 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 uses "%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 CALL_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.
@@ -194,50 +200,52 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
194200
ERR_FAIL_COND_V(p_code < 0 || p_code >= WARNING_MAX, String());
195201

196202
static const char *names[] = {
197-
PNAME("UNASSIGNED_VARIABLE"),
198-
PNAME("UNASSIGNED_VARIABLE_OP_ASSIGN"),
199-
PNAME("UNUSED_VARIABLE"),
200-
PNAME("UNUSED_LOCAL_CONSTANT"),
201-
PNAME("UNUSED_PRIVATE_CLASS_VARIABLE"),
202-
PNAME("UNUSED_PARAMETER"),
203-
PNAME("UNUSED_SIGNAL"),
204-
PNAME("SHADOWED_VARIABLE"),
205-
PNAME("SHADOWED_VARIABLE_BASE_CLASS"),
206-
PNAME("SHADOWED_GLOBAL_IDENTIFIER"),
207-
PNAME("UNREACHABLE_CODE"),
208-
PNAME("UNREACHABLE_PATTERN"),
209-
PNAME("STANDALONE_EXPRESSION"),
210-
PNAME("STANDALONE_TERNARY"),
211-
PNAME("INCOMPATIBLE_TERNARY"),
212-
PNAME("UNTYPED_DECLARATION"),
213-
PNAME("INFERRED_DECLARATION"),
214-
PNAME("UNSAFE_PROPERTY_ACCESS"),
215-
PNAME("UNSAFE_METHOD_ACCESS"),
216-
PNAME("UNSAFE_CAST"),
217-
PNAME("UNSAFE_CALL_ARGUMENT"),
218-
PNAME("UNSAFE_VOID_RETURN"),
219-
PNAME("RETURN_VALUE_DISCARDED"),
220-
PNAME("STATIC_CALLED_ON_INSTANCE"),
221-
PNAME("MISSING_TOOL"),
222-
PNAME("REDUNDANT_STATIC_UNLOAD"),
223-
PNAME("REDUNDANT_AWAIT"),
224-
PNAME("ASSERT_ALWAYS_TRUE"),
225-
PNAME("ASSERT_ALWAYS_FALSE"),
226-
PNAME("INTEGER_DIVISION"),
227-
PNAME("NARROWING_CONVERSION"),
228-
PNAME("INT_AS_ENUM_WITHOUT_CAST"),
229-
PNAME("INT_AS_ENUM_WITHOUT_MATCH"),
230-
PNAME("ENUM_VARIABLE_WITHOUT_DEFAULT"),
231-
PNAME("EMPTY_FILE"),
232-
PNAME("DEPRECATED_KEYWORD"),
233-
PNAME("CONFUSABLE_IDENTIFIER"),
234-
PNAME("CONFUSABLE_LOCAL_DECLARATION"),
235-
PNAME("CONFUSABLE_LOCAL_USAGE"),
236-
PNAME("CONFUSABLE_CAPTURE_REASSIGNMENT"),
237-
PNAME("INFERENCE_ON_VARIANT"),
238-
PNAME("NATIVE_METHOD_OVERRIDE"),
239-
PNAME("GET_NODE_DEFAULT_WITHOUT_ONREADY"),
240-
PNAME("ONREADY_WITH_EXPORT"),
203+
"UNASSIGNED_VARIABLE",
204+
"UNASSIGNED_VARIABLE_OP_ASSIGN",
205+
"UNUSED_VARIABLE",
206+
"UNUSED_LOCAL_CONSTANT",
207+
"UNUSED_PRIVATE_CLASS_VARIABLE",
208+
"UNUSED_PARAMETER",
209+
"UNUSED_SIGNAL",
210+
"SHADOWED_VARIABLE",
211+
"SHADOWED_VARIABLE_BASE_CLASS",
212+
"SHADOWED_GLOBAL_IDENTIFIER",
213+
"UNREACHABLE_CODE",
214+
"UNREACHABLE_PATTERN",
215+
"STANDALONE_EXPRESSION",
216+
"STANDALONE_TERNARY",
217+
"INCOMPATIBLE_TERNARY",
218+
"UNTYPED_DECLARATION",
219+
"INFERRED_DECLARATION",
220+
"UNSAFE_PROPERTY_ACCESS",
221+
"UNSAFE_METHOD_ACCESS",
222+
"UNSAFE_CAST",
223+
"UNSAFE_CALL_ARGUMENT",
224+
"UNSAFE_VOID_RETURN",
225+
"RETURN_VALUE_DISCARDED",
226+
"STATIC_CALLED_ON_INSTANCE",
227+
"MISSING_TOOL",
228+
"REDUNDANT_STATIC_UNLOAD",
229+
"REDUNDANT_AWAIT",
230+
"ASSERT_ALWAYS_TRUE",
231+
"ASSERT_ALWAYS_FALSE",
232+
"INTEGER_DIVISION",
233+
"NARROWING_CONVERSION",
234+
"INT_AS_ENUM_WITHOUT_CAST",
235+
"INT_AS_ENUM_WITHOUT_MATCH",
236+
"ENUM_VARIABLE_WITHOUT_DEFAULT",
237+
"EMPTY_FILE",
238+
"DEPRECATED_KEYWORD",
239+
"CONFUSABLE_IDENTIFIER",
240+
"CONFUSABLE_LOCAL_DECLARATION",
241+
"CONFUSABLE_LOCAL_USAGE",
242+
"CONFUSABLE_CAPTURE_REASSIGNMENT",
243+
"INFERENCE_ON_VARIANT",
244+
"NATIVE_METHOD_OVERRIDE",
245+
"GET_NODE_DEFAULT_WITHOUT_ONREADY",
246+
"ONREADY_WITH_EXPORT",
247+
"ACCESS_PRIVATE_MEMBER",
248+
"CALL_PRIVATE_METHOD",
241249
#ifndef DISABLE_DEPRECATED
242250
"PROPERTY_USED_AS_FUNCTION",
243251
"CONSTANT_USED_AS_FUNCTION",

modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class SuperMethodsRecognized extends BaseClass:
1515
return result
1616

1717
func test():
18+
@warning_ignore_start("call_private_method")
1819
var test1 = SuperClassMethodsRecognized.new()
1920
print(test1._get_property_list()) # Calls base class's method.
2021
var test2 = SuperMethodsRecognized.new()

modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.gd

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,30 @@ func test_unsafe_void_return() -> void:
151151
func get_class():
152152
pass
153153

154+
class PrivA:
155+
var _t = 1
156+
157+
func _priv_method():
158+
_t = 5
159+
160+
class PrivB:
161+
var a = PrivA.new()
162+
163+
func _foo():
164+
@warning_ignore("access_private_member")
165+
a._t = 2
166+
@warning_ignore("call_private_method")
167+
a._priv_method()
168+
169+
class PrivC extends PrivA:
170+
var a = PrivA.new()
171+
172+
func _foo():
173+
@warning_ignore("access_private_member")
174+
a._t = 2
175+
@warning_ignore("call_private_method")
176+
a._priv_method()
177+
154178
# We don't want to execute it because of errors, just analyze.
155179
func test():
156180
pass
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
GDTEST_OK
1+
GDTEST_PARSER_ERROR
2+
Used space character for indentation instead of tab as used before in the file.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class A:
2+
var _t = 1
3+
4+
func _priv_method():
5+
_t = 5
6+
7+
class B:
8+
var a = A.new()
9+
10+
func _foo():
11+
a._t = 2
12+
a._priv_method()
13+
14+
class C extends A:
15+
var a = A.new()
16+
17+
func _foo():
18+
a._t = 2
19+
a._priv_method()
20+
21+
func test():
22+
pass
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
GDTEST_OK
2+
~~ WARNING at line 11: (ACCESS_PRIVATE_MEMBER) Trying to access a private member "_t" from an external place, which would cause problems during runtime.
3+
~~ WARNING at line 12: (CALL_PRIVATE_METHOD) Trying to call a private method "_priv_method()" from an external place, which would cause problems during runtime.

modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.gd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ class B extends A:
1414

1515
func test():
1616
var node := B.new()
17+
@warning_ignore("call_private_method")
1718
node._ready()
1819
node.free()

0 commit comments

Comments
 (0)