Merge pull request #70464 from vonagam/unify-assignables

Unify typing of variables, constants and parameters in GDScript
This commit is contained in:
Rémi Verschelde 2023-01-06 00:34:10 +01:00
commit 95ce236b7d
No known key found for this signature in database
GPG key ID: C3336907360768E1
17 changed files with 237 additions and 357 deletions

View file

@ -758,80 +758,8 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
switch (member.type) {
case GDScriptParser::ClassNode::Member::VARIABLE: {
check_class_member_name_conflict(p_class, member.variable->identifier->name, member.variable);
member.variable->set_datatype(resolving_datatype);
GDScriptParser::DataType datatype;
datatype.kind = GDScriptParser::DataType::VARIANT;
datatype.type_source = GDScriptParser::DataType::UNDETECTED;
GDScriptParser::DataType specified_type;
if (member.variable->datatype_specifier != nullptr) {
specified_type = resolve_datatype(member.variable->datatype_specifier);
specified_type.is_meta_type = false;
}
if (member.variable->initializer != nullptr) {
reduce_expression(member.variable->initializer);
if ((member.variable->infer_datatype || (member.variable->datatype_specifier != nullptr && specified_type.has_container_element_type())) && member.variable->initializer->type == GDScriptParser::Node::ARRAY) {
// Typed array.
GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(member.variable->initializer);
// Can only infer typed array if it has elements.
if ((member.variable->infer_datatype && array->elements.size() > 0) || member.variable->datatype_specifier != nullptr) {
update_array_literal_element_type(specified_type, array);
}
}
datatype = member.variable->initializer->get_datatype();
if (datatype.type_source != GDScriptParser::DataType::UNDETECTED) {
datatype.type_source = GDScriptParser::DataType::INFERRED;
}
if (!datatype.is_set()) {
push_error(vformat(R"(Could not resolve initializer for member "%s".)", member.variable->identifier->name), member.variable->initializer);
datatype.kind = GDScriptParser::DataType::VARIANT;
}
}
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.
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;
}
datatype.is_constant = false;
member.variable->set_datatype(datatype);
resolve_variable(member.variable, false);
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.variable->annotations) {
@ -840,56 +768,8 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
} break;
case GDScriptParser::ClassNode::Member::CONSTANT: {
check_class_member_name_conflict(p_class, member.constant->identifier->name, member.constant);
member.constant->set_datatype(resolving_datatype);
GDScriptParser::DataType specified_type;
if (member.constant->datatype_specifier != nullptr) {
specified_type = resolve_datatype(member.constant->datatype_specifier);
specified_type.is_meta_type = false;
}
GDScriptParser::DataType datatype;
if (member.constant->initializer) {
reduce_expression(member.constant->initializer);
datatype = member.constant->initializer->get_datatype();
if (member.constant->initializer->type == GDScriptParser::Node::ARRAY) {
GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(member.constant->initializer);
const_fold_array(array);
// Can only infer typed array if it has elements.
if (array->elements.size() > 0 || (member.constant->datatype_specifier != nullptr && specified_type.has_container_element_type())) {
update_array_literal_element_type(specified_type, array);
}
} else if (member.constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(member.constant->initializer));
}
if (!datatype.is_set()) {
push_error(vformat(R"(Could not resolve initializer for member "%s".)", member.constant->identifier->name), member.constant->initializer);
datatype.kind = GDScriptParser::DataType::VARIANT;
}
if (!member.constant->initializer->is_constant) {
push_error(R"(Initializer for a constant must be a constant expression.)", member.constant->initializer);
}
if (member.constant->datatype_specifier != nullptr) {
datatype = specified_type;
if (!is_type_compatible(datatype, member.constant->initializer->get_datatype(), true)) {
push_error(vformat(R"(Value of type "%s" cannot be initialized to constant of type "%s".)", member.constant->initializer->get_datatype().to_string(), datatype.to_string()), member.constant->initializer);
} else if (datatype.builtin_type == Variant::INT && member.constant->initializer->get_datatype().builtin_type == Variant::FLOAT) {
#ifdef DEBUG_ENABLED
parser->push_warning(member.constant->initializer, GDScriptWarning::NARROWING_CONVERSION);
#endif
}
}
}
datatype.is_constant = true;
member.constant->set_datatype(datatype);
resolve_constant(member.constant, false);
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.constant->annotations) {
@ -1310,7 +1190,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root
}
break;
case GDScriptParser::Node::CONSTANT:
resolve_constant(static_cast<GDScriptParser::ConstantNode *>(p_node));
resolve_constant(static_cast<GDScriptParser::ConstantNode *>(p_node), true);
break;
case GDScriptParser::Node::FOR:
resolve_for(static_cast<GDScriptParser::ForNode *>(p_node));
@ -1326,7 +1206,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root
resolve_suite(static_cast<GDScriptParser::SuiteNode *>(p_node));
break;
case GDScriptParser::Node::VARIABLE:
resolve_variable(static_cast<GDScriptParser::VariableNode *>(p_node));
resolve_variable(static_cast<GDScriptParser::VariableNode *>(p_node), true);
break;
case GDScriptParser::Node::WHILE:
resolve_while(static_cast<GDScriptParser::WhileNode *>(p_node));
@ -1426,11 +1306,11 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
is_shadowing(p_function->parameters[i]->identifier, "function parameter");
#endif // DEBUG_ENABLED
#ifdef TOOLS_ENABLED
if (p_function->parameters[i]->default_value) {
if (p_function->parameters[i]->initializer) {
default_value_count++;
if (p_function->parameters[i]->default_value->is_constant) {
p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value);
if (p_function->parameters[i]->initializer->is_constant) {
p_function->default_arg_values.push_back(p_function->parameters[i]->initializer->reduced_value);
} else {
p_function->default_arg_values.push_back(Variant()); // Prevent shift.
}
@ -1601,6 +1481,132 @@ void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
}
}
void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assignable, const char *p_kind) {
GDScriptParser::DataType type;
type.kind = GDScriptParser::DataType::VARIANT;
bool is_variable = p_assignable->type == GDScriptParser::Node::VARIABLE;
bool is_constant = p_assignable->type == GDScriptParser::Node::CONSTANT;
GDScriptParser::DataType specified_type;
bool has_specified_type = p_assignable->datatype_specifier != nullptr;
if (has_specified_type) {
specified_type = resolve_datatype(p_assignable->datatype_specifier);
specified_type.is_meta_type = false;
type = specified_type;
}
if (p_assignable->initializer != nullptr) {
reduce_expression(p_assignable->initializer);
if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) {
GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer);
if ((p_assignable->infer_datatype && array->elements.size() > 0) || (has_specified_type && specified_type.has_container_element_type())) {
update_array_literal_element_type(specified_type, array);
}
}
if (is_constant) {
if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) {
const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer));
} else if (p_assignable->initializer->type == GDScriptParser::Node::DICTIONARY) {
const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_assignable->initializer));
}
if (!p_assignable->initializer->is_constant) {
push_error(vformat(R"(Assigned value for %s "%s" isn't a constant expression.)", p_kind, p_assignable->identifier->name), p_assignable->initializer);
}
}
GDScriptParser::DataType initializer_type = p_assignable->initializer->get_datatype();
if (p_assignable->infer_datatype) {
if (!initializer_type.is_set() || initializer_type.has_no_type()) {
push_error(vformat(R"(Cannot infer the type of "%s" %s because the value doesn't have a set type.)", p_assignable->identifier->name, p_kind), p_assignable->initializer);
} else if (initializer_type.is_variant() && !initializer_type.is_hard_type()) {
push_error(vformat(R"(Cannot infer the type of "%s" %s because the value is Variant. Use explicit "Variant" type if this is intended.)", p_assignable->identifier->name, p_kind), p_assignable->initializer);
} else if (initializer_type.kind == GDScriptParser::DataType::BUILTIN && initializer_type.builtin_type == Variant::NIL && !is_constant) {
push_error(vformat(R"(Cannot infer the type of "%s" %s because the value is "null".)", p_assignable->identifier->name, p_kind), p_assignable->initializer);
}
} else {
if (!initializer_type.is_set()) {
push_error(vformat(R"(Could not resolve type for %s "%s".)", p_kind, p_assignable->identifier->name), p_assignable->initializer);
}
}
if (!has_specified_type) {
type = initializer_type;
if (!type.is_set() || (type.is_hard_type() && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL && !is_constant)) {
type.kind = GDScriptParser::DataType::VARIANT;
}
if (p_assignable->infer_datatype || is_constant) {
type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
} else {
type.type_source = GDScriptParser::DataType::INFERRED;
}
} else if (!specified_type.is_variant()) {
if (initializer_type.is_variant() || !initializer_type.is_hard_type()) {
mark_node_unsafe(p_assignable->initializer);
if (is_variable) {
static_cast<GDScriptParser::VariableNode *>(p_assignable)->use_conversion_assign = true;
}
} else if (!is_type_compatible(specified_type, initializer_type, true, p_assignable->initializer)) {
if (is_variable && is_type_compatible(initializer_type, specified_type, true, p_assignable->initializer)) {
mark_node_unsafe(p_assignable->initializer);
static_cast<GDScriptParser::VariableNode *>(p_assignable)->use_conversion_assign = true;
} else {
push_error(vformat(R"(Cannot assign a value of type %s to %s "%s" with specified type %s.)", initializer_type.to_string(), p_kind, p_assignable->identifier->name, specified_type.to_string()), p_assignable->initializer);
}
#ifdef DEBUG_ENABLED
} else if (specified_type.builtin_type == Variant::INT && initializer_type.builtin_type == Variant::FLOAT) {
parser->push_warning(p_assignable->initializer, GDScriptWarning::NARROWING_CONVERSION);
#endif
}
}
}
type.is_constant = is_constant;
p_assignable->set_datatype(type);
}
void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable, bool p_is_local) {
static constexpr const char *kind = "variable";
resolve_assignable(p_variable, kind);
#ifdef DEBUG_ENABLED
if (p_is_local) {
if (p_variable->usages == 0 && !String(p_variable->identifier->name).begins_with("_")) {
parser->push_warning(p_variable, GDScriptWarning::UNUSED_VARIABLE, p_variable->identifier->name);
} else if (p_variable->assignments == 0) {
parser->push_warning(p_variable, GDScriptWarning::UNASSIGNED_VARIABLE, p_variable->identifier->name);
}
is_shadowing(p_variable->identifier, kind);
}
#endif
}
void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant, bool p_is_local) {
static constexpr const char *kind = "constant";
resolve_assignable(p_constant, kind);
#ifdef DEBUG_ENABLED
if (p_is_local) {
if (p_constant->usages == 0) {
parser->push_warning(p_constant, GDScriptWarning::UNUSED_LOCAL_CONSTANT, p_constant->identifier->name);
}
is_shadowing(p_constant->identifier, kind);
}
#endif
}
void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parameter) {
static constexpr const char *kind = "parameter";
resolve_assignable(p_parameter, kind);
}
void GDScriptAnalyzer::resolve_if(GDScriptParser::IfNode *p_if) {
reduce_expression(p_if->condition);
@ -1728,148 +1734,6 @@ void GDScriptAnalyzer::resolve_while(GDScriptParser::WhileNode *p_while) {
p_while->set_datatype(p_while->loop->get_datatype());
}
void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable) {
GDScriptParser::DataType type;
type.kind = GDScriptParser::DataType::VARIANT; // By default.
GDScriptParser::DataType specified_type;
if (p_variable->datatype_specifier != nullptr) {
specified_type = resolve_datatype(p_variable->datatype_specifier);
specified_type.is_meta_type = false;
}
if (p_variable->initializer != nullptr) {
reduce_expression(p_variable->initializer);
if ((p_variable->infer_datatype || (p_variable->datatype_specifier != nullptr && specified_type.has_container_element_type())) && p_variable->initializer->type == GDScriptParser::Node::ARRAY) {
// Typed array.
GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_variable->initializer);
// Can only infer typed array if it has elements.
if ((p_variable->infer_datatype && array->elements.size() > 0) || p_variable->datatype_specifier != nullptr) {
update_array_literal_element_type(specified_type, array);
}
}
type = p_variable->initializer->get_datatype();
if (p_variable->infer_datatype) {
type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
if (type.has_no_type()) {
push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value does not have a set type.)", p_variable->identifier->name), p_variable->initializer);
} else if (type.is_variant()) {
push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value is a variant. Use explicit "Variant" type if this is intended.)", p_variable->identifier->name), p_variable->initializer);
} else if (type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value is "null".)", p_variable->identifier->name), p_variable->initializer);
}
} else {
type.type_source = GDScriptParser::DataType::INFERRED;
}
}
if (p_variable->datatype_specifier != nullptr) {
type = specified_type;
type.is_meta_type = false;
if (p_variable->initializer != nullptr) {
if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true, p_variable->initializer)) {
// Try reverse test since it can be a masked subtype.
if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true, p_variable->initializer)) {
push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", p_variable->initializer->get_datatype().to_string(), type.to_string()), p_variable->initializer);
} else {
// TODO: Add warning.
mark_node_unsafe(p_variable->initializer);
p_variable->use_conversion_assign = true;
}
#ifdef DEBUG_ENABLED
} else if (type.builtin_type == Variant::INT && p_variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
parser->push_warning(p_variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
#endif
}
if (p_variable->initializer->get_datatype().is_variant() && !type.is_variant()) {
// TODO: Warn unsafe assign.
mark_node_unsafe(p_variable->initializer);
p_variable->use_conversion_assign = true;
}
}
} else if (p_variable->infer_datatype) {
if (type.has_no_type()) {
push_error(vformat(R"(Cannot infer the type of variable "%s" because the initial value doesn't have a set type.)", p_variable->identifier->name), p_variable->identifier);
}
type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
}
type.is_constant = false;
p_variable->set_datatype(type);
#ifdef DEBUG_ENABLED
if (p_variable->usages == 0 && !String(p_variable->identifier->name).begins_with("_")) {
parser->push_warning(p_variable, GDScriptWarning::UNUSED_VARIABLE, p_variable->identifier->name);
} else if (p_variable->assignments == 0) {
parser->push_warning(p_variable, GDScriptWarning::UNASSIGNED_VARIABLE, p_variable->identifier->name);
}
is_shadowing(p_variable->identifier, "variable");
#endif
}
void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant) {
GDScriptParser::DataType type;
GDScriptParser::DataType explicit_type;
if (p_constant->datatype_specifier != nullptr) {
explicit_type = resolve_datatype(p_constant->datatype_specifier);
explicit_type.is_meta_type = false;
}
if (p_constant->initializer != nullptr) {
reduce_expression(p_constant->initializer);
if (p_constant->initializer->type == GDScriptParser::Node::ARRAY) {
GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer);
const_fold_array(array);
// Can only infer typed array if it has elements.
if (array->elements.size() > 0 || (p_constant->datatype_specifier != nullptr && explicit_type.has_container_element_type())) {
update_array_literal_element_type(explicit_type, array);
}
} else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer));
}
if (!p_constant->initializer->is_constant) {
push_error(vformat(R"(Assigned value for constant "%s" isn't a constant expression.)", p_constant->identifier->name), p_constant->initializer);
}
type = p_constant->initializer->get_datatype();
}
if (p_constant->datatype_specifier != nullptr) {
if (!is_type_compatible(explicit_type, type, true)) {
push_error(vformat(R"(Assigned value for constant "%s" has type %s which is not compatible with defined type %s.)", p_constant->identifier->name, type.to_string(), explicit_type.to_string()), p_constant->initializer);
#ifdef DEBUG_ENABLED
} else if (explicit_type.builtin_type == Variant::INT && type.builtin_type == Variant::FLOAT) {
parser->push_warning(p_constant->initializer, GDScriptWarning::NARROWING_CONVERSION);
#endif
}
type = explicit_type;
} else if (p_constant->infer_datatype) {
if (type.has_no_type()) {
push_error(vformat(R"(Cannot infer the type of constant "%s" because the initial value doesn't have a set type.)", p_constant->identifier->name), p_constant->identifier);
}
type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
}
type.is_constant = true;
p_constant->set_datatype(type);
#ifdef DEBUG_ENABLED
if (p_constant->usages == 0) {
parser->push_warning(p_constant, GDScriptWarning::UNUSED_LOCAL_CONSTANT, p_constant->identifier->name);
}
is_shadowing(p_constant->identifier, "constant");
#endif
}
void GDScriptAnalyzer::resolve_assert(GDScriptParser::AssertNode *p_assert) {
reduce_expression(p_assert->condition);
if (p_assert->message != nullptr) {
@ -1981,41 +1845,6 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
p_match_pattern->set_datatype(result);
}
void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parameter) {
GDScriptParser::DataType result;
result.kind = GDScriptParser::DataType::VARIANT;
if (p_parameter->default_value != nullptr) {
reduce_expression(p_parameter->default_value);
result = p_parameter->default_value->get_datatype();
if (p_parameter->infer_datatype) {
result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
} else {
result.type_source = GDScriptParser::DataType::INFERRED;
}
}
if (p_parameter->datatype_specifier != nullptr) {
result = resolve_datatype(p_parameter->datatype_specifier);
result.is_meta_type = false;
if (p_parameter->default_value != nullptr) {
if (!is_type_compatible(result, p_parameter->default_value->get_datatype())) {
push_error(vformat(R"(Type of default value for parameter "%s" (%s) is not compatible with parameter type (%s).)", p_parameter->identifier->name, p_parameter->default_value->get_datatype().to_string(), p_parameter->datatype_specifier->get_datatype().to_string()), p_parameter->default_value);
} else if (p_parameter->default_value->get_datatype().is_variant()) {
mark_node_unsafe(p_parameter);
}
}
}
if (result.builtin_type == Variant::Type::NIL && result.type_source == GDScriptParser::DataType::ANNOTATED_INFERRED && p_parameter->datatype_specifier == nullptr) {
push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value is "null".)", p_parameter->identifier->name), p_parameter->default_value);
}
result.is_constant = false;
p_parameter->set_datatype(result);
}
void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
GDScriptParser::DataType result;
@ -4171,7 +4000,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
r_static = p_is_constructor || found_function->is_static;
for (int i = 0; i < found_function->parameters.size(); i++) {
r_par_types.push_back(found_function->parameters[i]->get_datatype());
if (found_function->parameters[i]->default_value != nullptr) {
if (found_function->parameters[i]->initializer != nullptr) {
r_default_arg_count++;
}
}

View file

@ -69,16 +69,17 @@ class GDScriptAnalyzer {
void resolve_function_body(GDScriptParser::FunctionNode *p_function);
void resolve_node(GDScriptParser::Node *p_node, bool p_is_root = true);
void resolve_suite(GDScriptParser::SuiteNode *p_suite);
void resolve_assignable(GDScriptParser::AssignableNode *p_assignable, const char *p_kind);
void resolve_variable(GDScriptParser::VariableNode *p_variable, bool p_is_local);
void resolve_constant(GDScriptParser::ConstantNode *p_constant, bool p_is_local);
void resolve_parameter(GDScriptParser::ParameterNode *p_parameter);
void resolve_if(GDScriptParser::IfNode *p_if);
void resolve_for(GDScriptParser::ForNode *p_for);
void resolve_while(GDScriptParser::WhileNode *p_while);
void resolve_variable(GDScriptParser::VariableNode *p_variable);
void resolve_constant(GDScriptParser::ConstantNode *p_constant);
void resolve_assert(GDScriptParser::AssertNode *p_assert);
void resolve_match(GDScriptParser::MatchNode *p_match);
void resolve_match_branch(GDScriptParser::MatchBranchNode *p_match_branch, GDScriptParser::ExpressionNode *p_match_test);
void resolve_match_pattern(GDScriptParser::PatternNode *p_match_pattern, GDScriptParser::ExpressionNode *p_match_test);
void resolve_parameter(GDScriptParser::ParameterNode *p_parameter);
void resolve_return(GDScriptParser::ReturnNode *p_return);
// Reduction functions.

View file

@ -2022,10 +2022,10 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
for (int i = 0; i < p_func->parameters.size(); i++) {
const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
GDScriptDataType par_type = _gdtype_from_datatype(parameter->get_datatype(), p_script);
uint32_t par_addr = codegen.generator->add_parameter(parameter->identifier->name, parameter->default_value != nullptr, par_type);
uint32_t par_addr = codegen.generator->add_parameter(parameter->identifier->name, parameter->initializer != nullptr, par_type);
codegen.parameters[parameter->identifier->name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::FUNCTION_PARAMETER, par_addr, par_type);
if (p_func->parameters[i]->default_value != nullptr) {
if (p_func->parameters[i]->initializer != nullptr) {
optional_parameters++;
}
}
@ -2097,7 +2097,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
codegen.generator->start_parameters();
for (int i = p_func->parameters.size() - optional_parameters; i < p_func->parameters.size(); i++) {
const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, r_error, parameter->default_value);
GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, r_error, parameter->initializer);
if (r_error) {
memdelete(codegen.generator);
return nullptr;

View file

@ -683,37 +683,37 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
arghint += par->identifier->name.operator String() + ": " + par->get_datatype().to_string();
}
if (par->default_value) {
if (par->initializer) {
String def_val = "<unknown>";
switch (par->default_value->type) {
switch (par->initializer->type) {
case GDScriptParser::Node::LITERAL: {
const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(par->default_value);
const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(par->initializer);
def_val = literal->value.get_construct_string();
} break;
case GDScriptParser::Node::IDENTIFIER: {
const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(par->default_value);
const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(par->initializer);
def_val = id->name.operator String();
} break;
case GDScriptParser::Node::CALL: {
const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(par->default_value);
const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(par->initializer);
if (call->is_constant && call->reduced) {
def_val = call->function_name.operator String() + call->reduced_value.operator String();
}
} break;
case GDScriptParser::Node::ARRAY: {
const GDScriptParser::ArrayNode *arr = static_cast<const GDScriptParser::ArrayNode *>(par->default_value);
const GDScriptParser::ArrayNode *arr = static_cast<const GDScriptParser::ArrayNode *>(par->initializer);
if (arr->is_constant && arr->reduced) {
def_val = arr->reduced_value.operator String();
}
} break;
case GDScriptParser::Node::DICTIONARY: {
const GDScriptParser::DictionaryNode *dict = static_cast<const GDScriptParser::DictionaryNode *>(par->default_value);
const GDScriptParser::DictionaryNode *dict = static_cast<const GDScriptParser::DictionaryNode *>(par->initializer);
if (dict->is_constant && dict->reduced) {
def_val = dict->reduced_value.operator String();
}
} break;
case GDScriptParser::Node::SUBSCRIPT: {
const GDScriptParser::SubscriptNode *sub = static_cast<const GDScriptParser::SubscriptNode *>(par->default_value);
const GDScriptParser::SubscriptNode *sub = static_cast<const GDScriptParser::SubscriptNode *>(par->initializer);
if (sub->is_constant) {
if (sub->datatype.kind == GDScriptParser::DataType::ENUM) {
def_val = sub->get_datatype().to_string();
@ -1856,9 +1856,9 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
}
break;
case GDScriptParser::SuiteNode::Local::PARAMETER:
if (local.parameter->default_value) {
last_assign_line = local.parameter->default_value->end_line;
last_assigned_expression = local.parameter->default_value;
if (local.parameter->initializer) {
last_assign_line = local.parameter->initializer->end_line;
last_assigned_expression = local.parameter->initializer;
}
is_function_parameter = true;
break;
@ -1939,12 +1939,12 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) {
id_type = parameter->get_datatype();
}
if (parameter->default_value) {
if (parameter->initializer) {
GDScriptParser::CompletionContext c = p_context;
c.current_function = parent_function;
c.current_class = base_type.class_type;
c.base = nullptr;
if (_guess_expression_type(c, parameter->default_value, r_type)) {
if (_guess_expression_type(c, parameter->initializer, r_type)) {
return true;
}
}

View file

@ -1219,7 +1219,7 @@ GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() {
if (match(GDScriptTokenizer::Token::EQUAL)) {
// Default value.
parameter->default_value = parse_expression(false);
parameter->initializer = parse_expression(false);
}
complete_extents(parameter);
@ -1250,7 +1250,7 @@ GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
push_error("Expected signal parameter name.");
break;
}
if (parameter->default_value != nullptr) {
if (parameter->initializer != nullptr) {
push_error(R"(Signal parameters cannot have a default value.)");
}
if (signal->parameters_indices.has(parameter->identifier->name)) {
@ -1395,7 +1395,7 @@ void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNod
if (parameter == nullptr) {
break;
}
if (parameter->default_value != nullptr) {
if (parameter->initializer != nullptr) {
default_used = true;
} else {
if (default_used) {
@ -4776,9 +4776,9 @@ void GDScriptParser::TreePrinter::print_parameter(ParameterNode *p_parameter) {
push_text(" : ");
print_type(p_parameter->datatype_specifier);
}
if (p_parameter->default_value != nullptr) {
if (p_parameter->initializer != nullptr) {
push_text(" = ");
print_expression(p_parameter->default_value);
print_expression(p_parameter->initializer);
}
}

View file

@ -57,6 +57,7 @@ public:
struct AnnotationNode;
struct ArrayNode;
struct AssertNode;
struct AssignableNode;
struct AssignmentNode;
struct AwaitNode;
struct BinaryOpNode;
@ -354,6 +355,19 @@ public:
}
};
struct AssignableNode : public Node {
IdentifierNode *identifier = nullptr;
ExpressionNode *initializer = nullptr;
TypeNode *datatype_specifier = nullptr;
bool infer_datatype = false;
int usages = 0;
virtual ~AssignableNode() {}
protected:
AssignableNode() {}
};
struct AssignmentNode : public ExpressionNode {
// Assignment is not really an expression but it's easier to parse as if it were.
enum Operation {
@ -732,12 +746,7 @@ public:
}
};
struct ConstantNode : public Node {
IdentifierNode *identifier = nullptr;
ExpressionNode *initializer = nullptr;
TypeNode *datatype_specifier = nullptr;
bool infer_datatype = false;
int usages = 0;
struct ConstantNode : public AssignableNode {
#ifdef TOOLS_ENABLED
String doc_description;
#endif // TOOLS_ENABLED
@ -902,13 +911,7 @@ public:
}
};
struct ParameterNode : public Node {
IdentifierNode *identifier = nullptr;
ExpressionNode *default_value = nullptr;
TypeNode *datatype_specifier = nullptr;
bool infer_datatype = false;
int usages = 0;
struct ParameterNode : public AssignableNode {
ParameterNode() {
type = PARAMETER;
}
@ -1157,18 +1160,13 @@ public:
}
};
struct VariableNode : public Node {
struct VariableNode : public AssignableNode {
enum PropertyStyle {
PROP_NONE,
PROP_INLINE,
PROP_SETGET,
};
IdentifierNode *identifier = nullptr;
ExpressionNode *initializer = nullptr;
TypeNode *datatype_specifier = nullptr;
bool infer_datatype = false;
PropertyStyle property = PROP_NONE;
union {
FunctionNode *setter = nullptr;
@ -1184,7 +1182,6 @@ public:
bool onready = false;
PropertyInfo export_info;
int assignments = 0;
int usages = 0;
bool use_conversion_assign = false;
#ifdef TOOLS_ENABLED
String doc_description;

View file

@ -350,8 +350,8 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
if (parameter->get_datatype().is_hard_type()) {
parameters += ": " + parameter->get_datatype().to_string();
}
if (parameter->default_value != nullptr) {
parameters += " = " + parameter->default_value->reduced_value.to_json_string();
if (parameter->initializer != nullptr) {
parameters += " = " + parameter->initializer->reduced_value.to_json_string();
}
}
r_symbol.detail += parameters + ")";
@ -695,8 +695,8 @@ Dictionary ExtendGDScriptParser::dump_function_api(const GDScriptParser::Functio
Dictionary arg;
arg["name"] = p_func->parameters[i]->identifier->name;
arg["type"] = p_func->parameters[i]->get_datatype().to_string();
if (p_func->parameters[i]->default_value != nullptr) {
arg["default_value"] = p_func->parameters[i]->default_value->reduced_value;
if (p_func->parameters[i]->initializer != nullptr) {
arg["default_value"] = p_func->parameters[i]->initializer->reduced_value;
}
parameters.push_back(arg);
}

View file

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Value of type "MyOtherEnum (enum)" cannot be assigned to a variable of type "MyEnum (enum)".
Cannot assign a value of type MyOtherEnum (enum) to variable "class_var" with specified type MyEnum (enum).

View file

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Value of type "MyOtherEnum (enum)" cannot be assigned to a variable of type "MyEnum (enum)".
Cannot assign a value of type MyOtherEnum (enum) to variable "local_var" with specified type MyEnum (enum).

View file

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Value of type "int" cannot be assigned to a variable of type "String".
Cannot assign a value of type int to variable "x" with specified type String.

View file

@ -0,0 +1,6 @@
func check(arg: float = 3):
return typeof(arg) == typeof(3.0)
func test():
if check():
print('ok')

View file

@ -0,0 +1,2 @@
GDTEST_OK
ok

View file

@ -0,0 +1,32 @@
func check(input: int) -> bool:
return input == 1
var recur = null
var prop = null
func check_arg(arg = null) -> void:
if arg != null:
print(check(arg))
func check_recur() -> void:
if recur != null:
print(check(recur))
else:
recur = 1
check_recur()
func test() -> void:
check_arg(1)
check_recur()
if prop == null:
set('prop', 1)
print(check(prop))
set('prop', null)
var loop = null
while loop != 2:
if loop != null:
print(check(loop))
loop = 1 if loop == null else 2

View file

@ -0,0 +1,5 @@
GDTEST_OK
true
true
true
true

View file

@ -0,0 +1,5 @@
func test():
var bar = 1
var foo: float = bar
print(typeof(foo))
print(foo is float)

View file

@ -0,0 +1,3 @@
GDTEST_OK
3
true

View file

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Assigned value for constant "arr" has type Array[String] which is not compatible with defined type Array[int].
Cannot assign a value of type Array[String] to constant "arr" with specified type Array[int].