diff options
author | SaracenOne <SaracenOne@gmail.com> | 2022-02-22 00:54:15 +0000 |
---|---|---|
committer | SaracenOne <SaracenOne@gmail.com> | 2022-02-22 01:21:21 +0000 |
commit | b6aa4ed55db510428883d83605ad7957dc4789c2 (patch) | |
tree | 70670d830d4161f3089cee52984e6ad1db8d7bfa /modules/gdscript | |
parent | c55aa03c436fb40f9a42128397b69872d85fb237 (diff) |
Fixes cyclic detection from variables assigning themselves to themselves in autocomplete, and restricts initialization of variables from other variables which have not been declared above it in class body
Diffstat (limited to 'modules/gdscript')
-rw-r--r-- | modules/gdscript/gdscript_analyzer.cpp | 66 | ||||
-rw-r--r-- | modules/gdscript/gdscript_editor.cpp | 29 |
2 files changed, 67 insertions, 28 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 94daba4bf6..9a79f3d016 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -646,41 +646,51 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } - if (member.variable->datatype_specifier != nullptr) { - datatype = specified_type; + // Check if initalizer is an unset identifier (ie: a variable within scope, but declared below) + if (member.variable->initializer && !member.variable->initializer->get_datatype().is_set()) { + if (member.variable->initializer->type == GDScriptParser::Node::IDENTIFIER) { + GDScriptParser::IdentifierNode *initializer_identifier = static_cast<GDScriptParser::IdentifierNode *>(member.variable->initializer); + push_error(vformat(R"(Identifier "%s" must be declared above current variable.)", initializer_identifier->name), member.variable->initializer); + } else { + ERR_PRINT("Parser bug (please report): tried to assign unset node without an identifier."); + } + } else { + if (member.variable->datatype_specifier != nullptr) { + datatype = specified_type; - if (member.variable->initializer != nullptr) { - if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) { - // Try reverse test since it can be a masked subtype. - if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) { - push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer); - } else { - // TODO: Add warning. + if (member.variable->initializer != nullptr) { + if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) { + // Try reverse test since it can be a masked subtype. + if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) { + push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer); + } else { + // TODO: Add warning. + mark_node_unsafe(member.variable->initializer); + member.variable->use_conversion_assign = true; + } + } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) { +#ifdef DEBUG_ENABLED + parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION); +#endif + } + if (member.variable->initializer->get_datatype().is_variant()) { + // TODO: Warn unsafe assign. mark_node_unsafe(member.variable->initializer); member.variable->use_conversion_assign = true; } - } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) { -#ifdef DEBUG_ENABLED - parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION); -#endif } - if (member.variable->initializer->get_datatype().is_variant()) { - // TODO: Warn unsafe assign. - mark_node_unsafe(member.variable->initializer); - member.variable->use_conversion_assign = true; + } else if (member.variable->infer_datatype) { + if (member.variable->initializer == nullptr) { + push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier); + } else if (!datatype.is_set() || datatype.has_no_type()) { + push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer); + } else if (datatype.is_variant()) { + push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer); + } else if (datatype.builtin_type == Variant::NIL) { + push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is "null".)", member.variable->identifier->name), member.variable->initializer); } + datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; } - } else if (member.variable->infer_datatype) { - if (member.variable->initializer == nullptr) { - push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier); - } else if (!datatype.is_set() || datatype.has_no_type()) { - push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer); - } else if (datatype.is_variant()) { - push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer); - } else if (datatype.builtin_type == Variant::NIL) { - push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is "null".)", member.variable->identifier->name), member.variable->initializer); - } - datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; } datatype.is_constant = false; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index f0dc830ed8..6fb95d32ca 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1198,6 +1198,27 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type); static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type); +static bool _is_expression_named_identifier(const GDScriptParser::ExpressionNode *p_expression, const StringName &p_name) { + if (p_expression) { + switch (p_expression->type) { + case GDScriptParser::Node::IDENTIFIER: { + const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression); + if (id->name == p_name) { + return true; + } + } break; + case GDScriptParser::Node::CAST: { + const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression); + return _is_expression_named_identifier(cn->operand, p_name); + } break; + default: + break; + } + } + + return false; +} + static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::ExpressionNode *p_expression, GDScriptCompletionIdentifier &r_type) { bool found = false; @@ -1904,6 +1925,14 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext & return true; } else if (init->start_line == p_context.current_line) { return false; + // Detects if variable is assigned to itself + } else if (_is_expression_named_identifier(init, member.variable->identifier->name)) { + if (member.variable->initializer->get_datatype().is_set()) { + r_type.type = member.variable->initializer->get_datatype(); + } else if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) { + r_type.type = member.variable->get_datatype(); + } + return true; } else if (_guess_expression_type(p_context, init, r_type)) { return true; } else if (init->get_datatype().is_set() && !init->get_datatype().is_variant()) { |