Merge pull request #118002 from dalexeev/gds-warn-confusable-temp-mod

GDScript: Add `CONFUSABLE_TEMPORARY_MODIFICATION` warning
This commit is contained in:
Thaddeus Crews
2026-04-21 08:48:12 -05:00
7 changed files with 140 additions and 1 deletions
+67
View File
@@ -2905,6 +2905,8 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
}
}
}
warn_confusable_temporary_modification(p_assignment);
#endif // DEBUG_ENABLED
if (p_assignment->assigned_value == nullptr || p_assignment->assignee == nullptr) {
@@ -3586,6 +3588,10 @@ 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
warn_confusable_temporary_modification(subscript);
#endif // DEBUG_ENABLED
} else {
// Invalid call. Error already sent in parser.
// TODO: Could check if Callable here too.
@@ -5127,6 +5133,10 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
}
p_subscript->set_datatype(result_type);
#ifdef DEBUG_ENABLED
warn_confusable_temporary_modification(p_subscript);
#endif // DEBUG_ENABLED
}
void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op, bool p_is_root) {
@@ -6092,6 +6102,7 @@ void GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p
}
#ifdef DEBUG_ENABLED
void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_identifier, const String &p_context, const bool p_in_local_scope) {
const StringName &name = p_identifier->name;
@@ -6170,6 +6181,62 @@ void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_identifier
native_base_class = ClassDB::get_parent_class(native_base_class);
}
}
void GDScriptAnalyzer::warn_confusable_temporary_modification(GDScriptParser::ExpressionNode *p_expression) {
ERR_FAIL_NULL(p_expression);
GDScriptParser::ExpressionNode *current = nullptr;
if (p_expression->type == GDScriptParser::Node::ASSIGNMENT) {
current = static_cast<GDScriptParser::AssignmentNode *>(p_expression)->assignee;
} else if (p_expression->type == GDScriptParser::Node::SUBSCRIPT) {
current = static_cast<GDScriptParser::SubscriptNode *>(p_expression);
} else {
ERR_FAIL_MSG("GDScript bug: Invalid expression type.");
}
while (current && current->type == GDScriptParser::Node::SUBSCRIPT) {
GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(current);
GDScriptParser::ExpressionNode *base = subscript->base;
if (base && base->datatype.is_typed_container_type() && !base->datatype.is_meta_type) {
GDScriptParser::IdentifierNode *base_id = nullptr;
GDScriptParser::DataType scope_type;
if (base->type == GDScriptParser::Node::IDENTIFIER) {
base_id = static_cast<GDScriptParser::IdentifierNode *>(base);
scope_type = type_from_metatype(parser->current_class->datatype);
} else if (base->type == GDScriptParser::Node::SUBSCRIPT) {
GDScriptParser::SubscriptNode *base_subscript = static_cast<GDScriptParser::SubscriptNode *>(base);
if (base_subscript->is_attribute) {
base_id = base_subscript->attribute;
if (base_subscript->base) {
scope_type = base_subscript->base->datatype;
}
}
}
if (base_id && base_id->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) {
const StringName &scope_native_type = scope_type.native_type;
if (class_exists(scope_native_type) && ClassDB::has_property(scope_native_type, base_id->name)) {
if (p_expression->type == GDScriptParser::Node::ASSIGNMENT) {
parser->push_warning(p_expression, GDScriptWarning::CONFUSABLE_TEMPORARY_MODIFICATION, scope_native_type, base_id->name);
} else if (p_expression->type == GDScriptParser::Node::SUBSCRIPT) {
StringName member;
if (subscript->is_attribute && subscript->attribute) {
member = subscript->attribute->name;
}
const Variant::Type builtin_type = base->datatype.builtin_type;
if (member && Variant::has_builtin_method(builtin_type, member) && !Variant::is_builtin_method_const(builtin_type, member)) {
parser->push_warning(subscript, GDScriptWarning::CONFUSABLE_TEMPORARY_MODIFICATION, scope_native_type, base_id->name, member);
}
}
}
}
}
current = base;
}
}
#endif // DEBUG_ENABLED
GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source) {