diff options
Diffstat (limited to 'modules/gdscript/gdscript_analyzer.cpp')
-rw-r--r-- | modules/gdscript/gdscript_analyzer.cpp | 101 |
1 files changed, 79 insertions, 22 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index aa91f8fe8d..c8cdac3702 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -879,6 +879,8 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, #endif switch (member.type) { case GDScriptParser::ClassNode::Member::VARIABLE: { + bool previous_static_context = static_context; + static_context = member.variable->is_static; check_class_member_name_conflict(p_class, member.variable->identifier->name, member.variable); member.variable->set_datatype(resolving_datatype); resolve_variable(member.variable, false); @@ -890,6 +892,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, E->apply(parser, member.variable); } } + static_context = previous_static_context; #ifdef DEBUG_ENABLED if (member.variable->exported && member.variable->onready) { parser->push_warning(member.variable, GDScriptWarning::ONREADY_WITH_EXPORT); @@ -897,7 +900,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, if (member.variable->initializer) { // Check if it is call to get_node() on self (using shorthand $ or not), so we can check if @onready is needed. // This could be improved by traversing the expression fully and checking the presence of get_node at any level. - if (!member.variable->onready && member.variable->initializer && (member.variable->initializer->type == GDScriptParser::Node::GET_NODE || member.variable->initializer->type == GDScriptParser::Node::CALL || member.variable->initializer->type == GDScriptParser::Node::CAST)) { + if (!member.variable->is_static && !member.variable->onready && member.variable->initializer && (member.variable->initializer->type == GDScriptParser::Node::GET_NODE || member.variable->initializer->type == GDScriptParser::Node::CALL || member.variable->initializer->type == GDScriptParser::Node::CAST)) { GDScriptParser::Node *expr = member.variable->initializer; if (expr->type == GDScriptParser::Node::CAST) { expr = static_cast<GDScriptParser::CastNode *>(expr)->operand; @@ -1082,6 +1085,10 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas p_source = p_class; } +#ifdef DEBUG_ENABLED + bool has_static_data = p_class->has_static_data; +#endif + if (!p_class->resolved_interface) { if (!parser->has_class(p_class)) { String script_path = p_class->get_datatype().script_path; @@ -1124,7 +1131,29 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas for (int i = 0; i < p_class->members.size(); i++) { resolve_class_member(p_class, i); + +#ifdef DEBUG_ENABLED + if (!has_static_data) { + GDScriptParser::ClassNode::Member member = p_class->members[i]; + if (member.type == GDScriptParser::ClassNode::Member::CLASS) { + has_static_data = member.m_class->has_static_data; + } + } +#endif + } + +#ifdef DEBUG_ENABLED + if (!has_static_data && p_class->annotated_static_unload) { + GDScriptParser::Node *static_unload = nullptr; + for (GDScriptParser::AnnotationNode *node : p_class->annotations) { + if (node->name == "@static_unload") { + static_unload = node; + break; + } + } + parser->push_warning(static_unload ? static_unload : p_class, GDScriptWarning::REDUNDANT_STATIC_UNLOAD); } +#endif } } @@ -1499,6 +1528,8 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * GDScriptParser::FunctionNode *previous_function = parser->current_function; parser->current_function = p_function; + bool previous_static_context = static_context; + static_context = p_function->is_static; GDScriptParser::DataType prev_datatype = p_function->get_datatype(); @@ -1514,7 +1545,11 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * resolve_parameter(p_function->parameters[i]); #ifdef DEBUG_ENABLED if (p_function->parameters[i]->usages == 0 && !String(p_function->parameters[i]->identifier->name).begins_with("_")) { - parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, function_name, p_function->parameters[i]->identifier->name); + String visible_name = function_name; + if (function_name == StringName()) { + visible_name = p_is_lambda ? "<anonymous lambda>" : "<unknown function>"; + } + parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, visible_name, p_function->parameters[i]->identifier->name); } is_shadowing(p_function->parameters[i]->identifier, "function parameter"); #endif // DEBUG_ENABLED @@ -1542,6 +1577,18 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * push_error("Constructor cannot have an explicit return type.", p_function->return_type); } } + } else if (!p_is_lambda && function_name == GDScriptLanguage::get_singleton()->strings._static_init) { + // Static constructor. + GDScriptParser::DataType return_type; + return_type.kind = GDScriptParser::DataType::BUILTIN; + return_type.builtin_type = Variant::NIL; + p_function->set_datatype(return_type); + if (p_function->return_type) { + GDScriptParser::DataType declared_return = resolve_datatype(p_function->return_type); + if (declared_return.kind != GDScriptParser::DataType::BUILTIN || declared_return.builtin_type != Variant::NIL) { + push_error("Static constructor cannot have an explicit return type.", p_function->return_type); + } + } } else { if (p_function->return_type != nullptr) { p_function->set_datatype(type_from_metatype(resolve_datatype(p_function->return_type))); @@ -1625,6 +1672,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * parser->ignored_warnings = previously_ignored_warnings; #endif parser->current_function = previous_function; + static_context = previous_static_context; } void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_function, bool p_is_lambda) { @@ -2027,7 +2075,7 @@ void GDScriptAnalyzer::resolve_assert(GDScriptParser::AssertNode *p_assert) { if (p_assert->condition->is_constant) { if (p_assert->condition->reduced_value.booleanize()) { parser->push_warning(p_assert->condition, GDScriptWarning::ASSERT_ALWAYS_TRUE); - } else { + } else if (!(p_assert->condition->type == GDScriptParser::Node::LITERAL && static_cast<GDScriptParser::LiteralNode *>(p_assert->condition)->value.get_type() == Variant::BOOL)) { parser->push_warning(p_assert->condition, GDScriptWarning::ASSERT_ALWAYS_FALSE); } } @@ -3050,13 +3098,17 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a base_type.is_meta_type = false; } - if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) { - // Get the parent function above any lambda. - GDScriptParser::FunctionNode *parent_function = parser->current_function; - while (parent_function->source_lambda) { - parent_function = parent_function->source_lambda->parent_function; + if (is_self && static_context && !is_static) { + if (parser->current_function) { + // Get the parent function above any lambda. + GDScriptParser::FunctionNode *parent_function = parser->current_function; + while (parent_function->source_lambda) { + parent_function = parent_function->source_lambda->parent_function; + } + push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call); + } else { + push_error(vformat(R"*(Cannot call non-static function "%s()" for static variable initializer.)*", p_call->function_name), p_call); } - push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call); } else if (!is_self && base_type.is_meta_type && !is_static) { 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); @@ -3073,7 +3125,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name); } - if (is_static && !base_type.is_meta_type && !(is_self && parser->current_function != nullptr && parser->current_function->is_static)) { + if (is_static && !is_constructor && !base_type.is_meta_type && !(is_self && static_context)) { String caller_type = String(base_type.native_type); if (caller_type.is_empty()) { @@ -3125,7 +3177,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string(); #ifdef SUGGEST_GODOT4_RENAMES String rename_hint = String(); - if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) { + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GODOT_4_HINT)).booleanize()) { const char *renamed_function_name = check_for_renamed_identifier(p_call->function_name, p_call->type); if (renamed_function_name) { rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", String(renamed_function_name) + "()"); @@ -3326,7 +3378,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod } else if (base.is_hard_type()) { #ifdef SUGGEST_GODOT4_RENAMES String rename_hint = String(); - if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) { + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GODOT_4_HINT)).booleanize()) { const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type); if (renamed_identifier_name) { rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name); @@ -3366,7 +3418,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod if (base.is_hard_type()) { #ifdef SUGGEST_GODOT4_RENAMES String rename_hint = String(); - if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) { + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GODOT_4_HINT)).booleanize()) { const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type); if (renamed_identifier_name) { rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name); @@ -3428,9 +3480,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod } case GDScriptParser::ClassNode::Member::VARIABLE: { - if (is_base && !base.is_meta_type) { + if (is_base && (!base.is_meta_type || member.variable->is_static)) { p_identifier->set_datatype(member.get_datatype()); - p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE; + p_identifier->source = member.variable->is_static ? GDScriptParser::IdentifierNode::STATIC_VARIABLE : GDScriptParser::IdentifierNode::MEMBER_VARIABLE; p_identifier->variable_source = member.variable; member.variable->usages += 1; return; @@ -3572,6 +3624,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident mark_lambda_use_self(); p_identifier->variable_source->usages++; [[fallthrough]]; + case GDScriptParser::IdentifierNode::STATIC_VARIABLE: case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: p_identifier->set_datatype(p_identifier->variable_source->get_datatype()); found_source = true; @@ -3602,13 +3655,17 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident if (found_source) { bool source_is_variable = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE; bool source_is_signal = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_SIGNAL; - if ((source_is_variable || source_is_signal) && parser->current_function && parser->current_function->is_static) { - // Get the parent function above any lambda. - GDScriptParser::FunctionNode *parent_function = parser->current_function; - while (parent_function->source_lambda) { - parent_function = parent_function->source_lambda->parent_function; + if ((source_is_variable || source_is_signal) && static_context) { + if (parser->current_function) { + // Get the parent function above any lambda. + GDScriptParser::FunctionNode *parent_function = parser->current_function; + while (parent_function->source_lambda) { + parent_function = parent_function->source_lambda->parent_function; + } + push_error(vformat(R"*(Cannot access %s "%s" from the static function "%s()".)*", source_is_signal ? "signal" : "instance variable", p_identifier->name, parent_function->identifier->name), p_identifier); + } else { + push_error(vformat(R"*(Cannot access %s "%s" for a static variable initializer.)*", source_is_signal ? "signal" : "instance variable", p_identifier->name), p_identifier); } - push_error(vformat(R"*(Cannot access %s "%s" from the static function "%s()".)*", source_is_signal ? "signal" : "instance variable", p_identifier->name, parent_function->identifier->name), p_identifier); } if (!lambda_stack.is_empty()) { @@ -3750,7 +3807,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident } else { #ifdef SUGGEST_GODOT4_RENAMES String rename_hint = String(); - if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) { + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GODOT_4_HINT)).booleanize()) { const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type); if (renamed_identifier_name) { rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name); |