diff options
author | Dmitrii Maganov <vonagam@gmail.com> | 2022-12-22 22:43:36 +0200 |
---|---|---|
committer | Dmitrii Maganov <vonagam@gmail.com> | 2023-01-06 01:10:04 +0200 |
commit | a1d06749f18c3f47c6443ece2ec625d8ee5f1761 (patch) | |
tree | 542a407dd9c60c76c3f2acf1a76f06cf44b1dfde /modules | |
parent | 1d14c054a12dacdc193b589e4afb0ef319ee2aae (diff) |
Unify typing of variables, constants and parameters in GDScript
Diffstat (limited to 'modules')
17 files changed, 237 insertions, 357 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 96c8894586..eb660ef09f 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -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++; } } diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 9af7264cb8..9ac33d674a 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -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. diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index dcbb3f7363..db32ef22b0 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -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; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 8027c41a8c..6f8b90bd06 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -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; } } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index cb1005a461..6107bb37c8 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -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); } } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index e0c8042162..65eace8088 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -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; diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index a96730b6ff..146ed10ceb 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -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); } diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out index b1710c798d..6fa2682d0a 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out @@ -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). diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out index b1710c798d..07fb19f1ff 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out @@ -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). diff --git a/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out index 9eb2a42ccd..2857cd53c8 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out @@ -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. diff --git a/modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.gd b/modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.gd new file mode 100644 index 0000000000..d0d04897e0 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.gd @@ -0,0 +1,6 @@ +func check(arg: float = 3): + return typeof(arg) == typeof(3.0) + +func test(): + if check(): + print('ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.out b/modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd b/modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd new file mode 100644 index 0000000000..5a413e2015 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd @@ -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 diff --git a/modules/gdscript/tests/scripts/analyzer/features/null_initializer.out b/modules/gdscript/tests/scripts/analyzer/features/null_initializer.out new file mode 100644 index 0000000000..f9783e4362 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/null_initializer.out @@ -0,0 +1,5 @@ +GDTEST_OK +true +true +true +true diff --git a/modules/gdscript/tests/scripts/analyzer/features/weak_initializer.gd b/modules/gdscript/tests/scripts/analyzer/features/weak_initializer.gd new file mode 100644 index 0000000000..c5f3ccc59e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/weak_initializer.gd @@ -0,0 +1,5 @@ +func test(): + var bar = 1 + var foo: float = bar + print(typeof(foo)) + print(foo is float) diff --git a/modules/gdscript/tests/scripts/analyzer/features/weak_initializer.out b/modules/gdscript/tests/scripts/analyzer/features/weak_initializer.out new file mode 100644 index 0000000000..5d798c1f24 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/weak_initializer.out @@ -0,0 +1,3 @@ +GDTEST_OK +3 +true diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out index 26b6e13d4f..ad2e6558d7 100644 --- a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out +++ b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out @@ -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]. |