|
@@ -758,80 +758,8 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
|
|
switch (member.type) {
|
|
switch (member.type) {
|
|
case GDScriptParser::ClassNode::Member::VARIABLE: {
|
|
case GDScriptParser::ClassNode::Member::VARIABLE: {
|
|
check_class_member_name_conflict(p_class, member.variable->identifier->name, member.variable);
|
|
check_class_member_name_conflict(p_class, member.variable->identifier->name, member.variable);
|
|
-
|
|
|
|
member.variable->set_datatype(resolving_datatype);
|
|
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.
|
|
// Apply annotations.
|
|
for (GDScriptParser::AnnotationNode *&E : member.variable->annotations) {
|
|
for (GDScriptParser::AnnotationNode *&E : member.variable->annotations) {
|
|
@@ -840,56 +768,8 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
|
|
} break;
|
|
} break;
|
|
case GDScriptParser::ClassNode::Member::CONSTANT: {
|
|
case GDScriptParser::ClassNode::Member::CONSTANT: {
|
|
check_class_member_name_conflict(p_class, member.constant->identifier->name, member.constant);
|
|
check_class_member_name_conflict(p_class, member.constant->identifier->name, member.constant);
|
|
-
|
|
|
|
member.constant->set_datatype(resolving_datatype);
|
|
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.
|
|
// Apply annotations.
|
|
for (GDScriptParser::AnnotationNode *&E : member.constant->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;
|
|
break;
|
|
case GDScriptParser::Node::CONSTANT:
|
|
case GDScriptParser::Node::CONSTANT:
|
|
- resolve_constant(static_cast<GDScriptParser::ConstantNode *>(p_node));
|
|
|
|
|
|
+ resolve_constant(static_cast<GDScriptParser::ConstantNode *>(p_node), true);
|
|
break;
|
|
break;
|
|
case GDScriptParser::Node::FOR:
|
|
case GDScriptParser::Node::FOR:
|
|
resolve_for(static_cast<GDScriptParser::ForNode *>(p_node));
|
|
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));
|
|
resolve_suite(static_cast<GDScriptParser::SuiteNode *>(p_node));
|
|
break;
|
|
break;
|
|
case GDScriptParser::Node::VARIABLE:
|
|
case GDScriptParser::Node::VARIABLE:
|
|
- resolve_variable(static_cast<GDScriptParser::VariableNode *>(p_node));
|
|
|
|
|
|
+ resolve_variable(static_cast<GDScriptParser::VariableNode *>(p_node), true);
|
|
break;
|
|
break;
|
|
case GDScriptParser::Node::WHILE:
|
|
case GDScriptParser::Node::WHILE:
|
|
resolve_while(static_cast<GDScriptParser::WhileNode *>(p_node));
|
|
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");
|
|
is_shadowing(p_function->parameters[i]->identifier, "function parameter");
|
|
#endif // DEBUG_ENABLED
|
|
#endif // DEBUG_ENABLED
|
|
#ifdef TOOLS_ENABLED
|
|
#ifdef TOOLS_ENABLED
|
|
- if (p_function->parameters[i]->default_value) {
|
|
|
|
|
|
+ if (p_function->parameters[i]->initializer) {
|
|
default_value_count++;
|
|
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 {
|
|
} else {
|
|
p_function->default_arg_values.push_back(Variant()); // Prevent shift.
|
|
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) {
|
|
void GDScriptAnalyzer::resolve_if(GDScriptParser::IfNode *p_if) {
|
|
reduce_expression(p_if->condition);
|
|
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());
|
|
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) {
|
|
void GDScriptAnalyzer::resolve_assert(GDScriptParser::AssertNode *p_assert) {
|
|
reduce_expression(p_assert->condition);
|
|
reduce_expression(p_assert->condition);
|
|
if (p_assert->message != nullptr) {
|
|
if (p_assert->message != nullptr) {
|
|
@@ -1981,41 +1845,6 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
|
|
p_match_pattern->set_datatype(result);
|
|
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) {
|
|
void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
|
|
GDScriptParser::DataType result;
|
|
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;
|
|
r_static = p_is_constructor || found_function->is_static;
|
|
for (int i = 0; i < found_function->parameters.size(); i++) {
|
|
for (int i = 0; i < found_function->parameters.size(); i++) {
|
|
r_par_types.push_back(found_function->parameters[i]->get_datatype());
|
|
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++;
|
|
r_default_arg_count++;
|
|
}
|
|
}
|
|
}
|
|
}
|