Merge pull request #58415 from V-Sekai/cyclic_assignment_gdscript_fixes
This commit is contained in:
commit
95783f7bfc
2 changed files with 67 additions and 28 deletions
|
@ -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;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
Loading…
Reference in a new issue